Channels & 통신
Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 107개 중 60번째.
채널(Channels)은 고루틴(goroutine) 간의 통신을 위한 Go의 주요 메커니즘입니다. 메모리를 직접 공유하기보다, 고루틴은 채널을 통해 값을 주고받으며 "메모리를 공유하여 통신하지 말고, 통신하여 메모리를 공유하라"는 Go의 철학을 따릅니다.
make 함수와 chan 키워드를 사용하여 채널을 생성합니다:
messages := make(chan string)화살표 연산자 <-를 사용하여 값을 보내고 받습니다. 화살표는 데이터 흐름의 방향을 가리킵니다:
func main() {
messages := make(chan string)
go func() {
messages <- "hello" // 채널로 보내기
}()
msg := <-messages // 채널로부터 받기
fmt.Println(msg) // 출력: hello
}기본적으로, 송신과 수신은 상대방이 준비될 때까지 차단(block)됩니다. 고루틴(goroutine)이 "hello"를 보낼 때, main이 수신할 준비가 될 때까지 기다립니다. 이러한 차단 동작은 내장된 동기화를 제공하므로 time.Sleep이 필요하지 않습니다.
더 이상 값을 보내지 않겠다는 신호를 보내기 위해 채널을 닫을 수 있습니다. 수신자는 이를 감지할 수 있습니다:
close(messages)
msg, ok := <-messages
if !ok {
fmt.Println("Channel closed")
}채널은 타입이 지정되어 있습니다. chan string은 문자열만 전달할 수 있습니다. 이러한 타입 안전성은 고루틴이 올바른 데이터 타입으로 통신하도록 보장하며, 런타임이 아닌 컴파일 타임에 오류를 잡아냅니다.
챌린지
쉬움고루틴이 채널을 통해 어떻게 통신하는지 보여주는 메시지 중계 시스템을 구축해 보겠습니다. 각 단계가 동시에 실행되는, 메시지가 서로 다른 처리 단계 사이를 흐르는 파이프라인을 만들 것입니다.
코드는 두 개의 파일로 구성합니다:
relay.go: 메시지 처리 파이프라인을 정의합니다.ID(int)와Content(string) 필드를 가진Message구조체를 생성하세요.파이프라인의 각 단계를 나타내는 세 가지 함수를 구현하세요:
Producer(messages []Message, out chan Message)- 메시지 슬라이스를 받아 각 메시지를 출력 채널로 보냅니다. 모든 메시지를 보낸 후, 완료를 알리기 위해 채널을 닫습니다.Transformer(in chan Message, out chan Message)- 입력 채널에서 메시지를 받아 각 메시지의 내용을 대문자로 변환하고, 변환된 메시지를 출력 채널로 보냅니다. 입력 채널이 닫히면(두 개의 값을 반환하는 수신 형태를 사용하여 감지), 출력 채널을 닫습니다.Consumer(in chan Message) []string- 입력 채널에서 모든 메시지를 받아 포맷된 문자열 슬라이스로 수집합니다. 각 문자열은"Message %d: %s"형식을 따라야 합니다. 채널이 닫히면 슬라이스를 반환합니다.
main.go: 파이프라인을 설정하고 동시 메시지 흐름을 조율합니다.메시지 수를 읽은 다음, 각 메시지의 ID와 내용을 읽습니다. 메시지를 생성하고 세 개의 파이프라인 단계를 연결할 두 개의 채널을 설정하세요. Producer와 Transformer를 고루틴으로 실행하고, 메인 고루틴에서 Consumer를 실행하여 결과를 수집합니다. 각 결과를 별도의 줄에 출력하세요.
다음과 같은 입력이 제공됩니다:
- 1행: 메시지 수 (정수)
- 다음 행들: 각 메시지에 대해 두 줄 - 메시지 ID (정수), 그 다음 내용 (문자열)
예를 들어, 다음과 같이 주어지면:
2
1
hello world
2
go channels출력은 다음과 같아야 합니다:
Message 1: HELLO WORLD
Message 2: GO CHANNELS파이프라인은 다음과 같이 흐릅니다: Producer가 메시지를 보냄 → Transformer가 대문자로 변환 → Consumer가 결과를 수집하고 포맷팅함. 각 화살표는 동시 단계를 연결하는 채널을 나타냅니다. 대문자 변환을 위해 strings 패키지를 사용하세요.
치트 시트
채널은 고루틴 간의 통신을 위한 Go의 주요 메커니즘입니다. chan 키워드와 함께 make를 사용하여 채널을 생성합니다:
messages := make(chan string)화살표 연산자 <-를 사용하여 값을 보내고 받습니다. 화살표는 데이터 흐름의 방향을 가리킵니다:
messages <- "hello" // 채널로 보내기
msg := <-messages // 채널로부터 받기기본적으로, 송신과 수신은 상대방이 준비될 때까지 차단(block)되어 내장된 동기화 기능을 제공합니다:
func main() {
messages := make(chan string)
go func() {
messages <- "hello" // 채널로 보내기
}()
msg := <-messages // 채널로부터 받기
fmt.Println(msg) // 출력: hello
}더 이상 값을 보내지 않음을 알리려면 채널을 닫습니다. 수신측은 두 개의 값을 반환하는 수신 형태를 사용하여 채널이 닫혔는지 감지할 수 있습니다:
close(messages)
msg, ok := <-messages
if !ok {
fmt.Println("Channel closed")
}채널은 타입이 지정됩니다. chan string은 문자열만 전달할 수 있어 컴파일 타임 타입 안정성을 제공합니다.
직접 해보기
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// 메시지 수 읽기
nLine, _ := reader.ReadString('\n')
n, _ := strconv.Atoi(strings.TrimSpace(nLine))
// 메시지 읽기
messages := make([]Message, n)
for i := 0; i < n; i++ {
idLine, _ := reader.ReadString('\n')
id, _ := strconv.Atoi(strings.TrimSpace(idLine))
contentLine, _ := reader.ReadString('\n')
content := strings.TrimSpace(contentLine)
messages[i] = Message{ID: id, Content: content}
}
// TODO: 파이프라인 단계를 연결할 두 개의 채널 생성
// TODO: Producer를 고루틴으로 실행
// TODO: Transformer를 고루틴으로 실행
// TODO: 메인 고루틴에서 Consumer를 실행하고 결과 수집
// TODO: 각 결과를 별도의 줄에 출력
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8에러 처리와 OOP
error 인터페이스사용자 정의 에러 타입에러 래핑 (fmt.Errorf)센티넬 에러errors.Is()와 errors.As()Panic, Defer, Recover요약 - 파일 파서