Go ๋””์ž์ธ ํŒจํ„ด: Go ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ํ•„์ˆ˜ ํŒจํ„ด๊ณผ ๋ฉด์ ‘ ๋Œ€๋น„ ๊ฐ€์ด๋“œ

Go์˜ ์„ค๊ณ„ ์ฒ ํ•™์— ๊ธฐ๋ฐ˜ํ•œ ๋””์ž์ธ ํŒจํ„ด์„ ์ฒด๊ณ„์ ์œผ๋กœ ํ•ด์„คํ•ฉ๋‹ˆ๋‹ค. Functional Options, Strategy, Observer, Middleware ๋“ฑ ๋ฉด์ ‘์—์„œ ์ž์ฃผ ์ถœ์ œ๋˜๋Š” ์‹ค์ „ ํŒจํ„ด๊ณผ ๋‹ต๋ณ€ ์ „๋žต์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

Go ๋””์ž์ธ ํŒจํ„ด: Go ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ํ•„์ˆ˜ ํŒจํ„ด ๋ฐ ๋ฉด์ ‘ ๊ฐ€์ด๋“œ

Go์˜ ๋””์ž์ธ ํŒจํ„ด์€ ๊ฐ์ฒด์ง€ํ–ฅ ์–ธ์–ด์˜ ํŒจํ„ด๊ณผ ๊ทผ๋ณธ์ ์œผ๋กœ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ํด๋ž˜์Šค๋‚˜ ์ƒ์† ์—†์ด Go๋Š” ์ปดํฌ์ง€์…˜, ์ธํ„ฐํŽ˜์ด์Šค, ์ผ๊ธ‰ ํ•จ์ˆ˜๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋™์ผํ•œ ๊ตฌ์กฐ์  ์œ ์—ฐ์„ฑ์„ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ํ”ํžˆ ์ ์€ ์ฝ”๋“œ์™€ ๋” ๋†’์€ ๋ช…ํ™•์„ฑ์œผ๋กœ ์ด๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

Go ์„ค๊ณ„ ์›์น™

Go๋Š” ์ƒ์†๋ณด๋‹ค ์ปดํฌ์ง€์…˜์„ ์šฐ์„ ํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ์ „ํ†ต์ ์ธ ๋””์ž์ธ ํŒจํ„ด์€ ํด๋ž˜์Šค ๊ณ„์ธต ๊ตฌ์กฐ๊ฐ€ ์•„๋‹Œ ์ธํ„ฐํŽ˜์ด์Šค, ๊ตฌ์กฐ์ฒด ์ž„๋ฒ ๋”ฉ, ์ผ๊ธ‰ ํ•จ์ˆ˜๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Functional Options ํŒจํ„ด: ์œ ์—ฐํ•œ ์„ค์ • ๊ตฌํ˜„

Functional Options ํŒจํ„ด์€ Go์—์„œ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ๋งŽ์€ ์„ ํƒ์  ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ง„ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ, ๋ฉ”์„œ๋“œ ์˜ค๋ฒ„๋กœ๋”ฉ์ด๋‚˜ ๊ธฐ๋ณธ ์ธ์ˆ˜๊ฐ€ ์—†๋Š” Go์—์„œ๋Š” ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ๊ฐ€ ๊นŒ๋‹ค๋กญ์Šต๋‹ˆ๋‹ค. Builder ํŒจํ„ด๋„ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์žฅํ™ฉํ•ด์ง€๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. Functional Options๋Š” ์š”๊ตฌ์‚ฌํ•ญ์ด ์ฆ๊ฐ€ํ•ด๋„ ํ•˜์œ„ ํ˜ธํ™˜์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๊น”๋”ํ•˜๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•œ API๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ด ํŒจํ„ด์€ Dave Cheney๊ฐ€ ๋Œ€์ค‘ํ™”ํ–ˆ์œผ๋ฉฐ, ํ˜„์žฌ Go ์ƒํƒœ๊ณ„ ์ „๋ฐ˜์—์„œ ํ‘œ์ค€์œผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ฐ€๋ณ€ ์ธ์ˆ˜ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์กฐ์ฒด๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

server.gogo
package server

import (
	"time"
	"log/slog"
)

// Server holds the HTTP server configuration.
type Server struct {
	host         string
	port         int
	timeout      time.Duration
	maxConns     int
	logger       *slog.Logger
}

// Option defines a functional option for Server.
type Option func(*Server)

// WithPort sets the server port.
func WithPort(port int) Option {
	return func(s *Server) {
		s.port = port
	}
}

// WithTimeout sets the request timeout.
func WithTimeout(d time.Duration) Option {
	return func(s *Server) {
		s.timeout = d
	}
}

// WithMaxConns sets the maximum concurrent connections.
func WithMaxConns(n int) Option {
	return func(s *Server) {
		s.maxConns = n
	}
}

// New creates a Server with sensible defaults and applies options.
func New(host string, opts ...Option) *Server {
	srv := &Server{
		host:     host,
		port:     8080,           // default port
		timeout:  30 * time.Second, // default timeout
		maxConns: 100,            // default max connections
		logger:   slog.Default(),
	}
	for _, opt := range opts {
		opt(srv)
	}
	return srv
}

ํ˜ธ์ถœํ•˜๋Š” ์ชฝ์—์„œ๋Š” ๊ธฐ๋ณธ๊ฐ’๊ณผ ๋‹ค๋ฅธ ๋ถ€๋ถ„๋งŒ ์ง€์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ์˜ต์…˜์„ ์ถ”๊ฐ€ํ•ด๋„ ๊ธฐ์กด ํ˜ธ์ถœ ์ฝ”๋“œ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ํŒจํ„ด์€ google.golang.org/grpc์™€ go.uber.org/zap ๊ฐ™์€ ํ”„๋กœ๋•์…˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ๋„ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

Strategy ํŒจํ„ด: ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋ฐ˜ ๋‹คํ˜•์„ฑ

Strategy ํŒจํ„ด์€ ๊ต์ฒด ๊ฐ€๋Šฅํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค ๋’ค์— ์บก์Аํ™”ํ•ฉ๋‹ˆ๋‹ค. Go์—์„œ๋Š” ์ถ”์ƒ ํด๋ž˜์Šค๋‚˜ ์ƒ์† ์ฒด์ธ ์—†์ด ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋ฐ˜ ๋‹คํ˜•์„ฑ์œผ๋กœ ์ง์ ‘ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ์ œ ์ฒ˜๋ฆฌ ์‹œ์Šคํ…œ์ด ์ด ํŒจํ„ด์˜ ์ข‹์€ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค. ๊ฐ ๊ฒฐ์ œ ๋ฐฉ์‹์ด ๋™์ผํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ , ์ฒดํฌ์•„์›ƒ ์„œ๋น„์Šค๊ฐ€ ๋Ÿฐํƒ€์ž„์— ์ „๋žต์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

payment.gogo
package payment

import "fmt"

// Processor defines the strategy interface.
type Processor interface {
	Pay(amount float64) (string, error)
}

// CreditCard implements Processor for card payments.
type CreditCard struct {
	CardNumber string
	Expiry     string
}

func (c *CreditCard) Pay(amount float64) (string, error) {
	// Charge the card via payment gateway
	return fmt.Sprintf("charged %.2f to card ending %s", amount, c.CardNumber[len(c.CardNumber)-4:]), nil
}

// BankTransfer implements Processor for wire transfers.
type BankTransfer struct {
	IBAN string
}

func (b *BankTransfer) Pay(amount float64) (string, error) {
	return fmt.Sprintf("initiated transfer of %.2f to %s", amount, b.IBAN), nil
}

// Checkout processes a payment using the given strategy.
func Checkout(p Processor, amount float64) error {
	receipt, err := p.Pay(amount)
	if err != nil {
		return fmt.Errorf("payment failed: %w", err)
	}
	fmt.Println(receipt)
	return nil
}

Go ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์•”๋ฌต์ ์œผ๋กœ ์ถฉ์กฑ๋ฉ๋‹ˆ๋‹ค. implements ํ‚ค์›Œ๋“œ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Pay(float64) (string, error) ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง„ ๋ชจ๋“  ํƒ€์ž…์ด Processor๋กœ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๊ฒฐํ•ฉ๋„๊ฐ€ ๋‚ฎ์•„์ง€๊ณ  ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ„ํŽธํ•ด์ง‘๋‹ˆ๋‹ค. ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” mock Processor๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜์™€ ์ƒ์„ฑ์ž ํŒจํ„ด

Go์—๋Š” ์ƒ์„ฑ์ž๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ด€์šฉ์ ์ธ ๋Œ€์•ˆ์€ New ๋˜๋Š” NewXxx๋กœ ๋ช…๋ช…๋œ ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜๋กœ, ์ดˆ๊ธฐํ™”๋œ ๊ตฌ์กฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜๋Š” ์ œ๋กœ ๊ฐ’ ์ดˆ๊ธฐํ™”๋กœ๋Š” ๋ณด์žฅํ•  ์ˆ˜ ์—†๋Š” ๋ถˆ๋ณ€ ์กฐ๊ฑด์„ ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค.

๋ฉด์ ‘ ์ค€๋น„์—์„œ ์ด ๊ตฌ๋ถ„์€ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์™€ ์ œ๋กœ ๊ฐ’์ด ๊ธฐ๋ณธ์œผ๋กœ ์œ ์šฉํ•œ ๊ฒฝ์šฐ๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํŒ๋‹จํ•˜๋Š” ๋Šฅ๋ ฅ์ด ์š”๊ตฌ๋ฉ๋‹ˆ๋‹ค.

connpool.gogo
package pool

import (
	"errors"
	"sync"
)

// ConnPool manages a pool of reusable connections.
type ConnPool struct {
	mu       sync.Mutex
	conns    []Conn
	maxSize  int
}

// Conn represents a database connection.
type Conn struct {
	ID     int
	Active bool
}

// NewConnPool validates parameters and returns an initialized pool.
func NewConnPool(maxSize int) (*ConnPool, error) {
	if maxSize <= 0 {
		return nil, errors.New("pool: maxSize must be positive")
	}
	return &ConnPool{
		conns:   make([]Conn, 0, maxSize),
		maxSize: maxSize,
	}, nil
}

// Acquire returns a connection from the pool.
func (p *ConnPool) Acquire() (*Conn, error) {
	p.mu.Lock()
	defer p.mu.Unlock()
	for i := range p.conns {
		if !p.conns[i].Active {
			p.conns[i].Active = true
			return &p.conns[i], nil
		}
	}
	if len(p.conns) >= p.maxSize {
		return nil, errors.New("pool: no available connections")
	}
	c := Conn{ID: len(p.conns) + 1, Active: true}
	p.conns = append(p.conns, c)
	return &p.conns[len(p.conns)-1], nil
}

ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜ NewConnPool์€ maxSize ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๊ฒ€์ฆํ•˜๊ณ  ์Šฌ๋ผ์ด์Šค๋ฅผ ์‚ฌ์ „ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค. ๊ตฌ์กฐ์ฒด๋ฅผ ์ง์ ‘ ์ดˆ๊ธฐํ™”(&ConnPool{})ํ•˜๋ฉด ๊ฒ€์ฆ์ด ์ƒ๋žต๋˜์–ด ๋Ÿฐํƒ€์ž„ ํŒจ๋‹‰์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํŒจํ„ด์€ Functional Options์™€ ๊ฒฐํ•ฉํ•˜์—ฌ ๋” ๋ณต์žกํ•œ ์„ค์ • ์‹œ๋‚˜๋ฆฌ์˜ค์—๋„ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

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

Observer ํŒจํ„ด: ์ฑ„๋„๊ณผ ๊ณ ๋ฃจํ‹ด์„ ํ™œ์šฉํ•œ ๊ตฌํ˜„

Observer ํŒจํ„ด์€ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์—ฌ๋Ÿฌ ๊ตฌ๋…์ž์—๊ฒŒ ์•Œ๋ฆผ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค. Java๋‚˜ C#์—์„œ๋Š” ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ์™€ ์ฝœ๋ฐฑ ๋“ฑ๋ก์ด ํ•„์š”ํ•˜์ง€๋งŒ, Go๋Š” ๋” ๊ด€์šฉ์ ์ธ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ฐ”๋กœ ์ฑ„๋„์ž…๋‹ˆ๋‹ค. ๊ฐ ๊ตฌ๋…์ž๋Š” ์ž์‹ ๋งŒ์˜ ์ฑ„๋„์„ ํ†ตํ•ด ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•˜๊ณ , ๊ณ ๋ฃจํ‹ด์ด ๋™์‹œ์— ์ด๋ฒคํŠธ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

์ด ์ ‘๊ทผ ๋ฐฉ์‹์€ Go์˜ ๋™์‹œ์„ฑ ํ”„๋ฆฌ๋ฏธํ‹ฐ๋ธŒ๋ฅผ ์ง์ ‘ ํ™œ์šฉํ•˜์—ฌ ์ฝœ๋ฐฑ ์ŠคํŒŒ๊ฒŒํ‹ฐ๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

eventbus.gogo
package events

import "sync"

// Event carries a topic and payload.
type Event struct {
	Topic   string
	Payload any
}

// Bus manages subscriptions and event dispatch.
type Bus struct {
	mu          sync.RWMutex
	subscribers map[string][]chan Event
}

// NewBus creates an event bus.
func NewBus() *Bus {
	return &Bus{
		subscribers: make(map[string][]chan Event),
	}
}

// Subscribe returns a channel that receives events for a topic.
func (b *Bus) Subscribe(topic string) <-chan Event {
	ch := make(chan Event, 16) // buffered to avoid blocking publisher
	b.mu.Lock()
	b.subscribers[topic] = append(b.subscribers[topic], ch)
	b.mu.Unlock()
	return ch
}

// Publish sends an event to all subscribers of the topic.
func (b *Bus) Publish(topic string, payload any) {
	b.mu.RLock()
	defer b.mu.RUnlock()
	for _, ch := range b.subscribers[topic] {
		select {
		case ch <- Event{Topic: topic, Payload: payload}:
		default:
			// subscriber too slow, drop event
		}
	}
}

select๋ฌธ์— default ์ผ€์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋А๋ฆฐ ๊ตฌ๋…์ž๊ฐ€ ์ „์ฒด ๋ฒ„์Šค๋ฅผ ์ฐจ๋‹จํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜ ์‹œ์Šคํ…œ์—์„œ๋Š” context ๊ธฐ๋ฐ˜ ์ทจ์†Œ ๋ฉ”์ปค๋‹ˆ์ฆ˜๊ณผ Unsubscribe ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๊ตฌํ˜„์„ ์™„์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Middleware ํŒจํ„ด: HTTP ์š”์ฒญ ํŒŒ์ดํ”„๋ผ์ธ

๋ฏธ๋“ค์›จ์–ด ์ฒด์ธ์€ Go HTTP ์„œ๋ฒ„์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค. ์ด ํŒจํ„ด์€ http.Handler๋ฅผ ๋ž˜ํ•‘ํ•˜์—ฌ ๋กœ๊น…, ์ธ์ฆ, ์†๋„ ์ œํ•œ ๋“ฑ์˜ ์ถ”๊ฐ€ ๋™์ž‘์„ ํ•ธ๋“ค๋Ÿฌ ์ž์ฒด๋ฅผ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ  ๋ถ€์—ฌํ•ฉ๋‹ˆ๋‹ค. ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ http.Handler ์ธํ„ฐํŽ˜์ด์Šค ๋•๋ถ„์— ์ด๋Ÿฌํ•œ ๊ตฌ์„ฑ์ด ๋งค์šฐ ๊ฐ„ํŽธํ•ฉ๋‹ˆ๋‹ค.

middleware.gogo
package middleware

import (
	"log/slog"
	"net/http"
	"time"
)

// Logging records request duration and status.
func Logging(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		wrapped := &statusWriter{ResponseWriter: w, status: 200}
		next.ServeHTTP(wrapped, r)
		slog.Info("request",
			"method", r.Method,
			"path", r.URL.Path,
			"status", wrapped.status,
			"duration", time.Since(start),
		)
	})
}

// statusWriter captures the HTTP status code.
type statusWriter struct {
	http.ResponseWriter
	status int
}

func (w *statusWriter) WriteHeader(code int) {
	w.status = code
	w.ResponseWriter.WriteHeader(code)
}

// Chain applies middleware in order: first listed = outermost.
func Chain(h http.Handler, mw ...func(http.Handler) http.Handler) http.Handler {
	for i := len(mw) - 1; i >= 0; i-- {
		h = mw[i](h)
	}
	return h
}

Chain ํ•จ์ˆ˜๋Š” ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์„ ์–ธ ์ˆœ์„œ๋Œ€๋กœ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ๋‹ค์Œ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ž˜ํ•‘ํ•˜์—ฌ ํŒŒ์ดํ”„๋ผ์ธ์„ ํ˜•์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด ํŒจํ„ด์€ chi ๊ฐ™์€ ์ธ๊ธฐ ๋ผ์šฐํ„ฐ์—์„œ๋„ ๋™์ผํ•˜๋ฉฐ, func(http.Handler) http.Handler ์‹œ๊ทธ๋‹ˆ์ฒ˜์˜ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์ˆ˜์šฉํ•ฉ๋‹ˆ๋‹ค.

๊ตฌ์กฐ์ฒด ์ž„๋ฒ ๋”ฉ์„ ํ†ตํ•œ ์ปดํฌ์ง€์…˜

Go๋Š” ์ƒ์†์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  ๊ตฌ์กฐ์ฒด ์ž„๋ฒ ๋”ฉ(struct embedding)์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‚ด๋ถ€ ํƒ€์ž…์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์™ธ๋ถ€ ํƒ€์ž…์œผ๋กœ ์Šน๊ฒฉ์‹œํ‚ค๊ณ , ๊ฐ•ํ•œ ๊ฒฐํ•ฉ ์—†์ด ์ฝ”๋“œ๋ฅผ ์žฌ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ƒ์†์ด ์•„๋‹™๋‹ˆ๋‹ค. ์ž„๋ฒ ๋”ฉ๋œ ํƒ€์ž…์€ ์ž„๋ฒ ๋”ฉํ•˜๋Š” ํƒ€์ž…์— ๋Œ€ํ•ด ์•„๋ฌด๊ฒƒ๋„ ์•Œ์ง€ ๋ชปํ•˜๋ฉฐ, ๊ฐ€์ƒ ๋””์ŠคํŒจ์น˜๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ด ๊ตฌ๋ถ„์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์€ Go ๋ฉด์ ‘์—์„œ ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ž„๋ฒ ๋”ฉ๊ณผ ์ƒ์†์„ ํ˜ผ๋™ํ•˜๋Š” ํ›„๋ณด์ž๋Š” Go์˜ ํƒ€์ž… ์‹œ์Šคํ…œ์— ๋Œ€ํ•œ ์ดํ•ด๊ฐ€ ํ”ผ์ƒ์ ์ด๋ผ๋Š” ํ‰๊ฐ€๋ฅผ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

models.gogo
package models

import "time"

// Timestamps provides common audit fields.
type Timestamps struct {
	CreatedAt time.Time
	UpdatedAt time.Time
}

// User embeds Timestamps to gain CreatedAt/UpdatedAt.
type User struct {
	Timestamps
	ID    int
	Email string
}

// Order also embeds Timestamps.
type Order struct {
	Timestamps
	ID     int
	UserID int
	Total  float64
}

User์™€ Order ๋ชจ๋‘ CreatedAt๊ณผ UpdatedAt ํ•„๋“œ์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Timestamps์— ์ •์˜๋œ ๋ฉ”์„œ๋“œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์Šน๊ฒฉ๋ฉ๋‹ˆ๋‹ค. ํ•ต์‹ฌ ๊ทœ์น™์€ ์ž„๋ฒ ๋”ฉ์ด ์œ„์ž„์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด์ง€ ๋Œ€์ฒด๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ์ ์ž…๋‹ˆ๋‹ค. User๋Š” Timestamps๊ฐ€ ์•„๋‹ˆ๋ผ Timestamps๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Go ๋””์ž์ธ ํŒจํ„ด ๋ฉด์ ‘ ์งˆ๋ฌธ

Go ๋ฉด์ ‘์—์„œ ๋””์ž์ธ ํŒจํ„ด ์งˆ๋ฌธ์€ ํ›„๋ณด์ž๊ฐ€ ํด๋ž˜์‹ ํŒจํ„ด์„ Go์˜ ํƒ€์ž… ์‹œ์Šคํ…œ์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ๊ธฐ์ˆ  ์Šคํฌ๋ฆฌ๋‹๊ณผ ํ˜„์žฅ ๋ฉด์ ‘์—์„œ ์ž์ฃผ ๋“ฑ์žฅํ•˜๋Š” ์งˆ๋ฌธ๋“ค์ž…๋‹ˆ๋‹ค.

Q: ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜๊ฐ€ ํŒจ๋‹‰ ๋Œ€์‹  ์—๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ์–ธ์ œ์ธ๊ฐ€?

ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜๋Š” ๊ฒ€์ฆ์ด ๋Ÿฐํƒ€์ž„ ์ž…๋ ฅ(์‚ฌ์šฉ์ž ์ œ๊ณต ์„ค์ •, ํ™˜๊ฒฝ ๋ณ€์ˆ˜, ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ)์— ์˜์กดํ•˜๋Š” ๊ฒฝ์šฐ ์—๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํŒจ๋‹‰์€ ํ”„๋กœ๊ทธ๋ž˜๋จธ ์˜ค๋ฅ˜, ์ฆ‰ API ๊ณ„์•ฝ์—์„œ ๊ธˆ์ง€ํ•˜๋Š” nil ํฌ์ธํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด ๋ฒ„๊ทธ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ƒํ™ฉ์—๋งŒ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋„ ์ด ๊ด€๋ก€๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. os.Open์€ ์—๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , regexp.MustCompile์€ ์ปดํŒŒ์ผ ํƒ€์ž„ ์ƒ์ˆ˜ ํŒจํ„ด์„ ๊ธฐ๋Œ€ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํŒจ๋‹‰ํ•ฉ๋‹ˆ๋‹ค.

Q: Functional Options ํŒจํ„ด์€ API ๋ฐœ์ „์„ ์–ด๋–ป๊ฒŒ ๊ฐœ์„ ํ•˜๋Š”๊ฐ€?

์ƒˆ๋กœ์šด WithXxx ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์€ ํ•˜์œ„ ํ˜ธํ™˜์„ฑ์„ ๊นจ๋œจ๋ฆฌ์ง€ ์•Š๋Š” ๋ณ€๊ฒฝ์ž…๋‹ˆ๋‹ค. ๊ธฐ์กด ํ˜ธ์ถœ ์ฝ”๋“œ๋Š” ์ˆ˜์ • ์—†์ด ๊ทธ๋Œ€๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์„ค์ • ๊ตฌ์กฐ์ฒด(ํ•„์ˆ˜ ํ•„๋“œ ์ถ”๊ฐ€ ์‹œ ๋ชจ๋“  ํ˜ธ์ถœ ์ง€์ ์ด ๊นจ์ง)๋‚˜ ์œ„์น˜ ๋งค๊ฐœ๋ณ€์ˆ˜(์ธ์ˆ˜ ์ˆœ์„œ ๋ณ€๊ฒฝ์ด ๋ฒ„๊ทธ๋ฅผ ์œ ๋ฐœ)์™€ ๋Œ€์กฐ๋ฉ๋‹ˆ๋‹ค.

Q: Go ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ Java๋‚˜ C# ์ธํ„ฐํŽ˜์ด์Šค์™€ ๋‹ค๋ฅธ ์ ์€ ๋ฌด์—‡์ธ๊ฐ€?

Go ์ธํ„ฐํŽ˜์ด์Šค๋Š” ์•”๋ฌต์ ์œผ๋กœ ์ถฉ์กฑ๋ฉ๋‹ˆ๋‹ค. ํƒ€์ž…์ด ํ•„์š”ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ€์ง€๊ธฐ๋งŒ ํ•˜๋ฉด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฒƒ์ด๋ฉฐ, implements ์„ ์–ธ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด "์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ฐ›๊ณ , ๊ตฌ์กฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค"๋Š” ์›์น™์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๊ตฌํ˜„ ์ธก์ด ์•„๋‹Œ ํ˜ธ์ถœ ์ธก์—์„œ ์ข์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ ๋‚ฎ์€ ๊ฒฐํ•ฉ๋„์™€ ๋†’์€ ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ์ด ๋‹ฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

๋ฉด์ ‘ ํŒจํ„ด

๋ฉด์ ‘๊ด€์€ ํ›„๋ณด์ž์—๊ฒŒ ๊ตฌ์ฒด์ ์ธ ์˜์กด์„ฑ์„ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋ฆฌํŒฉํ† ๋งํ•˜๋„๋ก ์ž์ฃผ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ํฌ์ธํŠธ๋Š” ํ•„์š”ํ•œ ์ตœ์†Œํ•œ์˜ ๋ฉ”์„œ๋“œ ์„ธํŠธ๋ฅผ ์‹๋ณ„ํ•˜๊ณ , ๊ตฌํ˜„ ์ธก์ด ์•„๋‹Œ ์†Œ๋น„์ž ์ธก์—์„œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€์ž…๋‹ˆ๋‹ค.

Q: ์ฑ„๋„ ๊ธฐ๋ฐ˜ Observer์™€ ์ฝœ๋ฐฑ ๊ธฐ๋ฐ˜ Observer์˜ ์ฐจ์ด์ ์€ ๋ฌด์—‡์ธ๊ฐ€?

์ฑ„๋„์€ ๋ฐœํ–‰์ž์™€ ๊ตฌ๋…์ž๋ฅผ ์‹œ๊ฐ„๊ณผ ๊ณต๊ฐ„ ๋ชจ๋‘์—์„œ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋ฐœํ–‰์ž๋Š” ๊ตฌ๋…์ž ํ•จ์ˆ˜์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๋ณด์œ ํ•˜์ง€ ์•Š๊ณ  ์ฑ„๋„์— ์ „์†ก๋งŒ ํ•ฉ๋‹ˆ๋‹ค. ๊ตฌ๋…์ž๋Š” ๋ฒ„ํผ๋ง๋œ ์ฑ„๋„์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž์‹ ์˜ ์†๋„๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. select๋ฌธ์„ ํ†ตํ•ด ํƒ€์ž„์•„์›ƒ ์ฒ˜๋ฆฌ, context๋ฅผ ํ†ตํ•œ ์ทจ์†Œ, ์—ฌ๋Ÿฌ ์ด๋ฒคํŠธ ์†Œ์Šค์˜ ๋ฉ€ํ‹ฐํ”Œ๋ ‰์‹ฑ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ์€ ์ฝœ๋ฐฑ์œผ๋กœ๋Š” ์‰ฝ๊ฒŒ ์–ป์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ฃผ์˜์‚ฌํ•ญ

๊ตฌ๋…์ž ์ฑ„๋„์„ ๋‹ซ๋Š” ๊ฒƒ์„ ์žŠ์œผ๋ฉด ๊ณ ๋ฃจํ‹ด ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  Subscribe์—๋Š” ์ฑ„๋„์„ ๋‹ซ๊ณ  ๋ฒ„์Šค์—์„œ ์ œ๊ฑฐํ•˜๋Š” ๋Œ€์‘ํ•˜๋Š” Unsubscribe๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

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

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

๊ฒฐ๋ก 

  • Functional Options ํŒจํ„ด์€ Builder์™€ ์„ค์ • ๊ตฌ์กฐ์ฒด๋ฅผ ๊ธฐ์กด ํ˜ธ์ถœ ์ฝ”๋“œ๋ฅผ ๊นจ๋œจ๋ฆฌ์ง€ ์•Š๋Š” ๊น”๋”ํ•˜๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•œ API๋กœ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค
  • Go์˜ Strategy ํŒจํ„ด์€ ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋ฐ˜ ๋‹คํ˜•์„ฑ์— ๋Œ€์‘ํ•ฉ๋‹ˆ๋‹ค. ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜๋Š” ๊ตฌํ˜„ ์ธก์ด ์•„๋‹Œ ์†Œ๋น„์ž ์ธก์—์„œ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค
  • ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜๋Š” ์ œ๋กœ ๊ฐ’ ์ดˆ๊ธฐํ™”๋กœ๋Š” ๋ณด์žฅํ•  ์ˆ˜ ์—†๋Š” ๋ถˆ๋ณ€ ์กฐ๊ฑด์„ ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค. ๋Ÿฐํƒ€์ž„ ์ž…๋ ฅ์—๋Š” ์—๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ํŒจ๋‹‰์€ ํ”„๋กœ๊ทธ๋ž˜๋จธ ๋ฒ„๊ทธ์—๋งŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค
  • ์ฑ„๋„ ๊ธฐ๋ฐ˜ Observer๋Š” ๊ณ ๋ฃจํ‹ด๊ณผ select๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ฝœ๋ฐฑ ์ฒด์ธ ์—†์ด ๋™์‹œ์ ์ด๊ณ  ๋А์Šจํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋œ ์ด๋ฒคํŠธ ์ „๋‹ฌ์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค
  • HTTP ๋ฏธ๋“ค์›จ์–ด ์ฒด์ธ์€ func(http.Handler) http.Handler ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ํ†ตํ•ด ๊ตฌ์„ฑ๋˜๋ฉฐ, ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์„œ๋“œํŒŒํ‹ฐ ๋ผ์šฐํ„ฐ์—์„œ ๋™์ผํ•ฉ๋‹ˆ๋‹ค
  • ๊ตฌ์กฐ์ฒด ์ž„๋ฒ ๋”ฉ์€ ์œ„์ž„์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด์ง€ ์ƒ์†์ด ์•„๋‹™๋‹ˆ๋‹ค. ์ด ๊ตฌ๋ถ„์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด Go๋ฅผ ๊นŠ์ด ์ดํ•ดํ•˜๋Š” ๊ฐœ๋ฐœ์ž์™€ Java ํŒจํ„ด์„ ๊ทธ๋Œ€๋กœ ์˜ฎ๊ธฐ๋Š” ๊ฐœ๋ฐœ์ž๋ฅผ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค
  • ๋ฉด์ ‘์—์„œ์˜ ์„ฑ๊ณต์€ GoF ์ •์˜์˜ ์•”๊ธฐ๊ฐ€ ์•„๋‹ˆ๋ผ ํŒจํ„ด์„ Go์˜ ํƒ€์ž… ์‹œ์Šคํ…œ์— ๊ด€์šฉ์ ์œผ๋กœ ์ ์šฉํ•˜๋Š” ๋Šฅ๋ ฅ์— ๋‹ฌ๋ ค ์žˆ์Šต๋‹ˆ๋‹ค

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

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

ํƒœ๊ทธ

#go
#design-patterns
#interview
#best-practices

๊ณต์œ 

๊ด€๋ จ ๊ธฐ์‚ฌ

Go 1.26 ๋ฉด์ ‘ ๋Œ€๋น„: Green Tea ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ์™€ ์„ฑ๋Šฅ ์ตœ์ ํ™” ํ•ด์„ค

Go 1.26 ๋ฉด์ ‘ ๋Œ€๋น„: Green Tea GC, go fix ๋„๊ตฌ, ์Šคํƒ ์ตœ์ ํ™” ์™„๋ฒฝ ์ •๋ฆฌ

Go 1.26 ๋ฉด์ ‘์—์„œ ์ž์ฃผ ์ถœ์ œ๋˜๋Š” Green Tea ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ, ์ƒˆ๋กœ์›Œ์ง„ go fix ๋„๊ตฌ, ์Šฌ๋ผ์ด์Šค ์Šคํƒ ํ• ๋‹น ์ตœ์ ํ™” ๋“ฑ ํ•ต์‹ฌ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์ƒ์„ธํžˆ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

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

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

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

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

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

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