본문 바로가기
JavaScript

[JavaScript] 싱글톤 패턴 사용하기

by teamnova 2025. 2. 17.
728x90

싱글톤(Singleton) 패턴 이란?

싱글톤 패턴은 하나의 클래스에 대해 단 하나의 인스턴스만 존재하도록 보장하는 디자인 패턴.
즉, 객체가 여러 개 생성되지 않도록 막고, 어디서든 같은 객체를 공유 하는 것.

 

싱글톤을 사용하는 이유?

1. 객체를 하나만 유지해야 하는 경우

어떤 객체는 하나만 존재해야 의미가 있는 경우.
예:

  • 게임 루프 (GameLoop): 게임이 한 번만 실행되어야 함.
  • 설정 관리 (SettingsManager): 모든 곳에서 같은 설정을 유지해야 함.
  • 데이터베이스 연결 (DatabaseConnection): 하나의 연결을 공유해야 함.
  • 로그 시스템 (Logger): 여러 개의 로거를 만들면 로그가 분산될 수 있음.

싱글톤을 사용하면 이런 객체를 중복 생성하지 않고 하나의 인스턴스만 사용 가능.

 

게임 루프와 캔버스 예제를 통한 싱글톤 이해하기.

 

 

singleTonHT.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Game Loop Test</title>
</head>
<body>
    <!-- 게임을 표시할 캔버스 -->
    <canvas id="gameCanvas" width="500" height="500"></canvas>
   
    <script type="module">
        import { GameLoop } from "./singleTon.js";

        const canvas = document.getElementById("gameCanvas");
        const ctx = canvas.getContext("2d");

        // 싱글톤 인스턴스 생성
        const game1 = GameLoop.Instance(canvas, ctx);
        game1.start();

        // 새로운 객체를 만들어도 기존 인스턴스가 반환되는지 확인
        const game2 = GameLoop.Instance(canvas, ctx);
        if (game1 == game2) {
            console.log("game1 == game2 true이면 싱글톤이 정상 동작하는 것 :", game1 === game2); // true이면 싱글톤이 정상 동작하는 것!
        }
       
    </script>
</body>
</html>

 

singleTon.js

// 싱글톤 패턴을 적용한 게임 루프 클래스
export class GameLoop {
    constructor(canvas, ctx) {
        console.log("🚀 GameLoop 생성자 호출됨!");

        // 기존 인스턴스가 있으면 반환 (싱글톤 패턴)
        if (GameLoop.instance) {
            console.log("⚠️ 이미 생성된 인스턴스를 반환합니다.");
            return GameLoop.instance;
        }

        // 캔버스와 컨텍스트 저장
        this.canvas = canvas;
        this.ctx = ctx;
        this.x = 100; // 원의 초기 위치
        this.speed = 2; // 이동 속도

        this.isRunning = false; // 게임 루프 실행 여부
        this.lastFrameTime = performance.now(); // 마지막 프레임 시간 저장

        GameLoop.instance = this; // 현재 인스턴스를 저장
        console.log("✅ 새로운 GameLoop 인스턴스가 생성되었습니다.");
    }

    // 싱글톤 인스턴스를 반환하는 정적 메서드
    static Instance(canvas, ctx) {
        if (!GameLoop.instance) {
            console.log("🎯 새로운 인스턴스를 생성합니다.");
            GameLoop.instance = new GameLoop(canvas, ctx);
        } else {
            console.log("🔄 기존 인스턴스를 반환합니다.");
        }
        return GameLoop.instance;
    }

    // 게임 루프 시작
    start() {
        if (this.isRunning) {
            console.log("⚠️ 이미 게임 루프가 실행 중입니다!");
            return;
        }
        console.log("▶️ 게임 루프 시작!");
        this.isRunning = true;
        this.loop(); // 루프 실행
    }

    // 게임 루프 (반복 실행)
    loop() {
        if (!this.isRunning) return; // 실행 중이 아니면 중단

        console.log("🎬 새 프레임 실행 중...");
        this.backgroundRender(); // 캔버스 지우기
        this.drawCircle(); // 원 그리기

        this.lastFrameTime = performance.now(); // 현재 프레임 시간 저장
        requestAnimationFrame(() => this.loop()); // 다음 프레임 요청
    }

    // 캔버스를 지우는 함수
    backgroundRender() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    }

    // 원을 그리는 함수 (애니메이션 효과 포함)
    drawCircle() {
       
        this.ctx.beginPath();
        this.ctx.arc(this.x, 100, 30, 0, Math.PI * 2); // 원을 그림
        this.ctx.fillStyle = "blue"; // 원 색상 지정
        this.ctx.fill();
        this.ctx.closePath();

        // 원을 이동시킴
        this.x += this.speed;

        // 캔버스 경계를 벗어나면 방향을 바꿈
        if (this.x > this.canvas.width || this.x < 0) {
            console.log("🔄 // 캔버스 경계를 벗어나면 방향을 바꿈!");
            this.speed *= -1;
        }
    }
}

 

 

시연영상

(게임 객체를 각각 생성하여 일치하는경우 콘솔창에서 로그로 확인이 가능함)