싱글턴 디자인 패턴은 소프트웨어 프로그래밍에서 가장 중요하고 자주 사용되는 패턴 중 하나입니다. 이는 애플리케이션 런타임 동안 클래스에 단일 인스턴스만 있도록 보장하고 해당 인스턴스에 대한 전역 액세스 지점을 제공합니다. 이 기사에서는 Singleton의 중요성, Golang에서 구현하는 방법, 특히 동시 환경에서 싱글턴이 가져오는 이점에 대해 논의합니다.
싱글톤이란 무엇입니까?
싱글톤은 클래스의 인스턴스를 단일 인스턴스로 제한하는 디자인 패턴입니다. 이는 다음과 같이 단일 제어 지점이나 단일 공유 리소스가 필요한 상황에서 특히 유용합니다.
애플리케이션 설정을 중앙 집중화해야 하는 구성 관리자입니다.-
제한된 수의 연결을 효율적으로 관리해야 하는 데이터베이스 연결 풀.-
로그 일관성이 중요한 로거.-
싱글톤을 사용하는 이유는 무엇입니까?
나는 Pattern의 구현에 대해 더 이해하기 쉬운 몇 가지 사항을 나열하고 모든 것이 장밋빛이 아니라는 점과 우리가 가질 수 있는 몇 가지 문제를 보여 드리겠습니다.
이익
전역 일관성: 애플리케이션의 모든 지점이 동일한 인스턴스를 사용하도록 보장하여 데이터 및 동작 일관성을 제공합니다.-
액세스 제어: 인스턴스 생성 및 액세스 제어를 중앙 집중화하여 객체 수명 주기의 유지 관리 및 관리를 용이하게 합니다.-
리소스 효율성: 불필요한 여러 인스턴스 생성을 방지하여 메모리를 절약하고 리소스를 처리합니다.-
단점
테스트 난이도: 싱글톤은 관리해야 하는 전역 상태를 도입하기 때문에 단위 테스트 작성을 더 어렵게 만들 수 있습니다.-
결합 증가: 싱글톤을 과도하게 사용하면 구성 요소 간의 결합이 더 긴밀해져서 애플리케이션을 유지 관리하고 발전시키기가 어려워질 수 있습니다.-
싱글톤 구현
싱글톤을 구현하기 위해 Golang을 사용하겠습니다. 이 언어에서는 여러 고루틴이 동시에 인스턴스에 액세스하려고 시도하는 경우에도 하나의 인스턴스만 생성되도록 동시성에 특별한 주의를 기울여야 합니다.
예제를 실제 세계에 더 가깝게 만들기 위해 애플리케이션용 로거를 만들어 보겠습니다. 로거는 로그 일관성을 보장하기 위해 고유해야 하는 애플리케이션의 일반적인 도구입니다.
1 - 구조 정의
먼저 단일 인스턴스를 갖고 싶은 구조를 정의합니다.
패키지 로거
수입 (
"fmt"
"동조"
)
로거 구조체 {} 유형
var loggerInstance *로거
package logger
import (
"fmt"
"sync"
)
type Logger struct {}
var loggerInstance *Logger
2 - NewInstance 함수 구현
NewInstance 함수는 싱글톤 구조의 단일 인스턴스를 반환하는 역할을 합니다. 동시 환경에서 보안을 보장하기 위해 뮤텍스를 사용하고 효율성을 위해 이중 확인 잠금을 구현합니다.
패키지 로거
수입 (
"fmt"
"동조"
)
로거 구조체 유형{}
var 로거 *로거
var mtx = &sync.Mutex{}
func NewInstance() *로거 {
로거 == nil인 경우 {
mtx.Lock()
mtx.Unlock() 연기
로거 == nil인 경우 {
fmt.Println("새 로거 생성 중")
로거 = &로거{}
}
} 또 다른 {
fmt.Println("로거가 이미 생성되었습니다.")
}
로거 반환
}
package logger
import (
"fmt"
"sync"
)
type Logger struct {}
var loggerInstance *Logger
3 - 로그 유형 구현
로그 도구에는 항상 정보만 표시하는 정보, 오류를 표시하는 오류 등과 같은 일부 로그 유형이 있습니다. 이는 애플리케이션에 표시하려는 정보 유형을 필터링하는 방법이기도 합니다.
이제 Info 유형의 로그를 표시하는 메서드를 만들어 보겠습니다. 이를 위해 로그 메시지를 수신하고 이를 INFO 형식으로 포맷하는 함수를 생성합니다.
패키지 로거
수입 (
"fmt"
"동조"
"팀"
)
const (
INFO 문자열 = "INFO"
)
로거 구조체 유형{}
var 로거 *로거
var mtx = &sync.Mutex{}
func NewInstance() *로거 {
로거 == nil인 경우 {
mtx.Lock()
mtx.Unlock() 연기
로거 == nil인 경우 {
fmt.Println("새 로거 생성 중")
로거 = &로거{}
}
} 또 다른 {
fmt.Println("로거가 이미 생성되었습니다.")
}
로거 반환
}
func (l *Logger) Info(메시지 문자열) {
fmt.Printf("%s - %s: %s\n", time.Now().UTC().Format(time.RFC3339Nano), INFO, 메시지)
}
package logger
import (
"fmt"
"sync"
)
type Logger struct {}
var loggerInstance *Logger
4 - 로거 사용
그리고 새 로거를 사용하기 위해 기본 패키지 내에서 이를 인스턴스화하고 로그를 생성하여 이 구현이 어떻게 작동하는지 확인합니다.
패키지 메인
수입 (
"놀이터-go/pkg/logger"
)
기능 메인() {
로그 := logger.NewInstance()
log.Info("로그의 예입니다.")
}
package logger
import (
"fmt"
"sync"
)
type Logger struct {}
var loggerInstance *Logger
프로그램을 실행했을 때의 결과는 다음과 같습니다.
새 로거 생성 중
2024-07-03T19:34:57.609599Z - INFO: 로그 예시입니다.
package logger
import (
"fmt"
"sync"
)
type Logger struct {}
var loggerInstance *Logger
NewInstance가 실제로 하나의 인스턴스만 실행되도록 보장하는지 테스트하려면 다음 테스트를 수행할 수 있습니다.
패키지 메인
수입 (
"fmt"
"놀이터-go/pkg/logger"
)
기능 메인() {
로그 := logger.NewInstance()
log.Info("로그의 예입니다.")
log2 := logger.NewInstance()
log2.Info("이것은 로그의 또 다른 예입니다.")
로그 == log2인 경우 {
fmt.Println("동일한 인스턴스")
} 또 다른 {
fmt.Println("다른 인스턴스")
}
}
package logger
import (
"fmt"
"sync"
)
type Logger struct {}
var loggerInstance *Logger
로그가 변경되었으며 이제 새 인스턴스 생성이 차단되었음을 확인할 수 있습니다.
새 로거 생성 중
2024-07-03T19:45:19.603783Z - INFO: 로그 예시입니다.
로거가 이미 생성되었습니다.
2024-07-03T19:45:19.603793Z - INFO: 이는 로그의 또 다른 예입니다.
동일한 인스턴스
package logger
import (
"fmt"
"sync"
)
type Logger struct {}
var loggerInstance *Logger
결론
싱글턴 패턴은 애플리케이션 런타임 동안 특정 클래스의 인스턴스가 하나만 존재하도록 보장하는 강력한 도구입니다. 로거 예제에서는 이 패턴을 적용하여 애플리케이션 전체에서 로그 일관성을 보장하는 방법을 확인했습니다.
이 글이 Golang의 싱글톤을 더 잘 이해하는 데 도움이 되기를 바랍니다.