고급 Go 동시성: 실무 문제 해결을 위한 채널 패턴
카테고리
프로그래밍/소프트웨어 개발
서브카테고리
개발 툴
대상자
- 대상: Go 언어 경험 1-2년 이상, goroutine 및 channel 기초 이해한 개발자
- 난이도: 중간~고급 (고급 패턴 및 실무 사례 중심)
핵심 요약
- 채널 유형:
unbuffered
(동기화),buffered
(비동기화)로 데이터 흐름 제어 - 핵심 패턴:
rate limiting
(토큰 풀),producer-consumer
(작업 분리),fan-out/fan-in
(병렬 처리) - 실무 고려사항:
context
로 goroutine 종료,pprof
로 메모리 누수 분석,close()
로 채널 종료 신호 전달
섹션별 세부 요약
1. 채널 기초 및 유형
unbuffered
채널: 송신자와 수신자가 동기화됨 (예:ch <- data
와data := <-ch
동시에 실행)buffered
채널: 버퍼 크기 제한으로 데이터 쌓임 (예:make(chan int, 10)
생성 시 10개까지 저장)Mutex
대비 장점: 동시성 안전한 데이터 흐름, 락 없는 설계 가능
2. Rate Limiting 패턴
- 문제: API 호출 과도로 DB/Redis 과부하
- 해결: 토큰 풀 채널(
make(chan struct{}, 5)
) 사용 (예: 5개 토큰 제한) - 코드 예시:
tokens := make(chan struct{}, 5)
for i := 1; i <= 10; i++ {
tokens <- struct{}{}
// 작업 수행
<-tokens
}
3. Producer-Consumer 패턴
- 문제: 로그 처리/다운로드 작업 병렬화 시 작업 분리 필요
- 해결:
unbuffered
채널로 생산자-소비자 분리 (예:tasks
채널로 작업 전달) - 코드 예시:
tasks := make(chan int)
for i := 1; i <= 3; i++ {
go func(id int) {
for task := range tasks {
fmt.Printf("Consumer %d: task %d\n", id, task)
}
}(i)
}
close(tasks)
누락 시 소비자 데드락 발생 (예: pprof
로 감지)4. Fan-out/Fan-in 패턴
- 문제: 병렬 API 호출 시 결과 수집 필요
- 해결:
fan-out
(작업 분산) +fan-in
(결과 집합) 패턴 - 코드 예시:
results := make(chan string, 10)
for i := 1; i <= 3; i++ {
go func(id int) {
for task := range tasks {
results <- fmt.Sprintf("Worker %d: task %d", id, task)
}
}(i)
}
context
사용 필수 (예: context.Done()
로 goroutine 종료)5. 실무 팁 및 오류 사례
- 메모리 누수:
close()
누락 시runtime.NumGoroutine()
으로 감지,pprof
로 분석 - 데드락:
go vet
로 채널 닫기 누락 확인 - 버퍼 과다: 1000개 이상 버퍼 시 메모리 사용량 급증 (예: 10개로 조정)
- 상황별 채널 선택: 동기화 →
unbuffered
, 비동기화 →buffered
결론
- 실무 권장:
context
로 goroutine 정리,pprof
로 성능 모니터링,close()
로 채널 종료 신호 전달 - 예시: 토큰 풀(5개) +
context
사용 시 API 호출 제한,fan-in
으로 병렬 작업 결과 수집 - 핵심: 채널은 동시성 제어의 핵심 도구로, 패턴에 따라 복잡한 문제 해결 가능