Go에서 context를 활용한 타임아웃 처리

문제 상황

결제 시스템에서 외부 PG사 API를 호출하는 부분에서 간헐적으로 응답이 지연되면서 고루틴이 누적되는 현상이 발생했다. 타임아웃 처리가 없어서 최악의 경우 수십 초간 대기하다가 전체 서비스가 느려지는 문제였다.

해결 방법

Go의 context 패키지를 사용해 타임아웃을 적용했다.

func callExternalAPI(url string) (*Response, error) {
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()

    req, err := http.NewRequestWithContext(ctx, "POST", url, body)
    if err != nil {
        return nil, err
    }

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("API call failed: %w", err)
    }
    defer resp.Body.Close()

    return parseResponse(resp)
}

WithTimeout을 사용해 3초 제한을 두었고, NewRequestWithContext로 요청에 context를 전달했다. 타임아웃이 발생하면 context deadline exceeded 에러가 반환된다.

추가 개선

단순 타임아웃 외에도 circuit breaker 패턴 도입을 검토 중이다. 연속된 실패 시 일정 시간 요청을 차단해서 외부 서비스 장애 전파를 막는 방식인데, go-hystrix 같은 라이브러리를 살펴보고 있다.

context는 취소, 데드라인, 값 전달 등 다양하게 활용할 수 있어서 Go 개발 시 필수적인 패턴이라는 걸 다시 확인했다.

Go에서 context를 활용한 타임아웃 처리