본문 바로가기
Golang

Functional Option Pattern in Go

by Limm_jk 2023. 7. 2.

Go 언어로 서버 프레임워크를 개발한다고 가정해봅시다. 서버는 다양한 옵션을 가질 수 있으며, 생성자를 통해 이를 설정할 수 있어야 합니다. 예를 들어 서버의 주소, 읽기 타임아웃, 로깅 등의 옵션을 설정할 수 있어야 하는데요. 예시를 들어보겠습니다.

// 예시

 

위와 같이 이러한 옵션은 필수적이거나 선택적일 수 있습니다. 그렇다면 저 상황에서 다른 옵션이 추가된다면 어떨까요? 그에 맞게 생성자를 추가해야하고, 각 옵션이 optional하다면 추가해야하는 생성자가 기하급수적으로 늘어납니다. 물론 메서드 오버로딩을 지원하지 않는 특성 상, 네이밍도 상당히 어렵습니다. 오늘 이야기 해보려 하는 Functional Option Pattern은 이러한 문제를 해결하고자 등장한 패턴입니다.

 

Functional Option Pattern

Functional Option Pattern은 생성자에 파라미터로 함수를 받아 필요한 옵션을 설정하는 방식입니다. 함수를 사용함으로써 유연성을 가지며, 필드의 기본값과 zero value를 구분할 수 있습니다. 또한 필드의 추가나 변경에도 자유롭게 대응할 수 있는데요.


위와 유사한 예시의 Server를 기반으로 Functional Option Pattern을 적용해보겠습니다.

type Server struct {
    Address     string
    ReadTimeout time.Duration
    Logger      *log.Logger
}

func NewServer(options ...func(*Server)) *Server {
    s := &Server{
        Address:     ":8080",
        ReadTimeout: time.Second * 10,
        Logger:      log.Default(),
    }

    for _, option := range options {
        option(s)
    }

    return s
}



위 코드에서는 `Server` 구조체와 `NewServer` 생성자 함수를 정의하였습니다. `Server` 구조체는 서버의 주소(`Address`), 읽기 타임아웃(`ReadTimeout`), 로거(`Logger`) 등을 포함하고 있습니다. `NewServer` 함수는 `Server` 구조체를 생성하는 생성자 함수로, 가변인자를 받아 옵션을 설정합니다.

option 함수는 아래와 같이 짜서 사용할 수 있습니다.

func WithAddress(address string) func(*Server) {
    return func(s *Server) {
        s.Address = address
    }
}

func WithReadTimeout(timeout time.Duration) func(*Server) {
    return func(s *Server) {
        s.ReadTimeout = timeout
    }
}

func WithLogger(logger *log.Logger) func(*Server) {
    return func(s *Server) {
        s.Logger = logger
    }
}



위의 예제에서는 `WithAddress`, `WithReadTimeout`, `WithLogger` 함수를 정의하여 각각의 옵션을 설정합니다. 각 함수는 `Server` 구조체에 대한 함수를 반환합니다. 반환된 함수는 생성자 함수에서 호출되어 해당 옵션을 설정하게 됩니다.

이제 위에서 만든 생성자 함수를 호출하여 구조체를 생성해보겠습니다.

func main() {
    server := NewServer(
        WithAddress(":8000"),
        WithReadTimeout(time.Minute),
        WithLogger(log.New(os.Stdout, "", log.LstdFlags)),
    )

    server.Start()
}



위의 코드에서는 `NewServer` 생성자 함수를 호출하여 서버를 생성합니다. `WithAddress`, `WithReadTimeout`, `WithLogger` 함수를 사용하여 주소, 읽기 타임아웃, 로거 등을 설정합니다. 생성된 서버 구조체를 사용하여 서버를 시작하거나 필요한 작업을 수행할 수 있습니다.

댓글