Menu
Coddy logo textTech

싱글톤 패턴

Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 104개 중 91번째.

Singleton 패턴은 프로그램 전체에서 클래스의 인스턴스가 단 하나만 존재하도록 보장하고, 이에 대한 전역적인 접근 지점을 제공합니다. 이는 설정 관리자, 로거 또는 데이터베이스 연결 풀과 같이 공유되어야 하는 리소스에 유용합니다.

핵심 아이디어는 생성자를 private으로 만들어 아무도 인스턴스를 직접 생성할 수 없게 한 다음, 단일 인스턴스를 반환하는 정적 메서드를 제공하는 것입니다:

#include <iostream>
#include <string>

class Logger {
private:
    std::string logFile;
    
    // Private constructor - prevents direct instantiation
    // private 생성자 - 직접적인 인스턴스화를 방지합니다.
    Logger() : logFile("app.log") {}
    
    // Delete copy constructor and assignment operator
    // 복사 생성자와 대입 연산자를 삭제합니다.
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

public:
    // Static method to access the single instance
    // 단일 인스턴스에 접근하기 위한 정적 메서드
    static Logger& getInstance() {
        static Logger instance;  // Created once, lives until program ends
        // 한 번 생성되며, 프로그램이 종료될 때까지 유지됩니다.
        return instance;
    }
    
    void log(const std::string& message) {
        std::cout << "[LOG] " << message << "\n";
    }
};

int main() {
    Logger::getInstance().log("Application started");
    
    Logger& logger = Logger::getInstance();
    logger.log("Processing data");
}

getInstance() 내부의 static Logger instance는 "매직 스태틱(magic statics)"이라 불리는 C++11의 기능입니다. 이는 스레드 안전(thread-safe)하며 단 한 번만 초기화됨을 보장합니다. 복사 생성자와 대입 연산자를 삭제함으로써, 싱글톤의 복사본이 생성되는 것을 방지합니다.

싱글톤(Singleton)은 신중하게 사용하세요. 이는 테스트를 어렵게 만들 수 있는 전역 상태를 도입하기 때문입니다. 여러 인스턴스가 존재할 경우 문제가 발생할 수 있는, 진정으로 공유되어야 하는 리소스에 가장 적합합니다.

challenge icon

챌린지

쉬움

게임 애플리케이션의 설정을 관리하는 GameSettings 싱글톤을 만들어 보겠습니다. 이는 싱글톤 패턴의 완벽한 사용 사례입니다. 게임의 모든 부분에서 접근할 수 있는 단 하나의 설정 관리자를 두어 전체적으로 일관된 구성을 보장하고자 할 때 사용합니다.

코드는 다음 세 개의 파일로 구성됩니다:

  • GameSettings.h: 게임 설정을 관리하는 싱글톤 클래스를 정의합니다.

    GameSettings 클래스는 세 가지 설정을 프라이빗 멤버로 저장해야 합니다: difficulty (문자열), volume (0-100 사이의 정수), fullscreen (불리언). 이들을 기본값인 "Normal", 50, false로 초기화하세요.

    싱글톤 패턴을 올바르게 구현하세요:

    • 생성자를 프라이빗(private)으로 만듭니다.
    • 복사 생성자와 대입 연산자를 삭제(delete)합니다.
    • 단일 인스턴스에 대한 참조를 반환하는 정적 getInstance() 메서드를 제공합니다.

    설정과 상호작용하기 위해 다음 퍼블릭 메서드들을 추가하세요:

    • setDifficulty(const std::string& diff) — 난이도를 업데이트합니다.
    • setVolume(int vol) — 볼륨을 업데이트합니다.
    • setFullscreen(bool fs) — 전체 화면 모드를 업데이트합니다.
    • displaySettings() — 현재의 모든 설정을 출력합니다.
  • GameModule.h: 싱글톤에 접근하는 별도의 모듈을 생성합니다.

    프리셋 이름(문자열)을 인자로 받는 applyGamePreset 함수를 정의하세요. 이 함수는 GameSettings 싱글톤에 접근하여 프리셋에 따라 설정을 구성해야 합니다:

    • "casual" — 난이도를 "Easy"로, 볼륨을 70으로, 전체 화면을 false로 설정합니다.
    • "competitive" — 난이도를 "Hard"로, 볼륨을 30으로, 전체 화면을 true로 설정합니다.
    • 그 외의 프리셋 — 기본 설정을 변경하지 않고 유지합니다.

    이는 프로그램의 서로 다른 부분에서 어떻게 동일한 싱글톤 인스턴스에 접근하고 수정할 수 있는지를 보여줍니다.

  • main.cpp: 모든 것을 하나로 모아 싱글톤이 작동하는 모습을 보여줍니다.

    두 개의 입력을 읽습니다:

    1. 프리셋 이름 (문자열)
    2. 사용자 정의 볼륨 레벨 (정수)

    먼저, 싱글톤에서 displaySettings()를 호출하여 초기 기본 설정을 표시합니다. 그 후 빈 줄을 하나 출력하세요.

    그런 다음 applyGamePreset 함수를 사용하여 프리셋을 적용하고 설정을 다시 표시합니다. 그 후 빈 줄을 하나 출력하세요.

    마지막으로, 사용자 입력값으로 볼륨만 덮어쓰고 최종 설정을 표시합니다.

displaySettings() 메서드는 반드시 다음 형식으로 출력해야 합니다:

Difficulty: [value]
Volume: [value]
Fullscreen: [yes/no]

예를 들어, 입력값이 casual85인 경우:

Difficulty: Normal
Volume: 50
Fullscreen: no

Difficulty: Easy
Volume: 70
Fullscreen: no

Difficulty: Easy
Volume: 85
Fullscreen: no

입력값이 competitive20인 경우:

Difficulty: Normal
Volume: 50
Fullscreen: no

Difficulty: Hard
Volume: 30
Fullscreen: yes

Difficulty: Hard
Volume: 20
Fullscreen: yes

main.cppGameModule.h가 어떻게 동일한 싱글톤 인스턴스에 접근하는지 주목하세요. 한 곳에서 변경한 내용이 모든 곳에서 확인됩니다. 이것이 게임 설정과 같은 공유 리소스에 대한 싱글톤 패턴의 강력함입니다.

치트 시트

싱글톤 패턴(Singleton pattern)은 프로그램 전체에서 클래스의 인스턴스가 단 하나만 존재하도록 보장하며, 이에 대한 전역적인 접근 지점을 제공합니다. 설정 관리자, 로거(logger), 또는 데이터베이스 연결 풀과 같은 공유 리소스에 유용합니다.

주요 구현 단계:

  • 직접적인 인스턴스화를 방지하기 위해 생성자를 private으로 설정합니다.
  • 복사를 방지하기 위해 복사 생성자와 대입 연산자를 삭제합니다.
  • 단일 인스턴스에 대한 참조를 반환하는 정적 메서드를 제공합니다.
class Logger {
private:
    std::string logFile;
    
    // Private 생성자
    Logger() : logFile("app.log") {}
    
    // 복사 생성자와 대입 연산자 삭제
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

public:
    // 단일 인스턴스에 접근하기 위한 정적 메서드
    static Logger& getInstance() {
        static Logger instance;  // 한 번만 생성되며 프로그램이 종료될 때까지 유지됨
        return instance;
    }
    
    void log(const std::string& message) {
        std::cout << "[LOG] " << message << "\n";
    }
};

// 사용 예시
Logger::getInstance().log("Application started");

Logger& logger = Logger::getInstance();
logger.log("Processing data");

getInstance() 내부의 static Logger instance는 C++11의 "매직 스태틱(magic statics)"을 사용합니다. 이는 스레드 안전(thread-safe)하며 단 한 번만 초기화됨을 보장합니다.

싱글톤은 테스트를 어렵게 만들 수 있는 전역 상태를 도입하므로 신중하게 사용해야 합니다. 여러 인스턴스가 존재할 경우 문제가 발생할 수 있는 진정한 공유 리소스에 가장 적합합니다.

직접 해보기

#include <iostream>
#include <string>
#include "GameSettings.h"
#include "GameModule.h"

using namespace std;

int main() {
    // 입력 읽기
    string presetName;
    int customVolume;
    cin >> presetName;
    cin >> customVolume;
    
    // TODO: GameSettings 싱글톤 인스턴스 가져오기
    
    // TODO: 초기 기본 설정 표시
    
    // TODO: 빈 줄 출력
    
    // TODO: applyGamePreset 함수를 사용하여 프리셋 적용
    
    // TODO: 프리셋 적용 후 설정 표시
    
    // TODO: 빈 줄 출력
    
    // TODO: customVolume으로 볼륨 재설정
    
    // TODO: 최종 설정 표시
    
    return 0;
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨