๐Ÿ“š[Design Pattern] ์‹ฑ๊ธ€ํ†คํŒจํ„ด

ํ…ํ…ยท2025๋…„ 4์›” 28์ผ

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด

ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋”ฑ 1๊ฐœ๋งŒ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•˜๋Š” ๋””์ž์ธ ํŒจํ„ด์ด๋‹ค.
๊ฐ์ฒด๋ฅผ new ํ‚ค์›Œ๋“œ๋ฅผ ํ†ตํ•ด ์ง์ ‘ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  getInstance() ๋ฉ”์„œ๋“œ์™€ ๊ฐ™์ด ์ •์˜๋œ ๋ฐฉ์‹์œผ๋กœ๋งŒ ๊ฐ€์ ธ์˜จ๋‹ค.
์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์€ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๊ณ  ๋ฐ˜๋ณต์ ์ธ ๊ฐ์ฒด ์ƒ์„ฑ์„ ํ”ผํ•  ์ˆ˜ ์žˆ์–ด ์„ฑ๋Šฅ๋ฉด์—์„œ๋„ ์ด์ ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.


์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์˜ ๊ตฌํ˜„

public class Singleton {
    //์ •์  ์ฐธ์กฐ๋ณ€์ˆ˜
    private static Singleton singleton;

    //new ํ‚ค์›Œ๋“œ๋ฅผ ๋ง‰๊ธฐ์œ„ํ•ด ์ƒ์„ฑ์ž์— private ์ ‘๊ทผ์ œ์–ด์ž๋ฅผ ์ง€์ •
    private Singleton(){

    }

    //getInstance() ๋ฉ”์„œ๋“œ ์ฒซ ํ˜ธ์ถœ์‹œ ๊ฐ์ฒด ์ƒ์„ฑ, ์ดํ›„ ํ˜ธ์ถœ์‹œ ์ƒ์„ฑ๋œ ๊ฐ์ฒด๋ฅผ ๋ถˆ๋Ÿฌ์˜ด
    public static Singleton getInstance(){
        if(singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์ ์šฉํ•  ์‹œ ๋‘ ๊ฐœ ์ด์ƒ์˜ ๊ฐ์ฒด๋Š” ์กด์žฌํ•  ์ˆ˜ ์—†๋‹ค.
์ด๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ์ฒด ์ƒ์„ฑ์„ ์œ„ํ•œ ์ƒ์„ฑ์ž์— ์ œ์•ฝ์„ ๊ฑธ๊ณ  ๋‹จ์ผ ๊ฐ์ฒด๋ฅผ
๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฉ”์„œ๋“œ๋ฅผ ํ™œ์šฉํ•ด์•ผ ํ•œ๋‹ค.

  • new ํ‚ค์›Œ๋“œ์˜ ์‚ฌ์šฉ์„ ๋ง‰๊ธฐ์œ„ํ•ด ์ƒ์„ฑ์ž์— private ์ ‘๊ทผ ์ œ์–ด์ž๋ฅผ ์ง€์ •ํ•œ๋‹ค.
  • ์œ ์ผํ•œ ๋‹จ์ผ ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ์ •์  ๋ฉ”์„œ๋“œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
  • ์œ ์ผํ•œ ๋‹จ์ผ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•  ์ •์  ์ฐธ์กฐ ๋ณ€์ˆ˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์˜ ์‚ฌ์šฉ

public class SingletonExam {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        Singleton s3 = Singleton.getInstance();

        System.out.println(s1); //singleton.Singleton@3b07d329
        System.out.println(s2); //singleton.Singleton@3b07d329
        System.out.println(s3); //singleton.Singleton@3b07d329

        System.out.println(s1 == s2); //true
        System.out.println(s2 == s3); //true

    }
}

getInstance() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ํ˜ธ์ถœ๋œ s1, s2, s3 ์ฐธ์กฐ๋ณ€์ˆ˜๊ฐ€ ๋ชจ๋‘ ๊ฐ™์€ ์ฐธ์กฐ๊ฐ’์„ ๊ฐ€์ง€๊ณ ์žˆ๋‹ค.
์ด๋Š” new ํ‚ค์›Œ๋“œ๋ฅผ ํ†ตํ•œ ๊ฐ์ฒด ์ƒ์„ฑ์ด ์•„๋‹Œ ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์œ ์ผํ•œ ๋‹จ์ผ ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ด๋ฐ›๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.


์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์˜ ๋ฌธ์ œ์ 

  1. ์˜์กด์„ฑ์ด ๋†’์•„์ง„๋‹ค.

    ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์€ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•œ ๊ฐ์ฒด๋ฅผ ์ •์  ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉํ•˜๊ธฐ์— ํด๋ž˜์Šค ๊ฐ„ ์˜์กด์„ฑ์ด ๋†’์•„์ง„๋‹ค.
    ์‹ฑ๊ธ€ํ†ค ์ธ์Šคํ„ด์Šค์˜ ๋ณ€๊ฒฝ ์‹œ ํ•ด๋‹น ์ธ์Šคํ„ด์Šค๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๋ชจ๋“  ํด๋ž˜์Šค๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

  2. ์ƒ์†์ด ์–ด๋ ต๋‹ค.

    ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์€ ๊ธฐ๋ณธ์ ์œผ๋กœ private ์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ์ฒด์˜ ์ค‘๋ณต ์ƒ์„ฑ์„ ๋ฐฉ์ง€ํ•œ๋‹ค.
    ์ด๋กœ ์ธํ•ด ์ƒ์†์„ ํ†ตํ•œ ์ž์‹ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์ˆ˜ ์—†๊ฒŒ ๋˜๋ฉฐ ๊ฐ์ฒด์ง€ํ–ฅ์˜ ์žฅ์  ์ค‘ ํ•˜๋‚˜์ธ ๋‹คํ˜•์„ฑ์„ ํ™œ์šฉํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

  3. ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋ ค์›Œ์ง„๋‹ค.

    ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด๋Š” ์ „์—ญ์ ์œผ๋กœ ํ•˜๋‚˜๋งŒ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋…๋ฆฝ์ ์ธ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ๋งŒ๋“ค๊ธฐ ์–ด๋ ต๋‹ค.
    ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์—์„œ ์‹ฑ๊ธ€ํ†ค ์ธ์Šคํ„ด์Šค๋ฅผ ๋ชจํ‚นํ•˜๊ฑฐ๋‚˜ ๊ต์ฒดํ•˜๊ธฐ๊ฐ€ ์–ด๋ ค์›Œ ํ…Œ์ŠคํŠธ์˜ ์œ ์—ฐ์„ฑ์ด ๋–จ์–ด์ง€๊ณ  ์ •์ƒ์ ์œผ๋กœ ์ˆ˜ํ–‰๋˜์ง€ ์•Š์„ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค.

    ๋ชจํ‚น(Mocking): ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์—์„œ ํŠน์ • ๊ฐ์ฒด๋‚˜ ํด๋ž˜์Šค๋ฅผ ์‹ค์ œ ๊ตฌํ˜„์ด ์•„๋‹Œ ๊ฐ€์งœ ๊ฐ์ฒด(Mock ๊ฐ์ฒด) ๋กœ ๋Œ€์ฒดํ•˜์—ฌ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ธฐ๋ฒ•

์ด๋Ÿฌํ•œ ๋ฌธ์ œ์ ๋“ค ๋•Œ๋ฌธ์— ์‹ฑ๊ธ€ํ†คํŒจํ„ด์€ ์•ˆํ‹ฐํŒจํ„ด์ด๋ผ ๋ถˆ๋ฆฌ๊ธฐ๋„ ํ•œ๋‹ค.


๋™์‹œ์„ฑ ๋ฌธ์ œ?

์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•˜๋Š” ๊ฒฝ์šฐ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ

public class SingletonThread{
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            Singleton singleton = Singleton.getInstance();
            System.out.println(singleton);
        });

        Thread t2 = new Thread(()->{
            Singleton singleton = Singleton.getInstance();
            System.out.println(singleton);
        });

        Thread t3 = new Thread(()->{
            Singleton singleton = Singleton.getInstance();
            System.out.println(singleton);
        });

        t1.start(); //singleton.Singleton@530def55
        t2.start(); //singleton.Singleton@7ddc7898
        t3.start(); //singleton.Singleton@7ddc7898
    }
}

์—ฌ๋Ÿฌ ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์‹คํ–‰๋˜๋ฉฐ instance == null ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๊ณ  ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋ ค๊ณ  ์‹œ๋„ํ•œ๋‹ค.
์ด๋กœ์ธํ•ด ๋‹ค๋ฅธ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜์–ด t1, t2 ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋‹ค๋ฅธ ์ฐธ์กฐ๊ฐ’์„ ๊ฐ€์ง€๊ฒŒ ๋˜๋ฉฐ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์ด ๊นจ์ง€๊ณ  ๋ง์•˜๋‹ค.
๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉฐ Thread-safe ํ•˜์ง€ ๋ชปํ•œ ์ƒํ™ฉ์ด ์ผ์–ด๋‚ฌ๋‹ค.

Thread-safe:
๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•ด๋„ ํ”„๋กœ๊ทธ๋žจ์ด ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ํŠน์„ฑ


๋™์‹œ์„ฑ ๋ฌธ์ œ์˜ ํ•ด๊ฒฐ๋ฐฉ๋ฒ•

  1. ๋™๊ธฐํ™”(syncronuized)
    ํ•ด๋‹น ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“ ๋‹ค.
//getInstance() ๋ฉ”์„œ๋“œ์— syncronized ํ‚ค์›Œ๋“œ ์ถ”๊ฐ€
    public static synchronized Singleton getInstance(){
        if(singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
  • ๊ฐ„๋‹จํ•œ ํ‚ค์›Œ๋“œ ์ถ”๊ฐ€๋ฅผ ํ†ตํ•ด ๋™์‹œ ์ ‘๊ทผ์„ ๋ง‰์„ ์ˆ˜ ์žˆ์œผ๋‚˜ ํ•ด๋‹น ๋ธ”๋ก ์‹คํ–‰ ์ค‘ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋Š” ๋Œ€๊ธฐํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ ์„ฑ๋Šฅ ์ €ํ•˜์˜ ์ฃผ์š” ์›์ธ์ด ๋œ๋‹ค.
  1. ์ด๋ฅธ ์ดˆ๊ธฐํ™”(Eager Initialization)
    ํด๋ž˜์Šค ๋กœ๋”ฉ ์‹œ ๊ฐ์ฒด๊ฐ€ ๋ฏธ๋ฆฌ ์ƒ์„ฑ๋˜๋ฉฐ ์ƒ์„ฑ๋œ ๊ฐ์ฒด๋Š” final ํ‚ค์›Œ๋“œ๋ฅผ ํ†ตํ•ด ๋ถˆ๋ณ€ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด์ง„๋‹ค.
    ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ๋„ ๋ฏธ๋ฆฌ ์ƒ์„ฑ๋œ ๊ฐ์ฒด๋งŒ์„ ์ฐธ์กฐํ•˜๊ธฐ์— ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด์†Œํ•  ์ˆ˜ ์žˆ๋‹ค.
public class Singleton {
    //๋ณ€์ˆ˜ ์„ ์–ธ๊ณผ ๋™์‹œ์— ์ดˆ๊ธฐํ™”
    private static final Singleton singleton = new Singleton();

    //new ํ‚ค์›Œ๋“œ๋ฅผ ๋ง‰๊ธฐ์œ„ํ•ด ์ƒ์„ฑ์ž์— private ์ ‘๊ทผ์ œ์–ด์ž๋ฅผ ์ง€์ •
    private Singleton(){

    }

    //getInstance() ๋ฉ”์„œ๋“œ
    public static Singleton getInstance(){
        return singleton;
    }
}
  • ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ๋ฏธ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ์— ์ƒ์„ฑํ•ด๋‘๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ œ๋กœ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ์ฐจ์ง€๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค.
  1. ๋”๋ธ” ์ฒดํฌ ๊ธฐ๋ฒ•(Double-Checked Locking)
    ์ฒซ ๋ฒˆ์งธ ์ฒดํฌ๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ๋น ๋ฅด๊ฒŒ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์„ฑ๋Šฅ๋„ ์œ ์ง€ํ•˜๋ฉด์„œ
    ๊ฐ์ฒด ์ƒ์„ฑ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋‘ ๋ฒˆ์งธ ์ฒดํฌ๋ฅผ ํ†ตํ•ด ๋™๊ธฐํ™” ๋ธ”๋ก์„ ๊ฑฐ์ณ ์•ˆ์ „ํ•œ ์ƒ์„ฑ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
public class Singleton {
    //volatile ํ‚ค์›Œ๋“œ ์‚ฌ์šฉ
    private static volatile Singleton singleton ;

    private Singleton(){

    }

    //getInstance() ๋ฉ”์„œ๋“œ
    public static Singleton getInstance(){
        if(singleton == null) {              //1์ฐจ๊ฒ€์‚ฌ
            synchronized (Singleton.class) {
                if (singleton == null) {     //2์ฐจ๊ฒ€์‚ฌ
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
  • volatile ํ‚ค์›Œ๋“œ๋ฅผ ํ†ตํ•ด ๋ช…๋ น์–ด ์žฌ๋ฐฐ์น˜๋ฅผ ๋ง‰์•„์•ผ ํ•œ๋‹ค.

    ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด์˜ ์ƒ์„ฑ๊ณผ์ •

    1. ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„ํ™•๋ณด
    2. ์ƒ์„ฑ์ž ํ˜ธ์ถœ
    3. ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜์— ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ ํ• ๋‹น

    ์œ„์ฒ˜๋Ÿผ 3๋‹จ๊ณ„๋ฅผ ๊ฑฐ์น˜์ง€๋งŒ ๋ช…๋ น์–ด ์‹คํ–‰์ˆœ์„œ๊ฐ€ ๊ผฌ์ผ์‹œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ์ˆ˜ ์žˆ๋‹ค. ์žฌ๋ฐฐ์น˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ๊ฐ์ฒด ์ƒ์„ฑ์ด ๋๋‚˜๊ธฐ ์ „์— ์ฐธ์กฐ๊ฐ’์ด ๋จผ์ € ๋ฐ˜ํ™˜๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ ํ•ด๋‹น ์ƒํ™ฉ ๋ฐœ์ƒ์‹œ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ์™„์„ฑ๋˜์ง€ ์•Š์€ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ์œ„ํ—˜์ด ์ƒ๊ธด๋‹ค.

๋ช…๋ น์–ด ์žฌ๋ฐฐ์น˜: ํ”„๋กœ๊ทธ๋žจ์˜ ๋น ๋ฅธ ์‹คํ–‰์„ ์œ„ํ•ด ์ˆœ์„œ๋ฅผ ์ปดํŒŒ์ผ๋Ÿฌ๋‚˜ CPU๊ฐ€ ์ž„์˜๋กœ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ


References

profile
์ฐจ๊ทผ์ฐจ๊ทผ

0๊ฐœ์˜ ๋Œ“๊ธ€