
Go 언어를 사용하면서 가장 많이 접하게 되는 고민 중 하나는 바로 여러 작업을 동시에 처리하면서, **필요할 때 안전하게 중단(cancel)**하는 것입니다. 특히 사용자 인터럽트(Ctrl+C)나 타임아웃 등으로 인해 여러 goroutine을 정리해야 하는 경우, 단순한 로직만으로는 복잡도가 급격히 올라갑니다.
이럴 때 context
패키지를 잘 활용하면 고루틴 수십 개도 한 번의 cancel로 깔끔하게 멈추게 할 수 있습니다.
이번 글에서는 Go의 context.WithCancel
과 os.Interrupt
를 조합해 여러 작업을 동시에 실행하고, 안전하게 중단하는 실전 패턴을 알려드리겠습니다.
왜 Cancel Context가 필요한가?
고루틴은 기본적으로 병렬로 독립 실행되기 때문에, 외부에서 중단 신호를 보내지 않는 이상 계속 실행됩니다. 이 말은 곧, 여러 개의 고루틴을 띄워놓고 **중간에 취소(cancel)**하고 싶다면, 각 고루틴에 신호를 전달할 **공통 수단(context)**이 필요하다는 뜻입니다.
핵심 기술: context + os.Interrupt + waitGroup
이번 예제에서는 다음 기술을 사용합니다:
기술 | 설명 |
---|---|
context.WithCancel | 수동으로 cancel 신호 전달 |
os/signal.Notify | Ctrl+C 등 인터럽트 감지 |
sync.WaitGroup | 모든 고루틴 종료 대기 |
실전 예제: 여러 작업을 실행하고 Cancel Context로 안전하게 멈추기
package main
import (
"context"
"fmt"
"os"
"os/signal"
"sync"
"time"
)
func main() {
// Cancel 가능한 context 생성
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 인터럽트 감지 채널
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt)
// Ctrl+C 입력 시 cancel() 호출
go func() {
<-sigChan
fmt.Println("\n인터럽트 시그널 수신! 모든 작업을 취소합니다.")
cancel()
}()
var wg sync.WaitGroup
jobCount := 5
fmt.Println("여러 작업을 동시에 실행합니다.")
for i := 1; i <= jobCount; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
runJob(ctx, id)
}(i)
}
// 모든 작업이 끝날 때까지 대기
wg.Wait()
fmt.Println("모든 작업이 종료되었습니다.")
}
func runJob(ctx context.Context, id int) {
for {
select {
case <-ctx.Done():
fmt.Printf("작업 #%d 종료됨 (사유: %v)\n", id, ctx.Err())
return
default:
fmt.Printf("작업 #%d 진행 중...\n", id)
time.Sleep(1 * time.Second)
}
}
}
실행 결과 예시
- 프로그램을 실행하면
작업 #1 진행 중...
,작업 #2 진행 중...
등의 메시지가 5개 고루틴에서 동시에 출력됩니다. - 이 상태에서 Ctrl+C를 누르면, 인터럽트가 감지되고
cancel()
이 호출되며 모든 고루틴이 순차적으로 종료됩니다. - 마지막으로
"모든 작업이 종료되었습니다."
메시지가 출력됩니다.
확장 가능한 구조
이 패턴은 다음과 같이 확장 가능합니다:
context.WithTimeout()
또는WithDeadline()
을 써서 일정 시간 후 자동 중단select
내부에 네트워크 요청, 파일 처리 등 실질 작업 삽입runJob()
함수 안에서 필요한 리소스 정리 로직 추가 (예:defer conn.Close()
)
실무에서의 활용 예
- 웹 크롤러: 수십 개의 URL을 병렬로 크롤링하다가 중간에 중단
- 서버 작업자(worker pool): 작업 큐 처리 중 서버 종료 신호 감지
- 데이터 마이그레이션 도구: 복수의 테이블을 동시에 이전하다가 취소 조건 발생 시 중단
마무리
Go의 context.WithCancel
은 단순한 함수 취소를 넘어서, 여러 고루틴을 동시에 제어하고, 안전하게 종료할 수 있는 매우 강력한 도구입니다. 여기에 os/signal
을 결합하면 사용자 인터럽트에 대응할 수 있고, sync.WaitGroup
을 추가하면 전체 작업이 끝날 때까지 기다릴 수 있습니다.
작업이 많아질수록 복잡도는 올라가지만, 이 패턴만 잘 익혀두면 언제든 안정적이고 유연하게 작업을 제어할 수 있습니다.