
Go 언어는 context.WithCancel
이나 context.WithTimeout
을 통해 손쉽게 작업 취소와 타임아웃 처리를 할 수 있는 강력한 기능을 제공합니다. 하지만, 간단한 프로그램이나 초경량 CLI 도구를 개발할 때는 오히려 context
를 사용하는 것이 과도한 설계처럼 느껴질 수도 있습니다.
이번 글에서는 context
를 사용하지 않고도 어떻게 Cancel + Timeout + Interrupt(인터럽트 시그널 처리)를 구현할 수 있는지, 실제 코드 예제와 함께 자세히 설명드리겠습니다.
Cancel Context with Interrupt란?
context.WithCancel()
과 os/signal.Notify()
를 조합하면, 프로그램이 Ctrl+C
같은 운영체제의 인터럽트 시그널을 받을 때 자동으로 작업을 중단(cancel)하도록 구성할 수 있습니다.
하지만, 여기서는 이 편리한 context
없이도 똑같은 기능을 구현하는 방식에 집중합니다.
핵심 원리: select + 채널
Go에서는 select
문을 활용해 여러 채널의 이벤트를 동시에 감시할 수 있습니다. 이를 이용해 다음 세 가지 상황을 감지합니다:
- 작업이 완료되었을 때
- 타임아웃이 발생했을 때
- 사용자가 Ctrl+C를 눌렀을 때
실제 예제: context 없이 Cancel Context with Interrupt 구현하기
package main
import (
"fmt"
"os"
"os/signal"
"time"
)
func main() {
// 사용자 인터럽트(Ctrl+C)를 수신할 채널
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt)
// 타임아웃 설정 (예: 10초 후 자동 취소)
timeout := time.After(10 * time.Second)
// 작업 완료 여부를 확인할 채널
done := make(chan struct{})
// 실제 작업 실행
go func() {
fmt.Println("작업을 시작합니다...")
// 예시: 30초 걸리는 작업 (하지만 타임아웃 or 인터럽트가 먼저 올 수 있음)
time.Sleep(30 * time.Second)
done <- struct{}{}
}()
// 이벤트 감시
select {
case <-done:
fmt.Println("작업이 정상적으로 완료되었습니다.")
case <-timeout:
fmt.Println("타임아웃 발생! 작업을 강제 종료합니다.")
case <-sigChan:
fmt.Println("사용자 인터럽트 감지! 작업을 중단합니다.")
}
}
실행 결과
- 10초 이내에 Ctrl+C를 누르면
사용자 인터럽트 감지!
메시지 출력 - 10초 경과 시 자동으로
타임아웃 발생!
출력 - 30초 기다리면
작업이 정상적으로 완료되었습니다.
출력 (단, 인터럽트나 타임아웃이 먼저 발생하지 않은 경우)
장점과 단점
✅ 장점
context
에 대한 의존이 없어서 더 간결하고 가벼운 코드- Go 채널과 select 문만으로도 직관적인 흐름 제어 가능
- 간단한 CLI 도구, 테스트 스크립트, 초기 프로토타입에 적합
❌ 단점
- 여러 goroutine 간 취소 신호 전파가 어려움
- 복잡한 로직에는 확장성이 떨어짐
- 외부 라이브러리(API, DB 등)는
context
를 요구하는 경우가 많음
응용 팁
이 방식은 다음과 같은 상황에 유용합니다:
- 간단한 파일 처리, 파싱 스크립트, 로컬 유틸리티
- 무거운 구조 없이 인터럽트 처리를 해야 할 때
- 리소스 정리가 단순하거나 필요 없는 경우
하지만, 웹 서버, 데이터베이스 연결, API 요청과 같이 중첩된 취소가 필요한 구조에서는 반드시 context
사용을 고려해야 합니다.
마무리
Go의 context
는 분명히 강력한 도구지만, 모든 상황에 필수는 아닙니다. 오히려 단순한 프로그램에서는 select
, time.After()
, os/signal
만으로도 충분히 취소와 타임아웃을 제어할 수 있습니다.
개발의 핵심은 “지금 필요한 만큼만 설계하자”는 것입니다. 필요한 순간에 context
를 도입하고, 그 전까지는 채널만으로도 충분히 깔끔한 로직을 만들 수 있다는 점, 꼭 기억해두시길 바랍니다.