ksundev 님의 블로그

Channels 2 - chan 타입 바꾸고 반복문 활용하기 본문

[개발] Go/중급 프로젝트

Channels 2 - chan 타입 바꾸고 반복문 활용하기

ksundev 2025. 7. 14. 14:02

Channels 2 - chan 타입 바꾸고 반복문 활용하기

1. 코드 분석

현재 우리가 살펴볼 코드는 문자열 타입 채널과 반복문을 활용한 개선된 예제입니다:

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan string)
    people := [4]string{"KSun", "Peter", "John", "Chris"}
    for _, person := range people {
        go isReady(person, c)
    }
    for i := 0; i < len(people); i++ {
        fmt.Println("Received:", <-c)
    }
}

func isReady(person string, c chan string) {
    time.Sleep(time.Second * 5)
    c <- person + " is ready"
}

2. 이전 코드와의 차이점

2.1 채널 타입 변경

// 이전: bool 타입 채널
c := make(chan bool)

// 현재: string 타입 채널
c := make(chan string)

2.2 더 풍부한 정보 전달

// 이전: 단순한 완료 신호
c <- true

// 현재: 구체적인 메시지 전달
c <- person + " is ready"

2.3 동적 메시지 수신

// 이전: 하드코딩된 2번의 수신
fmt.Println("Received:", <-c)
fmt.Println("Received:", <-c)

// 현재: 배열 길이에 따른 동적 수신
for i := 0; i < len(people); i++ {
    fmt.Println("Received:", <-c)
}

3. 코드 동작 과정

3.1 채널 생성

c := make(chan string)
  • string 타입의 언버퍼드 채널을 생성합니다
  • 이제 문자열 메시지를 주고받을 수 있습니다

3.2 고루틴 시작

people := [4]string{"KSun", "Peter", "John", "Chris"}
for _, person := range people {
    go isReady(person, c)
}
  • 4개의 고루틴이 동시에 시작됩니다
  • 각 고루틴은 서로 다른 사람의 이름을 받습니다

3.3 고루틴 함수 분석

func isReady(person string, c chan string) {
    time.Sleep(time.Second * 5)  // 5초 대기
    c <- person + " is ready"    // 개인화된 메시지 전송
}
  • 각 고루틴은 5초 동안 대기한 후
  • 해당 사람의 이름을 포함한 개인화된 메시지를 채널에 전송합니다

3.4 메인 고루틴에서 메시지 수신

for i := 0; i < len(people); i++ {
    fmt.Println("Received:", <-c)
}
  • len(people)만큼 반복하여 모든 고루틴의 완료를 기다립니다
  • 배열 크기가 변경되어도 자동으로 모든 메시지를 수신합니다

4. 실행 결과

이 코드를 실행하면 다음과 같은 결과가 나옵니다:

Received: KSun is ready
Received: John is ready
Received: Chris is ready
Received: Peter is ready

순서는 매번 다를 수 있습니다! 고루틴이 동시에 실행되므로 완료 순서는 실행할 때마다 달라집니다.

5. 핵심 개선사항

5.1 유연한 인원 조정

// 3명으로 변경
people := [3]string{"KSun", "Peter", "John"}

// 5명으로 변경
people := [5]string{"KSun", "Peter", "John", "Chris", "Alice"}

// 10명으로 변경
people := [10]string{"KSun", "Peter", "John", "Chris", "Alice", "Bob", "Eve", "Frank", "Grace", "Henry"}

코드를 수정하지 않고도 배열만 변경하면 원하는 만큼의 사람을 처리할 수 있습니다!

5.2 자동 동기화

  • 배열 크기가 변경되어도 자동으로 모든 고루틴의 완료를 기다립니다
  • 누락된 메시지나 추가 수신 없이 정확히 처리됩니다

6. 성능 특성

6.1 병렬 처리

  • 4명의 사람이 각각 5초씩 작업하지만
  • 총 실행 시간은 여전히 5초입니다 (병렬 실행)

6.2 메모리 효율성

  • 하나의 채널로 모든 고루틴과 통신
  • 각 고루틴은 독립적으로 실행되며 메모리를 효율적으로 사용

7. 실제 활용 시나리오

이 패턴은 다음과 같은 상황에서 유용합니다:

7.1 웹 요청 처리

// 여러 API 엔드포인트에서 동시에 데이터 수집
urls := []string{"api1.com", "api2.com", "api3.com", "api4.com"}
for _, url := range urls {
    go fetchData(url, resultChannel)
}

7.2 파일 처리

// 여러 파일을 동시에 처리
files := []string{"file1.txt", "file2.txt", "file3.txt", "file4.txt"}
for _, file := range files {
    go processFile(file, resultChannel)
}

7.3 데이터베이스 쿼리

// 여러 테이블에서 동시에 데이터 조회
tables := []string{"users", "orders", "products", "categories"}
for _, table := range tables {
    go queryTable(table, resultChannel)
}

8. 주의사항과 모범 사례

8.1 고루틴 수 제한

  • 너무 많은 고루틴을 동시에 실행하면 시스템 리소스 부족 가능성
  • 실제 프로덕션에서는 워커 풀 패턴 사용 권장

8.2 에러 처리

  • 현재 코드는 에러 처리가 없습니다
  • 실제 사용 시에는 각 고루틴에서 발생할 수 있는 에러를 처리해야 합니다

8.3 컨텍스트 사용

  • 긴 작업의 경우 context.Context를 사용하여 취소 기능 추가 권장

9. 결론

이 예제를 통해 Go 언어의 채널과 고루틴을 사용한 동시성 프로그래밍의 핵심 개념을 배웠습니다:

  1. 채널 타입 변경: bool에서 string으로 변경하여 더 풍부한 정보 전달
  2. 반복문 활용: 하드코딩된 수신에서 동적 수신으로 개선
  3. 병렬 성능: 동시 실행으로 전체 처리 시간 단축
  4. 확장성: 코드 수정 없이 처리할 작업 수 조정 가능

이 패턴은 Go 언어의 "CSP(Communicating Sequential Processes)" 모델을 잘 보여주며, 실제 프로젝트에서 자주 사용되는 중요한 패턴입니다.