𝗚𝗼 𝗖𝗼𝗻𝘁𝗲𝘅𝘁 𝗣𝗮𝗰𝗸𝗮𝗴𝗲
대부분의 Go 개발자들은 context.Context를 제대로 이해하지 못한 채 사용합니다. HTTP 핸들러, 데이터베이스 호출, SDK 메서드 등에서 이를 볼 수 있습니다. 많은 초보자들이 모든 곳에 context.Background()를 전달하곤 합니다.
이러한 실수는 문제를 일으킵니다. Lambda 함수가 타임아웃을 넘겨도 계속 대기 상태로 남아있을 수 있습니다. 사용자가 연결을 끊은 후에도 데이터베이스 쿼리가 계속 실행될 수 있습니다.
Context를 사용하면 코드 전체에 취소 신호와 데드라인(deadline)을 전달할 수 있습니다. 이는 다음 세 가지 질문에 답하는 데 도움을 줍니다.
• 이 작업이 계속 실행되어야 하는가? • 이 작업은 언제 종료되어야 하는가? • 이 호출과 함께 어떤 요청 데이터가 흐르는가?
Context 인터페이스에는 네 가지 메서드가 있습니다.
Deadline(): 컨텍스트가 취소될 시간을 반환합니다.Done(): 컨텍스트가 취소될 때 닫히는 채널을 반환합니다. 작업을 중단하기 위해select문에서 이를 사용하세요.Err(): 컨텍스트가 중단된 이유(DeadlineExceeded또는Canceled)를 반환합니다.Value(): Trace ID와 같은 요청 범위(request-scoped) 데이터를 가져옵니다.
Context는 트리 구조로 작동합니다. 부모 컨텍스트에서 시작하여 자식 컨텍스트를 생성합니다.
루트 컨텍스트(Root Contexts):
context.Background(): 프로그램 시작 시 사용합니다.context.TODO(): 리팩토링 중에 플레이스홀더로 사용합니다.
자식 컨텍스트(Child Contexts):
context.WithCancel(): 수동으로 작업을 중단할 수 있게 합니다.context.WithTimeout(): 특정 시간(duration)이 지나면 작업을 중단합니다.context.WithDeadline(): 특정 시각에 작업을 중단합니다.context.WithValue(): 사용자 ID와 같은 메타데이터를 전달합니다.
중요한 규칙: 항상 cancel 함수를 호출하세요. 자식 컨텍스트를 생성한 직후에 defer cancel()을 사용하세요. 이를 생략하면 메모리 누수(memory leak)가 발생할 수 있습니다.
권장 사항(Best Practices):
- 함수의 첫 번째 인자로 컨텍스트를 전달하세요.
- 구조체(struct)에 컨텍스트를 저장하지 마세요.
context.WithValue는 메타데이터 용도로만 사용하고, 데이터베이스 클라이언트와 같은 의존성(dependency)을 전달하는 용도로 사용하지 마세요.- 긴 루프 내부에서
ctx.Err()를 확인하여 조기에 종료하세요. - 키 충돌을 방지하기 위해 컨텍스트 키로 커스텀 타입을 사용하세요.
부모 컨텍스트가 취소되면 모든 자식 컨텍스트도 자동으로 취소됩니다. 이를 통해 시스템 전체에 걸친 일련의 작업 체인을 쉽게 중단할 수 있습니다.