๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Spring/[์ธํ”„๋Ÿฐ] ์Šคํ”„๋ง ํ•ต์‹ฌ ์›๋ฆฌ - ๊ธฐ๋ณธํŽธ

์Šคํ”„๋ง ํ•ต์‹ฌ ์›๋ฆฌ ์ดํ•ด

ํ•ด๋‹น ํฌ์ŠคํŒ…์€ ๊น€์˜ํ•œ๋‹˜์˜ ์ธํ”„๋Ÿฐ ๊ฐ•์˜(์Šคํ”„๋ง ํ•ต์‹ฌ ์›๋ฆฌ - ๊ธฐ๋ณธํŽธ)๋ฅผ ๋“ฃ๊ณ  ์ •๋ฆฌํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

๊ด€์‹ฌ์‚ฌ์˜ ๋ถ„๋ฆฌ

"์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ•˜๋‚˜์˜ ๊ณต์—ฐ์ด๋ผ๊ณ  ํ–ˆ์„ ๋•Œ, ์Šคํ”„๋ง์€ ๊ณต์—ฐ์˜ ๋ฐฐ์—ญ(์—ญํ• )์— ๋งž๋Š” ๋ฐฐ์šฐ(๊ตฌํ˜„์ฒด)๋ฅผ ์„ญ์™ธํ•˜๋Š” ๊ณต์—ฐ ๊ธฐํš์ž๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค."

์•„๋ž˜ ์ฝ”๋“œ๋Š” ๊ฐ์ฒด ์ง€ํ–ฅ ์„ค๊ณ„ ์›์น™ ์ค‘ OCP์™€ DIP๋ฅผ ์œ„๋ฐ˜ํ•˜๊ณ  ์žˆ๋‹ค.

public class OrderServiceImpl implements OrderService {

//    private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
      private final DiscountPolicy discountPolicy = new RateDiscountPolicy();

	
    ...
    
}

OCP ์œ„๋ฐ˜

  • OCP : ์†Œํ”„ํŠธ์›จ์–ด ์š”์†Œ๋Š” ํ™•์žฅ์—๋Š” ์—ด๋ ค์žˆ์œผ๋‚˜ ๋ณ€๊ฒฝ์—๋Š” ๋‹ซํ˜€์žˆ์–ด์•ผ ํ•œ๋‹ค.
  • ํ• ์ธ ์ •์ฑ…์„ ๋ณ€๊ฒฝํ•˜๋ ค๋ฉด ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ๊นŒ์ง€ ๋ณ€๊ฒฝํ•ด์•ผํ•œ๋‹ค.

DIP ์œ„๋ฐ˜

  • DIP : ํ”„๋กœ๊ทธ๋ž˜๋จธ๋Š” ์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผ์ง€, ๊ตฌ์ฒดํ™”์— ์˜์กดํ•˜๋ฉด ์•ˆ๋œ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ(OrderServiceImpl)๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค(DiscountPolicy) ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ตฌํ˜„์ฒด(FixDiscountPolicy, RateDiscountPolicy)๊นŒ์ง€ ์˜์กดํ•˜๊ณ  ์žˆ๋‹ค.

ํ•ด๊ฒฐ๋ฐฉ์•ˆ

  • ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ํด๋ผ์ด์–ธํŠธ์ธ OrderServiceImpl์— DiscountPolicy์˜ ๊ตฌํ˜„ ๊ฐ์ฒด๋ฅผ ๋Œ€์‹  ์ƒ์„ฑํ•˜๊ณ  ์ฃผ์ž…ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
  • ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด Orderservice์˜ ์—ญํ• ์„ ๋งก์€ OrderServiceImpl์€ MemberRepository์™€ DiscountPolicy๋ผ๋Š” ์—ญํ• ๋งŒ์„ ์•Œ๊ณ  ์žˆ๊ณ , ๊ทธ ๊ตฌํ˜„์ฒด๋Š” ์•Œ์ง€ ๋ชปํ•œ๋‹ค.
  • ๊ฐ๊ฐ์˜ ์—ญํ• ์˜ ๊ตฌํ˜„์ฒด๋Š” AppConfig๊ฐ€ ์ฃผ์ž…์„ ํ†ตํ•ด ์ œ๊ณตํ•œ๋‹ค.
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

	...
    
}


public class AppConfig {

    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    private MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    public DiscountPolicy discountPolicy() {
//        return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }

    ...
    
}

์‚ฌ์šฉ ์˜์—ญ๊ณผ ๊ตฌ์„ฑ ์˜์—ญ

  • AppConfig์˜ ๋“ฑ์žฅ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ํฌ๊ฒŒ ์‚ฌ์šฉ ์˜์—ญ๊ณผ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ตฌ์„ฑํ•˜๋Š” ๊ตฌ์„ฑ ์˜์—ญ์œผ๋กœ ๋ถ„๋ฆฌ๋˜์—ˆ๋‹ค.
  • ํ• ์ธ ์ •์ฑ…์„ ๋ณ€๊ฒฝํ•ด๋„ ๊ตฌ์„ฑ ์˜์—ญ๋งŒ ์˜ํ–ฅ์„ ๋ฐ›๊ณ , ์‚ฌ์šฉ ์˜์—ญ์€ ์ „ํ˜€ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค.
  • ๋”ฐ๋ผ์„œ ํด๋ผ์ด์–ธํŠธ ๊ฐ์ฒด๋Š” ์ž์‹ ์˜ ์—ญํ• ์„ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋‹ค. -> ์ฑ…์ž„์ด ๋ช…ํ™•ํ•ด์ง„๋‹ค(SRP)

์‚ฌ์šฉ์˜์—ญ๊ณผ ๊ตฌ์„ฑ์˜์—ญ

์ •๋ฆฌ

  • ๋‹คํ˜•์„ฑ๋งŒ์œผ๋กœ๋Š” ์ข‹์€ ๊ฐ์ฒด ์ง€ํ–ฅ ์„ค๊ณ„์˜ 5๊ฐ€์ง€ ์›์น™(SOLID) ์ค‘ OCP์™€ DIP๋ฅผ ์ง€ํ‚ค์ง€ ๋ชปํ•œ๋‹ค.
  • ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด AppConfig๊ฐ€ ๋“ฑ์žฅํ–ˆ๊ณ , ์ด๋ฅผ ํ†ตํ•ด ๊ด€์‹ฌ์‚ฌ๋ฅผ ๋ถ„๋ฆฌ์‹œํ‚ด์œผ๋กœ์„œ OCP์™€ DIP(๋”๋ถˆ์–ด SRP)๋ฅผ ์ง€ํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

IOC, DI, ๊ทธ๋ฆฌ๊ณ  ์ปจํ…Œ์ด๋„ˆ

์ œ์–ด์˜ ์—ญ์ „ IoC

ํ”„๋กœ๊ทธ๋žจ์˜ ์ œ์–ด ํ๋ฆ„์„ ์ง์ ‘ ์ œ์–ดํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์™ธ๋ถ€์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ์ œ์–ด์˜ ์—ญ์ „์ด๋ผ ํ•œ๋‹ค.

ํ”„๋ ˆ์ž„์›Œํฌ vs ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
- ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๋‚ด๊ฐ€ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅด ์ œ์–ดํ•˜๊ณ , ๋Œ€์‹  ์‹คํ–‰ํ•˜๋ฉด ๊ทธ๊ฒƒ์€ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๋งž๋‹ค. (JUnit)
- ๋ฐ˜๋ฉด์— ๋‚ด๊ฐ€ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๊ฐ€ ์ง์ ‘ ์ œ์–ด์˜ ํ๋ฆ„์„ ๋‹ด๋‹นํ•œ๋‹ค๋ฉด ๊ทธ๊ฒƒ์€ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‹ค.

์˜์กด๊ด€๊ณ„ ์ฃผ์ž… DI

์˜์กด๊ด€๊ณ„๋Š” ์ •์ ์ธ ํด๋ž˜์Šค ์˜์กด ๊ด€๊ณ„์™€, ์‹คํ–‰ ์‹œ์ ์— ๊ฒฐ์ •๋˜๋Š” ๋™์ ์ธ ๊ฐ์ฒด(์ธ์Šคํ„ด์Šค) ์˜์กด ๊ด€๊ณ„ ๋‘˜์„ ๋ถ„๋ฆฌํ•ด์„œ ์ƒ๊ฐํ•ด์•ผ ํ•œ๋‹ค. 

  • ์ •์ ์ธ ํด๋ž˜์Šค ์˜์กด๊ด€๊ณ„
    • ํด๋ž˜์Šค๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” import ์ฝ”๋“œ๋งŒ ๋ณด๊ณ  ์˜์กด๊ด€๊ณ„๋ฅผ ์‰ฝ๊ฒŒ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ๋‹ค. (์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜์ง€ ์•Š์•„๋„ ๋ถ„์„ํ•  ์ˆ˜ ์žˆ๋‹ค.)
    • ํด๋ž˜์Šค ์˜์กด๊ด€๊ณ„ ๋งŒ์œผ๋กœ๋Š” ์‹ค์ œ ์–ด๋–ค ๊ฐ์ฒด๊ฐ€ ํด๋ผ์ด์–ธํŠธ ๊ฐ์ฒด์— ์ฃผ์ž… ๋ ์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค.

ํด๋ž˜์Šค ๋‹ค์ด์–ด๊ทธ๋žจ

 

  • ๋™์ ์ธ ๊ฐ์ฒด(์ธ์Šคํ„ด์Šค) ์˜์กด ๊ด€๊ณ„
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์‹œ์ ์— ์‹ค์ œ ์ƒ์„ฑ๋œ ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค์˜ ์ฐธ์กฐ๊ฐ€ ์—ฐ๊ฒฐ๋œ ์˜์กด ๊ด€๊ณ„๋‹ค.

๊ฐ์ฒด ๋‹ค์ด์–ด๊ทธ๋žจ

 

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์‹œ์ ์— ์™ธ๋ถ€์—์„œ ์‹ค์ œ ๊ตฌํ˜„ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํด๋ผ์ด์–ธํŠธ์— ์ „๋‹ฌํ•ด์„œ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„์˜ ์‹ค์ œ ์˜์กด๊ด€๊ณ„๊ฐ€ ์—ฐ๊ฒฐ ๋˜๋Š” ๊ฒƒ์„ ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…์ด๋ผ ํ•œ๋‹ค.

์˜์กด๊ด€๊ณ„ ์ฃผ์ž…์„ ์‚ฌ์šฉํ•˜๋ฉด ์ •์ ์ธ ํด๋ž˜์Šค ์˜์กด๊ด€๊ณ„๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ , ๋™์ ์ธ ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค ์˜์กด๊ด€๊ณ„๋ฅผ ์‰ฝ๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค. -> ์‹คํ–‰ ์‹œ์ ์— ๊ตฌํ˜„์ฒด๋ฅผ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

DI ์ปจํ…Œ์ด๋„ˆ(IoC ์ปจํ…Œ์ด๋„ˆ)

์•ž์„  ์˜ˆ์ œ์—์„œ์˜ AppConfig ์ฒ˜๋Ÿผ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋ฉด์„œ ์˜์กด๊ด€๊ณ„๋ฅผ ์—ฐ๊ฒฐํ•ด ์ฃผ๋Š” ๊ฒƒ์„ DI ์ปจํ…Œ์ด๋„ˆ(๋˜๋Š” Ioc ์ปจํ…Œ์ด๋„ˆ)๋ผ ํ•œ๋‹ค.


์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ

  • ApplicationContext๋ฅผ ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋ผ ํ•œ๋‹ค.
  • ๊ธฐ์กด์—๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ AppConfig๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ง์ ‘ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  DI๋ฅผ ํ–ˆ์ง€๋งŒ, ์ด์ œ๋ถ€ํ„ฐ๋Š” ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ†ตํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค.
public class OrderApp {

    public static void main(String[] args) {
    
    	// AppConfig๋ฅผ ์ง์ ‘ ์ƒ์„ฑ ๋ฐ ์‚ฌ์šฉ
        // AppConfig appConfig = new AppConfig();
        // MemberService memberService = appConfig.memberService();
        // OrderService orderService = appConfig.orderService();

        // ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ ์‚ฌ์šฉ
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
        OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
		
        ...
        
        
    }
}
@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    @Bean
    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
    }

}
// ์Šคํ”„๋ง ์‹คํ–‰ ์‹œ ๋กœ๊ทธ
23:59:27.548 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberService'
23:59:27.562 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberRepository'
23:59:27.563 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderService'
23:59:27.564 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'discountPolicy'