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์ผ๋ก ํฌ์ฅ๋์ด ๋์ค๋ ๊ฒ์ด ์๋๊ธฐ ๋๋ฌธ์ ๊ทธ๋๋ก ์ก์์ ์ฒ๋ฆฌํ๋ฉด ๋จ
์ ๋ฆฌ
ํธ๋์ญ์ ๊ฒฝ๊ณ์ค์ ์ฝ๋ ๋ถ๋ฆฌ
- ๋ฉ์๋ ๋ถ๋ฆฌ
- DI ๋ฅผ ํตํ ํด๋์ค ๋ถ๋ฆฌ
- ๊ฐ์ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ํ๋ก์ ๊ฐ์ฒด๋ฅผ ์ค๊ฐ์ ๋ผ์
- ๋ฐ์ฝ๋ ์ดํฐ ํจํด๊ณผ ํ๋ก์ ํจํด
- ๋ฐ์ฝ๋ ์ดํฐ ํจํด - ๋ถ๊ฐ๊ธฐ๋ฅ์ ๋ฐํ์ ์ ๋ค์ด๋ด๋ฏนํ๊ฒ ๋ถ์ฌ
- ํ๋ก์ ํจํด- ํ๊น์ ๋ํ ์ ๊ทผ ๋ฐฉ๋ฒ์ ์ ์ด
- ๋ค์ด๋ด๋ฏน ํ๋ก์
- ํ๋ก์๋ฅผ ๋ง๋๋ ๊ณผ์ ์ด ๋ฒ๊ฑฐ๋ก์
- ๋ถ๊ฐ๊ธฐ๋ฅ์ด ํ์์๋ ๋ฉ์๋๋ ๊ตฌํํด์ ์์ํด์ผ ํจ
- ๋ถ๊ฐ๊ธฐ๋ฅ ์ฝ๋๊ฐ ์ค๋ณต๋ ๊ฐ๋ฅ์ฑ์ด ๋์
- ์ด๋ ๋ค์ด๋ด๋ฏน ํ๋ก์ ์ฌ์ฉ
- ๋ฆฌํ๋ ์ ์ ํตํด ๋ฐํ์ ์ ๋ค์ด๋ด๋ฏนํ๊ฒ ์ค๋ธ์ ํธ ์์ฑ
InvocationHandler๋ฅผ ๊ตฌํํด ๋ถ๊ฐ๊ธฐ๋ฅ์ ์ถ๊ฐ
- ํ๋ก์ ๊ฐ์ฒด โ ๋ค์ด๋ด๋ฏน ํ๋ก์ ๊ฐ์ฒด๋ก ๋ณ๊ฒฝ
- ํ๋ก์๋ฅผ ๋ง๋๋ ๊ณผ์ ์ด ๋ฒ๊ฑฐ๋ก์
- ํฉํ ๋ฆฌ ๋น
- ๋ค์ด๋ด๋ฏน ํ๋ก์ ๊ฐ์ฒด๋ ์ผ๋ฐ์ ์ธ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋กํ ์ ์์
- ๋น์ผ๋ก ๋ฑ๋กํด์ผ ํด๋ผ์ด์ธํธ๊ฐ ์ฌ์ฉํ ์ ์์
- ํฉํ ๋ฆฌ ๋น ์ธํฐํ์ด์ค๋ฅผ ํตํด ๋ค์ด๋ด๋ฏน ํ๋ก์ ๋น ๋ฑ๋ก
- ํฉํ ๋ฆฌ ๋น ๋ฐฉ์์ ํ๊ณ
- ๋น์ทํ ํ๋ก์ ํฉํ ๋ฆฌ ๋น์ ์ค์ ์ด ์ค๋ณต
- ๋ถ๊ฐ๊ธฐ๋ฅ์ ๊ฐ์๋งํผ ํ๋ก์ ํฉํ ๋ฆฌ ๋น ์ค์ ์ด ์ถ๊ฐ
- TransactionHandler ์ค๋ธ์ ํธ๊ฐ ํ๋ก์ ํฉํ ๋ฆฌ ๋น ๊ฐ์๋งํผ ์ถ๊ฐ
- ์คํ๋ง์ ํ๋ก์ ํฉํ ๋ฆฌ ๋น
ProxyFactoryBean์ผ๋ก ์์ ํ๋ก์ ์์ฑ- ์ฑ๊ธํค์ผ๋ก ๊ณต์ ๊ฐ๋ฅ
MethodInterceptor๋ฅผ ๊ตฌํํด ๋ถ๊ฐ๊ธฐ๋ฅ ๋ถ์ฌ- ์ฑ๊ธํค์ผ๋ก ๊ณต์ ๊ฐ๋ฅ
MethodInvocation์ผ๋ก ํ๊น ์ค๋ธ์ ํธ ๋ฉ์๋ ์คํ- ์ฑ๊ธํค์ผ๋ก ๊ณต์ ๊ฐ๋ฅ
- ์ด๋๋ฐ์ด์ค์ ํฌ์ธํธ์ปท
- ์ด๋๋ฐ์ด์ค - ๋ถ๊ฐ๊ธฐ๋ฅ ์ ๊ณต ์ค๋ธ์ ํธ
- ํฌ์ธํธ์ปท - ๋ฉ์๋ ์ ์ ์๊ณ ๋ฆฌ์ฆ
- ์ด๋๋ฐ์ด์ = ์ด๋๋ฐ์ด์ค + ํฌ์ธํธ์ปท
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);