본문 바로가기
프로그래밍

👨‍💻 [Go] Go 에서 Singleton 디자인 패턴은 어떻게 설계할 수 있나요?

by 밝음의기둥 2025. 1. 22.
반응형

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."


👨‍💻 안녕하세요, 윌리 프로젝트의 윌리 입니다.

Go 에서 Singleton 디자인 패턴은 어떻게 설계할 수 있는지 간략히 알아보았습니다.


질문 >

Go 에서 Singleton 디자인 패턴은 어떻게 설계할 수 있나요?

답변 >

Singleton 디자인 패턴은 애플리케이션에서 단일 인스턴스만 존재하도록 보장하며, 해당 인스턴스에 대한 글로벌 접근점을 제공합니다. Go 언어는 동시성을 지원하므로 Singleton 설계에서 스레드 안전성을 고려해야 합니다. Go에서 Singleton을 구현하는 다양한 방법을 소개합니다.


1. 기본 Singleton (스레드 안전성 없음)

아래 코드는 Singleton의 기본적인 형태입니다. 하지만 여러 고루틴이 동시에 접근할 경우, 예상치 못한 동작이 발생할 수 있습니다.

package singleton

type Singleton struct {
    // 구조체 필드 정의
}

var instance *Singleton

// GetInstance는 Singleton 인스턴스를 반환합니다.
func GetInstance() *Singleton {
    if instance == nil {
        instance = &Singleton{}
    }
    return instance
}

문제점:

  • 여러 고루틴이 GetInstance를 동시에 호출하면 경쟁 상태(race condition)가 발생할 수 있습니다.

2. 스레드 안전한 Singleton (sync.Once 사용)

Go에서는 sync.Once를 사용해 스레드 안전한 Singleton을 간단히 구현할 수 있습니다. sync.Once는 초기화 코드가 정확히 한 번만 실행되도록 보장합니다.

package singleton

import "sync"

type Singleton struct {
    // 구조체 필드 정의
}

var (
    instance *Singleton
    once     sync.Once
)

// GetInstance는 Singleton의 인스턴스를 반환합니다.
func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{}
    })
    return instance
}

장점:

  • 스레드 안전성을 보장.
  • 초기화가 필요할 때만 실행되는 지연 초기화(lazy initialization).

3. 스레드 안전한 Singleton (Mutex 사용)

sync.Mutex를 사용하여 직접 스레드 안전성을 구현할 수도 있습니다.

package singleton

import "sync"

type Singleton struct {
    // 구조체 필드 정의
}

var (
    instance *Singleton
    mu       sync.Mutex
)

// GetInstance는 Singleton의 인스턴스를 반환합니다.
func GetInstance() *Singleton {
    mu.Lock()
    defer mu.Unlock()
    if instance == nil {
        instance = &Singleton{}
    }
    return instance
}

장점:

  • 스레드 안전성을 보장.
  • sync.Once보다 직접적인 컨트롤 가능.

단점:

  • sync.Once를 사용할 때보다 코드가 다소 복잡.
  • 성능 면에서 약간의 오버헤드 발생.

4. 초기화 시 Singleton 생성 (Eager Initialization)

프로그램 시작 시 Singleton 인스턴스를 미리 생성하는 방식입니다.

package singleton

type Singleton struct {
    // 구조체 필드 정의
}

var instance = &Singleton{}

// GetInstance는 Singleton의 인스턴스를 반환합니다.
func GetInstance() *Singleton {
    return instance
}

장점:

  • 구현이 간단하고 스레드 안전성 문제가 없음.
  • 초기화 코드가 필요 없으므로 실행 속도가 빠름.

단점:

  • Singleton이 필요하지 않은 경우에도 메모리를 사용.

5. 예제: Singleton을 활용한 설정 관리

아래는 Singleton 패턴을 활용해 애플리케이션 설정을 관리하는 예제입니다.

설정 구조체

package config

import (
    "sync"
)

type Config struct {
    AppName string
    Port    int
}

var (
    instance *Config
    once     sync.Once
)

// GetConfig는 Config의 단일 인스턴스를 반환합니다.
func GetConfig() *Config {
    once.Do(func() {
        instance = &Config{
            AppName: "MyApp",
            Port:    8080,
        }
    })
    return instance
}

사용

package main

import (
    "fmt"
    "myproject/config"
)

func main() {
    cfg := config.GetConfig()
    fmt.Printf("App: %s, Port: %d\\n", cfg.AppName, cfg.Port)
}


6. 선택 기준

방법  장점  단점
기본 Singleton 간단한 구현 스레드 안전성 없음
sync.Once 사용 간단하고 스레드 안전성 보장 약간의 초기화 오버헤드
sync.Mutex 사용 스레드 안전성 보장, 직접 컨트롤 가능 코드 복잡성 증가, 성능 약간 저하
초기화 시 Singleton 생성 구현이 간단하고 스레드 안전성 보장 메모리 사용 비효율적 (미사용 시 낭비)

7. 결론

  • 대부분의 경우, sync.Once를 사용하는 방식이 가장 권장됩니다. 이는 간단하고 안전하며 효율적입니다.
  • 초기화 시 Singleton을 생성하는 방식은 단순하고 스레드 안전한 대안으로 사용할 수 있습니다.
  • 애플리케이션의 요구 사항에 따라 적합한 방법을 선택하세요.


🎬 유튜브 채널 🎬

 

위로그@WiLog

📢 안녕하세요, 위로그@WiLog 시청자 여러분, 저는 윌리(Willee) 입니다. 📢 위로그@WiLog 는 자기계발을 목적으로 하는 채널 입니다. 📢 오늘도 즐겁게~ 자신을 위한 계발을 함께 해보아요~ d^_^b 📌

www.youtube.com

🎬 치지직 채널 🎬

 

위로그 채널 - CHZZK

지금, 스트리밍이 시작됩니다. 치지직-

chzzk.naver.com


반응형