Go 언어 예제 100문항

메타데이터


🎯 학습 목표

  • Go 언어 기초 문법을 실습으로 체화
  • 파이썬 경험자가 빠르게 Go 패턴 익히기
  • MIT 6.824 Lab 준비를 위한 핵심 개념 마스터

📚 문제 구성

  • Level 1 (1-30): 기초 문법
  • Level 2 (31-60): 함수, 구조체, 인터페이스
  • Level 3 (61-85): 고루틴, 채널
  • Level 4 (86-100): 네트워크, 실전 응용

Level 1: 기초 문법 (1-30)

1-5: 변수와 타입

// 1. 정수형 변수 선언하고 값 할당
package main
import "fmt"
func main() {
    // 여기에 코드 작성
}

문제 1: age 변수를 선언하고 25를 할당한 후 출력하세요.

// 답안
var age int = 25
fmt.Println(age)

문제 2: 짧은 선언문으로 name에 “Alice” 할당하고 출력하세요.

// 답안
name := "Alice"
fmt.Println(name)

문제 3: float64 타입으로 원주율 값(3.14159)을 저장하고 출력하세요.

// 답안
var pi float64 = 3.14159
fmt.Println(pi)

문제 4: 여러 변수를 한 번에 선언하세요. (이름, 나이, 키)

// 답안
var name, age, height = "Bob", 30, 175.5
fmt.Println(name, age, height)

문제 5: 상수를 선언하고 사용하세요. (원의 반지름 5)

// 답안
const radius = 5
area := 3.14 * radius * radius
fmt.Println("원의 넓이:", area)

6-10: 조건문과 반복문

문제 6: 숫자가 짝수인지 홀수인지 판별하세요.

// 답안
num := 7
if num%2 == 0 {
    fmt.Println("짝수")
} else {
    fmt.Println("홀수")
}

문제 7: 1부터 10까지 for문으로 출력하세요.

// 답안
for i := 1; i <= 10; i++ {
    fmt.Println(i)
}

문제 8: switch문으로 요일 판별하세요.

// 답안
day := 3
switch day {
case 1:
    fmt.Println("월요일")
case 2:
    fmt.Println("화요일")
case 3:
    fmt.Println("수요일")
default:
    fmt.Println("기타")
}

문제 9: while 대신 for문으로 무한루프 만들고 조건으로 빠져나오기

// 답안
count := 0
for {
    count++
    if count > 5 {
        break
    }
    fmt.Println(count)
}

문제 10: range를 사용해서 문자열의 각 문자 출력하기

// 답안
text := "Hello"
for i, char := range text {
    fmt.Printf("%d: %c\n", i, char)
}

11-20: 배열과 슬라이스

문제 11: 길이 5인 정수 배열 선언하고 초기화하세요.

// 답안
var numbers [5]int = [5]int{1, 2, 3, 4, 5}
fmt.Println(numbers)

문제 12: 슬라이스 만들고 요소 추가하기

// 답안
fruits := []string{"apple", "banana"}
fruits = append(fruits, "orange")
fmt.Println(fruits)

문제 13: 슬라이스에서 특정 범위 잘라내기

// 답안
nums := []int{1, 2, 3, 4, 5, 6}
slice := nums[2:5]  // [3, 4, 5]
fmt.Println(slice)

문제 14: 슬라이스 길이와 용량 출력하기

// 답안
s := make([]int, 5, 10)
fmt.Printf("길이: %d, 용량: %d\n", len(s), cap(s))

문제 15: 2차원 슬라이스 만들기 (3x3 행렬)

// 답안
matrix := make([][]int, 3)
for i := range matrix {
    matrix[i] = make([]int, 3)
}
fmt.Println(matrix)

문제 16: 슬라이스에서 최대값 찾기

// 답안
nums := []int{3, 7, 2, 9, 1}
max := nums[0]
for _, num := range nums {
    if num > max {
        max = num
    }
}
fmt.Println("최대값:", max)

문제 17: 슬라이스 정렬하기 (sort 패키지 사용)

// 답안
import "sort"
nums := []int{3, 1, 4, 1, 5}
sort.Ints(nums)
fmt.Println(nums)

문제 18: 문자열 슬라이스에서 특정 문자열 찾기

// 답안
names := []string{"Alice", "Bob", "Charlie"}
target := "Bob"
for i, name := range names {
    if name == target {
        fmt.Printf("%s found at index %d\n", target, i)
        break
    }
}

문제 19: 슬라이스 복사하기

// 답안
original := []int{1, 2, 3}
copied := make([]int, len(original))
copy(copied, original)
fmt.Println("원본:", original, "복사:", copied)

문제 20: 슬라이스에서 중복 제거하기

// 답안
nums := []int{1, 2, 2, 3, 3, 3, 4}
unique := []int{}
for _, num := range nums {
    found := false
    for _, u := range unique {
        if u == num {
            found = true
            break
        }
    }
    if !found {
        unique = append(unique, num)
    }
}
fmt.Println(unique)

21-30: 맵(Map)

문제 21: 맵 선언하고 값 할당하기

// 답안
scores := make(map[string]int)
scores["Alice"] = 95
scores["Bob"] = 87
fmt.Println(scores)

문제 22: 맵 리터럴로 초기화하기

// 답안
colors := map[string]string{
    "red":   "#FF0000",
    "green": "#00FF00", 
    "blue":  "#0000FF",
}
fmt.Println(colors)

문제 23: 맵에서 키 존재 확인하기

// 답안
scores := map[string]int{"Alice": 95, "Bob": 87}
if score, exists := scores["Charlie"]; exists {
    fmt.Println("Charlie의 점수:", score)
} else {
    fmt.Println("Charlie의 점수를 찾을 수 없습니다")
}

문제 24: 맵 순회하기

// 답안
ages := map[string]int{"Alice": 25, "Bob": 30, "Charlie": 35}
for name, age := range ages {
    fmt.Printf("%s%d살입니다\n", name, age)
}

문제 25: 맵에서 키 삭제하기

// 답안
m := map[string]int{"a": 1, "b": 2, "c": 3}
delete(m, "b")
fmt.Println(m)

문제 26: 맵의 모든 키 추출하기

// 답안
m := map[string]int{"apple": 5, "banana": 3, "orange": 8}
keys := []string{}
for key := range m {
    keys = append(keys, key)
}
fmt.Println("키들:", keys)

문제 27: 맵의 모든 값 합계 구하기

// 답안
prices := map[string]int{"apple": 1000, "banana": 500, "orange": 800}
total := 0
for _, price := range prices {
    total += price
}
fmt.Println("총 가격:", total)

문제 28: 두 맵 합치기

// 답안
m1 := map[string]int{"a": 1, "b": 2}
m2 := map[string]int{"c": 3, "d": 4}
for k, v := range m2 {
    m1[k] = v
}
fmt.Println(m1)

문제 29: 맵에서 최대값을 가진 키 찾기

// 답안
scores := map[string]int{"Alice": 95, "Bob": 87, "Charlie": 92}
maxScore := 0
maxName := ""
for name, score := range scores {
    if score > maxScore {
        maxScore = score
        maxName = name
    }
}
fmt.Printf("최고 점수: %s (%d점)\n", maxName, maxScore)

문제 30: 맵 값들로 슬라이스 만들기

// 답안
ages := map[string]int{"Alice": 25, "Bob": 30, "Charlie": 35}
ageList := []int{}
for _, age := range ages {
    ageList = append(ageList, age)
}
fmt.Println("나이들:", ageList)

Level 2: 함수, 구조체, 인터페이스 (31-60)

31-40: 함수

문제 31: 두 수를 더하는 함수 만들기

// 답안
func add(a, b int) int {
    return a + b
}
 
func main() {
    result := add(3, 5)
    fmt.Println(result)
}

문제 32: 다중 반환값을 가진 함수 (몫과 나머지)

// 답안
func divMod(a, b int) (int, int) {
    return a / b, a % b
}
 
func main() {
    quot, rem := divMod(17, 5)
    fmt.Printf("몫: %d, 나머지: %d\n", quot, rem)
}

문제 33: 가변 인자 함수로 여러 수의 합 구하기

// 답안
func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}
 
func main() {
    result := sum(1, 2, 3, 4, 5)
    fmt.Println(result)
}

문제 34: 함수를 매개변수로 받는 함수

// 답안
func apply(fn func(int) int, value int) int {
    return fn(value)
}
 
func square(x int) int {
    return x * x
}
 
func main() {
    result := apply(square, 5)
    fmt.Println(result)  // 25
}

문제 35: 클로저 함수 만들기 (카운터)

// 답안
func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}
 
func main() {
    c := counter()
    fmt.Println(c())  // 1
    fmt.Println(c())  // 2
    fmt.Println(c())  // 3
}

문제 36: 재귀함수로 팩토리얼 구하기

// 답안
func factorial(n int) int {
    if n <= 1 {
        return 1
    }
    return n * factorial(n-1)
}
 
func main() {
    fmt.Println(factorial(5))  // 120
}

문제 37: defer를 사용한 함수

// 답안
func example() {
    defer fmt.Println("마지막에 실행됨")
    fmt.Println("첫 번째")
    fmt.Println("두 번째")
}
 
func main() {
    example()
}

문제 38: 에러 반환하는 함수

// 답안
import "errors"
 
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("0으로 나눌 수 없습니다")
    }
    return a / b, nil
}
 
func main() {
    result, err := divide(10, 2)
    if err != nil {
        fmt.Println("에러:", err)
        return
    }
    fmt.Println("결과:", result)
}

문제 39: 명명된 반환값 사용하기

// 답안
func calculate(a, b int) (sum, product int) {
    sum = a + b
    product = a * b
    return  // 명시적으로 sum, product 반환
}
 
func main() {
    s, p := calculate(3, 4)
    fmt.Printf("합: %d, 곱: %d\n", s, p)
}

문제 40: 함수 타입을 변수로 사용하기

// 답안
type operation func(int, int) int
 
func add(a, b int) int { return a + b }
func multiply(a, b int) int { return a * b }
 
func main() {
    var op operation
    op = add
    fmt.Println(op(3, 4))  // 7
    
    op = multiply
    fmt.Println(op(3, 4))  // 12
}

41-50: 구조체

문제 41: 기본 구조체 정의하고 사용하기

// 답안
type Person struct {
    Name string
    Age  int
}
 
func main() {
    p := Person{Name: "Alice", Age: 25}
    fmt.Printf("%+v\n", p)
}

문제 42: 구조체 포인터 사용하기

// 답안
type Point struct {
    X, Y int
}
 
func main() {
    p := &Point{X: 1, Y: 2}
    fmt.Printf("X: %d, Y: %d\n", p.X, p.Y)
}

문제 43: 구조체에 메서드 추가하기

// 답안
type Rectangle struct {
    Width, Height float64
}
 
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}
 
func main() {
    rect := Rectangle{Width: 5, Height: 3}
    fmt.Println("넓이:", rect.Area())
}

문제 44: 포인터 리시버 메서드

// 답안
type Counter struct {
    Value int
}
 
func (c *Counter) Increment() {
    c.Value++
}
 
func main() {
    counter := Counter{Value: 0}
    counter.Increment()
    fmt.Println(counter.Value)  // 1
}

문제 45: 구조체 임베딩 (상속)

// 답안
type Animal struct {
    Name string
}
 
func (a Animal) Speak() string {
    return "동물이 소리를 냅니다"
}
 
type Dog struct {
    Animal
    Breed string
}
 
func main() {
    dog := Dog{
        Animal: Animal{Name: "멍멍이"},
        Breed:  "골든 리트리버",
    }
    fmt.Println(dog.Name)    // Animal의 필드
    fmt.Println(dog.Speak()) // Animal의 메서드
}

문제 46: 구조체 슬라이스 정렬하기

// 답안
import "sort"
 
type Student struct {
    Name  string
    Score int
}
 
type ByScore []Student
 
func (s ByScore) Len() int           { return len(s) }
func (s ByScore) Less(i, j int) bool { return s[i].Score > s[j].Score }
func (s ByScore) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
 
func main() {
    students := []Student{
        {"Alice", 85},
        {"Bob", 95},
        {"Charlie", 78},
    }
    sort.Sort(ByScore(students))
    fmt.Println(students)
}

문제 47: JSON 마샬링/언마샬링

// 답안
import (
    "encoding/json"
    "fmt"
)
 
type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
    Age   int    `json:"age"`
}
 
func main() {
    user := User{Name: "Alice", Email: "alice@example.com", Age: 25}
    
    // JSON으로 변환
    jsonData, _ := json.Marshal(user)
    fmt.Println(string(jsonData))
    
    // JSON에서 구조체로 변환
    var newUser User
    json.Unmarshal(jsonData, &newUser)
    fmt.Printf("%+v\n", newUser)
}

문제 48: 구조체 생성자 패턴

// 답안
type BankAccount struct {
    owner   string
    balance float64
}
 
func NewBankAccount(owner string, initialBalance float64) *BankAccount {
    return &BankAccount{
        owner:   owner,
        balance: initialBalance,
    }
}
 
func (ba *BankAccount) GetBalance() float64 {
    return ba.balance
}
 
func main() {
    account := NewBankAccount("Alice", 1000.0)
    fmt.Println("잔고:", account.GetBalance())
}

문제 49: 익명 구조체 사용하기

// 답안
func main() {
    person := struct {
        Name string
        Age  int
    }{
        Name: "Bob",
        Age:  30,
    }
    fmt.Printf("%+v\n", person)
}

문제 50: 구조체 비교하기

// 답안
type Point struct {
    X, Y int
}
 
func main() {
    p1 := Point{X: 1, Y: 2}
    p2 := Point{X: 1, Y: 2}
    p3 := Point{X: 2, Y: 3}
    
    fmt.Println("p1 == p2:", p1 == p2)  // true
    fmt.Println("p1 == p3:", p1 == p3)  // false
}

51-60: 인터페이스

문제 51: 기본 인터페이스 정의하고 구현하기

// 답안
type Shape interface {
    Area() float64
}
 
type Circle struct {
    Radius float64
}
 
func (c Circle) Area() float64 {
    return 3.14 * c.Radius * c.Radius
}
 
func main() {
    var s Shape = Circle{Radius: 5}
    fmt.Println("넓이:", s.Area())
}

문제 52: 여러 타입이 같은 인터페이스 구현하기

// 답안
type Speaker interface {
    Speak() string
}
 
type Dog struct{}
func (d Dog) Speak() string { return "멍멍!" }
 
type Cat struct{}
func (c Cat) Speak() string { return "야옹!" }
 
func makeNoise(s Speaker) {
    fmt.Println(s.Speak())
}
 
func main() {
    makeNoise(Dog{})  // 멍멍!
    makeNoise(Cat{})  // 야옹!
}

문제 53: 빈 인터페이스 (interface{}) 사용하기

// 답안
func printAnything(value interface{}) {
    fmt.Printf("값: %v, 타입: %T\n", value, value)
}
 
func main() {
    printAnything(42)
    printAnything("hello")
    printAnything([]int{1, 2, 3})
}

문제 54: 타입 단언 (Type Assertion) 사용하기

// 답안
func checkType(value interface{}) {
    if str, ok := value.(string); ok {
        fmt.Printf("문자열: %s (길이: %d)\n", str, len(str))
    } else {
        fmt.Printf("문자열이 아님: %T\n", value)
    }
}
 
func main() {
    checkType("hello")
    checkType(42)
}

문제 55: 타입 스위치 사용하기

// 답안
func describeType(value interface{}) {
    switch v := value.(type) {
    case string:
        fmt.Printf("문자열: %s\n", v)
    case int:
        fmt.Printf("정수: %d\n", v)
    case bool:
        fmt.Printf("불린: %t\n", v)
    default:
        fmt.Printf("알 수 없는 타입: %T\n", v)
    }
}
 
func main() {
    describeType("hello")
    describeType(42)
    describeType(true)
    describeType(3.14)
}

문제 56: 인터페이스 합성

// 답안
type Reader interface {
    Read() string
}
 
type Writer interface {
    Write(string)
}
 
type ReadWriter interface {
    Reader
    Writer
}
 
type File struct {
    content string
}
 
func (f *File) Read() string {
    return f.content
}
 
func (f *File) Write(data string) {
    f.content = data
}
 
func main() {
    var rw ReadWriter = &File{}
    rw.Write("Hello, World!")
    fmt.Println(rw.Read())
}

문제 57: 인터페이스 값 nil 체크

// 답안
type Printer interface {
    Print()
}
 
type ConsolePrinter struct{}
func (cp ConsolePrinter) Print() { fmt.Println("콘솔 출력") }
 
func main() {
    var p Printer
    if p == nil {
        fmt.Println("Printer가 nil입니다")
    }
    
    p = ConsolePrinter{}
    if p != nil {
        p.Print()
    }
}

문제 58: 인터페이스를 매개변수로 받는 함수

// 답안
type Comparable interface {
    Compare(other Comparable) int
}
 
type Number struct {
    Value int
}
 
func (n Number) Compare(other Comparable) int {
    if otherNum, ok := other.(Number); ok {
        if n.Value < otherNum.Value {
            return -1
        } else if n.Value > otherNum.Value {
            return 1
        }
        return 0
    }
    return 0
}
 
func findMax(items []Comparable) Comparable {
    if len(items) == 0 {
        return nil
    }
    max := items[0]
    for _, item := range items[1:] {
        if item.Compare(max) > 0 {
            max = item
        }
    }
    return max
}
 
func main() {
    numbers := []Comparable{Number{5}, Number{2}, Number{8}, Number{1}}
    max := findMax(numbers)
    fmt.Printf("최대값: %+v\n", max)
}

문제 59: 인터페이스 슬라이스 다루기

// 답안
type Animal interface {
    MakeSound() string
}
 
type Dog struct{ Name string }
func (d Dog) MakeSound() string { return "멍멍" }
 
type Cat struct{ Name string }
func (c Cat) MakeSound() string { return "야옹" }
 
func main() {
    animals := []Animal{
        Dog{Name: "멍멍이"},
        Cat{Name: "야옹이"},
        Dog{Name: "왈왈이"},
    }
    
    for _, animal := range animals {
        fmt.Println(animal.MakeSound())
    }
}

문제 60: 메서드 셋과 인터페이스

// 답안
type Incrementer interface {
    Increment()
    GetValue() int
}
 
type Counter struct {
    value int
}
 
func (c *Counter) Increment() {
    c.value++
}
 
func (c *Counter) GetValue() int {
    return c.value
}
 
func useIncrementer(inc Incrementer) {
    inc.Increment()
    fmt.Println("현재 값:", inc.GetValue())
}
 
func main() {
    counter := &Counter{value: 0}
    useIncrementer(counter)
}

Level 3: 고루틴과 채널 (61-85)

61-70: 고루틴 기초

문제 61: 기본 고루틴 사용하기

// 답안
import "time"
 
func sayHello() {
    fmt.Println("Hello from goroutine!")
}
 
func main() {
    go sayHello()
    time.Sleep(time.Second)  // 고루틴이 실행될 때까지 기다림
    fmt.Println("Main function ending")
}

문제 62: 여러 고루틴 동시 실행

// 답안
func worker(id int) {
    fmt.Printf("Worker %d started\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d finished\n", id)
}
 
func main() {
    for i := 1; i <= 3; i++ {
        go worker(i)
    }
    time.Sleep(2 * time.Second)
}

문제 63: sync.WaitGroup 사용하기

// 답안
import "sync"
 
func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d working...\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}
 
func main() {
    var wg sync.WaitGroup
    
    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }
    
    wg.Wait()
    fmt.Println("All workers finished")
}

문제 64: 경쟁 상태(Race Condition) 예제

// 답안
var counter int
 
func increment(wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 1000; i++ {
        counter++  // 경쟁 상태 발생!
    }
}
 
func main() {
    var wg sync.WaitGroup
    
    for i := 0; i < 2; i++ {
        wg.Add(1)
        go increment(&wg)
    }
    
    wg.Wait()
    fmt.Printf("Counter: %d\n", counter)  // 예상: 2000, 실제: ?
}

문제 65: Mutex로 경쟁 상태 해결하기

// 답안
var (
    counter int
    mutex   sync.Mutex
)
 
func safeIncrement(wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 1000; i++ {
        mutex.Lock()
        counter++
        mutex.Unlock()
    }
}
 
func main() {
    var wg sync.WaitGroup
    
    for i := 0; i < 2; i++ {
        wg.Add(1)
        go safeIncrement(&wg)
    }
    
    wg.Wait()
    fmt.Printf("Safe Counter: %d\n", counter)  // 정확히 2000
}

문제 66: 고루틴에서 패닉 처리하기

// 답안
func riskyOperation(id int) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("Goroutine %d recovered from: %v\n", id, r)
        }
    }()
    
    if id == 2 {
        panic("Something went wrong!")
    }
    
    fmt.Printf("Goroutine %d completed successfully\n", id)
}
 
func main() {
    for i := 1; i <= 3; i++ {
        go riskyOperation(i)
    }
    time.Sleep(time.Second)
}

문제 67: 고루틴 풀 패턴

// 답안
func workerPool(jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for job := range jobs {
        results <- job * 2  // 작업 처리 (2배)
    }
}
 
func main() {
    const numWorkers = 3
    const numJobs = 9
    
    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)
    
    var wg sync.WaitGroup
    
    // 워커 시작
    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go workerPool(jobs, results, &wg)
    }
    
    // 작업 전송
    for i := 1; i <= numJobs; i++ {
        jobs <- i
    }
    close(jobs)
    
    // 워커 종료 대기
    go func() {
        wg.Wait()
        close(results)
    }()
    
    // 결과 수집
    for result := range results {
        fmt.Printf("Result: %d\n", result)
    }
}

문제 68: 고루틴 리크 방지

// 답안
func leakyGoroutine() {
    ch := make(chan string)
    
    go func() {
        time.Sleep(2 * time.Second)
        ch <- "data"  // 받을 고루틴이 없으면 영원히 블록됨
    }()
    
    // 1초 후 타임아웃
    select {
    case data := <-ch:
        fmt.Println("Received:", data)
    case <-time.After(1 * time.Second):
        fmt.Println("Timeout!")
        return  // 여기서 반환하면 ch <- "data"가 영원히 블록됨
    }
}
 
func fixedGoroutine() {
    ch := make(chan string, 1)  // 버퍼드 채널 사용
    
    go func() {
        time.Sleep(2 * time.Second)
        ch <- "data"  // 버퍼가 있어서 블록되지 않음
    }()
    
    select {
    case data := <-ch:
        fmt.Println("Received:", data)
    case <-time.After(1 * time.Second):
        fmt.Println("Timeout!")
    }
}

문제 69: 컨텍스트로 고루틴 취소하기

// 답안
import "context"
 
func cancellableWorker(ctx context.Context, id int) {
    for {
        select {
        case <-ctx.Done():
            fmt.Printf("Worker %d cancelled\n", id)
            return
        default:
            fmt.Printf("Worker %d working...\n", id)
            time.Sleep(500 * time.Millisecond)
        }
    }
}
 
func main() {
    ctx, cancel := context.WithCancel(context.Background())
    
    for i := 1; i <= 3; i++ {
        go cancellableWorker(ctx, i)
    }
    
    time.Sleep(2 * time.Second)
    cancel()  // 모든 워커 취소
    time.Sleep(time.Second)
}

문제 70: 고루틴으로 팬아웃/팬인 패턴

// 답안
func producer(nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        for _, n := range nums {
            out <- n
        }
        close(out)
    }()
    return out
}
 
func square(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        for n := range in {
            out <- n * n
        }
        close(out)
    }()
    return out
}
 
func main() {
    // 팬아웃
    c := producer(2, 3, 4)
    
    // 팬인
    c1 := square(c)
    c2 := square(c)
    
    // 결과 수집
    for n := range c1 {
        fmt.Println(n)
    }
    for n := range c2 {
        fmt.Println(n)
    }
}

71-80: 채널 활용

문제 71: 기본 채널 사용하기

// 답안
func main() {
    ch := make(chan string)
    
    go func() {
        ch <- "Hello, Channel!"
    }()
    
    message := <-ch
    fmt.Println(message)
}

문제 72: 버퍼드 채널 사용하기

// 답안
func main() {
    ch := make(chan int, 2)
    
    ch <- 1
    ch <- 2
    // ch <- 3  // 이건 블록됨 (버퍼 크기 2)
    
    fmt.Println(<-ch)  // 1
    fmt.Println(<-ch)  // 2
}

문제 73: 채널 닫기와 범위 루프

// 답안
func fibonacci(n int, c chan int) {
    x, y := 0, 1
    for i := 0; i < n; i++ {
        c <- x
        x, y = y, x+y
    }
    close(c)
}
 
func main() {
    c := make(chan int, 10)
    go fibonacci(cap(c), c)
    
    for num := range c {
        fmt.Println(num)
    }
}

문제 74: select 문으로 다중 채널 처리

// 답안
func main() {
    c1 := make(chan string)
    c2 := make(chan string)
    
    go func() {
        time.Sleep(1 * time.Second)
        c1 <- "one"
    }()
    
    go func() {
        time.Sleep(2 * time.Second)
        c2 <- "two"
    }()
    
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("Received", msg1)
        case msg2 := <-c2:
            fmt.Println("Received", msg2)
        }
    }
}

문제 75: select와 default로 논블로킹 처리

// 답안
func main() {
    ch := make(chan string)
    
    select {
    case msg := <-ch:
        fmt.Println("Received:", msg)
    default:
        fmt.Println("No message received")
    }
}

문제 76: 타이머와 채널

// 답안
func main() {
    timer1 := time.NewTimer(2 * time.Second)
    
    <-timer1.C
    fmt.Println("Timer 1 expired")
    
    timer2 := time.NewTimer(time.Second)
    go func() {
        <-timer2.C
        fmt.Println("Timer 2 expired")
    }()
    
    stop2 := timer2.Stop()
    if stop2 {
        fmt.Println("Timer 2 stopped")
    }
    
    time.Sleep(2 * time.Second)
}

문제 77: 티커(Ticker) 사용하기

// 답안
func main() {
    ticker := time.NewTicker(500 * time.Millisecond)
    done := make(chan bool)
    
    go func() {
        for {
            select {
            case <-done:
                return
            case t := <-ticker.C:
                fmt.Println("Tick at", t)
            }
        }
    }()
    
    time.Sleep(1600 * time.Millisecond)
    ticker.Stop()
    done <- true
    fmt.Println("Ticker stopped")
}

문제 78: 파이프라인 패턴

// 답안
func pipeline() {
    // Stage 1: 숫자 생성
    numbers := make(chan int)
    go func() {
        for i := 1; i <= 5; i++ {
            numbers <- i
        }
        close(numbers)
    }()
    
    // Stage 2: 제곱 계산
    squares := make(chan int)
    go func() {
        for n := range numbers {
            squares <- n * n
        }
        close(squares)
    }()
    
    // Stage 3: 결과 출력
    for s := range squares {
        fmt.Println("Square:", s)
    }
}
 
func main() {
    pipeline()
}

문제 79: 채널로 세마포어 구현하기

// 답안
type Semaphore chan struct{}
 
func NewSemaphore(maxConcurrency int) Semaphore {
    return make(chan struct{}, maxConcurrency)
}
 
func (s Semaphore) Acquire() {
    s <- struct{}{}
}
 
func (s Semaphore) Release() {
    <-s
}
 
func worker(id int, sem Semaphore, wg *sync.WaitGroup) {
    defer wg.Done()
    
    sem.Acquire()
    defer sem.Release()
    
    fmt.Printf("Worker %d working...\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}
 
func main() {
    sem := NewSemaphore(2)  // 최대 2개 동시 실행
    var wg sync.WaitGroup
    
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, sem, &wg)
    }
    
    wg.Wait()
}

문제 80: 채널로 브로드캐스트 구현하기

// 답안
type Broadcaster struct {
    listeners []chan string
    mutex     sync.Mutex
}
 
func NewBroadcaster() *Broadcaster {
    return &Broadcaster{
        listeners: make([]chan string, 0),
    }
}
 
func (b *Broadcaster) Subscribe() <-chan string {
    b.mutex.Lock()
    defer b.mutex.Unlock()
    
    ch := make(chan string, 10)
    b.listeners = append(b.listeners, ch)
    return ch
}
 
func (b *Broadcaster) Broadcast(message string) {
    b.mutex.Lock()
    defer b.mutex.Unlock()
    
    for _, listener := range b.listeners {
        select {
        case listener <- message:
        default:  // 버퍼가 가득 차면 스킵
        }
    }
}
 
func main() {
    broadcaster := NewBroadcaster()
    
    // 구독자들
    sub1 := broadcaster.Subscribe()
    sub2 := broadcaster.Subscribe()
    
    go func() {
        for msg := range sub1 {
            fmt.Println("Sub1 received:", msg)
        }
    }()
    
    go func() {
        for msg := range sub2 {
            fmt.Println("Sub2 received:", msg)
        }
    }()
    
    broadcaster.Broadcast("Hello")
    broadcaster.Broadcast("World")
    
    time.Sleep(time.Second)
}

81-85: 동시성 패턴

문제 81: Once를 사용한 초기화

// 답안
var once sync.Once
var instance *Singleton
 
type Singleton struct {
    value string
}
 
func GetSingleton() *Singleton {
    once.Do(func() {
        fmt.Println("Creating singleton instance")
        instance = &Singleton{value: "I'm a singleton"}
    })
    return instance
}
 
func main() {
    var wg sync.WaitGroup
    
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            s := GetSingleton()
            fmt.Printf("Goroutine %d got: %s\n", id, s.value)
        }(i)
    }
    
    wg.Wait()
}

문제 82: RWMutex 사용하기

// 답안
type SafeCounter struct {
    mutex sync.RWMutex
    count map[string]int
}
 
func (c *SafeCounter) Get(key string) int {
    c.mutex.RLock()
    defer c.mutex.RUnlock()
    return c.count[key]
}
 
func (c *SafeCounter) Set(key string, value int) {
    c.mutex.Lock()
    defer c.mutex.Unlock()
    if c.count == nil {
        c.count = make(map[string]int)
    }
    c.count[key] = value
}
 
func main() {
    counter := &SafeCounter{}
    
    var wg sync.WaitGroup
    
    // 여러 고루틴에서 동시 읽기/쓰기
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            key := fmt.Sprintf("key%d", id)
            counter.Set(key, id*10)
            fmt.Printf("Set %s = %d\n", key, id*10)
        }(i)
    }
    
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            key := fmt.Sprintf("key%d", id%3)
            value := counter.Get(key)
            fmt.Printf("Get %s = %d\n", key, value)
        }(i)
    }
    
    wg.Wait()
}

문제 83: 조건 변수(Cond) 사용하기

// 답안
func main() {
    var mutex sync.Mutex
    cond := sync.NewCond(&mutex)
    ready := false
    
    // 기다리는 고루틴들
    for i := 1; i <= 3; i++ {
        go func(id int) {
            mutex.Lock()
            for !ready {
                fmt.Printf("Worker %d waiting...\n", id)
                cond.Wait()
            }
            fmt.Printf("Worker %d starting work!\n", id)
            mutex.Unlock()
        }(i)
    }
    
    time.Sleep(2 * time.Second)
    
    // 신호 보내기
    mutex.Lock()
    ready = true
    mutex.Unlock()
    cond.Broadcast()  // 모든 대기 중인 고루틴에게 신호
    
    time.Sleep(time.Second)
}

문제 84: 원자적 연산 사용하기

// 답안
import "sync/atomic"
 
var counter int64
 
func increment(wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 1000; i++ {
        atomic.AddInt64(&counter, 1)
    }
}
 
func main() {
    var wg sync.WaitGroup
    
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go increment(&wg)
    }
    
    wg.Wait()
    fmt.Printf("Atomic counter: %d\n", atomic.LoadInt64(&counter))
}

문제 85: 고루틴 풀을 이용한 작업 스케줄러

// 답안
type Job func()
 
type WorkerPool struct {
    jobQueue    chan Job
    workerQueue chan chan Job
    workers     []*Worker
    quit        chan bool
}
 
type Worker struct {
    id          int
    jobChannel  chan Job
    workerQueue chan chan Job
    quit        chan bool
}
 
func NewWorker(id int, workerQueue chan chan Job) *Worker {
    return &Worker{
        id:          id,
        jobChannel:  make(chan Job),
        workerQueue: workerQueue,
        quit:        make(chan bool),
    }
}
 
func (w *Worker) Start() {
    go func() {
        for {
            w.workerQueue <- w.jobChannel
            
            select {
            case job := <-w.jobChannel:
                job()
            case <-w.quit:
                return
            }
        }
    }()
}
 
func NewWorkerPool(maxWorkers int, maxQueue int) *WorkerPool {
    pool := &WorkerPool{
        jobQueue:    make(chan Job, maxQueue),
        workerQueue: make(chan chan Job, maxWorkers),
        workers:     make([]*Worker, maxWorkers),
        quit:        make(chan bool),
    }
    
    for i := 0; i < maxWorkers; i++ {
        worker := NewWorker(i+1, pool.workerQueue)
        pool.workers[i] = worker
        worker.Start()
    }
    
    go pool.dispatch()
    return pool
}
 
func (p *WorkerPool) dispatch() {
    for {
        select {
        case job := <-p.jobQueue:
            go func() {
                workerJobChannel := <-p.workerQueue
                workerJobChannel <- job
            }()
        case <-p.quit:
            return
        }
    }
}
 
func (p *WorkerPool) Submit(job Job) {
    p.jobQueue <- job
}
 
func main() {
    pool := NewWorkerPool(3, 10)
    
    for i := 1; i <= 10; i++ {
        jobID := i
        pool.Submit(func() {
            fmt.Printf("Job %d started\n", jobID)
            time.Sleep(time.Second)
            fmt.Printf("Job %d completed\n", jobID)
        })
    }
    
    time.Sleep(5 * time.Second)
}

Level 4: 네트워크와 실전 응용 (86-100)

86-95: 네트워크 프로그래밍

문제 86: TCP 서버 만들기

// 답안
import "net"
 
func handleConnection(conn net.Conn) {
    defer conn.Close()
    
    buffer := make([]byte, 1024)
    for {
        n, err := conn.Read(buffer)
        if err != nil {
            fmt.Println("Connection closed")
            return
        }
        
        message := string(buffer[:n])
        fmt.Printf("Received: %s", message)
        
        // 에코
        conn.Write(buffer[:n])
    }
}
 
func main() {
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        panic(err)
    }
    defer listener.Close()
    
    fmt.Println("Server listening on :8080")
    
    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting connection:", err)
            continue
        }
        
        go handleConnection(conn)
    }
}

문제 87: TCP 클라이언트 만들기

// 답안
func main() {
    conn, err := net.Dial("tcp", "localhost:8080")
    if err != nil {
        panic(err)
    }
    defer conn.Close()
    
    // 메시지 전송
    message := "Hello, Server!"
    _, err = conn.Write([]byte(message))
    if err != nil {
        panic(err)
    }
    
    // 응답 받기
    buffer := make([]byte, 1024)
    n, err := conn.Read(buffer)
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("Server response: %s\n", string(buffer[:n]))
}

문제 88: HTTP 서버 만들기

// 답안
import "net/http"
 
func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}
 
func jsonHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    
    response := map[string]interface{}{
        "message": "Hello, JSON!",
        "status":  "success",
        "data":    []int{1, 2, 3, 4, 5},
    }
    
    json.NewEncoder(w).Encode(response)
}
 
func main() {
    http.HandleFunc("/hello/", helloHandler)
    http.HandleFunc("/json", jsonHandler)
    
    fmt.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

문제 89: HTTP 클라이언트로 API 호출하기

// 답안
func main() {
    // GET 요청
    resp, err := http.Get("https://jsonplaceholder.typicode.com/posts/1")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
    
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("Response: %s\n", string(body))
    
    // POST 요청
    postData := map[string]interface{}{
        "title":  "My Post",
        "body":   "This is the body",
        "userId": 1,
    }
    
    jsonData, _ := json.Marshal(postData)
    
    postResp, err := http.Post(
        "https://jsonplaceholder.typicode.com/posts",
        "application/json",
        bytes.NewBuffer(jsonData),
    )
    if err != nil {
        panic(err)
    }
    defer postResp.Body.Close()
    
    postBody, _ := ioutil.ReadAll(postResp.Body)
    fmt.Printf("POST Response: %s\n", string(postBody))
}

문제 90: RPC 서버 만들기

// 답안
import (
    "net"
    "net/http"
    "net/rpc"
)
 
type Calculator struct{}
 
type Args struct {
    A, B int
}
 
func (c *Calculator) Add(args *Args, result *int) error {
    *result = args.A + args.B
    return nil
}
 
func (c *Calculator) Multiply(args *Args, result *int) error {
    *result = args.A * args.B
    return nil
}
 
func main() {
    calculator := &Calculator{}
    rpc.Register(calculator)
    rpc.HandleHTTP()
    
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        panic(err)
    }
    
    fmt.Println("RPC server starting on :8080")
    http.Serve(listener, nil)
}

문제 91: RPC 클라이언트 만들기

// 답안
func main() {
    client, err := rpc.DialHTTP("tcp", "localhost:8080")
    if err != nil {
        panic(err)
    }
    defer client.Close()
    
    // Add 호출
    args := &Args{A: 5, B: 3}
    var addResult int
    err = client.Call("Calculator.Add", args, &addResult)
    if err != nil {
        panic(err)
    }
    fmt.Printf("5 + 3 = %d\n", addResult)
    
    // Multiply 호출
    var mulResult int
    err = client.Call("Calculator.Multiply", args, &mulResult)
    if err != nil {
        panic(err)
    }
    fmt.Printf("5 * 3 = %d\n", mulResult)
}

문제 92: WebSocket 서버 (gorilla/websocket 사용)

// 답안
// go get github.com/gorilla/websocket
import "github.com/gorilla/websocket"
 
var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        return true  // 모든 오리진 허용 (프로덕션에서는 제한 필요)
    },
}
 
func wsHandler(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        fmt.Println("Upgrade error:", err)
        return
    }
    defer conn.Close()
    
    for {
        messageType, message, err := conn.ReadMessage()
        if err != nil {
            fmt.Println("Read error:", err)
            break
        }
        
        fmt.Printf("Received: %s\n", message)
        
        // 에코
        err = conn.WriteMessage(messageType, message)
        if err != nil {
            fmt.Println("Write error:", err)
            break
        }
    }
}
 
func main() {
    http.HandleFunc("/ws", wsHandler)
    fmt.Println("WebSocket server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

문제 93: 파일 업로드 HTTP 서버

// 답안
func uploadHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != "POST" {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        return
    }
    
    err := r.ParseMultipartForm(10 << 20)  // 10 MB 제한
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    file, handler, err := r.FormFile("uploadfile")
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    defer file.Close()
    
    fmt.Printf("Uploaded File: %s\n", handler.Filename)
    fmt.Printf("File Size: %d\n", handler.Size)
    
    // 파일 저장
    f, err := os.OpenFile("./uploads/"+handler.Filename, 
        os.O_WRONLY|os.O_CREATE, 0666)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    defer f.Close()
    
    io.Copy(f, file)
    
    fmt.Fprintf(w, "File uploaded successfully: %s", handler.Filename)
}
 
func main() {
    os.MkdirAll("./uploads", os.ModePerm)
    
    http.HandleFunc("/upload", uploadHandler)
    fmt.Println("File upload server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

문제 94: 미들웨어 패턴으로 HTTP 서버

// 답안
type Middleware func(http.HandlerFunc) http.HandlerFunc
 
func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next(w, r)
        fmt.Printf("%s %s %v\n", r.Method, r.URL.Path, time.Since(start))
    }
}
 
func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        if token != "Bearer secret-token" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        next(w, r)
    }
}
 
func chainMiddleware(h http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc {
    for i := len(middlewares) - 1; i >= 0; i-- {
        h = middlewares[i](h)
    }
    return h
}
 
func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, authenticated user!")
}
 
func publicHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "This is a public endpoint")
}
 
func main() {
    protectedHandler := chainMiddleware(helloHandler, 
        loggingMiddleware, authMiddleware)
    
    publicHandlerWithLogging := chainMiddleware(publicHandler, 
        loggingMiddleware)
    
    http.HandleFunc("/protected", protectedHandler)
    http.HandleFunc("/public", publicHandlerWithLogging)
    
    fmt.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

문제 95: 로드 밸런서 구현하기

// 답안
import "sync/atomic"
 
type Backend struct {
    URL   string
    Alive bool
}
 
type LoadBalancer struct {
    backends []*Backend
    current  uint64
}
 
func (lb *LoadBalancer) GetNextBackend() *Backend {
    if len(lb.backends) == 0 {
        return nil
    }
    
    // 라운드 로빈
    next := atomic.AddUint64(&lb.current, 1)
    return lb.backends[next%uint64(len(lb.backends))]
}
 
func (lb *LoadBalancer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    backend := lb.GetNextBackend()
    if backend == nil {
        http.Error(w, "No backend available", http.StatusServiceUnavailable)
        return
    }
    
    // 프록시 요청
    backendURL := backend.URL + r.URL.Path
    resp, err := http.Get(backendURL)
    if err != nil {
        http.Error(w, "Backend error", http.StatusBadGateway)
        return
    }
    defer resp.Body.Close()
    
    // 응답 복사
    for k, v := range resp.Header {
        w.Header()[k] = v
    }
    w.WriteHeader(resp.StatusCode)
    io.Copy(w, resp.Body)
}
 
func main() {
    backends := []*Backend{
        {URL: "http://localhost:8081", Alive: true},
        {URL: "http://localhost:8082", Alive: true},
        {URL: "http://localhost:8083", Alive: true},
    }
    
    lb := &LoadBalancer{backends: backends}
    
    fmt.Println("Load balancer starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", lb))
}

96-100: 종합 실전 문제

문제 96: 채팅 서버 (WebSocket + 채널)

// 답안
type Hub struct {
    clients    map[*Client]bool
    broadcast  chan []byte
    register   chan *Client
    unregister chan *Client
}
 
type Client struct {
    hub  *Hub
    conn *websocket.Conn
    send chan []byte
}
 
func (h *Hub) Run() {
    for {
        select {
        case client := <-h.register:
            h.clients[client] = true
            fmt.Println("Client connected")
            
        case client := <-h.unregister:
            if _, ok := h.clients[client]; ok {
                delete(h.clients, client)
                close(client.send)
                fmt.Println("Client disconnected")
            }
            
        case message := <-h.broadcast:
            for client := range h.clients {
                select {
                case client.send <- message:
                default:
                    close(client.send)
                    delete(h.clients, client)
                }
            }
        }
    }
}
 
func (c *Client) readPump() {
    defer func() {
        c.hub.unregister <- c
        c.conn.Close()
    }()
    
    for {
        _, message, err := c.conn.ReadMessage()
        if err != nil {
            break
        }
        c.hub.broadcast <- message
    }
}
 
func (c *Client) writePump() {
    defer c.conn.Close()
    
    for {
        select {
        case message, ok := <-c.send:
            if !ok {
                c.conn.WriteMessage(websocket.CloseMessage, []byte{})
                return
            }
            c.conn.WriteMessage(websocket.TextMessage, message)
        }
    }
}
 
func serveWS(hub *Hub, w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        return
    }
    
    client := &Client{
        hub:  hub,
        conn: conn,
        send: make(chan []byte, 256),
    }
    
    client.hub.register <- client
    
    go client.readPump()
    go client.writePump()
}
 
func main() {
    hub := &Hub{
        broadcast:  make(chan []byte),
        register:   make(chan *Client),
        unregister: make(chan *Client),
        clients:    make(map[*Client]bool),
    }
    
    go hub.Run()
    
    http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
        serveWS(hub, w, r)
    })
    
    fmt.Println("Chat server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

문제 97: 분산 워드카운트 (MapReduce 스타일)

// 답안
import (
    "strings"
    "unicode"
)
 
type KeyValue struct {
    Key   string
    Value string
}
 
func Map(filename, contents string) []KeyValue {
    ff := func(r rune) bool { return !unicode.IsLetter(r) }
    words := strings.FieldsFunc(contents, ff)
    
    var kva []KeyValue
    for _, w := range words {
        kv := KeyValue{strings.ToLower(w), "1"}
        kva = append(kva, kv)
    }
    return kva
}
 
func Reduce(key string, values []string) string {
    return fmt.Sprintf("%d", len(values))
}
 
func distributedWordCount(texts []string) map[string]int {
    // Map 단계
    mapResults := make(chan []KeyValue, len(texts))
    var wg sync.WaitGroup
    
    for i, text := range texts {
        wg.Add(1)
        go func(id int, content string) {
            defer wg.Done()
            result := Map(fmt.Sprintf("text-%d", id), content)
            mapResults <- result
        }(i, text)
    }
    
    go func() {
        wg.Wait()
        close(mapResults)
    }()
    
    // 중간 결과 수집 및 그룹화
    intermediate := make(map[string][]string)
    for kvs := range mapResults {
        for _, kv := range kvs {
            intermediate[kv.Key] = append(intermediate[kv.Key], kv.Value)
        }
    }
    
    // Reduce 단계
    result := make(map[string]int)
    reduceResults := make(chan struct {
        key   string
        count int
    }, len(intermediate))
    
    wg = sync.WaitGroup{}
    for key, values := range intermediate {
        wg.Add(1)
        go func(k string, vals []string) {
            defer wg.Done()
            countStr := Reduce(k, vals)
            count, _ := strconv.Atoi(countStr)
            reduceResults <- struct {
                key   string
                count int
            }{k, count}
        }(key, values)
    }
    
    go func() {
        wg.Wait()
        close(reduceResults)
    }()
    
    for r := range reduceResults {
        result[r.key] = r.count
    }
    
    return result
}
 
func main() {
    texts := []string{
        "hello world hello",
        "world of go programming",
        "go is awesome go go",
    }
    
    result := distributedWordCount(texts)
    
    fmt.Println("Word count results:")
    for word, count := range result {
        fmt.Printf("%s: %d\n", word, count)
    }
}

문제 98: 캐시 서버 (LRU 캐시)

// 답안
type Node struct {
    key, value string
    prev, next *Node
}
 
type LRUCache struct {
    capacity int
    cache    map[string]*Node
    head     *Node
    tail     *Node
    mutex    sync.RWMutex
}
 
func NewLRUCache(capacity int) *LRUCache {
    head := &Node{}
    tail := &Node{}
    head.next = tail
    tail.prev = head
    
    return &LRUCache{
        capacity: capacity,
        cache:    make(map[string]*Node),
        head:     head,
        tail:     tail,
    }
}
 
func (c *LRUCache) addNode(node *Node) {
    node.prev = c.head
    node.next = c.head.next
    c.head.next.prev = node
    c.head.next = node
}
 
func (c *LRUCache) removeNode(node *Node) {
    node.prev.next = node.next
    node.next.prev = node.prev
}
 
func (c *LRUCache) moveToHead(node *Node) {
    c.removeNode(node)
    c.addNode(node)
}
 
func (c *LRUCache) popTail() *Node {
    lastNode := c.tail.prev
    c.removeNode(lastNode)
    return lastNode
}
 
func (c *LRUCache) Get(key string) (string, bool) {
    c.mutex.Lock()
    defer c.mutex.Unlock()
    
    if node, exists := c.cache[key]; exists {
        c.moveToHead(node)
        return node.value, true
    }
    return "", false
}
 
func (c *LRUCache) Put(key, value string) {
    c.mutex.Lock()
    defer c.mutex.Unlock()
    
    if node, exists := c.cache[key]; exists {
        node.value = value
        c.moveToHead(node)
    } else {
        newNode := &Node{key: key, value: value}
        
        if len(c.cache) >= c.capacity {
            tail := c.popTail()
            delete(c.cache, tail.key)
        }
        
        c.cache[key] = newNode
        c.addNode(newNode)
    }
}
 
func (c *LRUCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    key := r.URL.Query().Get("key")
    
    switch r.Method {
    case "GET":
        if value, exists := c.Get(key); exists {
            w.WriteHeader(http.StatusOK)
            w.Write([]byte(value))
        } else {
            w.WriteHeader(http.StatusNotFound)
            w.Write([]byte("Key not found"))
        }
        
    case "POST":
        value := r.URL.Query().Get("value")
        c.Put(key, value)
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("OK"))
        
    default:
        w.WriteHeader(http.StatusMethodNotAllowed)
    }
}
 
func main() {
    cache := NewLRUCache(100)
    
    fmt.Println("Cache server starting on :8080")
    fmt.Println("GET /?key=mykey")
    fmt.Println("POST /?key=mykey&value=myvalue")
    
    log.Fatal(http.ListenAndServe(":8080", cache))
}

문제 99: 리더 선출 알고리즘 (Raft 스타일)

// 답안
import "math/rand"
 
type NodeState int
 
const (
    Follower NodeState = iota
    Candidate
    Leader
)
 
type Node struct {
    id          int
    state       NodeState
    currentTerm int
    votedFor    int
    votes       int
    peers       []*Node
    
    electionTimer  *time.Timer
    heartbeatTimer *time.Timer
    
    mutex sync.RWMutex
}
 
func NewNode(id int, peers []*Node) *Node {
    n := &Node{
        id:       id,
        state:    Follower,
        votedFor: -1,
        peers:    peers,
    }
    n.resetElectionTimer()
    return n
}
 
func (n *Node) resetElectionTimer() {
    timeout := time.Duration(150+rand.Intn(150)) * time.Millisecond
    if n.electionTimer != nil {
        n.electionTimer.Stop()
    }
    n.electionTimer = time.AfterFunc(timeout, n.startElection)
}
 
func (n *Node) startElection() {
    n.mutex.Lock()
    n.state = Candidate
    n.currentTerm++
    n.votedFor = n.id
    n.votes = 1
    term := n.currentTerm
    n.mutex.Unlock()
    
    fmt.Printf("Node %d starting election for term %d\n", n.id, term)
    
    votes := make(chan bool, len(n.peers))
    
    for _, peer := range n.peers {
        if peer.id != n.id {
            go func(p *Node) {
                vote := n.requestVote(p, term)
                votes <- vote
            }(peer)
        }
    }
    
    voteCount := 1
    for i := 0; i < len(n.peers)-1; i++ {
        if <-votes {
            voteCount++
        }
        
        if voteCount > len(n.peers)/2 {
            n.becomeLeader()
            return
        }
    }
    
    n.mutex.Lock()
    n.state = Follower
    n.mutex.Unlock()
    n.resetElectionTimer()
}
 
func (n *Node) requestVote(peer *Node, term int) bool {
    peer.mutex.Lock()
    defer peer.mutex.Unlock()
    
    if term < peer.currentTerm {
        return false
    }
    
    if term > peer.currentTerm {
        peer.currentTerm = term
        peer.votedFor = -1
        peer.state = Follower
    }
    
    if peer.votedFor == -1 || peer.votedFor == n.id {
        peer.votedFor = n.id
        peer.resetElectionTimer()
        return true
    }
    
    return false
}
 
func (n *Node) becomeLeader() {
    n.mutex.Lock()
    n.state = Leader
    n.mutex.Unlock()
    
    fmt.Printf("Node %d became leader for term %d\n", n.id, n.currentTerm)
    
    n.sendHeartbeats()
    n.heartbeatTimer = time.AfterFunc(50*time.Millisecond, n.sendHeartbeats)
}
 
func (n *Node) sendHeartbeats() {
    if n.state != Leader {
        return
    }
    
    for _, peer := range n.peers {
        if peer.id != n.id {
            go func(p *Node) {
                p.mutex.Lock()
                if n.currentTerm >= p.currentTerm {
                    p.currentTerm = n.currentTerm
                    p.state = Follower
                    p.resetElectionTimer()
                }
                p.mutex.Unlock()
            }(peer)
        }
    }
    
    if n.state == Leader {
        n.heartbeatTimer = time.AfterFunc(50*time.Millisecond, n.sendHeartbeats)
    }
}
 
func main() {
    const numNodes = 5
    nodes := make([]*Node, numNodes)
    
    // 모든 노드 생성
    for i := 0; i < numNodes; i++ {
        nodes[i] = &Node{
            id:       i,
            state:    Follower,
            votedFor: -1,
            peers:    nodes,
        }
    }
    
    // 각 노드 시작
    for _, node := range nodes {
        node.resetElectionTimer()
    }
    
    fmt.Println("Starting Raft leader election simulation...")
    time.Sleep(10 * time.Second)
}

문제 100: 간단한 분산 키-값 저장소

// 답안
type KVStore struct {
    data  map[string]string
    mutex sync.RWMutex
    peers []string
}
 
func NewKVStore(peers []string) *KVStore {
    return &KVStore{
        data:  make(map[string]string),
        peers: peers,
    }
}
 
func (kv *KVStore) Get(key string) (string, bool) {
    kv.mutex.RLock()
    defer kv.mutex.RUnlock()
    
    value, exists := kv.data[key]
    return value, exists
}
 
func (kv *KVStore) Put(key, value string) error {
    kv.mutex.Lock()
    kv.data[key] = value
    kv.mutex.Unlock()
    
    // 다른 노드들에게 복제
    return kv.replicate(key, value)
}
 
func (kv *KVStore) replicate(key, value string) error {
    var wg sync.WaitGroup
    errors := make(chan error, len(kv.peers))
    
    for _, peer := range kv.peers {
        wg.Add(1)
        go func(peerAddr string) {
            defer wg.Done()
            
            data := map[string]string{"key": key, "value": value}
            jsonData, _ := json.Marshal(data)
            
            resp, err := http.Post(
                "http://"+peerAddr+"/replicate",
                "application/json",
                bytes.NewBuffer(jsonData),
            )
            if err != nil {
                errors <- err
                return
            }
            defer resp.Body.Close()
            
            if resp.StatusCode != http.StatusOK {
                errors <- fmt.Errorf("replication failed for %s", peerAddr)
            }
        }(peer)
    }
    
    go func() {
        wg.Wait()
        close(errors)
    }()
    
    // 최소 하나의 복제 성공 확인
    errorCount := 0
    for err := range errors {
        if err != nil {
            fmt.Printf("Replication error: %v\n", err)
            errorCount++
        }
    }
    
    if errorCount == len(kv.peers) {
        return fmt.Errorf("all replications failed")
    }
    
    return nil
}
 
func (kv *KVStore) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case "GET":
        key := r.URL.Query().Get("key")
        if value, exists := kv.Get(key); exists {
            w.WriteHeader(http.StatusOK)
            json.NewEncoder(w).Encode(map[string]string{"value": value})
        } else {
            w.WriteHeader(http.StatusNotFound)
            json.NewEncoder(w).Encode(map[string]string{"error": "Key not found"})
        }
        
    case "POST":
        var data map[string]string
        if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
            w.WriteHeader(http.StatusBadRequest)
            return
        }
        
        key := data["key"]
        value := data["value"]
        
        if r.URL.Path == "/replicate" {
            // 복제 요청 처리 (로컬만)
            kv.mutex.Lock()
            kv.data[key] = value
            kv.mutex.Unlock()
            w.WriteHeader(http.StatusOK)
        } else {
            // 일반 PUT 요청 (복제 포함)
            if err := kv.Put(key, value); err != nil {
                w.WriteHeader(http.StatusInternalServerError)
                json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
            } else {
                w.WriteHeader(http.StatusOK)
                json.NewEncoder(w).Encode(map[string]string{"status": "success"})
            }
        }
        
    default:
        w.WriteHeader(http.StatusMethodNotAllowed)
    }
}
 
func main() {
    port := flag.String("port", "8080", "Server port")
    peers := flag.String("peers", "", "Comma-separated list of peer addresses")
    flag.Parse()
    
    var peerList []string
    if *peers != "" {
        peerList = strings.Split(*peers, ",")
    }
    
    store := NewKVStore(peerList)
    
    fmt.Printf("KV Store starting on port %s\n", *port)
    fmt.Printf("Peers: %v\n", peerList)
    
    log.Fatal(http.ListenAndServe(":"+*port, store))
}

🎯 학습 완료 체크리스트

Level 1 완료 (1-30) ✅

  • 변수 선언과 타입 시스템
  • 조건문과 반복문
  • 배열과 슬라이스 조작
  • 맵 사용법과 순회

Level 2 완료 (31-60) ✅

  • 함수 정의와 고급 기능
  • 구조체와 메서드
  • 인터페이스와 다형성
  • JSON 처리

Level 3 완료 (61-85) ✅

  • 고루틴 기본 사용법
  • 채널을 통한 통신
  • 동시성 제어 (Mutex, WaitGroup)
  • 고급 동시성 패턴

Level 4 완료 (86-100) ✅

  • TCP/HTTP 네트워크 프로그래밍
  • RPC 구현
  • WebSocket 실시간 통신
  • 분산 시스템 패턴

🚀 다음 단계

이 100문항을 완료했다면:

  1. MIT 6.824 Lab 준비 완료!
  2. Go 언어 중급 수준 달성
  3. 분산 시스템 구현 기초 완성

이제 자신있게 MIT_6824_주말_학습_가이드Phase 1으로 넘어가세요! 🎉