backend

Go 언어 goroutine과 동시성 기초: 병렬 처리의 기본 단위 이해하기

mirabo01 2026. 1. 16. 21:58

defer까지 이해했다면, 이제 Go를 선택하는 가장 큰 이유 중 하나인
동시성(concurrency) 을 다룰 차례다.

Go의 동시성은 복잡한 스레드 제어 대신
goroutinechannel이라는 비교적 단순한 개념을 중심으로 설계되어 있다.
문법은 간단하지만, 개념을 제대로 이해하지 않으면 예상치 못한 동작을 만들기 쉽다.

이 글에서는

  • goroutine이 무엇인지
  • 어떻게 실행되고 관리되는지
  • 실제로 써보면서 느끼는 특징

을 중심으로 정리한다.


goroutine이란 무엇인가

goroutine은 Go에서 제공하는 경량 스레드(lightweight thread) 다.

go doWork()
  • go 키워드를 붙이면 새로운 goroutine에서 함수가 실행된다
  • 함수 호출 자체는 즉시 반환된다

이 한 줄로 동시 실행이 가능해진다는 점이
Go 동시성 모델의 가장 큰 특징이다.

[이미지: goroutine 실행 개념 구조]


goroutine은 얼마나 가벼운가

운영체제 스레드와 비교했을 때 goroutine은 훨씬 가볍다.

  • 초기 스택 크기가 작다
  • 필요에 따라 스택이 자동으로 확장된다
  • 수천~수만 개도 현실적으로 생성 가능하다

실제로 써보면,
“이걸 스레드로 만들어도 되나?” 고민하던 지점에서
goroutine을 부담 없이 사용하게 된다.


goroutine의 실행 시점은 보장되지 않는다

go fmt.Println("hello")
fmt.Println("world")

출력 순서는 보장되지 않는다.

  • goroutine은 스케줄러에 의해 실행
  • 언제 실행될지는 알 수 없다

⚠️ 주의할 점
goroutine 실행 순서나 완료 시점을 가정하고 코드를 작성하면
불안정한 코드가 된다.

이 때문에 goroutine은
혼자서는 거의 의미가 없고,
반드시 동기화 수단과 함께 사용된다.


main 함수와 goroutine의 관계

func main() {
    go doWork()
}

이 코드는 대부분의 경우 아무 일도 하지 않고 종료된다.

  • main 함수가 종료되면 프로그램도 종료
  • 실행 중인 goroutine이 있어도 기다려주지 않는다

그래서 goroutine을 사용할 때는
작업 완료를 기다리는 구조가 반드시 필요하다.

[이미지: main 종료 시 goroutine 종료 흐름]


가장 단순한 동기화: time.Sleep

초기 학습 단계에서 흔히 보는 예제다.

go doWork()
time.Sleep(time.Second)
  • goroutine이 실행될 시간을 벌어준다
  • 학습용으로는 이해하기 쉽다

하지만 실무에서는 거의 사용하지 않는다.

⚠️ 주의할 점

  • 실행 환경에 따라 충분하지 않을 수 있다
  • 정확한 동기화 수단이 아니다

이 한계를 해결하기 위해
Go는 channel과 sync 패키지를 제공한다.


여러 goroutine 실행 예제

for i := 0; i < 3; i++ {
    go func(n int) {
        fmt.Println(n)
    }(i)
}
  • 반복문에서 goroutine을 만들 때는
    루프 변수 캡처에 주의해야 한다
  • 위 예제처럼 인자를 명시적으로 넘기는 게 안전하다

이 부분은 실제로 실수하기 가장 쉬운 포인트 중 하나다.

[이미지: goroutine과 반복문 변수 캡처 문제]


goroutine 설계 시 기본적인 기준

실무에서 goroutine을 사용할 때는
대략 다음 기준을 두는 편이다.

  • 독립적인 작업 단위인가?
  • 실행 순서에 의존하지 않는가?
  • 실패 시 전체 흐름에 어떤 영향을 주는가?

goroutine을 많이 쓴다고 해서
코드가 자동으로 빨라지거나 좋아지지는 않는다.
오히려 제어하지 않으면 복잡도가 급격히 올라간다.


정리

goroutine은
동시 실행을 쉽게 만들지만, 제어는 개발자의 책임이다.

  • go 키워드 하나로 실행 가능
  • 실행 시점과 순서는 보장되지 않는다
  • 반드시 동기화 수단과 함께 사용해야 한다

goroutine의 개념을 정확히 이해하고 나면,
다음 단계로 channel을 통한 통신과 동기화를 배우는 게 자연스럽다.
다음 글에서는 goroutine과 항상 함께 등장하는
channel의 기본 개념과 사용 패턴을 다뤄보자.