JiwonDev

#9 ์˜์กด๊ด€๊ณ„ ์ž๋™์ฃผ์ž…์—์„œ ์ƒ๊ธฐ๋Š” ๋ฌธ์ œ (+Lombok ์†Œ๊ฐœ)

by JiwonDev

ํ•ด๋‹น ๊ธ€์€ ๊น€์˜ํ•œ๋‹˜์˜ ์ธํ”„๋Ÿฐ ๊ฐ•์˜๋ฅผ ๊ณต๋ถ€ํ•˜๊ณ  ์ •๋ฆฌํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

 

๋ง‰์ƒ ๊ฐœ๋ฐœ์„ ํ•ด๋ณด๋ฉด ์•Œ๊ฒ ์ง€๋งŒ, ์ตœ๊ทผ์— ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์€ Side-effect๋ฅผ ์—†์• ๊ธฐ ์œ„ํ•ด ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด๋“ค์„ ๋ถˆ๋ณ€(final)์œผ๋กœ ๋งŒ๋“ค๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋งค๋ฒˆ ์ƒ์„ฑ์ž๋ฅผ ๋งŒ๋“ค๊ณ , ์ฃผ์ž… ๋ฐ›์€ ๊ฐ’์„ ๋Œ€์ž…ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค๊ณ .. ์ด๋ฅผ ์ž๋™ํ™”ํ•ด์ฃผ๋Š” ๋„๊ตฌ๋Š” ์—†์„๊นŒ?

 

#Lombok ์†Œ๊ฐœ (๋‚˜์ค‘์— ๋”ฐ๋กœ ๋‹ค๋ฃฐ ์˜ˆ์ •)

๊ทธ๊ฒŒ ๋ฐ”๋กœ Lombok ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์œผ๋กœ Getter, Setter๋“ฑ์„ ์ž๋™์ƒ์„ฑ ํ•ด์ค€๋‹ค๊ณ  ๋“ค์–ด๋ณธ ์  ์žˆ์„ ๊ฒƒ์ด๋‹ค. ์ง€๊ธˆ์€ ํ•˜๋‚˜ํ•˜๋‚˜ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€๋งŒ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Lombok์„ ๋Œ€๋ถ€๋ถ„ ์‚ฌ์šฉํ•˜๊ธฐ์—, ์กฐ๊ธˆ ์•ž๋‹น๊ฒจ์„œ ๋ฏธ๋ฆฌ ์†Œ๊ฐœ๋ฅผ ํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

// ๊ธฐ์กด ์ฝ”๋“œ
@Component
public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    // @Autowired ์ƒ์„ฑ์ž๊ฐ€ ๋‹จ ํ•˜๋‚˜์ธ ๊ฒฝ์šฐ, ์ƒ๋žตํ•ด๋„ ๋˜‘๊ฐ™์ด ๋™์ž‘ํ•œ๋‹ค.
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy
            discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}

์œ„์˜ ์ฝ”๋“œ๋ฅผ Lombok ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ค์–ด๋ณด์ž. ์—ฌ๊ธฐ์—์„œ๋Š” @RequiredArgsConstructor๋ฅผ ์ด์šฉํ•˜์—ฌ final์ด ๋ถ™์€ ํ•„๋“œ๋งŒ ๋ชจ์•„์„œ ์ƒ์„ฑ์ž๋ฅผ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ๋‹ค.

@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
}

์ฐธ๊ณ ๋กœ IntelliJ์•ˆ์— Annotation Processing ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™” ์‹œํ‚ค๋ฉด Lombok์ด ์ ์šฉ๋œ ์ฝ”๋“œ๋ฅผ ์ž๋™์™„์„ฑ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์Šคํ”„๋ง๋ถ€ํŠธ์—์„œ Lombok์„ ์ ์šฉํ•˜๋ ค๋ฉด build.gradle์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

plugins {
    id 'org.springframework.boot' version '2.3.2.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
}
group = 'hello'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
//** lombok ์„ค์ • ์ถ”๊ฐ€ ์‹œ์ž‘
configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}
//lombok ์„ค์ • ์ถ”๊ฐ€ ๋ **

repositories {
    mavenCentral()
}
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    
    //** lombok ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€ ์‹œ์ž‘
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testCompileOnly 'org.projectlombok:lombok'
    testAnnotationProcessor 'org.projectlombok:lombok'
    //lombok ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€ ๋ **
    
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}
test {
    useJUnitPlatform()
}

 


# ๋‹ค์‹œ ๋ณธ๋ก , ์กฐํšŒ ๋นˆ์ด 2๊ฐœ ์ด์ƒ(์ƒ์† ๊ด€๊ณ„)์ผ ๋•Œ

@Autowired๋ฅผ ์ด์šฉํ•ด์„œ ์˜์กด๊ด€๊ณ„๋ฅผ ์ž๋™๋“ฑ๋ก ํ•  ๋•Œ, ํ•ด๋‹นํ•˜๋Š” ๋นˆ์ด ์—†๊ฑฐ๋‚˜ ๋นˆ ์ปดํฌ๋„ŒํŠธ์˜ ์ด๋ฆ„์ด ์ค‘๋ณต๋˜๋Š” ๊ฒฝ์šฐ๋Š” ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌ๋˜๋Š”์ง€ ์•Œ์•„๋ณด์•˜๋‹ค. ํ•˜์ง€๋งŒ ์ค‘๋ณต์ด ์•„๋‹ˆ๋ผ ์ƒ์†๊ด€๊ณ„์ธ ๊ฒฝ์šฐ์—๋Š” ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ๊นŒ?

  • Autowried๋Š” ๋นˆ์˜ ํƒ€์ž…(Type)์„ ์กฐํšŒํ•˜์—ฌ ์˜์กด๊ด€๊ณ„๋ฅผ ๋“ฑ๋กํ•œ๋‹ค.
  • ์ฆ‰ ac.getBean(DiscountPolicy.class)๋ฅผ ์ด์šฉํ•ด์„œ ํ•ด๋‹น ๋นˆ์„ ๊ตฌํ˜„์ฒด๋กœ ๋“ฑ๋กํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋™์ž‘ํ•œ๋‹ค.
  • ๊ทธ๋ ‡๋‹ค๋ฉด DiscountPolicy์˜ ํ•˜์œ„ํƒ€์ž…์ธ FixDiscount ์™€ RateDiscount๋Š” ์–ด๋–ป๊ฒŒ ๊ตฌ๋ถ„ํ• ๊นŒ?
@Autowired
private DiscountPolicy discountPolicy
// ๋‘˜ ์ค‘ ๋ฌด์—‡์„ ์˜์กด๊ด€๊ณ„๋กœ ๋“ฑ๋กํ•ด์•ผํ•˜๋Š”์ง€ ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์•Œ ์ˆ˜ ์žˆ์„๊นŒ?
@Component
public class FixDiscountPolicy implements DiscountPolicy {}

@Component
public class RateDiscountPolicy implements DiscountPolicy {}

์ •๋‹ต์€ '์ž๋™์œผ๋กœ ๋“ฑ๋กํ•˜์ง€ ๋ชปํ•œ๋‹ค'์ด๋‹ค. NoUniqueBeanDefinitionException์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ์˜ค๋ฅ˜ ๋‚ด์šฉ์„ ์ฝ์–ด๋ณด๋ฉด ๊ฐ™์€ ํƒ€์ž…์ธ ๋นˆ์ด 2๊ฐœ๊ฐ€ ์žˆ์–ด ๋งค์นญ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ์นœ์ ˆํ•˜๊ฒŒ ์•Œ๋ ค์ค€๋‹ค.

NoUniqueBeanDefinitionException: No qualifying bean of type 
'hello.core.discount.DiscountPolicy' available: expected single matching bean 
but found 2: fixDiscountPolicy,rateDiscountPolicy

๋ฌผ๋ก  DiscountPolicy ์ธํ„ฐํŽ˜์ด์Šค ๋Œ€์‹ ์— ํ•˜์œ„ํƒ€์ž…์˜ ๊ตฌํ˜„์ฒด๋ฅผ ์ง์ ‘ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์œผ๋‚˜, ์ด๋Š” DIP๋ฅผ ์œ„๋ฐฐํ•˜๊ณ  ์•ž์—์„œ ๋ฐฐ์› ๋˜ ๋‹คํ˜•์„ฑ์„ ์ง€ํ‚ค์ง€ ๋ชปํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋˜๋ฒ„๋ฆฐ๋‹ค. ๋˜๋Š” ์Šคํ”„๋ง ๋นˆ์„ ์ˆ˜๋™(@Bean)์œผ๋กœ ์ง์ ‘ ์ง€์ •ํ•˜์—ฌ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๊ฒ ์ง€๋งŒ, ์˜์กด ๊ด€๊ณ„ ์ž๋™ ์ฃผ์ž…์—์„œ๋„ ํ•ด๊ฒฐํ•˜๋Š” ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•๋“ค์ด ์žˆ๋‹ค.

 


 # ํ•ด๊ฒฐ์ฑ…

@Autowired [filed-name]

[ @Autowired ] ๋Š” ํƒ€์ž… ๋งค์นญ์„ ์‹œ๋„ํ•  ๋•Œ ๋™์ผํ•œ ์—ฌ๋Ÿฌ ๋นˆ์ด ์žˆ์œผ๋ฉด ํ•„๋“œ ์ด๋ฆ„, ํŒŒ๋ผ๋ฏธํ„ฐ ์ด๋ฆ„์œผ๋กœ ๋นˆ ์ด๋ฆ„์„ ๋งค์นญํ•œ๋‹ค. ์ฆ‰ ๋‹จ์ˆœํžˆ ๋ณ€์ˆ˜ ์ด๋ฆ„์„ ๋“ฑ๋กํ•˜๊ณ ์žํ•˜๋Š” ๋นˆ์ด๋ฆ„์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ธฐ๋งŒ ํ•ด๋„ ํ•ด๋‹น ์ด๋ฆ„์˜ ๋นˆ์„ ์ฐพ์•„์„œ ๋“ฑ๋กํ•ด์ค€๋‹ค.

* ์•ž์—์„œ ์ปดํฌ๋„ŒํŠธ ์ž๋™์Šค์บ”์œผ๋กœ ๋“ฑ๋กํ•œ ๋นˆ ์ด๋ฆ„์ด ํด๋ž˜์Šค๋ช…๊ณผ ๋™์ผํ•œ ์ด์œ . (MyClass -> myClass ๋กœ ์ƒ์„ฑ) 

// ์—๋Ÿฌ. ํ•ด๋‹น ํƒ€์ž…์˜ ๋นˆ์ด ์—ฌ๋Ÿฌ๊ฐœ์ž„
@Autowired
private DiscountPolicy discountPolicy

// ํ•ด๋‹น ํƒ€์ž…์˜ ๋นˆ์ด ์—ฌ๋Ÿฌ๊ฐœ์ง€๋งŒ, rateDiscountPolicy ๋ผ๋Š” ์ด๋ฆ„์„ ๊ฐ€์ง„ ๋นˆ์ด ์žˆ์œผ๋ฏ€๋กœ ์ž๋™๋“ฑ๋ก.
@Autowired
private DiscountPolicy rateDiscountPolicy

๋‹จ, ํƒ€์ž… ๋งค์นญ์ด ์‹คํŒจํ–ˆ์„ ๋•Œ๋งŒ(=๋นˆ์ด ์—ฌ๋Ÿฌ๊ฐœ์ผ ๋•Œ) ํ•„๋“œ, ํŒŒ๋ผ๋ฉ”ํƒ€ ์ด๋ฆ„์„ ์ถ”๊ฐ€์ ์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์žŠ์ง€๋ง์ž.

 


@Qualifier ๋ผ๋ฆฌ ๋งค์นญํ•˜๊ธฐ

[ @Qualifier ] ๋Š” ์ถ”๊ฐ€์ ์ธ ๊ตฌ๋ถ„์ž๋ฅผ ๋ถ™์—ฌ์ฃผ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. ๋งˆ์น˜ ๋นˆ ์ด๋ฆ„์„ ๋“ฑ๋กํ•ด์ฃผ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ์ž๋™ ๋“ฑ๋ก์„ ํ•˜๊ธฐ์œ„ํ•ด ๊ตฌ๋ถ„์ž๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ผ ๋ฟ ์‹ค์ œ ๋นˆ ์ด๋ฆ„์ด ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.

๋งŒ์•ฝ ํ•ด๋‹น ์ด๋ฆ„์˜ Qualifier๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ, ๊ฐ™์€ ์ด๋ฆ„์˜ ์Šคํ”„๋ง ๋นˆ์„ ์ฐพ์•„์„œ ์—ฐ๊ฒฐํ•ด์ฃผ๊ธด ํ•˜์ง€๋งŒ ๋ณดํ†ต์€ ๊ทธ๋ ‡๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ์—†๋‹ค. Qualifier์˜ ์ด๋ฆ„์„ ์ง€์ •ํ•ด์ฃผ์ž.

@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}

@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {}

// ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์ด ์•„๋‹ˆ๋ผ ์ˆ˜๋™์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ๊ฒฝ์šฐ
@Bean
@Qualifier("mainDiscountPolicy")
public DiscountPolicy discountPolicy() {
    return new ...
}
@Autowired // ํŒŒ๋ผ๋ฉ”ํƒ€ ์•ž์— @Qualifier๋ฅผ ์ถ”๊ฐ€๋กœ ์ ์–ด์ค€๋‹ค.
public OrderServiceImpl(MemberRepository memberRepository,
@Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy){
        this.memberRepository=memberRepository;
        this.discountPolicy=discountPolicy;
}

 


@Primary ์‚ฌ์šฉํ•˜๊ธฐ

์ด๋Š” ์ด๋ฆ„์„ ์ง€์ •ํ•ด์ฃผ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ์šฐ์„ ์ˆœ์œ„๋ฅผ ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. @Autowired์—์„œ ์—ฌ๋Ÿฌ ๋นˆ์ด ๋งค์นญ๋˜๋ฉด, @Primary ๋นˆ์ด ์šฐ์„ ๊ถŒ์„ ๊ฐ€์ง€๊ฒŒ ๋œ๋‹ค. ์œ„์˜ @Qualifier์˜ ๊ฒฝ์šฐ ๋ชจ๋“  ์ฝ”๋“œ์— ์ง์ ‘ ๊ตฌ๋ถ„์ž๋ฅผ ๋‹ฌ์•„์ค˜์•ผํ•ด์„œ ๋ฒˆ๊ฑฐ๋กญ์ง€๋งŒ, @Primary๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ณ„๋‹ค๋ฅธ ์ถ”๊ฐ€์ž‘์—…์—†์ด ์›ํ•˜๋Š” ์˜์กด๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {}

@Component
public class FixDiscountPolicy implements DiscountPolicy {}

 


@ Primary์™€ Qualifier์˜ ํ™œ์šฉํŒ

์ฝ”๋“œ์—์„œ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์ธ DB ์ปค๋„ฅ์…˜์„ ํš๋“ํ•˜๋Š” ์Šคํ”„๋ง๋นˆ๊ณผ, ํŠน๋ณ„ํ•œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ๊ฐ€๋”์‹ ์‚ฌ์šฉํ•˜๋Š” ์„œ๋ธŒ DB์˜ ์ปค๋„ฅ์…˜์„ ํš๋“ํ•˜๋Š” ์Šคํ”„๋ง ๋นˆ์ด ๊ฐ๊ฐ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž. ๋ฉ”์ธ DB๋นˆ์€ @Primary๋ฅผ ์ ์šฉํ•ด์„œ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ณ  ๊ฐ€๋” ์‚ฌ์šฉํ•˜๋Š” ์„œ๋ธŒDB๋Š” @Qualifier๋ฅผ ์ง€์ •ํ•˜์—ฌ ๋ช…์‹œ์ ์œผ๋กœ ํš๋“ํ•˜๋Š” ๋ฐฉ์‹์ด ์ฝ”๋“œ๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ์œ ์ง€ํ•˜๋ฉด์„œ, ๊ฐœ๋ฐœ์ž์˜ ์‹ค์ˆ˜๋ฅผ ๋ฏธ์—ฐ์— ๋ฐฉ์ง€ํ•ด์ค€๋‹ค. (๋ฌผ๋ก  ์ด๋Š” ์‚ฌ์šฉํ•˜๊ธฐ ๋‚˜๋ฆ„์ด๋‹ค. ํ•„์š”์— ๋”ฐ๋ผ ์ „๋ถ€ @Qualifier๋กœ ํ•ด๋„ ์ „ํ˜€ ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค.)

 

* ์ฐธ๊ณ ๋กœ ๋‹ค๋ฅธ ์šฐ์„ ์ˆœ์œ„์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ๋™์‹œ์— ์‚ฌ์šฉํ–ˆ์„ ๊ฒฝ์šฐ ๊ธฐ๋ณธ๊ฐ’์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๋Š” @Primary๋ณด๋‹ค ์ง์ ‘ ์ง€์ •ํ•ด์ฃผ๋Š” @Qualifier๊ฐ€ ์šฐ์„ ๊ถŒ์ด ๋†’๋‹ค.

 

 


@ ์ปดํŒŒ์ผ์‹œ ํƒ€์ž… ์ฒดํฌ (์–ด๋…ธํ…Œ์ด์…˜)

@Qualifier("mainDiscountPolicy") ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์€ 'mainDiscountPolicy'๋ผ๋Š” ๋ฌธ์ž๋Š” ์ปดํŒŒ์ผ์‹œ ์ฒดํฌ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. ์ฆ‰ ํƒ€์ž… ์ฒดํฌ๊ฐ€ ์•ˆ๋œ๋‹ค. ์ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์–ด๋…ธํ…Œ์ด์…˜์œผ๋กœ ๊ฐ์‹ธ์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
        ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Qualifier("mainDiscountPolicy")
public @interface MainDiscountPolicy {
}
@Component
@MainDiscountPolicy // ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•์ด๋‹ค.
public class RateDiscountPolicy implements DiscountPolicy {}
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
@MainDiscountPolicy DiscountPolicy discountPolicy){
        this.memberRepository=memberRepository;
        this.discountPolicy=discountPolicy;
}

๋‹ค๋งŒ Qualifier ์ฒ˜๋Ÿผ ํŠน๋ณ„ํ•œ ๋ชฉ์ ์ด ์žˆ๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ๋ฉด, ๋ณดํ†ต์€ ์ƒˆ๋กœ ์–ด๋…ธํ…Œ์ด์…˜์„ ๋งŒ๋“ค์ง€ ๋ง๊ณ  ์Šคํ”„๋ง์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š”๊ฒŒ ๊น”๋”ํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜์— ์ข‹๋‹ค.

 


# ์กฐํšŒํ•œ ๋นˆ์ด ๋ชจ๋‘ ํ•„์š”ํ•  ๋•Œ (List, Map)

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

class DiscountService {
        private final Map<String, DiscountPolicy> policyMap;
        private final List<DiscountPolicy> policies;
		
        // Map ๋˜๋Š” List๋กœ ๋ชจ๋“  DiscountPolicy ๋นˆ์„ ์ฃผ์ž… ๋ฐ›๋Š”๋‹ค.
        // ๋ณดํ†ต ๋‘˜ ์ค‘ ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ์˜ˆ์ œ๋กœ ๋ณด์—ฌ์ค„๋ ค๊ณ  Map, List๋กœ ๊ฐ๊ฐ ์˜์กด๊ด€๊ณ„๋ฅผ ์ฃผ์ž…๋ฐ›์•˜๋‹ค.
        @Autowired
        public DiscountService(Map<String, DiscountPolicy> policyMap,
                               List<DiscountPolicy> policies) {
            this.policyMap = policyMap;
            this.policies = policies;
        }
		
        public int discount(Member member, int price, String discountCode) {
            DiscountPolicy discountPolicy = policyMap.get(discountCode);
            return discountPolicy.discount(member, price);
        }
}

@Autowired๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์˜์กด๊ด€๊ณ„๋ฅผ Map์ด๋‚˜ List๋กœ ๋ฐ›๊ฒŒ๋˜๋ฉด ํ•ด๋‹นํ•˜๋Š” ๋ชจ๋“  Bean๋“ค์ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ์ „๋žตํŒจํ„ด(์กด์žฌํ•˜๋Š” ํ• ์ธ์ •์ฑ…์ค‘์— ํ•˜๋‚˜๋ฅผ ์„ ํƒ)์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ์Šคํ”„๋ง ์—†์ด ๊ตฌํ˜„ํ•ด๋ณด๋ฉด ์•Œ๊ฒ ์ง€๋งŒ, ์ž‘์„ฑํ•ด์•ผํ•  ์ฝ”๋“œ๊ฐ€ ์ •๋ง ๋งŽ์€๋ฐ ์Šคํ”„๋ง์˜ ์ž๋™ ์˜์กด๊ด€๊ณ„ ์ฃผ์ž…์„ ํ™œ์šฉํ•˜๋ฉด ์ •๋ง ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

public class AllBeanTest {
    @Test
    void findAllBean() {
        // ์Šคํ”„๋ง ๋นˆ ์ƒ์„ฑ. ์•„์ง Map๊ณผ List๋Š” ๋น„์–ด์žˆ๋‹ค. (์˜์กด์„ฑ ์ฃผ์ž… X)
        ApplicationContext ac = new
                AnnotationConfigApplicationContext(AutoAppConfig.class, DiscountService.class);
        
        // ์ž๋™ ์˜์กด๊ด€๊ณ„ ์ƒ์„ฑ์ž ์ฃผ์ž…์„ ์ด์šฉํ•œ๋‹ค.
        // DiscountService์— ์žˆ๋Š” Map๊ณผ List์— DiscountPolicy๋“ค์ด ์ฑ„์›Œ์ ธ ์žˆ๋‹ค. (fix, rate)
        DiscountService discountService = ac.getBean(DiscountService.class);
        
        // discount() ๋ฉ”์„œ๋“œ๋Š” "fixDiscountPolicy" ๊ฐ€ ๋„˜์–ด์˜ค๋ฉด map์—์„œ ํ•ด๋‹น ์ด๋ฆ„์˜ ๋นˆ์„ ์ฐพ์•„ ์‹คํ–‰ํ•œ๋‹ค.
        Member member = new Member(1L, "userA", Grade.VIP);
        int discountPrice = discountService.discount(member, 10000,"fixDiscountPolicy");
        
        assertThat(discountService).isInstanceOf(DiscountService.class);
        assertThat(discountPrice).isEqualTo(1000);
    }
}

 


# ์ž๋™๊ณผ ์ˆ˜๋™, ์˜ฌ๋ฐ”๋ฅธ ์„ ํƒ์˜ ๊ธฐ์ค€

์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์—…๋ฐ์ดํŠธ๋ฅผ ๊ณ„์†ํ•˜๋ฉฐ ๋งŽ์€ ๋ถ€๋ถ„์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ๊ณ  ์žˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๊ณ  ์ „๋ถ€ ์ž๋™์œผ๋กœ ์‚ฌ์šฉํ•˜๊ฒŒ๋˜๋ฉด ํ”„๋กœ์ ํŠธ์˜ ๊ตฌ์กฐ๋ฅผ ํŒŒ์•…ํ•˜๊ธฐ ํž˜๋“ค๊ณ  ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ์„ ๋•Œ ์ฐพ๊ธฐ ํž˜๋“ค ๊ฒƒ ๊ฐ™์€๋ฐ, ์–ด๋””๊นŒ์ง€ ์ž๋™์œผ๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ?

 

๊ฒฐ๋ก ๋ถ€ํ„ฐ ์ด์•ผ๊ธฐํ•˜๋ฉด, ์ œ๊ณตํ•˜๋Š” ํŽธ๋ฆฌํ•œ ์ž๋™ ๊ธฐ๋Šฅ์„ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค. ์Šคํ”„๋ง์€ @Component ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ @Controller, @Service, @Repository ์ฒ˜๋Ÿผ ๊ณ„์ธต์— ๋งž์ถ”์–ด ์ผ๋ฐ˜์ ์ธ ์•ฑ ๋กœ์ง์„ ์ž๋™์Šค์บ” ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•œ๋‹ค. ๊ฑฐ๊ธฐ์— ๋”ํ•ด 2014๋…„ ์Šคํ”„๋ง ๋ถ€ํŠธ์˜ ๋“ฑ์žฅ์œผ๋กœ ์ปดํฌ๋„ŒํŠธ ์Šค์บ”์ด ์„ค์ •์ด ์•„๋‹Œ ๊ธฐ๋ณธ ๋™์ž‘์œผ๋กœ ๋ฐ”๋€Œ๊ฒŒ ๋˜์—ˆ๊ณ , ์Šคํ”„๋ง ๋ถ€ํŠธ์˜ ๋‹ค์–‘ํ•œ ๋นˆ๋“ค๋„ ์กฐ๊ฑด์ด ๋งž์œผ๋ฉด ์ž๋™์œผ๋กœ ๋“ฑ๋กํ•˜๋„๋ก ์„ค๊ณ„๋˜์–ด์ ธ์žˆ๋‹ค.

 

๋ฌผ๋ก  ์„ค์ • ์ •๋ณด(AppConfig)๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹คํ–‰๋ถ€๋ถ„๊ณผ ๊ตฌ์„ฑ๋ถ€๋ถ„์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋‚˜๋ˆ„๋Š” ๊ฒƒ์ด ์ด์ƒ์ ์ด๊ธด ํ•˜๋‹ค. ํ•˜์ง€๋งŒ ๊ฐœ๋ฐœ์ž ์ž…์žฅ์—์„œ @Component ๋งŒ ์ ์–ด์ฃผ๋ฉด ๋๋‚˜๋Š” ์ผ์„ ํ•˜๋‚˜ํ•˜๋‚˜ ์ง€์ •ํ•ด์ฃผ๋Š”๊ฑด ์ƒ๋‹นํžˆ ๋ฒˆ๊ฑฐ๋กญ๊ธฐ๋„ ํ•˜๊ณ , ๊ด€๋ฆฌํ•  ๋นˆ์ด ๋งŽ์•„์ง€๋ฉด ์„ค์ • ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ ์ž์ฒด๊ฐ€ ๋ถ€๋‹ด์ด ๋œ๋‹ค. ๊ฒฐ์ •์ ์œผ๋กœ ์ž๋™ ๋นˆ์„ ์‚ฌ์šฉํ•ด๋„ OCP, DIP๋ฅผ ์ง€ํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

 


@ ๊ทธ๋Ÿผ ์ˆ˜๋™ ๋นˆ์€ ์–ธ์ œ ์“ฐ๋‚˜์š”?

๋ณดํ†ต ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์—…๋ฌด ๋กœ์ง(๋น„์ฆˆ๋‹ˆ์Šค)์™€ ๊ธฐ์ˆ  ์ง€์› ๋กœ์ง์œผ๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ๋‹ค.

  • ์—…๋ฌด๋กœ์ง ๋นˆ : ์›น์„ ์ง€์›ํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ, ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊ฐ€์ง„ ์„œ๋น„์Šค, ๋ฐ์ดํ„ฐ ๊ณ„์ธต์˜ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ๋“ฑ์ด ๋ชจ๋‘ ์—…๋ฌด ๋กœ์ง์— ํ•ด๋‹น๋œ๋‹ค. ๋ณดํ†ต ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ์„ ๊ฐœ๋ฐœํ•  ๋•Œ ๋ณ€๊ฒฝ๋œ๋‹ค.
  • ๊ธฐ์ˆ ์ง€์› ๋นˆ : ๊ธฐ์ˆ ์ ์ธ ๋ฌธ์ œ๋‚˜ ๊ณตํ†ต ๊ด€์‹ฌ์‚ฌ(AOP)๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ, ๊ณตํ†ต ๋กœ๊ทธ์ฒ˜๋ฆฌ ์ฒ˜๋Ÿผ ๋น„์ฆˆ๋‹ˆ์Šค ์š”๊ตฌ์‚ฌํ•ญ ๊ทธ ์ž์ฒด๋ผ๊ธฐ ๋ณด๋‹ค๋Š”, ์—…๋ฌด ๋กœ์ง์„ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•œ ํ•˜๋ถ€ ๊ธฐ์ˆ ๋“ค์ด๋‹ค.

์—…๋ฌด ๋กœ์ง ๋นˆ์„ ๋งŒ๋“ค ๋•Œ์—๋Š” ์ž๋™๊ธฐ๋Šฅ์„ ์ ๊ทน ์‚ฌ์šฉํ•˜์ž. ์• ์ดˆ์— ๊ด€๋ฆฌํ•ด์•ผํ•  ์ฝ”๋“œ๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์•„ ๊ทธ๋Ÿฐ ๊ฒƒ๋„ ์žˆ์ง€๋งŒ ์ปจํŠธ๋กค๋Ÿฌ-์„œ๋น„์Šค-๋ฆฌํฌ์ง€ํ† ๋ฆฌ์ฒ˜๋Ÿผ ์–ด๋Š์ •๋„ ์œ ์‚ฌํ•œ ํŒจํ„ด์ด ์กด์žฌํ•œ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋”๋ผ๋„ ์™œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ๋ช…ํ™•ํ•˜๊ฒŒ ํŒŒ์•…ํ•˜๊ธฐ ์‰ฝ๋‹ค. (์–ด๋Š ์„œ๋น„์Šค์—์„œ ๋ฌธ์ œ๊ฐ€ ๋‚ฌ๋Š”์ง€ ์ฐพ๊ธฐ ์‰ฝ๋‹ค.)

 

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

 

 


@ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์—์„œ ์ˆ˜๋™ ๋นˆ์„ ํ†ตํ•œ ๋‹คํ˜•์„ฑ์˜ ํ™œ์šฉ

์œ„์—์„œ ์‚ฌ์šฉํ–ˆ๋˜ ์ „๋žตํŒจํ„ด์„ ์ƒ๊ฐํ•ด๋ณด์ž. ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ํ•œ๋ˆˆ์— ๋ณด๊ณ  ์–ด๋– ํ•œ ๋™์ž‘์„ ํ•˜๋Š”์ง€ ์•Œ์ˆ˜ ์žˆ์„๊นŒ?

class DiscountService {
        private final Map<String, DiscountPolicy> policyMap;
        private final List<DiscountPolicy> policies;
		
        // Map ๋˜๋Š” List๋กœ ๋ชจ๋“  DiscountPolicy ๋นˆ์„ ์ฃผ์ž… ๋ฐ›๋Š”๋‹ค.
        // ๋ณดํ†ต ๋‘˜ ์ค‘ ํ•˜๋‚˜๋งŒ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ์˜ˆ์ œ๋กœ ๋ณด์—ฌ์ค„๋ ค๊ณ  Map, List๋กœ ๊ฐ๊ฐ ์˜์กด๊ด€๊ณ„๋ฅผ ์ฃผ์ž…๋ฐ›์•˜๋‹ค.
        @Autowired
        public DiscountService(Map<String, DiscountPolicy> policyMap,
                               List<DiscountPolicy> policies) {
            this.policyMap = policyMap;
            this.policies = policies;
        }
		
        public int discount(Member member, int price, String discountCode) {
            DiscountPolicy discountPolicy = policyMap.get(discountCode);
            return discountPolicy.discount(member, price);
        }
}

์ด๋Ÿฐ ๊ฒฝ์šฐ ํ•ด๋‹น ๋ถ€๋ถ„์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜๋™์œผ๋กœ ๋“ฑ๋กํ•˜์—ฌ ํ•œ๋ˆˆ์— ์˜๋ฏธ๋ฅผ ์•Œ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ๊ฒƒ๋„ ๋‚˜์˜์ง€ ์•Š๋‹ค. ๋˜ํ•œ ์ž๋™์œผ๋กœ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ์ตœ์†Œํ•œ 'ํŠน์ • ํŒจํ‚ค์ง€'์— ํ•˜๋‚˜๋กœ ๋ฌถ์–ด์„œ ์˜๋ฏธ๋ฅผ ์‰ฝ๊ฒŒ ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด์ฃผ์ž.

@Configuration
public class DiscountPolicyConfig {

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

    @Bean
    public DiscountPolicy fixDiscountPolicy() {
        return new FixDiscountPolicy();
    }
}

 


@ ๋‹จ, ์Šคํ”„๋ง๊ณผ ๋ถ€ํŠธ๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ž๋™ ๋“ฑ๋กํ•˜๋Š” ๋นˆ์€ ์˜ˆ์™ธ์ด๋‹ค.

์Šคํ”„๋ง๋ถ€ํŠธ์˜ ๊ฒฝ์šฐ DataSource ๊ฐ™์€ DB์—ฐ๊ฒฐ์— ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ์ˆ ์ง€์› ๋กœ์ง๊นŒ์ง€ ์ž๋™์œผ๋กœ ๋“ฑ๋กํ•˜๋Š”๋ฐ, ์ด๋Ÿฐ ๋ถ€๋ถ„์€ ๋ฉ”๋‰ด์–ผ์„ ์ž˜ ์ฐธ๊ณ ํ•ด์„œ ์Šคํ”„๋ง๋ถ€ํŠธ๊ฐ€ ์˜๋„ํ•œ ๋Œ€๋กœ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

 

๋งŒ์•ฝ ์Šคํ”„๋ง๋ถ€ํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ ๋‚ด๊ฐ€ ์ง์ ‘ ๊ธฐ์ˆ ์ง€์› ๊ฐ์ฒด๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•œ๋‹ค๋ฉด, ์ด๋Š” ์ˆ˜๋™์œผ๋กœ ๋“ฑ๋กํ•ด์„œ ์˜๋ฏธ๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ๋“œ๋Ÿฌ๋‚ด์ฃผ์ž. 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ