Spring ๐ŸŒฑ

[Spring] ์‹ฑ๊ธ€ํ†ค ์ปจํ…Œ์ด๋„ˆ

z.zzz 2023. 7. 19. 20:51

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

๊ฐ์ฒด์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ๋”ฑ 1๊ฐœ๋งŒ ์ƒ์„ฑ๋˜์–ด ๊ณต์œ ๋˜๋Š” ํŒจํ„ด์„ ์˜๋ฏธํ•œ๋‹ค.

์ธ์Šคํ„ด์Šค๊ฐ€ ํ•˜๋‚˜์”ฉ๋งŒ ์ƒ์„ฑ๋˜๋ฏ€๋กœ ํšจ์œจ์ ์ธ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

๊ฐ์ฒด๊ฐ€ ํ˜ธ์ถœ๋์„ ๋•Œ, ์ด๋ฏธ ๋งŒ๋“ค์–ด๋†“์€ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•จ์œผ๋กœ์จ ํšจ์œจ์ ์ธ ์žฌ์‚ฌ์šฉ ๋˜ํ•œ ๊ฐ€๋Šฅํ•˜๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์€ โ‘  ๊ตฌํ˜„ ์ฝ”๋“œ๊ฐ€ ๋งŽ๊ณ , โ‘ก ์˜์กด๊ด€๊ณ„์ƒ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ตฌ์ฒด ํด๋ž˜์Šค์— ์˜์กดํ•˜์—ฌ DIP, OCP๋ฅผ ์œ„๋ฐ˜ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์œผ๋ฉฐ*, โ‘ข ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์–ด๋ ต๋‹จ ์ด์œ  ๋“ฑ์œผ๋กœ ์œ ์—ฐ์„ฑ์ด ๋–จ์–ด์ง„๋‹ค. 

public class SingletonService {

    public static final SingletonService instance = new SingletonService();
    ...
}

* ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ตฌ์ฒด ํด๋ž˜์Šค(SingletonService)์— ์˜์กดํ•˜๋ฉฐ DIP, OCP๋ฅผ ์œ„๋ฐ˜ํ•œ๋‹ค.

 

์‹ฑ๊ธ€ํ†ค ์ปจํ…Œ์ด๋„ˆ

- ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์˜ ๋‹จ์ ์„ ๋ณด์™„ํ•˜์˜€๋‹ค.

- ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋Š” ์‹ฑ๊ธ€ํ†ค ์ปจํ…Œ์ด๋„ˆ์˜ ์—ญํ• ์„ ํ•œ๋‹ค.

  ์ด๋ ‡๊ฒŒ ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ, ๊ด€๋ฆฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์‹ฑ๊ธ€ํ†ค ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ(Singleton Registry)๋ผ๊ณ  ํ•œ๋‹ค.

- ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ์ด์ œ๋ถ€ํ„ด ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ถ”์ƒํ™”์—๋งŒ ์˜์กดํ•˜๋ฏ€๋กœ DIP, OCP๋ฅผ ์ค€์ˆ˜ํ•˜๋ฉฐ, ํ…Œ์ŠคํŠธ๋กœ๋ถ€ํ„ฐ ์ž์œ ๋กญ๊ฒŒ ์‹ฑ๊ธ€ํ†ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์‹ฑ๊ธ€ํ†ค ๋ฐฉ์‹์˜ ์ฃผ์˜์ 

๋ฌด์ƒํƒœ(stateless)๋กœ ์„ค๊ณ„ํ•ด์•ผ ํ•œ๋‹ค!

์‹ฑ๊ธ€ํ†ค ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•˜๋‚˜์˜ ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•œ๋‹ค. ์ด๋•Œ ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด๋Š” ์ƒํƒœ๋ฅผ ์œ ์ง€(stateful)ํ•˜๊ฒŒ ์„ค๊ณ„ํ•˜๋ฉด ์•ˆ ๋œ๋‹ค. ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋ฅผ ๊ณต์œ ํ•˜๋Š” ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์˜ ํŠน์„ฑ์ƒ ์ƒํƒœ๊ฐ€ ๊ณต์œ ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

StatefulSerice.java

class StatefulService {

    private int price; //์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋Š” ํ•„๋“œ 10000 -> 20000

    public int order(String name, int price) {
        System.out.println("name = " + name + " price = " + price);
        this.price = price; //์—ฌ๊ธฐ๊ฐ€ ๋ฌธ์ œ!
    }

    public int getPrice() {
        return price;
    }
}

StatefulSericeTest.java

class StatefulServiceTest {

    @Test
    void statefulServiceSingleton() {

        ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
        StatefulService statefulService1 = ac.getBean(StatefulService.class);
        StatefulService statefulService2 = ac.getBean(StatefulService.class);

        //ThreadA : A ์‚ฌ์šฉ์ž 10000์› ์ฃผ๋ฌธ
        statefulService1.order("userA", 10000);
        //ThreadB : B ์‚ฌ์šฉ์ž 20000์› ์ฃผ๋ฌธ
        statefulService2.order("userB", 20000);

        //ThreadA : ์‚ฌ์šฉ์ž A ์ฃผ๋ฌธ ๊ธˆ์•ก ์กฐํšŒ
        int price = statefulService1.getPrice();
        System.out.println("price = " + price);

        assertThat(statefulService1.getPrice()).isEqualTo(statefulService2.getPrice());
    }

    static class TestConfig {

        @Bean
        public StatefulService statefulService() {
            return new StatefulService();
        }
    }
}

StatefulService์˜ order ๋ฉ”์„œ๋“œ๋Š” ์ด๋ฆ„, ๊ฐ€๊ฒฉ์„ ๋ฐ›์•„ price ๋ณ€์ˆ˜์— ์ €์žฅํ•œ๋‹ค.

StatefulServiceTest์—์„œ ์‚ฌ์šฉ์ž A๋Š” 10,000์›, B๋Š” 20,000์›์„ order ๋ฉ”์„œ๋“œ์— ๋„˜๊ฒจ์คฌ์„ ๋•Œ, StatefulService์˜ price๋Š” 10,000์ด ์ €์žฅ๋œ ํ›„ B๊ฐ€ ์ฃผ๋ฌธํ•œ ์ •๋ณด๋ฅผ ๋„˜๊ธฐ๋ฉฐ 20,000์œผ๋กœ ๋ฐ”๋€๋‹ค.

๋”ฐ๋ผ์„œ ์‚ฌ์šฉ์ž A์˜ ์ฃผ๋ฌธ ๊ธˆ์•ก์„ ์กฐํšŒํ•˜๋ฉด price์— ๋งˆ์ง€๋ง‰์œผ๋กœ ์ €์žฅ๋œ 20000์ด๋ผ๋Š” ์ž˜๋ชป๋œ ๊ฐ’์ด ๋ฐ˜ํ™˜๋œ๋‹ค.

์‹ค๋ฌด์—์„œ ๊ฐ์ฒด๋ฅผ statefulํ•˜๊ฒŒ ์„ค๊ณ„ํ•œ๋‹ค๋ฉด, ๋กœ๊ทธ์ธ์„ ํ–ˆ์„ ๋•Œ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ์ •๋ณด๊ฐ€ ๋…ธ์ถœ๋˜๋Š” ๋“ฑ์˜ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค!!!๐Ÿšจ

๋”ฐ๋ผ์„œ ๊ฐ์ฒด๋Š” ๋ฌด์ƒํƒœ๋กœ ์„ค๊ณ„๋˜์–ด์•ผ ํ•œ๋‹ค.

 

๋ฌด์ƒํƒœ๋กœ ์„ค๊ณ„ํ•œ๋‹ค๋Š” ๊ฒƒ(stateless)์€ ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•˜๋Š” ํ•„๋“œ ๋ณ€์ˆ˜๊ฐ€ ์—†๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ๋‹ค์Œ ์กฐ๊ฑด์„ ์ง€ํ‚ค๋ฉฐ ์„ค๊ณ„ํ•˜์ž.

1. ํŠน์ • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์˜์กด์ ์ธ ํ•„๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ์•ˆ๋จ
2. ํŠน์ • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ํ•„๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ์•ˆ๋จ
3. ๊ฐ€๊ธ‰์  ์ฝ๊ธฐ๋งŒ ๊ฐ€๋Šฅํ•ด์•ผ ํ•จ
4. ํ•„๋“œ ๋Œ€์‹  ์ž๋ฐ”์—์„œ ๊ณต์œ ๋˜์ง€ ์•Š๋Š” ์ง€์—ญ๋ณ€์ˆ˜, ํŒŒ๋ผ๋ฏธํ„ฐ, ThreadLocal ๋“ฑ์„ ์‚ฌ์šฉํ•ด์•ผํ•จ

  * ThreadLocal : Thread ๋‹จ์œ„๋กœ ๋กœ์ปฌ๋ณ€์ˆ˜ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

 

์œ„ ์˜ˆ์ œ๋ฅผ ๋ฌด์ƒํƒœ๋กœ ์„ค๊ณ„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

StatefulSerice.java

class StatefulService {
    public int order(String name, int price) {
        System.out.println("name = " + name + " price = " + price);
        return price;
    }
}

StatefulSericeTest.java

class StatefulServiceTest {

    @Test
    void statefulServiceSingleton() {
       ...
        //ThreadA : A ์‚ฌ์šฉ์ž 10000์› ์ฃผ๋ฌธ
        int userAPrice = statefulService1.order("userA", 10000);
        //ThreadB : B ์‚ฌ์šฉ์ž 20000์› ์ฃผ๋ฌธ
        int userBPrice = statefulService2.order("userB", 20000);

        //ThreadA : ์‚ฌ์šฉ์ž A ์ฃผ๋ฌธ ๊ธˆ์•ก ์กฐํšŒ
        System.out.println("price = " + userAPrice);
    }
    ...
}

์ƒํƒœ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๋ณ€์ˆ˜์ธ price๋ฅผ ์—†์• ๊ณ  order ๋ฉ”์„œ๋“œ์˜ ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ price๋ฅผ ๋ฆฌํ„ดํ•˜๋ฉฐ ์‚ฌ์šฉ์ž ๊ฐ๊ฐ์˜ ์ฃผ๋ฌธ ๊ธˆ์•ก์„ ์ •ํ™•ํ•˜๊ฒŒ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.

 

+ @Configuration๊ณผ ์‹ฑ๊ธ€ํ†ค

๋”๋ณด๊ธฐ

@Configuration๊ณผ ์‹ฑ๊ธ€ํ†ค
AppConfig๋ฅผ ๋ณด๋ฉด memberService, orderService ๋นˆ์„ ๋งŒ๋“ค๋•Œ ๋™์ผํ•˜๊ฒŒ new MemoryMemberRepository๋ฅผ ์ˆ˜ํ–‰ํ•จ
-> 2๊ฐœ์˜ ๋‹ค๋ฅธ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋˜๋ฉฐ ์‹ฑ๊ธ€ํ†ค์ด ๊นจ์ง€๋Š” ๊ฒƒ์ธ๊ฐ€?
-> ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋กœ ๊ฐ์ฒด ๋น„๊ต ๊ฒฐ๊ณผ, ๋™์ผํ•œ ์ฐธ์กฐ ๊ฐ’์„ ๊ฐ€์ง„๋‹ค๋Š” ๊ฑธ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๊ฐ™์€ ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ณต์œ ๋˜๊ณ  ์žˆ๋Š” ๊ฒƒ!
-> ๋‘๋ฒˆ newํ•˜๋Š”๋ฐ ์–ด๋–ป๊ฒŒ ์ด๊ฒŒ ๊ฐ€๋Šฅ? ๋‘ ๋ฒˆ ํ˜ธ์ถœ์ด ์•ˆ๋˜๋Š”๊ฑฐ ์•„๋‹˜?
-> ใ…‡ใ…‡ new MemoryMemberRepository๋Š” ํ•œ ๋ฒˆ๋งŒ ์ˆ˜ํ–‰๋จ
-> ์ด๊ฒŒ ๊ฐ€๋Šฅํ•œ ์ด์œ ๋Š”?

@Configuration๊ณผ ๋ฐ”์ดํŠธ์ฝ”๋“œ ์กฐ์ž‘
AppConfig.java์˜ @Configuration์ด ์ด๋ฅผ ๊ฐ€๋Šฅ์ผ€ ํ•ด์ค€๋‹ค.
AppConfig์˜ ๋นˆ์„ ์กฐํšŒํ•˜์—ฌ ํด๋ž˜์Šค ์ •๋ณด๋ฅผ ๋ณด๋ฉด class hello.core.AppConfig๊ฐ€ ์•„๋‹Œ xxxCGLIB๊ฐ€ ๋ถ™์€ ํด๋ž˜์Šค๊ฐ€ ์กฐํšŒ๋œ๋‹ค
- ์ด๋Š” ๋‚ด๊ฐ€ ๋งŒ๋“  AppConfig์™€๋Š” ๋‹ค๋ฆ„
- ์Šคํ”„๋ง์ด GBLIB๋ผ๋Š” ๋ฐ”์ดํŠธ ์กฐ์ž‘ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ AppConfigํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์€ ์ž„์˜์˜ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“  ๊ฒƒ! ๊ทธ๋ฆฌ๊ณ  AppConfig๊ฐ€ ์•„๋‹Œ ๋ฐ”์ดํŠธ ์กฐ์ž‘๋œ AppConfig ํด๋ž˜์Šค๊ฐ€ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก๋๋‹ค
- ์ด ์ž„์˜์˜ ํด๋ž˜์Šค๊ฐ€ ์‹ฑ๊ธ€ํ†ค์„ ๋ณด์žฅํ•ด์คŒ : Bean์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ์— ์Šคํ”„๋ง ๋นˆ์ด ์ด๋ฏธ ์กด์žฌํ•˜๋ฉด ์กด์žฌํ•˜๋Š” ๋นˆ์„ ์ฐพ์•„ ๋ฐ˜ํ™˜ / ์Šคํ”„๋ง ๋นˆ์ด ์—†๋‹ค๋ฉด ์ƒ์„ฑ, ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก ํ›„ ๋ฐ˜ํ™˜

AppConfig@CGLIB ์˜ˆ์ƒ ํด๋ž˜์Šค ๋‚ด๋ถ€ ๊ตฌ์กฐ

@Bean
public MemberRepository memberRepository() {
    if (memoryMemberRepository in ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ) 
        return ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์—์„œ ์ธ์Šคํ„ด์Šค ์ฐพ์•„์„œ ๋ฐ˜ํ™˜
    else
        ๊ธฐ์กด ๋กœ์ง(new)์„ ํ˜ธ์ถœํ•ด์„œ MemoryMemberRepository ์ƒ์„ฑ & ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋ก
        return ์ธ์Šคํ„ด์Šค ๋ฐ˜ํ™˜
}


@Configuration์„ ์ ์šฉํ•˜์ง€ ์•Š๊ณ  @Bean๋งŒ ์ ์šฉํ•œ๋‹ค๋ฉด?
์Šคํ”„๋ง ๋นˆ์€ ์ƒ์„ฑ๋˜์ง€๋งŒ ์‹ฑ๊ธ€ํ†ค์€ ๋ณด์žฅ ์•ˆ ๋จ
์ด๋•Œ, AppConfig ๋ฉ”์„œ๋“œ์˜ new์—ฐ์‚ฐ์€ ๊ตฌ์ฒด ํด๋ž˜์Šค๋ฅผ ํ• ๋‹นํ•œ ๊ฒƒ๊ณผ ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ƒ„(MemberRepository memberRepository = new MemoryMemberRepository() - DIP, OCP ์œ„๋ฐ˜!)

⇒  ์Šคํ”„๋ง ์„ค์ • ์ •๋ณด๋Š” ํ•ญ์ƒ @Configuration์„ ์‚ฌ์šฉํ•  ๊ฒƒ!