
Go(Golang)는 병행 처리(concurrency)에 강력한 기능을 제공하는 언어입니다. 특히 context
패키지를 활용하면 작업 중 취소(cancel), 타임아웃(timeout), 마감 기한(deadline) 등을 깔끔하게 제어할 수 있습니다. 이번 글에서는 Go에서 흔히 쓰이는 패턴 중 하나인 “Cancel Context with Interrupt”, 즉 운영체제의 인터럽트 신호(Ctrl+C 등)를 감지하여 context를 취소하는 방법에 대해 설명드리겠습니다.
왜 Context와 Interrupt를 함께 써야 할까?
운영 환경에서 Go 애플리케이션은 다음과 같은 상황에 직면할 수 있습니다:
- 사용자가
Ctrl+C
를 눌러 서버를 종료하려는 경우 - 쿠버네티스(kubernetes)가 Pod를 종료하는 시그널을 보낸 경우
- 작업 도중에 타임아웃이 발생해 중단해야 하는 경우
이럴 때, 단순히 종료하는 것이 아니라 작업 중이던 goroutine이나 리소스를 안전하게 정리하고 싶다면 context
와 os/signal
을 함께 사용하는 패턴이 유용합니다.
기본 구조: context + os.Interrupt
Go에서는 os/signal
패키지를 통해 운영체제의 인터럽트 시그널을 감지할 수 있고, context.WithCancel()
을 통해 수동으로 취소 가능한 컨텍스트를 만들 수 있습니다.
ctx, cancel := context.WithCancel(context.Background())
인터럽트가 들어오면 cancel()
함수를 호출하여 context를 종료할 수 있게 됩니다.
실제 코드 예제
package main
import (
"context"
"fmt"
"os"
"os/signal"
"time"
)
func main() {
// 취소 가능한 context 생성
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 운영체제 시그널 수신 설정
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt)
// 인터럽트 시 cancel() 실행
go func() {
<-sigChan
fmt.Println("인터럽트 시그널 수신! 종료합니다.")
cancel()
}()
// 백그라운드 작업 시작
go doWork(ctx)
// context가 종료될 때까지 대기
<-ctx.Done()
fmt.Println("메인 함수 종료")
}
func doWork(ctx context.Context) {
fmt.Println("작업을 시작합니다.")
for {
select {
case <-ctx.Done():
fmt.Println("작업이 취소되었습니다:", ctx.Err())
return
default:
fmt.Println("작업 진행 중...")
time.Sleep(1 * time.Second)
}
}
}
실행 흐름
- 프로그램 실행
doWork
goroutine이 무한 루프 작업 수행Ctrl+C
입력 시os.Interrupt
시그널 감지 →cancel()
실행- context 종료 →
doWork
루틴이ctx.Done()
을 감지하고 종료 - 메인 함수도
<-ctx.Done()
에 의해 종료
이 패턴을 써야 하는 이유
상황 | context + interrupt가 필요한 이유 |
---|---|
CLI 도구 또는 서버 종료 | 안전하게 리소스를 해제하고 로그를 남기기 위함 |
외부 API 호출 또는 DB 트랜잭션 | 취소 가능한 요청을 통해 자원 낭비를 줄이기 위함 |
병렬 작업 중 취소 필요 | 전체 작업 중 하나라도 실패하면 나머지도 취소 가능 |
WithTimeout, WithDeadline과 함께 쓰기
context.WithCancel()
외에도 context.WithTimeout()
을 함께 사용하면 인터럽트와 타임아웃 두 가지 상황 모두에 대응할 수 있습니다:
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
마무리
Go에서 Cancel Context with Interrupt
는 매우 강력하고 실용적인 패턴입니다. 특히 시스템 종료나 유저 인터럽트에 반응해서 안정적인 종료 처리를 할 수 있는 점은 서버 개발이나 CLI 도구 등 다양한 영역에서 필수적인 기능입니다.
이 패턴을 익혀두시면 goroutine 처리, 자원 정리, 사용자 경험 모두 향상될 수 있습니다.