Go ๊ธฐ์ˆ  ๋ฉด์ ‘: Goroutine, Channel, ๋™์‹œ์„ฑ ํŒจํ„ด ์™„๋ฒฝ ๊ฐ€์ด๋“œ

Go ๊ธฐ์ˆ  ๋ฉด์ ‘์—์„œ ์ž์ฃผ ์ถœ์ œ๋˜๋Š” goroutine, channel, ๋™์‹œ์„ฑ ๊ด€๋ จ ์งˆ๋ฌธ์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜ ์ˆ˜์ค€์˜ ์ฝ”๋“œ ์˜ˆ์ œ์™€ ๊ฐ ๋‹ต๋ณ€์˜ ์„ค๊ณ„ ๊ทผ๊ฑฐ๋ฅผ 2026๋…„ ๋ฉด์ ‘ ๋Œ€๋น„์šฉ์œผ๋กœ ์ƒ์„ธํžˆ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

Go ๊ธฐ์ˆ  ๋ฉด์ ‘ ๋Œ€๋น„ goroutine channel ๋™์‹œ์„ฑ ํŒจํ„ด

Go ๊ธฐ์ˆ  ๋ฉด์ ‘์—์„œ goroutine, channel, ๋™์‹œ์„ฑ์— ๊ด€ํ•œ ์งˆ๋ฌธ์€ ๊ฐ€์žฅ ๋‚œ์ด๋„๊ฐ€ ๋†’์€ ์ฃผ์ œ๋กœ ๊ผฝํž™๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฐœ๋…์— ๋Œ€ํ•œ ๊นŠ์€ ์ดํ•ด๊ฐ€ ์‹œ๋‹ˆ์–ด Go ์—”์ง€๋‹ˆ์–ด์™€ ํ•™์Šต ๋‹จ๊ณ„์˜ ๊ฐœ๋ฐœ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ํ•ต์‹ฌ ์š”์†Œ์ž…๋‹ˆ๋‹ค. ์ด ๊ฐ€์ด๋“œ์—์„œ๋Š” 2026๋…„ ๋ฉด์ ‘์—์„œ ์‹ค์ œ๋กœ ์ถœ์ œ๋˜๋Š” ์งˆ๋ฌธ์„ ํ”„๋กœ๋•์…˜๊ธ‰ ์ฝ”๋“œ ์˜ˆ์ œ ๋ฐ ๋‹ต๋ณ€์˜ ๊ทผ๊ฑฐ์™€ ํ•จ๊ป˜ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

๋ฉด์ ‘๊ด€์ด ์‹ค์ œ๋กœ ํ‰๊ฐ€ํ•˜๋Š” ์˜์—ญ

Go ๋™์‹œ์„ฑ ๋ฉด์ ‘์€ ์„ธ ๊ฐ€์ง€ ์˜์—ญ์— ์ง‘์ค‘๋ฉ๋‹ˆ๋‹ค: goroutine ๋ผ์ดํ”„์‚ฌ์ดํด ๊ด€๋ฆฌ, channel ์‹œ๋งจํ‹ฑ์Šค(๋ฒ„ํผ ์žˆ๋Š”/์—†๋Š”, ๋ฐฉํ–ฅ ํƒ€์ž…), ํŒจํ„ด ์กฐํ•ฉ(fan-out/fan-in, ์›Œ์ปค ํ’€, context ์ทจ์†Œ). ๋ฌธ๋ฒ• ์•”๊ธฐ๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•˜๋ฉฐ, ๋ ˆ์ด์Šค ์ปจ๋””์…˜๊ณผ ๋ฐ๋“œ๋ฝ์— ๋Œ€ํ•œ ๋…ผ๋ฆฌ์  ์ถ”๋ก  ๋Šฅ๋ ฅ์ด ์š”๊ตฌ๋ฉ๋‹ˆ๋‹ค.

Goroutine ๊ธฐ๋ณธ ๊ฐœ๋…: ๋ฉด์ ‘ ํ•„์ˆ˜ ์งˆ๋ฌธ

๋ฉด์ ‘์˜ ์ฒซ ๋‹จ๊ณ„์—์„œ๋Š” ํ›„๋ณด์ž๊ฐ€ goroutine์˜ ๋ณธ์งˆ์„ ์ดํ•ดํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ goroutine์„ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•„๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Q: goroutine์ด๋ž€ ๋ฌด์—‡์ด๋ฉฐ, OS ์Šค๋ ˆ๋“œ์™€ ์–ด๋–ป๊ฒŒ ๋‹ค๋ฆ…๋‹ˆ๊นŒ?

goroutine์€ ์šด์˜์ฒด์ œ๊ฐ€ ์•„๋‹Œ Go ๋Ÿฐํƒ€์ž„ ์Šค์ผ€์ค„๋Ÿฌ์— ์˜ํ•ด ๊ด€๋ฆฌ๋˜๋Š” ๊ฒฝ๋Ÿ‰ ๋™์‹œ ์‹คํ–‰ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. Go ๋Ÿฐํƒ€์ž„์€ M:N ์Šค์ผ€์ค„๋ง ๋ชจ๋ธ(M๊ฐœ์˜ goroutine์„ N๊ฐœ์˜ OS ์Šค๋ ˆ๋“œ์— ๋งคํ•‘)์„ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜์ฒœ ๊ฐœ์˜ goroutine์„ ์†Œ์ˆ˜์˜ OS ์Šค๋ ˆ๋“œ์— ๋‹ค์ค‘ํ™”ํ•ฉ๋‹ˆ๋‹ค.

goroutine_basics.gogo
package main

import (
	"fmt"
	"runtime"
	"sync"
)

func main() {
	// Print the number of OS threads available
	fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0))

	var wg sync.WaitGroup

	for i := 0; i < 10000; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			// Each goroutine starts with ~2-8KB stack
			// OS threads typically start with 1-8MB
			_ = id
		}(i)
	}

	wg.Wait()
	fmt.Println("All 10,000 goroutines completed")
}

๋ฉด์ ‘์—์„œ ๋ฐ˜๋“œ์‹œ ์–ธ๊ธ‰ํ•ด์•ผ ํ•  ์ฃผ์š” ์ฐจ์ด์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. goroutine์€ ๋™์ ์œผ๋กœ ํ™•์žฅ๋˜๋Š” 2-8KB ์Šคํƒ์œผ๋กœ ์‹œ์ž‘๋˜๋Š” ๋ฐ˜๋ฉด, OS ์Šค๋ ˆ๋“œ๋Š” ๊ณ ์ •๋œ 1-8MB ์Šคํƒ์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. goroutine ๊ฐ„์˜ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ์€ Go ์Šค์ผ€์ค„๋Ÿฌ์— ์˜ํ•ด ์œ ์ € ๊ณต๊ฐ„์—์„œ ์ฒ˜๋ฆฌ๋˜๋ฏ€๋กœ, OS ์Šค๋ ˆ๋“œ์˜ ์ปค๋„ ๋ ˆ๋ฒจ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ์— ๋”ฐ๋ฅธ ๋น„์šฉ์„ ํ”ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ 10๋งŒ ๊ฐœ์˜ goroutine์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์€ ํ˜„์‹ค์ ์ด์ง€๋งŒ, 10๋งŒ ๊ฐœ์˜ OS ์Šค๋ ˆ๋“œ๋Š” ์‹œ์Šคํ…œ ๋ฆฌ์†Œ์Šค๋ฅผ ๊ณ ๊ฐˆ์‹œํ‚ต๋‹ˆ๋‹ค.

Q: goroutine์—์„œ panic์ด ๋ฐœ์ƒํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ฉ๋‹ˆ๊นŒ?

goroutine ๋‚ด์—์„œ ๋ณต๊ตฌ๋˜์ง€ ์•Š์€ panic์€ ํ”„๋กœ๊ทธ๋žจ ์ „์ฒด๋ฅผ ํฌ๋ž˜์‹œ์‹œํ‚ต๋‹ˆ๋‹ค. Java๋‚˜ Python์˜ ์˜ˆ์™ธ์™€ ๋‹ฌ๋ฆฌ, panic์€ ํ•ด๋‹น goroutine ์ž์ฒด์˜ ์ฝœ ์Šคํƒ๋งŒ์„ ๋”ฐ๋ผ ์ „ํŒŒ๋ฉ๋‹ˆ๋‹ค(์ƒ์„ฑํ•œ goroutine์˜ ์Šคํƒ์ด ์•„๋‹™๋‹ˆ๋‹ค). ์ด๋ฅผ ์žก๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์€ ๋™์ผ goroutine ๋‚ด์˜ defer ํ•จ์ˆ˜์—์„œ recover()๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

panic_recovery.gogo
package main

import "fmt"

func safeGo(fn func()) {
	go func() {
		defer func() {
			if r := recover(); r != nil {
				fmt.Println("recovered from panic:", r)
			}
		}()
		fn() // execute the actual work
	}()
}

func main() {
	safeGo(func() {
		panic("something went wrong")
	})

	// Give goroutine time to complete
	select {}
}

๋ฉด์ ‘๊ด€์€ ํ”„๋กœ๋•์…˜ Go ์„œ๋น„์Šค์—์„œ goroutine ์‹คํ–‰์„ ๋ณต๊ตฌ ํŒจํ„ด์œผ๋กœ ๊ฐ์‹ธ๋Š” ๊ด€ํ–‰์— ๋Œ€ํ•œ ์ธ์‹์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. errgroup ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋” ์„ธ๋ จ๋œ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

Channel ์‹œ๋งจํ‹ฑ์Šค: ๋ฒ„ํผ ์žˆ๋Š”/์—†๋Š” ์ฑ„๋„๊ณผ ๋ฐฉํ–ฅ ํƒ€์ž…

channel ๊ด€๋ จ ์งˆ๋ฌธ์€ ํ›„๋ณด์ž๊ฐ€ Go์˜ ๋™์‹œ์„ฑ ๋ชจ๋ธ์„ ์ง„์ •์œผ๋กœ ์ดํ•ดํ•˜๊ณ  ์žˆ๋Š”์ง€, ์•„๋‹ˆ๋ฉด ํŒจํ„ด๋งŒ ์•”๊ธฐํ•œ ๊ฒƒ์ธ์ง€๋ฅผ ํŒ๋ณ„ํ•ฉ๋‹ˆ๋‹ค.

Q: ๋ฒ„ํผ ์žˆ๋Š” channel๊ณผ ๋ฒ„ํผ ์—†๋Š” channel์˜ ์ฐจ์ด์ ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๋ฒ„ํผ ์—†๋Š” channel(make(chan T))์€ ์†ก์‹ ์ธก๊ณผ ์ˆ˜์‹ ์ธก์ด ๋™์‹œ์— ์ค€๋น„๋˜์–ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์†ก์‹ ์€ ๋‹ค๋ฅธ goroutine์ด ์ˆ˜์‹ ํ•  ๋•Œ๊นŒ์ง€ ๋ธ”๋กœํ‚น๋ฉ๋‹ˆ๋‹ค. ๋ฒ„ํผ ์žˆ๋Š” channel(make(chan T, n))์€ ๋ธ”๋กœํ‚น ์—†์ด ์ตœ๋Œ€ n๊ฐœ์˜ ๊ฐ’์„ ์†ก์‹ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

channel_semantics.gogo
package main

import "fmt"

func main() {
	// Unbuffered: send blocks until receive is ready
	ch := make(chan string)
	go func() {
		ch <- "hello" // blocks here until main reads
	}()
	msg := <-ch
	fmt.Println(msg)

	// Buffered: send does not block until buffer is full
	buf := make(chan int, 3)
	buf <- 1 // does not block (buffer has space)
	buf <- 2 // does not block
	buf <- 3 // does not block
	// buf <- 4 would block โ€” buffer is full

	fmt.Println(<-buf, <-buf, <-buf) // 1 2 3
}

๋ฉด์ ‘๊ด€์ด ์ž์ฃผ ์ถ”๊ฐ€ํ•˜๋Š” ์งˆ๋ฌธ์€ "์–ธ์ œ ์–ด๋–ค ๊ฒƒ์„ ์„ ํƒํ•ฉ๋‹ˆ๊นŒ?"์ž…๋‹ˆ๋‹ค. ๋ฒ„ํผ ์—†๋Š” channel์€ ๋™๊ธฐํ™”๋ฅผ ๊ฐ•์ œํ•˜๋ฏ€๋กœ, ์†ก์‹ ์ธก์ด ์ˆ˜์‹ ์ธก์˜ ์ฒ˜๋ฆฌ ์™„๋ฃŒ๋ฅผ ํ™•์ธํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ๋ฒ„ํผ ์žˆ๋Š” channel์€ ์†ก์‹ ์ธก๊ณผ ์ˆ˜์‹ ์ธก์˜ ํƒ€์ด๋ฐ์„ ๋ถ„๋ฆฌํ•˜๋ฏ€๋กœ, ์›Œํฌ ํ๋‚˜ ์†๋„ ์ œํ•œ์ฒ˜๋Ÿผ ์–ด๋А ์ •๋„์˜ ์—ฌ์œ ๊ฐ€ ํ—ˆ์šฉ๋˜๋Š” ์ƒํ™ฉ์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

Q: channel์„ closeํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ฉ๋‹ˆ๊นŒ?

channel์„ closeํ•˜๋ฉด ๋” ์ด์ƒ ๊ฐ’์ด ์†ก์‹ ๋˜์ง€ ์•Š์Œ์„ ์•Œ๋ฆฝ๋‹ˆ๋‹ค. close๋œ channel์—์„œ์˜ ์ˆ˜์‹ ์€ ์ œ๋กœ ๊ฐ’์„ ์ฆ‰์‹œ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. close๋œ channel์— ์†ก์‹ ํ•˜๋ฉด panic์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. range ๋ฃจํ”„๋Š” channel์ด close๋˜๋ฉด ์ž๋™์œผ๋กœ ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.

close_channel.gogo
package main

import "fmt"

func producer(ch chan<- int, count int) {
	for i := 0; i < count; i++ {
		ch <- i
	}
	close(ch) // signal: no more values
}

func main() {
	ch := make(chan int, 5)
	go producer(ch, 5)

	// range exits automatically when channel closes
	for val := range ch {
		fmt.Println("received:", val)
	}

	// Reading from closed channel returns zero value + false
	val, ok := <-ch
	fmt.Printf("after close: val=%d, ok=%v\n", val, ok)
}

ํ•ต์‹ฌ ์ฃผ์˜์‚ฌํ•ญ์œผ๋กœ, channel์„ closeํ•˜๋Š” ๊ฒƒ์€ ์†ก์‹ ์ธก๋งŒ ํ•ด์•ผ ํ•˜๋ฉฐ ์ˆ˜์‹ ์ธก์ด closeํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ goroutine์ด ์•„์ง ์“ฐ๊ธฐ ์ค‘์ธ channel์„ closeํ•˜๋ฉด panic์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

Go ๋ฉด์ ‘ ์ค€๋น„๊ฐ€ ๋˜์…จ๋‚˜์š”?

์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ, flashcards, ๊ธฐ์ˆ  ํ…Œ์ŠคํŠธ๋กœ ์—ฐ์Šตํ•˜์„ธ์š”.

select ๋ฌธ: channel ๋‹ค์ค‘ํ™”

Q: select๋Š” ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋ฉฐ, ์—ฌ๋Ÿฌ case๊ฐ€ ๋™์‹œ์— ์ค€๋น„๋œ ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ ๋ฉ๋‹ˆ๊นŒ?

select ๋ฌธ์€ channel ์—ฐ์‚ฐ ์ค‘ ํ•˜๋‚˜๊ฐ€ ์‹คํ–‰ ๊ฐ€๋Šฅํ•ด์งˆ ๋•Œ๊นŒ์ง€ ๋ธ”๋กœํ‚น๋ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ case๊ฐ€ ๋™์‹œ์— ์ค€๋น„๋œ ๊ฒฝ์šฐ, Go๋Š” ๋ฌด์ž‘์œ„๋กœ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํŠน์ • case๊ฐ€ ๊ธฐ์•„ ์ƒํƒœ์— ๋น ์ง€๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

select_multiplex.gogo
package main

import (
	"context"
	"fmt"
	"time"
)

func fetchFromAPI(ctx context.Context, url string) (string, error) {
	resultCh := make(chan string, 1)
	errCh := make(chan error, 1)

	go func() {
		// Simulate API call
		time.Sleep(200 * time.Millisecond)
		resultCh <- fmt.Sprintf("data from %s", url)
	}()

	select {
	case result := <-resultCh:
		return result, nil
	case err := <-errCh:
		return "", err
	case <-ctx.Done():
		// Context cancelled or timed out
		return "", ctx.Err()
	}
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel()

	result, err := fetchFromAPI(ctx, "https://api.example.com/data")
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Println(result)
}

๋ฉด์ ‘๊ด€์ด select๋กœ ํ‰๊ฐ€ํ•˜๋Š” ๊ฒƒ์€ ๋‘ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ๋ฌด์ž‘์œ„ ์„ ํƒ ๊ทœ์น™์— ๋Œ€ํ•œ ์ดํ•ด, ๊ทธ๋ฆฌ๊ณ  channel์„ context.Context์™€ ๊ฒฐํ•ฉํ•˜์—ฌ ํƒ€์ž„์•„์›ƒ ๋ฐ ์ทจ์†Œ ํŒจํ„ด์„ ๊ตฌํ˜„ํ•˜๋Š” ๋Šฅ๋ ฅ์ž…๋‹ˆ๋‹ค.

๋ฉด์ ‘์—์„œ ์ถœ์ œ๋˜๋Š” ๋™์‹œ์„ฑ ํŒจํ„ด

์‹œ๋‹ˆ์–ด ๋ ˆ๋ฒจ Go ๋ฉด์ ‘์—์„œ๋Š” ์•„๋ž˜ ํŒจํ„ด ์ค‘ ํ•˜๋‚˜๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ตฌํ˜„ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๊ฑฐ์˜ ๋ฐ˜๋“œ์‹œ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค.

Fan-Out/Fan-In ํŒจํ„ด

Q: ํ•ญ๋ชฉ์„ ๋™์‹œ์— ์ฒ˜๋ฆฌํ•˜๋Š” fan-out/fan-in ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌํ˜„ํ•˜์‹ญ์‹œ์˜ค.

fan-out์€ ์—ฌ๋Ÿฌ goroutine์— ์ž‘์—…์„ ๋ถ„๋ฐฐํ•ฉ๋‹ˆ๋‹ค. fan-in์€ ์—ฌ๋Ÿฌ goroutine์˜ ๊ฒฐ๊ณผ๋ฅผ ํ•˜๋‚˜์˜ channel๋กœ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค.

fanout_fanin.gogo
package main

import (
	"fmt"
	"sync"
)

// generator produces values on a channel
func generator(nums ...int) <-chan int {
	out := make(chan int)
	go func() {
		for _, n := range nums {
			out <- n
		}
		close(out)
	}()
	return out
}

// square reads from input, squares each value
func square(in <-chan int) <-chan int {
	out := make(chan int)
	go func() {
		for n := range in {
			out <- n * n
		}
		close(out)
	}()
	return out
}

// fanIn merges multiple channels into one
func fanIn(channels ...<-chan int) <-chan int {
	var wg sync.WaitGroup
	merged := make(chan int)

	for _, ch := range channels {
		wg.Add(1)
		go func(c <-chan int) {
			defer wg.Done()
			for val := range c {
				merged <- val
			}
		}(ch)
	}

	go func() {
		wg.Wait()
		close(merged) // close after all inputs are drained
	}()

	return merged
}

func main() {
	in := generator(2, 3, 4, 5, 6)

	// Fan out: two goroutines reading from same channel
	c1 := square(in)
	c2 := square(in)

	// Fan in: merge results
	for result := range fanIn(c1, c2) {
		fmt.Println(result)
	}
}

๋ฉด์ ‘๊ด€์ด ๊ธฐ๋Œ€ํ•˜๋Š” ํ•ต์‹ฌ ํ†ต์ฐฐ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. generator์˜ channel์€ c1๊ณผ c2 ์‚ฌ์ด์— ๊ณต์œ ๋˜๋ฏ€๋กœ, ๊ฐ ๊ฐ’์€ ์ •ํ™•ํžˆ ํ•˜๋‚˜์˜ ์›Œ์ปค์— ์˜ํ•ด์„œ๋งŒ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค(์ค‘๋ณต ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š์Œ). fanIn ํ•จ์ˆ˜๋Š” WaitGroup์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“  ์ž…๋ ฅ channel์ด ์†Œ์ง„๋œ ํ›„์— ๋ณ‘ํ•ฉ๋œ channel์„ closeํ•ฉ๋‹ˆ๋‹ค.

errgroup์„ ํ™œ์šฉํ•œ ์›Œ์ปค ํ’€

Q: ์—๋Ÿฌ ํ•ธ๋“ค๋ง์ด ํฌํ•จ๋œ ์ œํ•œ๋œ ์›Œ์ปค ํ’€์„ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๊นŒ?

golang.org/x/sync/errgroup ํŒจํ‚ค์ง€(Go ํ™•์žฅ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)๊ฐ€ ์ด ๋ฌธ์ œ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. goroutine ๋ผ์ดํ”„์‚ฌ์ดํด ๊ด€๋ฆฌ, ์ฒซ ๋ฒˆ์งธ ์—๋Ÿฌ ์ˆ˜์ง‘, context์™€์˜ ํ†ตํ•ฉ์„ ํ†ตํ•œ ์ทจ์†Œ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

worker_pool.gogo
package main

import (
	"context"
	"fmt"
	"golang.org/x/sync/errgroup"
)

func processItem(ctx context.Context, id int) error {
	// Check for cancellation before heavy work
	select {
	case <-ctx.Done():
		return ctx.Err()
	default:
	}

	if id == 7 {
		return fmt.Errorf("failed to process item %d", id)
	}
	fmt.Printf("processed item %d\n", id)
	return nil
}

func main() {
	g, ctx := errgroup.WithContext(context.Background())
	g.SetLimit(3) // maximum 3 concurrent goroutines

	for i := 0; i < 10; i++ {
		id := i
		g.Go(func() error {
			return processItem(ctx, id)
		})
	}

	// Wait blocks until all goroutines finish
	// Returns the first non-nil error
	if err := g.Wait(); err != nil {
		fmt.Println("pipeline error:", err)
	}
}

2026๋…„ ์ดˆ ๊ธฐ์ค€ ํ˜„์žฌ ์•ˆ์ • ๋ฆด๋ฆฌ์Šค์ธ Go 1.24์—์„œ๋„ ์ด ํŒจํ„ด์ด ๊ถŒ์žฅ ์ ‘๊ทผ๋ฒ•์œผ๋กœ ์œ ์ง€๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. SetLimit ๋ฉ”์„œ๋“œ๋Š” Go 1.20์—์„œ ์ถ”๊ฐ€๋˜์–ด, ์„ธ๋งˆํฌ์–ด ๊ธฐ๋ฐ˜ ๋™์‹œ์„ฑ ์ œํ•œ์„ ์ˆ˜๋™์œผ๋กœ ๊ตฌํ˜„ํ•  ํ•„์š”๊ฐ€ ์—†์–ด์กŒ์Šต๋‹ˆ๋‹ค.

๋ ˆ์ด์Šค ์ปจ๋””์…˜๊ณผ sync ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ

Q: Go์—์„œ ๋ ˆ์ด์Šค ์ปจ๋””์…˜์„ ์–ด๋–ป๊ฒŒ ๊ฐ์ง€ํ•˜๊ณ  ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๊นŒ?

Go๋Š” -race ํ”Œ๋ž˜๊ทธ๋กœ ํ™œ์„ฑํ™”๋˜๋Š” ๋‚ด์žฅ ๋ ˆ์ด์Šค ๋””ํ…ํ„ฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋Ÿฐํƒ€์ž„์— ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ์— ๋Œ€ํ•œ ๋น„๋™๊ธฐํ™”๋œ ๋™์‹œ ์ ‘๊ทผ์„ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค.

race_condition.gogo
package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

// BAD: race condition โ€” do not use in production
func unsafeCounter() int {
	counter := 0
	var wg sync.WaitGroup
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			counter++ // DATA RACE: concurrent read/write
		}()
	}
	wg.Wait()
	return counter // result is non-deterministic
}

// GOOD: atomic operations for simple counters
func atomicCounter() int64 {
	var counter atomic.Int64
	var wg sync.WaitGroup
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			counter.Add(1) // thread-safe atomic increment
		}()
	}
	wg.Wait()
	return counter.Load() // always 1000
}

// GOOD: mutex for complex shared state
type SafeMap struct {
	mu sync.RWMutex
	data map[string]int
}

func (m *SafeMap) Set(key string, val int) {
	m.mu.Lock()         // exclusive lock for writes
	defer m.mu.Unlock()
	m.data[key] = val
}

func (m *SafeMap) Get(key string) (int, bool) {
	m.mu.RLock()         // shared lock for reads
	defer m.mu.RUnlock()
	v, ok := m.data[key]
	return v, ok
}

func main() {
	fmt.Println("unsafe:", unsafeCounter())  // unpredictable
	fmt.Println("atomic:", atomicCounter())  // always 1000
}

๋ฉด์ ‘ ๋‹ต๋ณ€์€ ์„ธ ๊ฐ€์ง€ ๋™๊ธฐํ™” ์ „๋žต์„ ๋‹ค๋ฃจ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ณต์žกํ•œ ๊ณต์œ  ์ƒํƒœ์—๋Š” sync.Mutex/sync.RWMutex, ๋‹จ์ˆœ ์นด์šดํ„ฐ์™€ ํ”Œ๋ž˜๊ทธ์—๋Š” sync/atomic, goroutine ๊ฐ„ ํ†ต์‹ ์—๋Š” channel("๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ณต์œ ํ•˜์—ฌ ํ†ต์‹ ํ•˜์ง€ ๋ง๊ณ , ํ†ต์‹ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ณต์œ ํ•˜๋ผ")์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. go test -race ./... ์‹คํ–‰์€ ๋ชจ๋“  CI ํŒŒ์ดํ”„๋ผ์ธ์— ํฌํ•จ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Context์™€ ์ทจ์†Œ ํŒจํ„ด

Q: context.Context๊ฐ€ goroutine์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์„ ์–ด๋–ป๊ฒŒ ์ œ์–ดํ•˜๋Š”์ง€ ์„ค๋ช…ํ•˜์‹ญ์‹œ์˜ค.

context ํŒจํ‚ค์ง€๋Š” ์ทจ์†Œ ์‹œ๊ทธ๋„, ๋ฐ๋“œ๋ผ์ธ, ์š”์ฒญ ๋ฒ”์œ„ ๊ฐ’์„ goroutine ๊ฒฝ๊ณ„๋ฅผ ๋„˜์–ด ์ „ํŒŒํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์žฅ์‹œ๊ฐ„ ์‹คํ–‰๋˜๋Š” ๋ชจ๋“  goroutine์€ ์ฒซ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ context.Context๋ฅผ ๋ฐ›์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

context_cancellation.gogo
package main

import (
	"context"
	"fmt"
	"time"
)

// worker simulates a long-running task
func worker(ctx context.Context, id int, results chan<- string) {
	select {
	case <-time.After(time.Duration(id*100) * time.Millisecond):
		results <- fmt.Sprintf("worker %d: done", id)
	case <-ctx.Done():
		results <- fmt.Sprintf("worker %d: cancelled (%v)", id, ctx.Err())
	}
}

func main() {
	// Parent context with 250ms deadline
	ctx, cancel := context.WithTimeout(context.Background(), 250*time.Millisecond)
	defer cancel()

	results := make(chan string, 5)

	// Launch 5 workers with increasing durations
	for i := 1; i <= 5; i++ {
		go worker(ctx, i, results)
	}

	// Collect all results
	for i := 0; i < 5; i++ {
		fmt.Println(<-results)
	}
}

์›Œ์ปค 1๊ณผ 2๋Š” 250ms ๋ฐ๋“œ๋ผ์ธ ๋‚ด์— ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค. ์›Œ์ปค 3, 4, 5๋Š” ctx.Done()์„ ํ†ตํ•ด ์ทจ์†Œ ์‹œ๊ทธ๋„์„ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค. ์ด ํŒจํ„ด์€ Go์—์„œ ๋ณต์›๋ ฅ ์žˆ๋Š” HTTP ์„œ๋ฒ„์™€ ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐ ๊ทผ๋ณธ์ด ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ์š”์ฒญ ํ•ธ๋“ค๋Ÿฌ๋Š” ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ์ด ๋Š์–ด์งˆ ๋•Œ ์ทจ์†Œ๊ฐ€ ์ „ํŒŒ๋˜๋Š” context๋ฅผ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค.

๋ฉด์ ‘์—์„œ ์ž์ฃผ ๋‚˜์˜ค๋Š” ํ•จ์ •

context.Context๋ฅผ ๊ตฌ์กฐ์ฒด ํ•„๋“œ์— ์ €์žฅํ•ด์„œ๋Š” ์•ˆ ๋ฉ๋‹ˆ๋‹ค. Go ๊ณต์‹ ๋ฌธ์„œ์—๋Š” "Context๋ฅผ ๊ตฌ์กฐ์ฒด ํƒ€์ž… ๋‚ด๋ถ€์— ์ €์žฅํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ๋Œ€์‹ , Context๊ฐ€ ํ•„์š”ํ•œ ๊ฐ ํ•จ์ˆ˜์— ๋ช…์‹œ์ ์œผ๋กœ ์ „๋‹ฌํ•˜์‹ญ์‹œ์˜ค"๋ผ๊ณ  ๋ช…์‹œ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฉด์ ‘๊ด€์€ ์ด ์งˆ๋ฌธ์œผ๋กœ ํ›„๋ณด์ž๊ฐ€ Go ๊ด€๋ก€๋ฅผ ๋”ฐ๋ฅด๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

๋ฐ๋“œ๋ฝ ๊ฐ์ง€: ๋ฉด์ ‘ ๋‚œ์ด๋„ ๋†’์€ ์งˆ๋ฌธ

Q: ์ด ์ฝ”๋“œ๋Š” ๋ฐ๋“œ๋ฝ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๊นŒ? ๊ทธ ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

๋ฐ๋“œ๋ฝ ์งˆ๋ฌธ์€ ํ›„๋ณด์ž๊ฐ€ goroutine ์Šค์ผ€์ค„๋ง๊ณผ channel ์—ฐ์‚ฐ์— ๋Œ€ํ•ด ์ถ”๋ก ํ•˜๋Š” ๋Šฅ๋ ฅ์„ ํ…Œ์ŠคํŠธํ•˜๋ฏ€๋กœ ์ž์ฃผ ์ถœ์ œ๋ฉ๋‹ˆ๋‹ค.

deadlock_example.gogo
package main

func main() {
	ch := make(chan int)
	ch <- 42 // DEADLOCK: unbuffered send with no receiver
	// The main goroutine blocks here forever
	// Go runtime detects this: "fatal error: all goroutines are asleep"
}

์ˆ˜์ • ๋ฐฉ๋ฒ•์€ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. channel์„ ๋ฒ„ํผ ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜(make(chan int, 1)), ์†ก์‹  ์ „์— ์ˆ˜์‹ ์šฉ goroutine์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. Go ๋Ÿฐํƒ€์ž„์€ ๋ชจ๋“  goroutine์ด ๋ธ”๋กœํ‚น๋œ ๊ฒฝ์šฐ ๋ฐ๋“œ๋ฝ์„ ๊ฐ์ง€ํ•˜์ง€๋งŒ, ๋ชจ๋“  goroutine์ด ์Šฌ๋ฆฝ ์ƒํƒœ์ธ ๊ฒฝ์šฐ์—๋งŒ ํ•ด๋‹น๋ฉ๋‹ˆ๋‹ค. ๋‹จ ํ•˜๋‚˜์˜ goroutine์ด๋ผ๋„ ์‹คํ–‰ ์ค‘์ด๋ฉด(์˜ˆ: ๋ฐฑ๊ทธ๋ผ์šด๋“œ HTTP ์„œ๋ฒ„), ๋Ÿฐํƒ€์ž„์€ ๋ถ€๋ถ„์  ๋ฐ๋“œ๋ฝ์„ ๊ฐ์ง€ํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.

๋ถ€๋ถ„์  ๋ฐ๋“œ๋ฝ์€ ๊ฐ์ง€๋˜์ง€ ์•Š๋Š”๋‹ค

Go ๋Ÿฐํƒ€์ž„์ด ๋ฐ๋“œ๋ฝ์„ ๊ฐ์ง€ํ•˜๋Š” ๊ฒƒ์€ ํ”„๋กœ๊ทธ๋žจ ๋‚ด์˜ ๋ชจ๋“  goroutine์ด ๋ธ”๋กœํ‚น๋œ ๊ฒฝ์šฐ์—๋งŒ ํ•ด๋‹น๋ฉ๋‹ˆ๋‹ค. HTTP ์„œ๋ฒ„๋‚˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์›Œ์ปค๊ฐ€ ์žˆ๋Š” ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ๋ฐ๋“œ๋ฝ ์ƒํƒœ์˜ goroutine ๋ˆ„์ˆ˜๊ฐ€ ๋Ÿฐํƒ€์ž„ ๋””ํ…ํ„ฐ์— ์˜ํ•ด ๊ฐ์ง€๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ์ง„๋‹จํ•˜๋ ค๋ฉด pprof์™€ goroutine ๋คํ”„(runtime.Stack) ๊ฐ™์€ ๋„๊ตฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๊ณ ๊ธ‰ ํŒจํ„ด: ์†๋„ ์ œํ•œ ๋™์‹œ ์ฒ˜๋ฆฌ

Q: ์†๋„ ์ œํ•œ์ด ์ ์šฉ๋œ ๋™์‹œ API ํ˜ธ์ถœ์„ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๊นŒ?

์ด ์งˆ๋ฌธ์€ ์—ฌ๋Ÿฌ ๋™์‹œ์„ฑ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋ฅผ ํ•˜๋‚˜์˜ ํ†ตํ•ฉ๋œ ์†”๋ฃจ์…˜์œผ๋กœ ์กฐํ•ฉํ•˜๋Š” ๋Šฅ๋ ฅ์„ ํ‰๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

rate_limited.gogo
package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

// RateLimiter controls concurrent and temporal access
type RateLimiter struct {
	semaphore chan struct{}   // limits concurrency
	ticker    *time.Ticker    // limits rate
}

func NewRateLimiter(maxConcurrent int, interval time.Duration) *RateLimiter {
	return &RateLimiter{
		semaphore: make(chan struct{}, maxConcurrent),
		ticker:    time.NewTicker(interval),
	}
}

func (rl *RateLimiter) Execute(ctx context.Context, fn func() error) error {
	// Wait for rate limit tick
	select {
	case <-rl.ticker.C:
	case <-ctx.Done():
		return ctx.Err()
	}

	// Acquire concurrency slot
	select {
	case rl.semaphore <- struct{}{}:
	case <-ctx.Done():
		return ctx.Err()
	}

	defer func() { <-rl.semaphore }() // release slot
	return fn()
}

func main() {
	rl := NewRateLimiter(3, 100*time.Millisecond)
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()

	var wg sync.WaitGroup
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			err := rl.Execute(ctx, func() error {
				fmt.Printf("[%v] processing %d\n", time.Now().Format("04:05.000"), id)
				time.Sleep(150 * time.Millisecond) // simulate work
				return nil
			})
			if err != nil {
				fmt.Printf("item %d: %v\n", id, err)
			}
		}(i)
	}
	wg.Wait()
}

์ด ํŒจํ„ด์€ channel ๊ธฐ๋ฐ˜ ์„ธ๋งˆํฌ์–ด(๋™์‹œ์„ฑ ์ œํ•œ)์™€ ticker(์†๋„ ์ œํ•œ)๋ฅผ ๊ฒฐํ•ฉํ•ฉ๋‹ˆ๋‹ค. context ์ฒดํฌ๊ฐ€ ํฌํ•จ๋œ ์ด์ค‘ select๊ฐ€ ๊ทธ๋ ˆ์ด์Šคํ’€ ์…ง๋‹ค์šด์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด์™€ ๊ฐ™์€ ํ”„๋กœ๋•์…˜ ์ˆ˜์ค€์˜ ๋‹ต๋ณ€์ด ์‹œ๋‹ˆ์–ด ํ›„๋ณด์ž๋ฅผ ์ฐจ๋ณ„ํ™”ํ•ฉ๋‹ˆ๋‹ค.

์—ฐ์Šต์„ ์‹œ์ž‘ํ•˜์„ธ์š”!

๋ฉด์ ‘ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์™€ ๊ธฐ์ˆ  ํ…Œ์ŠคํŠธ๋กœ ์ง€์‹์„ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

๊ฒฐ๋ก 

  • goroutine์€ M:N ์Šค์ผ€์ค„๋ง์„ ๊ฐ€์ง„ Go ๋Ÿฐํƒ€์ž„ ๊ด€๋ฆฌ ์œ ์ € ๊ณต๊ฐ„ ์Šค๋ ˆ๋“œ์ด๋ฉฐ, ์ƒ์„ฑ๋œ goroutine ๋‚ด์—์„œ panic์„ ๋ฐ˜๋“œ์‹œ ๋ณต๊ตฌํ•ด์•ผ ํ•œ๋‹ค
  • ๋ฒ„ํผ ์—†๋Š” channel์€ ์†ก์‹ ์ธก๊ณผ ์ˆ˜์‹ ์ธก์„ ๋™๊ธฐํ™”ํ•˜๊ณ , ๋ฒ„ํผ ์žˆ๋Š” channel์€ ํƒ€์ด๋ฐ์„ ๋ถ„๋ฆฌํ•œ๋‹ค. ์†ก์‹ ์ธก์ด ํ™•์ธ์„ ํ•„์š”๋กœ ํ•˜๋Š”์ง€์— ๋”ฐ๋ผ ์„ ํƒํ•œ๋‹ค
  • select ๋ฌธ์€ ๋ฌด์ž‘์œ„ ์„ ํƒ์œผ๋กœ channel ์—ฐ์‚ฐ์„ ๋‹ค์ค‘ํ™”ํ•œ๋‹ค. ํƒ€์ž„์•„์›ƒ ์ฒ˜๋ฆฌ์—๋Š” context.Context์™€ ๊ฒฐํ•ฉํ•˜์—ฌ ์‚ฌ์šฉํ•œ๋‹ค
  • fan-out/fan-in๊ณผ ์›Œ์ปค ํ’€(errgroup.SetLimit ์‚ฌ์šฉ)์ด ๋ฉด์ ‘์—์„œ ๊ฐ€์žฅ ์ž์ฃผ ์ถœ์ œ๋˜๋Š” ๋™์‹œ์„ฑ ํŒจํ„ด์ด๋‹ค
  • ๋ณต์žกํ•œ ๊ณต์œ  ์ƒํƒœ์—๋Š” sync.Mutex, ๋‹จ์ˆœ ์นด์šดํ„ฐ์—๋Š” sync/atomic, goroutine ๊ฐ„ ํ†ต์‹ ์—๋Š” channel์„ ์‚ฌ์šฉํ•œ๋‹ค
  • ๋ฐ์ดํ„ฐ ๋ ˆ์ด์Šค ๊ฐ์ง€๋ฅผ ์œ„ํ•ด CI์—์„œ go test -race๋ฅผ ๋ฐ˜๋“œ์‹œ ์‹คํ–‰ํ•œ๋‹ค. ๋ถ€๋ถ„์  ๋ฐ๋“œ๋ฝ ์ง„๋‹จ์—๋Š” pprof๊ฐ€ ํ•„์š”ํ•˜๋‹ค
  • context.Context๋ฅผ ๊ตฌ์กฐ์ฒด์— ์ €์žฅํ•˜์ง€ ์•Š๊ณ , ์ฒซ ๋ฒˆ์งธ ํ•จ์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌํ•œ๋‹ค
  • Go์˜ ์†๋„ ์ œํ•œ์€ channel ์„ธ๋งˆํฌ์–ด์™€ ticker๋ฅผ ๊ฒฐํ•ฉํ•˜๊ณ , context ์ธ์‹ select ๋ฌธ์œผ๋กœ ๋ž˜ํ•‘ํ•œ๋‹ค

์—ฐ์Šต์„ ์‹œ์ž‘ํ•˜์„ธ์š”!

๋ฉด์ ‘ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์™€ ๊ธฐ์ˆ  ํ…Œ์ŠคํŠธ๋กœ ์ง€์‹์„ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

Anthony Fillion-Maillet

์ž‘์„ฑ์ž

Anthony Fillion-Maillet

ํ’€์Šคํƒ ๊ฐœ๋ฐœ์ž, SharpSkill ์ฐฝ์—…์ž

10๋…„ ์ด์ƒ ํ’€์Šคํƒ ๊ฐœ๋ฐœ์„ ํ•ด์™”์Šต๋‹ˆ๋‹ค. SharpSkill์„ ์šด์˜ํ•˜๋ฉฐ ์ด๊ณณ์— ๊ฒŒ์‹œ๋˜๋Š” ๋ชจ๋“  ๋‚ด์šฉ์— ์ฑ…์ž„์„ ์ง‘๋‹ˆ๋‹ค.

2026๋…„ 4์›” 1์ผ ์—…๋ฐ์ดํŠธ

ํƒœ๊ทธ

#go
#golang
#interview
#goroutines
#channels
#concurrency

๊ณต์œ 

๊ด€๋ จ ๊ธฐ์‚ฌ

Go ๋™์‹œ์„ฑ - ๊ณ ๋ฃจํ‹ด๊ณผ ์ฑ„๋„์˜ ์ž‘๋™

Go ๋™์‹œ์„ฑ: ๊ณ ๋ฃจํ‹ด๊ณผ ์ฑ„๋„ - ์™„๋ฒฝ ๊ฐ€์ด๋“œ

๊ณ ๋ฃจํ‹ด๊ณผ ์ฑ„๋„์„ ์‚ฌ์šฉํ•ด Go ๋™์‹œ์„ฑ์„ ๋งˆ์Šคํ„ฐํ•˜์„ธ์š”. ๊ณ ๊ธ‰ ํŒจํ„ด, ๋™๊ธฐํ™”, select ๋ฌธ, ๋ชจ๋ฒ” ์‚ฌ๋ก€๋ฅผ ์ž์„ธํ•œ ์ฝ”๋“œ ์˜ˆ์ œ์™€ ํ•จ๊ป˜ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

Go ๋ฉด์ ‘ ์งˆ๋ฌธ - ์™„์ „ ์ค€๋น„ ๊ฐ€์ด๋“œ

Go ๋ฉด์ ‘ ํ•ต์‹ฌ 25๋ฌธํ•ญ: ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ์™„์ „ ๊ฐ€์ด๋“œ

Go ๋ฉด์ ‘์— ๊ฐ€์žฅ ๋งŽ์ด ๋“ฑ์žฅํ•˜๋Š” 25๊ฐœ ์งˆ๋ฌธ์œผ๋กœ ํ•ฉ๊ฒฉ์„ ๋…ธ๋ฆฌ์„ธ์š”. ๊ณ ๋ฃจํ‹ด, ์ฑ„๋„, ์ธํ„ฐํŽ˜์ด์Šค, ๋™์‹œ์„ฑ ํŒจํ„ด์„ ์ฝ”๋“œ ์˜ˆ์ œ๋กœ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

Java ๋ฐ Python ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ Go ๊ฐ€์ด๋“œ

Go: Java/Python ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ๊ธฐ์ดˆ 2026

Java๋‚˜ Python ๊ฒฝํ—˜์„ ํ™œ์šฉํ•˜์—ฌ Go๋ฅผ ๋น ๋ฅด๊ฒŒ ๋ฐฐ์šฐ์„ธ์š”. ๊ณ ๋ฃจํ‹ด, ์ฑ„๋„, ์ธํ„ฐํŽ˜์ด์Šค ๋ฐ ์›ํ™œํ•œ ์ „ํ™˜์„ ์œ„ํ•œ ํ•ต์‹ฌ ํŒจํ„ด์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.