AOP ๋ž€


AOP ๊ฐ€ ํ•„์š”ํ•œ ์ƒํ™ฉ

์˜ˆ๋ฅผ ๋“ค์–ด, ๋ชจ๋“  ๋ฉ”์„œ๋“œ์˜ ํ˜ธ์ถœ ์‹œ๊ฐ„์„ ์ธก์ •ํ•˜๊ณ  ์‹ถ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž. ๊ทธ๋ ‡๋‹ค๋ฉด ์œ„ ์ฒ˜๋Ÿผ ๋ชจ๋“  ๋ฉ”์„œ๋“œ์— ์‹œ๊ฐ„ ์ธก์ • ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค.

public class MemberService {
 
    public Long join(Member member) {
        long start = System.currentTimeMillis();
        try {
            validateDuplicateMember(member); //์ค‘๋ณต ํšŒ์› ๊ฒ€์ฆ
            memberRepository.save(member);
            return member.getId();
        } finally {
            long finish = System.currentTimeMillis();
            long timeMs = finish - start;
            System.out.println("join " + timeMs + "ms");
        }
    }
 
    public List<Member> findMembers() {
        long start = System.currentTimeMillis();
        try {
            return memberRepository.findAll();
        } finally {
            long finish = System.currentTimeMillis();
            long timeMs = finish - start;
            System.out.println("findMembers " + timeMs + "ms");
        }
    }
}

์š”๊ตฌ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•˜๊ธฐ ์œ„ํ•ด์„  ์œ„์ฒ˜๋Ÿผ ์ฝ”๋“œ๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ์ฝ”๋“œ๊ฐ€ ๋ณ€๊ฒฝ๋œ๋‹ค๋ฉด ์–ด๋–ค ๋ฌธ์ œ์ ๋“ค์ด ์žˆ์„๊นŒ?

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

AOP ์ ์šฉ

AOP ๋ž€ Aspect Oriented Programming ์ฆ‰, ๊ด€์  ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด๋‹ค. AOP ๋Š” ๊ณตํ†ต ๊ด€์‹ฌ ์‚ฌํ•ญ(cross-cutting concern) ๊ณผ ํ•ต์‹ฌ ๊ด€์‹ฌ ์‚ฌํ•ญ(core concern) ์„ ๋ถ„๋ฆฌํ•˜์—ฌ ์›ํ•˜๋Š” ๊ณณ์—๋งŒ ๊ณตํ†ต ๊ด€์‹ฌ ์‚ฌํ•ญ์„ ์ ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

AOP ๋นˆ ๋“ฑ๋ก

package hello.hellospring;
 
import hello.hellospring.aop.TimeTraceAop;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class SpringConfig {
 
    private final MemberRepository memberRepository;
 
    public SpringConfig(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
 
    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository);
    }
 
    @Bean
    public TimeTraceAop timeTraceAop() {
        return new TimeTraceAop();
    }
}

์‹œ๊ฐ„ ์ธก์ • AOP ๋“ฑ๋ก

package hello.hellospring.aop;
 
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
 
@Aspect
public class TimeTraceAop {
 
//    @Around("execution(* hello.hellospring..*(..))")
    @Around("execution(* hello.hellospring..*(..)) && !target(hello.hellospring.SpringConfig)")
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("START: " + joinPoint.toString());
        try {
            return joinPoint.proceed();
        } finally {
            long finish = System.currentTimeMillis();
            long timeMs = finish - start;
            System.out.println("END: " + joinPoint.toString() + " " + timeMs + "ms");
        }
    }
}

AOP ๋“ฑ๋ก ์‹œ, @Component ์• ๋„ˆํ…Œ์ด์…˜์„ ํ†ตํ•ด Bean ๋“ฑ๋ก์„ ์ˆ˜ํ–‰ํ•ด๋„ ๋˜์ง€๋งŒ, ์ •ํ˜•์ ์ธ Component ๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋Œ€๋ถ€๋ถ„ SpringConfig ์—์„œ Bean ๋“ฑ๋ก์„ ๋”ฐ๋กœ ํ•ด์ฃผ์–ด์•ผ ๋ช…์‹œ์ ์œผ๋กœ AOP ๊ฐ€ ์ ์šฉ๋˜๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. @Around("execution(* hello.hellospring..*(..))") ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์ฃผ์–ด์ง„ ๊ฒฝ๋กœ์— ํ•ด๋‹นํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์„œ๋“œ ์‹คํ–‰ ์‹œ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ผ๋Š” ์˜๋ฏธ๊ฐ€ ๋˜๋Š”๋ฐ, ์ด๋ ‡๊ฒŒ ๋˜๋ฉด SpringConfig ์˜ timeTraceAop() ๋ฉ”์„œ๋“œ๋„ AOP ๋กœ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค. ์ด๋Ÿด ๊ฒฝ์šฐ, ์ž๊ธฐ ์ž์‹ ์„ ์ƒ์„ฑํ•˜๋Š” ๋ฉ”์„œ๋“œ์—์„œ๋„ AOP ๊ฐ€ ์ ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ˆœํ™˜์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ๋•Œ๋ฌธ์—, ๋ฐ”๋กœ ์•„๋ž˜ ์žˆ๋Š” @Around ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด SpringConfig ๋ฅผ AOP ๋Œ€์ƒ์—์„œ ์ œ์™ธํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

6.1 ํŠธ๋žœ์žญ์…˜ ์ฝ”๋“œ์˜ ๋ถ„๋ฆฌ


6.1.1 ๋ฉ”์†Œ๋“œ ๋ถ„๋ฆฌ

  • ๋‘ ๊ฐ€์ง€ ์ข…๋ฅ˜์˜ ์ฝ”๋“œ๊ฐ€ ๊ตฌ๋ถ„๋˜์–ด ์žˆ๊ณ , ์ฝ”๋“œ ๊ฐ„์— ์„œ๋กœ ์ฃผ๊ณ ๋ฐ›๋Š” ์ •๋ณด๊ฐ€ ์—†์Œ
  • ๋”ฐ๋ผ์„œ ์ด ๋‘ ๊ฐ€์ง€ ์ฝ”๋“œ๋Š” ์„ฑ๊ฒฉ์ด ๋‹ค๋ฅผ ๋ฟ ์•„๋‹ˆ๋ผ, ์™„๋ฒฝํ•˜๊ฒŒ ๋…๋ฆฝ์ ์ธ ์ฝ”๋“œ

6.1.2 DI๋ฅผ ์ด์šฉํ•œ ํด๋ž˜์Šค์˜ ๋ถ„๋ฆฌ

  • ํŠธ๋žœ์žญ์…˜ ์ฝ”๋“œ๋ฅผ UserService ๋ฐ–์œผ๋กœ ๋นผ๋ฉด ํŠธ๋žœ์žญ์…˜ ๊ธฐ๋Šฅ์ด ๋น ์ง„ UserService๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋  ๊ฒƒ
  • ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋งŒ๋“ค๊ณ  ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ ํด๋ž˜์Šค์— ๋„ฃ์–ด๋‘์ž
  • UserServiceTx ๋Š” UserServiceImpl ์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ์œ„์ž„
  • ์ถ”๊ฐ€๋กœ ํŠธ๋žœ์žญ์…˜ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•จ
  • XML ์„ค์ •์„ ํ†ตํ•ด ๋ถ„๋ฆฌ๋œ ํŠธ๋žœ์žญ์…˜ ๊ธฐ๋Šฅ์„ ์œ„์™€ ๊ฐ™์ด ์ ์šฉ

6.2 ๊ณ ๋ฆฝ๋œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ


6.2.1 ๋ณต์žกํ•œ ์˜์กด๊ด€๊ณ„ ์†์˜ ํ…Œ์ŠคํŠธ

  • ํ˜„์ œ UserDao, TransactionManager, MailSender ๋ผ๋Š” 3๊ฐ€์ง€ ์˜์กด๊ด€๊ณ„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Œ
  • ๋”ฐ๋ผ์„œ UserService ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ์‚ฌ์‹ค์€ ๊ทธ ๋’ค์— ์กด์žฌํ•˜๋Š” ํ›จ์”ฌ ๋” ๋งŽ์€ ์˜ค๋ธŒ์ ํŠธ์™€ ํ™˜๊ฒฝ, ์„œ๋น„์Šค, ์„œ๋ฒ„, ์‹ฌ์ง€์–ด ๋„คํŠธ์›Œํฌ๊นŒ์ง€ ํ•จ๊ป˜ ํ…Œ์ŠคํŠธํ•˜๋Š” ์…ˆ์ด ๋จ

6.2.2 ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ์˜ค๋ธŒ์ ํŠธ ๊ณ ๋ฆฝ์‹œํ‚ค๊ธฐ

  • ํ…Œ์ŠคํŠธ๋ฅผ ์˜์กด ๋Œ€์ƒ์œผ๋กœ๋ถ€ํ„ฐ ๋ถ„๋ฆฌํ•ด์„œ ๊ณ ๋ฆฝ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์€ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ๋Œ€์—ญ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ
  • ๋‘ ๊ฐœ์˜ ๋ชฉ ์˜ค๋ธŒ์ ํŠธ๋งŒ ์˜์กดํ•˜๋Š”, ์™„๋ฒฝํ•˜๊ฒŒ ๊ณ ๋ฆฝ๋œ ํ…Œ์ŠคํŠธ ๋Œ€์ƒ์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ
  • ๋ชฉ UserDao ๋ฅผ ์œ„์™€ ๊ฐ™์ด ๋งŒ๋“ค์–ด์„œ ํ…Œ์ŠคํŠธ์— ์‚ฌ์šฉ
  • ๋ฒŒ์จ ๋ถˆํŽธํ•จโ€ฆ
  • ๊ณ ๋ฆฝ๋œ ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ๋ชฉ ์˜ค๋ธŒ์ ํŠธ ์ž‘์„ฑ๊ณผ ๊ฐ™์€ ์•ฝ๊ฐ„์˜ ์ˆ˜๊ณ ๊ฐ€ ๋” ํ•„์š”ํ•˜์ง€๋งŒ ํ…Œ์ŠคํŠธ ์„ฑ๋Šฅ์„ ํ™•์‹คํ•˜๊ฒŒ ํ–ฅ์ƒ๋œ๋‹ค.

6.2.3 ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ

๋‹จ์œ„ ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ค‘์— ์–ด๋–ค ๋ฐฉ๋ฒ•์„ ์“ธ ๊ฒƒ์ธ๊ฐ€?

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

6.2.4 ๋ชฉ ํ”„๋ ˆ์ž„์›Œํฌ

  • ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ ์Šคํ…์ด๋‚˜ ๋ชฉ ์˜ค๋ธŒ์ ํŠธ ์‚ฌ์šฉ์€ ํ•„์ˆ˜์ 
  • ํŠนํžˆ ๋ชฉ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ์ผ์ด ๊ฐ€์žฅ ํฐ ์ง
  • Mockito ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•ด ๋ชฉ ํด๋ž˜์Šค๋ฅผ ์ผ์ผ์ด ์ค€๋น„ํ•˜๋Š” ์ˆ˜๊ณ ๋ฅผ ๋œ์–ด๋ณด์ž.

6.3 ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ์™€ ํŒฉํ† ๋ฆฌ ๋นˆ


6.3.1 ํ”„๋ก์‹œ์™€ ํ”„๋ก์‹œ ํŒจํ„ด, ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด

  • ๋‹จ์ˆœ ํ™•์žฅ์„ฑ์„ ๊ณ ๋ คํ•ด ์ „๋žต ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด๋„ ํŠธ๋žœ์žญ์…˜๊ณผ ๊ฐ™์€ ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์„ ์œ„์ž„์„ ํ†ตํ•ด ์™ธ๋ถ€๋กœ ๋ถ„๋ฆฌํ–ˆ์„ ๋ฟ ํ•ต์‹ฌ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ๋‚จ์•„ ์žˆ์Œ
  • ํŠธ๋žœ์žญ์…˜์€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ๋Š” ์„ฑ๊ฒฉ์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์ ์šฉ ์‚ฌ์‹ค ์ž์ฒด๋ฅผ ๋ฐ–์œผ๋กœ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ
  • UserServiceTx โ†’ UserServiceImpl ์„ ์˜์กดํ•˜๋„๋ก ๋ณ€๊ฒฝ
  • ์ฆ‰, ๋ถ€๊ฐ€๊ธฐ๋Šฅ์ด ํ•ต์‹ฌ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ์กฐ
  • ํ•˜์ง€๋งŒ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•ต์‹ฌ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ํด๋ž˜์Šค๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•ด๋ฒ„๋ฆฌ๋ฉด ๋ถ€๊ฐ€๊ธฐ๋Šฅ์ด ์ ์šฉ๋  ๊ธฐํšŒ๊ฐ€ ์—†์Œ
  • ๋•Œ๋ฌธ์— ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋งˆ์น˜ ์ž์‹ ์ด ํ•ต์‹ฌ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ํด๋ž˜์Šค์ธ ๊ฒƒ์ฒ˜๋Ÿผ ๊พธ๋ฉฐ์„œ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ž์‹ ์„ ๊ฑฐ์ณ ํ•ต์‹ฌ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋„๋ก ๋งŒ๋“ค์–ด์•ผ ํ•จ
  • ๋งˆ์น˜ ์ž์‹ ์ด ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•˜๋Š” ์‹ค์ œ ๋Œ€์ƒ์ธ ๊ฒƒ์ฒ˜๋Ÿผ ์œ„์žฅํ•ด์„œ ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์„ ๋ฐ›์•„์ฃผ๋Š” ๊ฒƒ์„ ๋Œ€๋ฆฌ์ž, ๋Œ€๋ฆฌ์ธ๊ณผ ๊ฐ™์€ ์—ญํ• ์„ ํ•œ๋‹ค๊ณ  ํ•ด์„œ ํ”„๋ก์‹œ๋ผ๊ณ  ๋ถ€๋ฆ„
  • ํ”„๋ก์‹œ์˜ ํŠน์ง•
    • ํƒ€๊นƒ๊ณผ ๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•จ
    • ํ”„๋ก์‹œ๊ฐ€ ํƒ€๊นƒ์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Œ
  • ํ”„๋ก์‹œ์˜ ์‚ฌ์šฉ ๋ชฉ์ 
    • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํƒ€๊นƒ์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•จ
    • ํƒ€๊นƒ์— ๋ถ€๊ฐ€์ ์ธ ๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•ด์ฃผ๊ธฐ ์œ„ํ•จ
    • ๋‘ ๊ฐ€์ง€ ๋ชจ๋‘ ๋Œ€๋ฆฌ ์˜ค๋ธŒ์ ํŠธ๋ผ๋Š” ๊ฐœ๋…์˜ ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋Š” ์ ์€ ๋™์ผํ•˜์ง€๋งŒ, ๋ชฉ์ ์— ๋”ฐ๋ผ ๋””์ž์ธ ํŒจํ„ด์—์„œ๋Š” ๋‹ค๋ฅธ ํŒจํ„ด์œผ๋กœ ๊ตฌ๋ถ„๋จ

๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด

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

ํ”„๋ก์‹œ ํŒจํ„ด

  • ํƒ€๊นƒ์— ๋Œ€ํ•œ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์„ ์ œ์–ดํ•˜๋ ค๋Š” ๋ชฉ์ 
  • Collections์˜ unmodifiableCollection()์„ ํ†ตํ•ด ๋งŒ๋“ค์–ด์ง€๋Š” ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์ „ํ˜•์ ์ธ ์ ‘๊ทผ๊ด€ํ•œ ์ œ์–ด์šฉ ํ”„๋ก์‹œ๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Œ

6.3.2 ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ

  • ํ”„๋ก์‹œ๋Š” ๊ธฐ์กด ์ฝ”๋“œ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์œผ๋ฉด์„œ ํƒ€๊นƒ์˜ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•˜๊ฑฐ๋‚˜ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์šฉํ•œ ๋ฐฉ๋ฒ•
  • ํ•˜์ง€๋งŒ ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“œ๋Š” ์ผ์ด ์ƒ๋‹นํžˆ ๊ท€์ฐฎ์Œ
  • Reflection ์„ ํ†ตํ•ด ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ
  • ํ”„๋ก์‹œ์˜ ์—ญํ• ์€ ์œ„์ž„๊ณผ ๋ถ€๊ฐ€์ž‘์—…
  • ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค๊ธฐ ๋ฒˆ๊ฑฐ๋กœ์šด ์ด์œ ๋Š” ๋ฌด์—‡์ผ๊นŒ?
    • ๋ถ€๊ฐ€๊ธฐ๋Šฅ์ด ํ•„์š” ์—†๋Š” ๋ฉ”์†Œ๋“œ๋„ ๊ตฌํ˜„ํ•ด์„œ ํƒ€๊นƒ์œผ๋กœ ์œ„์ž„ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ผ์ผ์ด ๋งŒ๋“ค์–ด์ค˜์•ผ ํ•จ
    • ๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ฝ”๋“œ๊ฐ€ ์ค‘๋ณต๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋งŽ์Œ
  • ์ด๋•Œ ์œ ์šฉํ•œ ๊ฒƒ์ด ๋ฐ”๋กœ ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ
    • ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋Š” Reflection ์„ ์ด์šฉํ•ด ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ฌ

๋ฆฌํ”Œ๋ ‰์…˜

  • ์ž๋ฐ”์˜ ๋ชจ๋“  ํด๋ž˜์Šค๋Š” ๊ทธ ํด๋ž˜์Šค ์ž์ฒด์˜ ๊ตฌ์„ฑ์ •๋ณด๋ฅผ ๋‹ด์€ Class ํƒ€์ž…์˜ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ํ•˜๋‚˜์”ฉ ๊ฐ€์ง€๊ณ  ์žˆ์Œ
  • getClass() ๋ฅผ ํ†ตํ•ด ํด๋ž˜์Šค ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , ์ด ํด๋ž˜์Šค ์˜ค๋ธŒ์ ํŠธ๋ฅผ ํ†ตํ•ด ํด๋ž˜์Šค ์ฝ”๋“œ์— ๋Œ€ํ•œ ๋ฉ”ํƒ€์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ์Œ

๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ ์ ์šฉ

  • ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋Š” ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ์— ์˜ํ•ด ๋Ÿฐํƒ€์ž„ ์‹œ ๋‹ค์ด๋‚ด๋ฏนํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ง€๋Š” ์˜ค๋ธŒ์ ํŠธ
  • ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ ์˜ค๋ธŒ์ ํŠธ๋Š” ํƒ€๊นƒ์˜ ์ธํ„ฐํŽ˜์ด์Šค์™€ ๊ฐ™์€ ํƒ€์ž…์œผ๋กœ ๋งŒ๋“ค์–ด์ง
  • ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ์—๊ฒŒ ์ธํ„ฐํŽ˜์ด์Šค ์ •๋ณด๋งŒ ์ œ๊ณตํ•ด์ฃผ๋ฉด ํ•ด๋‹น ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค์˜ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์คŒ
  • ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ ํด๋ž˜์Šค์˜ ์˜ค๋ธŒ์ ํŠธ๋Š” ๋งŒ๋“ค์–ด์ฃผ์ง€๋งŒ, ํ”„๋ก์‹œ๋กœ์„œ ํ•„์š”ํ•œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ œ๊ณต ์ฝ”๋“œ๋Š” ์ง์ ‘ ์ž‘์„ฑํ•ด์•ผ ํ•จ
  • ๋ถ€๊ฐ€๊ธฐ๋Šฅ์€ ํ”„๋ก์‹œ ์˜ค๋ธŒ์ ํŠธ์™€ ๋…๋ฆฝ์ ์œผ๋กœ InvocationHandler ๋ฅผ ๊ตฌํ˜„ํ•œ ์˜ค๋ธŒ์ ํŠธ์— ๋‹ด์Œ
  • ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ ์˜ค๋ธŒ์ ํŠธ๋Š” ํด๋ผ์ด์–ธํŠธ์˜ ๋ชจ๋“  ์š”์ฒญ์„ ๋ฆฌํ”Œ๋ ‰์…˜ ์ •๋ณด๋กœ ๋ณ€ํ™˜ํ•ด์„œ InvocationHandler ๊ตฌํ˜„ ์˜ค๋ธŒ์ ํŠธ์˜ invoke() ๋ฉ”์„œ๋“œ๋กœ ๋„˜๊น€
  • ํƒ€๊นƒ ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ชจ๋“  ๋ฉ”์„œ๋“œ ์š”์ฒญ์ด ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ๋กœ ์ง‘์ค‘๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ค‘๋ณต๋˜๋Š” ๊ธฐ๋Šฅ์„ ํšจ๊ณผ์ ์œผ๋กœ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Œ
  • ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋กœ๋ถ€ํ„ฐ ์š”์ฒญ์„ ์ „๋‹ฌ๋ฐ›์œผ๋ ค๋ฉด InvocationHandler ๋ฅผ ๊ตฌํ˜„ํ•ด์•ผ ํ•จ
  • ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ์˜ ์ƒ์„ฑ์€ Proxy.newProxyInstance() ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋จ
  • Hello ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ๋งŽ์•„์ ธ๋„ ์–ด์ฐจํ”ผ invoke() ๋ฉ”์„œ๋“œ๋กœ ์ฒ˜๋ฆฌ๋˜๊ธฐ ๋•Œ๋ฌธ์— ํ™•์žฅ์— ์œ ์—ฐ
  • ํ•˜์ง€๋งŒ ์ŠคํŠธ๋ง ์™ธ์˜ ๋ฆฌํ„ด ํƒ€์ž…์„ ๊ฐ–๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์ถ”๊ฐ€๋œ๋‹ค๋ฉด?
    • Method ๋ฅผ ์ด์šฉํ•œ ํƒ€๊นƒ ์˜ค๋ธŒ์ ํŠธ์˜ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ํ›„ ๋ฆฌํ„ด ํƒ€์ž…์„ ํ™•์ธํ•ด์„œ ์ ์šฉํ•˜์ž.
  • ๋˜ํ•œ, Method ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ด์šฉํ•ด ํƒ€๊นƒ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์— Hello ํƒ€์ž…์˜ ํƒ€๊นƒ์œผ๋กœ ์ œํ•œํ•  ํ•„์š”๋„ ์—†์Œ

6.3.3 ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋ฅผ ์ด์šฉํ•œ ํŠธ๋žœ์žญ์…˜ ๋ถ€๊ฐ€๊ธฐ๋Šฅ

  • UserServiceTx ๋ฅผ ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝ
  • ์ฃผ์˜ํ•  ์ ์€ RuntimeException ๋Œ€์‹  InvocationTargetException ์œผ๋กœ ์žก์•„์„œ ์ฒ˜๋ฆฌ

6.3.4 ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋ฅผ ์œ„ํ•œ ํŒฉํ† ๋ฆฌ ๋นˆ

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

ํŒฉํ† ๋ฆฌ ๋นˆ

  • ์Šคํ”„๋ง์„ ๋Œ€์‹ ํ•ด์„œ ์˜ค๋ธŒ์ ํŠธ ์ƒ์„ฑ ๋กœ์ง์„ ๋‹ด๋‹นํ•˜๋„๋ก ๋งŒ๋“ค์–ด์ง„ ํŠน๋ณ„ํ•œ ๋นˆ
  • FactoryBean ์„ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๋ฅผ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๋ฉด ํŒฉํ† ๋ฆฌ ๋นˆ์œผ๋กœ ๋™์ž‘ํ•จ
  • ์Šคํ”„๋ง์€ FactoryBean ์„ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค๊ฐ€ ๋นˆ์˜ ํด๋ž˜์Šค๋กœ ์ง€์ •๋˜๋ฉด, ํŒฉํ† ๋ฆฌ ๋นˆ ํด๋ž˜์Šค์˜ ์˜ค๋ธŒ์ ํŠธ๋ฅผ getObject() ๋ฅผ ํ†ตํ•ด ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , ์ด๋ฅผ ๋นˆ ์˜ค๋ธŒ์ ํŠธ๋กœ ์‚ฌ์šฉ

๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋Š” ํŒฉํ† ๋ฆฌ ๋นˆ

  • Proxy.newProxyInstance() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด์„œ๋งŒ ์ƒ์„ฑ์ด ๊ฐ€๋Šฅํ•œ ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ํŒฉํ† ๋ฆฌ ๋นˆ์„ ํ†ตํ•ด ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋งŒ๋“ค์–ด์ค„ ์ˆ˜ ์žˆ์Œ
  • ์ด๋ฏธ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ ํŠธ๋žœ์žญ์…˜ ํ”„๋ก์‹œ ์˜ค๋ธŒ์ ํŠธ์˜ ํƒ€๊นƒ์„ ๋ณ€๊ฒฝํ•ด์ฃผ๊ธฐ ์–ด๋ ค์›€
  • TxProxyFactory ๋นˆ์„ ์ง์ ‘ ๊ฐ€์ ธ์™€์„œ ํ•ด๋ณด์ž

6.3.5 ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ ๋ฐฉ์‹์˜ ์žฅ์ ๊ณผ ํ•œ๊ณ„

ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ ๋ฐฉ์‹์˜ ์žฅ์ 

  • ํ•˜๋‚˜์˜ ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์ˆ˜๋งŽ์€ ๋ฉ”์„œ๋“œ์— ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋ถ€์—ฌํ•ด์ค„ ์ˆ˜ ์žˆ์Œ
  • ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ ์ƒ์„ฑ ์ฝ”๋“œ๋„ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Œ

ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ์˜ ํ•œ๊ณ„

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

6.4 ์Šคํ”„๋ง์˜ ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ


6.4.1 ProxyFactoryBean

  • ์Šคํ”„๋ง์€ ์ผ๊ด€๋œ ๋ฐฉ๋ฒ•์œผ๋กœ ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” ์ถ”์ƒ ๋ ˆ์ด์–ด๋ฅผ ์ œ๊ณต
  • ์Šคํ”„๋ง์˜ ProxyFactoryBean์€ ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•ด์„œ ๋นˆ ์˜ค๋ธŒ์ ํŠธ๋กœ ๋“ฑ๋กํ•ด์ฃผ๋Š” ํŒฉํ† ๋ฆฌ ๋นˆ์ด๋ฉฐ, ์ˆœ์ˆ˜ํ•˜๊ฒŒ ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ž‘์—…๋งŒ์„ ๋‹ด๋‹น
  • ๋ถ€๊ฐ€๊ธฐ๋Šฅ์€ MethodInterceptor ๋ฅผ ๊ตฌํ˜„ํ•ด์„œ ๋งŒ๋“ค๋ฉฐ, ํƒ€๊นƒ ์˜ค๋ธŒ์ ํŠธ์— ์ƒ๊ด€์—†์ด ๋…๋ฆฝ์ ์œผ๋กœ ๋งŒ๋“ค์–ด์งˆ ์ˆ˜ ์žˆ๊ธฐ์— ์‹ฑ๊ธ€ํ†ค ๋นˆ์œผ๋กœ ๋“ฑ๋ก ๊ฐ€๋Šฅ

์–ด๋“œ๋ฐ”์ด์Šค: ํƒ€๊นƒ์ด ํ•„์š” ์—†๋Š” ์ˆœ์ˆ˜ํ•œ ๋ถ€๊ฐ€๊ธฐ๋Šฅ

  • MethodInvocation ์€ ํƒ€๊นƒ ์˜ค๋ธŒ์ ํŠธ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ด ์žˆ์Œ
  • ๋•Œ๋ฌธ์— MethodInterceptor ๋Š” ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ฐ๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ์Œ
  • MethodInvocation ์„ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๋‘๊ณ  ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์Œ
  • ProxyFactoryBean ํ•˜๋‚˜๋งŒ์œผ๋กœ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด์ฃผ๋Š” ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ
    • ์ด๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ์šด ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ๋•Œ๋งˆ๋‹ค ํ”„๋ก์‹œ์™€ ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ๋„ ์ถ”๊ฐ€ํ•ด์ค˜์•ผ ํ•œ๋‹ค๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ
  • ProxyFactoryBean ์— ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค ์ž๋™๊ฒ€์ถœ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด ํƒ€๊นƒ ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค ์ •๋ณด๋ฅผ ์•Œ์•„๋‚ธ ํ›„ ํƒ€๊นƒ ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š” ๋ชจ๋“  ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋™์ผํ•˜๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š” ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค์–ด์คŒ
  • ๊ธฐ๋ณธ์ ์œผ๋กœ JDK ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค๊ณ , ๊ฒฝ์šฐ์— ๋”ฐ๋ผ CGLib ์ด๋ผ๋Š” ๋ฐ”์ดํŠธ์ฝ”๋“œ ์ƒ์„ฑ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ด์šฉํ•ด ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค๊ธฐ๋„ ํ•จ

ํฌ์ธํŠธ์ปท: ๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ ์šฉ ๋Œ€์ƒ ๋ฉ”์„œ๋“œ ์„ ์ • ๋ฐฉ๋ฒ•

  • MethodInterceptor ์˜ค๋ธŒ์ ํŠธ๋Š” ํƒ€๊นƒ ์ •๋ณด๋ฅผ ๊ฐ–๊ณ  ์žˆ์ง€ ์•Š๋„๋ก ๋งŒ๋“ค์—ˆ์Œ
  • ์—ฌ๋Ÿฌ ํ”„๋ก์‹œ๊ฐ€ ๊ณต์œ ํ•˜๋Š” MethodInterceptor ์— ํŠน์ • ํ”„๋ก์‹œ์—๋งŒ ์ ์šฉ๋˜๋Š” ํŒจํ„ด์„ ๋„ฃ์œผ๋ฉด ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Œ
  • ์–ด๋“œ๋ฐ”์ด์Šค - ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์˜ค๋ธŒ์ ํŠธ
  • ํฌ์ธํŠธ์ปท - ๋ฉ”์„œ๋“œ ์„ ์ • ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋‹ด์€ ์˜ค๋ธŒ์ ํŠธ
  • ์™œ ๊ตณ์ด ๋ณ„๊ฐœ์˜ ์˜ค๋ธŒ์ ํŠธ๋กœ ๋ฌถ์–ด์„œ ๋“ฑ๋กํ•˜๋Š”๊ฐ€?
    • ProxyFactoryBean ์—๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์–ด๋“œ๋ฐ”์ด์Šค์™€ ํฌ์ธํŠธ์ปท์ด ์ถ”๊ฐ€๋  ์ˆ˜ ์žˆ์Œ
  • ์–ด๋“œ๋ฐ”์ด์ € = ํฌ์ธํŠธ์ปท + ์–ด๋“œ๋ฐ”์ด์Šค

6.4.2 ProxyFactoryBean ์ ์šฉ

  • ํƒ€๊นƒ ๋ฉ”์„œ๋“œ๊ฐ€ ๋˜์ง€๋Š” ์˜ˆ์™ธ๋„ InvocationTargetException ์œผ๋กœ ํฌ์žฅ๋˜์–ด ๋‚˜์˜ค๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋Œ€๋กœ ์žก์•„์„œ ์ฒ˜๋ฆฌํ•˜๋ฉด ๋จ

์ •๋ฆฌ


ํŠธ๋žœ์žญ์…˜ ๊ฒฝ๊ณ„์„ค์ • ์ฝ”๋“œ ๋ถ„๋ฆฌ

  1. ๋ฉ”์„œ๋“œ ๋ถ„๋ฆฌ
  2. DI ๋ฅผ ํ†ตํ•œ ํด๋ž˜์Šค ๋ถ„๋ฆฌ
    • ๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ค‘๊ฐ„์— ๋ผ์›€
  3. ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด๊ณผ ํ”„๋ก์‹œ ํŒจํ„ด
    • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด - ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋Ÿฐํƒ€์ž„ ์‹œ ๋‹ค์ด๋‚ด๋ฏนํ•˜๊ฒŒ ๋ถ€์—ฌ
    • ํ”„๋ก์‹œ ํŒจํ„ด- ํƒ€๊นƒ์— ๋Œ€ํ•œ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์„ ์ œ์–ด
  4. ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ
    • ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ •์ด ๋ฒˆ๊ฑฐ๋กœ์›€
      • ๋ถ€๊ฐ€๊ธฐ๋Šฅ์ด ํ•„์š”์—†๋Š” ๋ฉ”์„œ๋“œ๋„ ๊ตฌํ˜„ํ•ด์„œ ์œ„์ž„ํ•ด์•ผ ํ•จ
      • ๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ฝ”๋“œ๊ฐ€ ์ค‘๋ณต๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Œ
    • ์ด๋•Œ ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ ์‚ฌ์šฉ
      • ๋ฆฌํ”Œ๋ ‰์…˜์„ ํ†ตํ•ด ๋Ÿฐํƒ€์ž„ ์‹œ ๋‹ค์ด๋‚ด๋ฏนํ•˜๊ฒŒ ์˜ค๋ธŒ์ ํŠธ ์ƒ์„ฑ
      • InvocationHandler ๋ฅผ ๊ตฌํ˜„ํ•ด ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€
    • ํ”„๋ก์‹œ ๊ฐ์ฒด โ†’ ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ ๊ฐ์ฒด๋กœ ๋ณ€๊ฒฝ
  5. ํŒฉํ† ๋ฆฌ ๋นˆ
    • ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ์ผ๋ฐ˜์ ์ธ ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•  ์ˆ˜ ์—†์Œ
    • ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•ด์•ผ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
    • ํŒฉํ† ๋ฆฌ ๋นˆ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ๋‹ค์ด๋‚ด๋ฏน ํ”„๋ก์‹œ ๋นˆ ๋“ฑ๋ก
  6. ํŒฉํ† ๋ฆฌ ๋นˆ ๋ฐฉ์‹์˜ ํ•œ๊ณ„
    • ๋น„์Šทํ•œ ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ์˜ ์„ค์ •์ด ์ค‘๋ณต
    • ๋ถ€๊ฐ€๊ธฐ๋Šฅ์˜ ๊ฐœ์ˆ˜๋งŒํผ ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ ์„ค์ •์ด ์ถ”๊ฐ€
    • TransactionHandler ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ ๊ฐœ์ˆ˜๋งŒํผ ์ถ”๊ฐ€
  7. ์Šคํ”„๋ง์˜ ํ”„๋ก์‹œ ํŒฉํ† ๋ฆฌ ๋นˆ
    • ProxyFactoryBean ์œผ๋กœ ์ˆœ์ˆ˜ ํ”„๋ก์‹œ ์ƒ์„ฑ
      • ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ณต์œ  ๊ฐ€๋Šฅ
    • MethodInterceptor ๋ฅผ ๊ตฌํ˜„ํ•ด ๋ถ€๊ฐ€๊ธฐ๋Šฅ ๋ถ€์—ฌ
      • ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ณต์œ  ๊ฐ€๋Šฅ
    • MethodInvocation ์œผ๋กœ ํƒ€๊นƒ ์˜ค๋ธŒ์ ํŠธ ๋ฉ”์„œ๋“œ ์‹คํ–‰
      • ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ณต์œ  ๊ฐ€๋Šฅ
  8. ์–ด๋“œ๋ฐ”์ด์Šค์™€ ํฌ์ธํŠธ์ปท
    • ์–ด๋“œ๋ฐ”์ด์Šค - ๋ถ€๊ฐ€๊ธฐ๋Šฅ ์ œ๊ณต ์˜ค๋ธŒ์ ํŠธ
    • ํฌ์ธํŠธ์ปท - ๋ฉ”์„œ๋“œ ์„ ์ • ์•Œ๊ณ ๋ฆฌ์ฆ˜
    • ์–ด๋“œ๋ฐ”์ด์ € = ์–ด๋“œ๋ฐ”์ด์Šค + ํฌ์ธํŠธ์ปท

AOP Interceptor ๋กœ ์ธ๊ฐ€ ์ฒ˜๋ฆฌ


AOP ๋กœ ์ ์šฉ

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HostOnly {
}
@Aspect
@Component
public class HostVerifier {
 
    private final AuthenticationContext authenticationContext;
 
    public HostVerifier(final AuthenticationContext authenticationContext) {
        this.authenticationContext = authenticationContext;
    }
 
    @Before("@annotation(com.woowacourse.gongcheck.application.HostOnly)")
    public void checkHost() {
        final Authority authority = authenticationContext.getAuthority();
        if (!authority.equals(Authority.HOST)) {
            throw new UnauthorizedException("ํ˜ธ์ŠคํŠธ๋งŒ ์ž…์žฅ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.");
        }
    }
}

๋ฌธ์ œ์ 

  • @Valid ๊ฐ€ ๋จผ์ € ํ„ฐ์ง
    • ์™œ? AOP proxy ๊ฐ€ ํ˜ธ์ถœ๋˜๊ธฐ ์ „์— ArgumentResolver (JacksonMapper) ๊ฐ€ ๋จผ์ € ๋™์ž‘
  • ์ธ๊ฐ€๊ฐ€ ๋จผ์ € ํ„ฐ์ ธ์•ผ ํ•จ

Interceptor ๋กœ ์ ์šฉ

@Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler)
        throws Exception {
    if (CorsUtils.isPreFlightRequest(request)) {
        return true;
    }
 
    String token = AuthorizationTokenExtractor.extractToken(request)
            .orElseThrow(() -> new UnauthorizedException("ํ—ค๋”์— ํ† ํฐ ๊ฐ’์ด ์ •์ƒ์ ์œผ๋กœ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."));
 
    String subject = jwtTokenProvider.extractSubject(token);
    authenticationContext.setPrincipal(subject);
 
    Authority authority = jwtTokenProvider.extractAuthority(token);
    if (HandlerMethod.class.isAssignableFrom(handler.getClass())) {
        authorize((HandlerMethod) handler, authority);
    }
 
    return HandlerInterceptor.super.preHandle(request, response, handler);
}
 
private void authorize(HandlerMethod handlerMethod, Authority authority) {
    if (handlerMethod.getMethodAnnotation(HostOnly.class) != null) {
        if (!authority.isHost()) {
            throw new UnauthorizedException("ํ˜ธ์ŠคํŠธ๋งŒ ์ž…์žฅ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.");
        }
    }
}

Documentation Mocking ์ฒ˜๋ฆฌ

when(jwtTokenProvider.extractAuthority(anyString())).thenReturn(Authority.HOST);

AOP ๋ฅผ ํ™œ์šฉํ•œ ๋กœ๊น