Go에서 context.Context로 타임아웃 처리하기
문제 상황
외부 결제 API를 호출하는 서비스에서 간헐적으로 응답이 오지 않아 고루틴이 무한 대기하는 문제가 있었다. 모니터링 결과 특정 시간대에 고루틴 수가 계속 증가하는 패턴을 발견했다.
Context 기본 개념
Go 1.7부터 표준 라이브러리에 포함된 context 패키지는 요청 범위의 데이터, 취소 신호, 타임아웃을 전달하는 용도로 사용된다.
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
적용 코드
기존 코드를 context를 받도록 수정했다.
func callPaymentAPI(ctx context.Context, payload PaymentRequest) (*PaymentResponse, error) {
req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// 응답 처리
return parseResponse(resp)
}
호출부에서는 이렇게 사용한다.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
result, err := callPaymentAPI(ctx, req)
if err != nil {
if err == context.DeadlineExceeded {
log.Println("API timeout")
}
return err
}
결과
타임아웃 설정 후 고루틴 누수 문제가 해결되었다. context.DeadlineExceeded 에러를 별도로 처리해 모니터링 지표에도 추가했다. API 응답 시간이 임계값을 넘으면 알림이 오도록 설정해 장애를 사전에 감지할 수 있게 되었다.
데이터베이스 쿼리나 gRPC 호출 등 다른 I/O 작업에도 동일한 패턴을 적용했다.