우선 간단하게 내용만 정리하고 나중에 뼈와 살을 보충 예정입니다.
요약
| 특징 | sync.WaitGroup | errgroup.Group |
| 목적 | 여러 고루틴이 완료될때까지 기다리기 | 여러 고루틴이 완료될때까지 기다리되, 오류 처리와 작업 취소를 통합 관리 |
| 오류 처리 | 내장 기능 없음. 오류를 전달하려면 별도의 채널(channel) 등을 직접 구현 필요 |
내장 기능 있음. Wait() 메서드가 가장 먼저 발생한 error를 반환 |
| 작업 취소 | 내장 기능 없음. context.Context를 직접 전달하고 관리해야함. |
errgroup.WithContext를 통해 자동 취소 기능 제공 |
| 코드 복잡성 | 단순하지만, 오류/취소 처리를 추가하면 코드가 복잡해짐 | 기능이 많지만, 오히려 코드는 더 간결하고 명확해짐 |
| 패키지 위치 | sync | golang.org/x/sync/errgroup |
sync.WaitGroup
WaitGroup은 고루틴의 완료 시점을 동기화 하기 위한 가장 기본적인 도구. (공식문서)
- Add(delta int): 기다려야할 고루틴의 수를 더함
- Done(): 고루틴 하나의 작업이 끝났음을 알림. 카운터를 1 감소 시킴
- Wait(): 카운터가 0이 될때까지 대기
장점
- 가볍고 단순: 표준 라이브러리에 포함되어 있으며, 오직 "작업 완료 대기"라는 한 가지 기능에만 집중하여 매우 가벼움
- 유연성: 반환값이 없는 "Fire-and-Forget" 스타일의 작업을 병렬 처리하고 완료 여부만 확인할 때 매우 유용
단점
- 오류 처리의 부재: 각 고루틴에서 발생한 에러를 수집하려면, 에러를 수신할 채널을 만들고, 각 고루틴에서 에러 발생 시 채널에 보내는 등의 코드를 직접 작성해야 함
- 작업 취소의 부재: 한 고루틴이 실패했을 때 다른 고루틴을 중단 시키려면, context.WithCancel 을 생성하고 모든 고루틴에 Context를 전달하며, 각 고루틴 내에서 ctx.Done()을 체크하는 로직을 일일히 구현해야 함.
활용 예시
- 여러 고루틴의 성공/실패 여부가 중요하지 않고, 단순히 모든 작업이 끝나기만 하면 되는 경우.
ex) 여러개의 캐시를 동시에 초기화(warm-up)하는 작업 - 에러가 발생할 일이 없는 명확한 작업을 병렬 실행할 때
errgroup.Group
errgroup은 WaitGroup의 기본 기능에 오류 전파(Error Propagation)와 컨텍스트 기반 취소(Context Cancellation)을 결합한 패키지. (공식문서)
- Go(f func() error): 인자로 받은 함수를 고루틴으로 실행. 이 함수는 반드시 error를 반환해야 한다!
- Wait() error: 모든 고루틴이 끝날 때까지 대기. 만약 실행된 함수 중 하나라도 nil이 아닌 error를 반환했다면, Wait는 그 중 가장 먼저 발생한 에러를 반환
장점
- 내장된 오류 처리: 여러 고루틴 중 하나라도 실패하면 그 에러를 즉시 상위 호출자로 전달할 수 있어, 오류 처리가 깔끔하고 직관적
- 간결한 작업 취소: errgroup.WithContext(ctx)를 사용하면, 한 고루틴이 에러를 반환하는 즉시 그룹에 연결된 Context가 취소됨. 다른 고루틴들은 이 Context의 취소 신호를 받아 작업을 조기에 중단할 수 있으므로, 불필요한 리소스 낭비를 막을 수 있음
- 코드 가독성 및 유지보수성 향상: 오류/취소 처리를 위한 bolier plate 코드가 사라져, 비즈니스 로직에 집중 가능
단점
- 첫번째 에러만 반환: 설계 상 가장 먼저 발생한 에러 하나만 반환함. 만약 모든 고루틴에서 발생한 에러를 전부 수집해야 한다면, errgroup은 적합하지 않으며 별도의 에러 수집 로직이 필요
활용 예시
- 여러 개의 독립적인 작업 중 하나라도 실패하면 전체 작업이 실패로 간주되는 경우 (= 실패 즉시 나머지 작업을 중단시켜 리소스를 아끼고 싶을 때)
ex) MSA 환경에서 여러 API를 동시에 호출하여 데이터를 조회할 때, 하나의 요청을 처리하기 위해 여러 개의 DB쿼리를 병렬로 실행할 때 - 유지보수하기 좋은 동시성 코드를 작성하고 싶을 때
'Language > Go' 카테고리의 다른 글
| [Go] Context에 관한 고찰 - 3 : Graceful Shutdown의 미학 (0) | 2025.07.28 |
|---|---|
| [Go] Context에 관한 고찰 - 2 : Context의 중요성과 메모리 누수 (0) | 2025.07.24 |
| [Go] Context에 관한 고찰 - 1 (0) | 2025.07.11 |
| [Go] field 이름을 기준으로 field 초기화하기 with reflect (0) | 2024.11.08 |
| [Go] Decimal decoder : Decimal을 MongoDB에 바로 String으로 저장 (1) | 2024.06.10 |