Backend

Enterprise Go 시리즈 #4: Goroutine과 Channel 실전 활용

Enterprise Go 시리즈 #4: Goroutine과 Channel 실전 활용

다른 언어 경험자를 위한 매핑

  • Java: ExecutorService, CompletableFuture
  • Node.js: Worker Threads, Promise.all
  • Rust: tokio::spawn, mpsc

Goroutine의 실체

Java Thread vs Go Goroutine

특성Java ThreadGo Goroutine
초기 스택~1MB (OS 할당)~2KB (Go 런타임)
최대 스택고정동적 확장 (최대 1GB)
스케줄링OS 커널Go 런타임 (M:N)
생성 비용높음낮음
실용적 상한수천 개수십만 개

왜 제한이 필요한가?

Goroutine이 가볍다고 해서 무제한은 아닙니다.

실증적 근거:

  • 초기 스택 2KB + 디스크립터 ~400바이트 ≈ 약 2.4KB / goroutine
  • 100,000개 goroutine = 최소 240MB 메모리
  • 컨테이너 메모리 제한(512MB~2GB)에서 OOM 위험

실제 OOM 시나리오:

  1. 요청마다 goroutine 생성
  2. 외부 API 응답 지연으로 goroutine 대기
  3. 대기 중인 goroutine 누적
  4. 컨테이너 메모리 제한 도달 → 강제 종료

Java 개발자를 위한 패턴 매핑

ExecutorService → Worker Pool

JavaGo
Executors.newFixedThreadPool(10)Worker Pool 패턴
executor.submit(task)jobs <- task
Future.get()<-results

CompletableFuture.allOf → errgroup

Java와의 차이점:

  • CompletableFuture.allOf는 모든 완료를 기다림
  • errgroup은 첫 에러 시 나머지 취소 가능 (Context 연동)

Channel 설계 원칙

Buffered vs Unbuffered

타입Java 대응사용 시점
UnbufferedSynchronousQueue핸드셰이크, 동기 통신
BufferedArrayBlockingQueue백프레셔, 비동기

닫기 규칙

Java와의 차이:

  • Java: BlockingQueue는 보통 닫지 않음
  • Go: Producer가 close() 호출 (규칙)

동기화 도구 선택

Mutex vs Channel

상황JavaGo
공유 상태 보호synchronizedsync.Mutex
작업 분배BlockingQueueChannel
병렬 실행 후 수집CompletableFutureerrgroup

Race Detector

Go의 강력한 장점 중 하나입니다. Java의 ThreadSanitizer에 대응되나, 표준 도구로 내장되어 있습니다:

go test -race ./...
go build -race ./cmd/myapp

CI 필수 적용 권장 - 런타임 오버헤드가 있으나 테스트 환경에서는 문제없음


정리

개념Java 대응Go
스레드 풀ExecutorServiceWorker Pool + Channel
병렬 실행CompletableFutureerrgroup
공유 상태synchronizedsync.Mutex
메시지 전달BlockingQueueChannel
경쟁 상태 탐지ThreadSanitizer-race 플래그

다음 편 예고

5편: 데이터베이스 연동 패턴에서는 Spring의 @Transactional에 대응하는 Go의 트랜잭션 관리 전략을 다룹니다.


참고 자료

Share

Related Articles

Comments

이 블로그는 제가 알고 있는 것들을 잊지 않기 위해 기록하는 공간입니다.
직접 작성한 글도 있고, AI의 도움을 받아 정리한 글도 있습니다.
정확하지 않은 내용이 있을 수 있으니 참고용으로 봐주세요.

© 2026 Seogyu Kim