8-9์ฃผ์ฐจ ์๋ฃ์ ๋ชจ๋ ํ ํฝ์ ๋ ์ฃผ์ ๊ฑธ์ณ ์ ๋ฆฌํ ํ์ต ๊ฒฝ๋ก.
1) 8์ฃผ์ฐจ โ ํ๋ก์์ ์งํ (AOP๊ฐ ํ์ํ ์ด์ , ๋์์ธ ํจํด, ๋์ ํ๋ก์, ProxyFactory)
2) 9์ฃผ์ฐจ โ Spring AOP ์ค์ (์๋ ํ๋ก์, @Aspect, AOP ์ฉ์ด, @Transactional ํจ์ , ํธ๋์ญ์ ์ ํ)7์ฃผ์ฐจ์์ @Transactional์ ํ๋ก์ ํจํด์ ๋ง๋ดค๋ค๋ฉด, 8-9์ฃผ์ฐจ๋ ๊ทธ ํ๋ก์๊ฐ ์ด๋ป๊ฒ ๋ง๋ค์ด์ง๊ณ ์ ์ฉ๋๋์ง ์ ๋ชจ๋ ๋ฉ์ปค๋์ฆ์ ํํค์น๋ค.
๊น์ํ์ ์คํ๋ง ํต์ฌ ์๋ฆฌ - ๊ณ ๊ธํธ ์ ์ฒด ํ๋ฆ์ด ์์ถ๋์ด ์๋ค. F-lab ์๋ฐ ์ปค๋ฆฌํ๋ผ์ ํ์ด๋ผ์ดํธ ์ด์ ๊ฐ์ฅ ๋ถ๋์ด ๋ง์ ์ฃผ์ฐจ๋ค.
[Part A โ 8์ฃผ์ฐจ: ํ๋ก์์ ์งํ]
[Phase 1] AOP ์
๋ฌธ๊ณผ ๋๊ธฐ
โ
[Phase 2] ๋์์ธ ํจํด์ ์งํ โ ํ
ํ๋ฆฟ ๋ฉ์๋ โ ์ ๋ต ํจํด
โ
[Phase 3] ์ฝ๋ฐฑ๊ณผ ํ๋ก์์ ๋ง๋จ โ ํ
ํ๋ฆฟ ์ฝ๋ฐฑ โ ํ๋ก์ ๊ฐ๋
โ
[Phase 4] ํ๋ก์ ํจํด vs ๋ฐ์ฝ๋ ์ดํฐ ํจํด
โ
[Phase 5] ๋์ ํ๋ก์ ๊ธฐ์ โ Reflection / JDK / CGLIB
โ
[Phase 6] ProxyFactory โ ์คํ๋ง์ ํตํฉ ์ถ์ํ โ 8์ฃผ์ฐจ ์ ์
[Part B โ 9์ฃผ์ฐจ: Spring AOP ์ค์ ]
[Phase 7] ๋น ํ์ฒ๋ฆฌ๊ธฐ์ ์๋ ํ๋ก์ ์์ฑ๊ธฐ
โ
[Phase 8] @Aspect์ AOP ์ฉ์ด ์์ ์ ๋ฆฌ
โ
[Phase 9] Spring AOP ์ค์ ๊ตฌํ ํจํด
โ
[Phase 10] @Transactional์ ํจ์ ๊ณผ ํธ๋์ญ์
์ ํ โ 9์ฃผ์ฐจ ์ ์
์ด 10 Phase ร 35 Unit (5ยท6์ฃผ์ฐจ์ ํฉ๊ณผ ๋น์ทํ ๋ถ๋)
| ์ฃผ์ฐจ | ์ฃผ์ | ํต์ฌ ๋ณํ |
|---|---|---|
| 1์ฃผ์ฐจ | OOPยทJVMยทGCยท์ปฌ๋ ์ ยทI/O ๊ฐ๋ก | ์๋ฐ ํฐ ๊ทธ๋ฆผ |
| 2์ฃผ์ฐจ | JVM ๋ด๋ถยท๋ฐ์ดํธ์ฝ๋ยทG1 GC | "์ด๋ป๊ฒ ๋์๊ฐ๋" |
| 3์ฃผ์ฐจ | ์ปฌ๋ ์ ยท์ ๋ค๋ฆญยทํจ์ํ | ์๋ฐ ํํ๋ ฅ |
| 4์ฃผ์ฐจ | ๋ฉํฐ์ค๋ ๋ฉยท๋์์ฑยทExecutor | ๋์์ฑ ์ ๋ณต |
| 5์ฃผ์ฐจ | Atomic + Spring IoC/DI ์ ๋ฌธ | ์๋ฐ โ Spring ๋ค๋ฆฌ |
| 6์ฃผ์ฐจ | ํ ์คํธ + ์น ์ธํ๋ผ + DB ์ ๊ทผ ์งํ | Spring ์ค์ ํ๊ฒฝ |
| 7์ฃผ์ฐจ | JPA/ORM + ํธ๋์ญ์ ์ถ์ํ | DB ์ถ์ํ์ ์ ์ |
| 8์ฃผ์ฐจ (์ง๊ธ) | ํ๋ก์์ ์งํ | AOP ๋ฉ์ปค๋์ฆ ์ดํด |
| 9์ฃผ์ฐจ (์ง๊ธ) | Spring AOP ์ค์ + ํธ๋์ญ์ ์ ํ | AOP ์ค์ ํ์ฉ |
| Day | Phase | ํ์ต ๋ชฉํ |
|---|---|---|
| Week 1 | ||
| 1์ผ์ฐจ | Phase 1 | AOP ๋๊ธฐ, ๋ก๊ทธ ์ถ์ ๊ธฐ, ThreadLocal |
| 2์ผ์ฐจ | Phase 2 | ํ ํ๋ฆฟ ๋ฉ์๋ + ์ ๋ต ํจํด |
| 3์ผ์ฐจ | Phase 3 | ํ ํ๋ฆฟ ์ฝ๋ฐฑ ํจํด, ํ๋ก์ ๊ฐ๋ |
| 4์ผ์ฐจ | Phase 4 | ํ๋ก์ vs ๋ฐ์ฝ๋ ์ดํฐ |
| 5์ผ์ฐจ | Phase 5 | Reflection + JDK ๋์ ํ๋ก์ + CGLIB |
| 6-7์ผ์ฐจ | Phase 6 | ProxyFactory + Advice + Pointcut + Advisor (โ 8์ฃผ์ฐจ ์ ์ ) |
| Week 2 | ||
| 8์ผ์ฐจ | Phase 7 | ๋น ํ์ฒ๋ฆฌ๊ธฐ + ์๋ ํ๋ก์ ์์ฑ๊ธฐ |
| 9์ผ์ฐจ | Phase 8 | @Aspect + AOP ์ฉ์ด |
| 10์ผ์ฐจ | Phase 9 | Spring AOP ์ค์ ํจํด |
| 11-12์ผ์ฐจ | Phase 10 | @Transactional ํจ์ + ํธ๋์ญ์ ์ ํ (โ 9์ฃผ์ฐจ ์ ์ ) |
| 13-14์ผ์ฐจ | ์ข ํฉ ์๊ธฐ ์ ๊ฒ + ์ค์ต | ์ ์ฒด ์ ๋ฆฌ |
์ฌ์ ์ผ์ (21์ผ): ๊ฐ ์ ์ Phase์ +2์ผ์ฉ, ๊ทธ๋ฆฌ๊ณ 9-์น์ ๋ง์คํฐ ํ๋กฌํํธ๋ก ํต์ฌ Unit์ ๊น์ด ํ๋ ์๊ฐ ํ๋ณด.
๋ชฉํ: "์ AOP๊ฐ ํ์ํ๊ฐ"๋ฅผ ์ฝ๋์ ๊ณ ํต์ผ๋ก ์ง์ ์ดํดํ๋ค. 7์ฃผ์ฐจ์์ ๋ณธ @Transactional์ ์ ์ฒด๋ฅผ ๋ ๊น์ด ์๊ธฐ ์ํ ์ถ๋ฐ์ .
์ ์ ์ง์: 7์ฃผ์ฐจ Phase 7 (@Transactional)
ํต์ฌ ๊ฐ๋
AOP (Aspect Oriented Programming) = ๊ด์ ์งํฅ ํ๋ก๊ทธ๋๋ฐ
ํต์ฌ ํต์ฐฐ:
ํต์ฌ vs ๋ถ๊ฐ:
ํฉ์ด์ง ๊ด์ฌ์ฌ (Crosscutting Concerns):
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 1.1
ํต์ฌ ์๋๋ฆฌ์ค
๋ก๊ทธ ์ถ์ ๊ธฐ๋?:
์์ ์ถ๋ ฅ:
์ ์ ์์ฒญ
[796bccd9] OrderController.request()
[796bccd9] |-->OrderService.orderItem()
[796bccd9] | |-->OrderRepository.save()
[796bccd9] | |<--OrderRepository.save() time=1004ms
[796bccd9] |<--OrderService.orderItem() time=1014ms
[796bccd9] OrderController.request() time=1016ms
์์ธ ๋ฐ์
[b7119f27] OrderController.request()
[b7119f27] |-->OrderService.orderItem()
[b7119f27] | |<X-OrderService.orderItem() time=10ms ex=...
๋ฌธ์ :
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: 4์ฃผ์ฐจ Phase 4 (synchronized), 5์ฃผ์ฐจ Phase 8 (์ฑ๊ธํค ๋น)
ํต์ฌ ๊ฐ๋
๋ฌธ์ ์ํฉ:
ThreadLocal์ ํด๊ฒฐ:
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("Thread-1 ๋ฐ์ดํฐ"); // ์ด ์ค๋ ๋๋ง ๋ณด์
String value = threadLocal.get(); // ๋ค๋ฅธ ์ค๋ ๋๋ ๋ค๋ฅธ ๊ฐ
โ ๏ธ ์น๋ช
์ ํจ์ โ remove() ํ์:
remove()try {
threadLocal.set(data);
// ์์
} finally {
threadLocal.remove(); // โ
ํ์!
}
์๊ธฐ ์ ๊ฒ
๋ชฉํ: "๋ณํ๋ ๊ฒ๊ณผ ๋ณํ์ง ์๋ ๊ฒ์ ๋ถ๋ฆฌ"๋ผ๋ ์ข์ ์ค๊ณ์ ์์น์ ๋ ๊ฐ์ง ํจํด(ํ ํ๋ฆฟ ๋ฉ์๋ โ ์ ๋ต)์ ํตํด ์ฝ๋๋ก ์ตํ๋ค.
์ ์ ์ง์: Phase 1, 5์ฃผ์ฐจ Phase 5
ํต์ฌ ์์น
"์ข์ ์ค๊ณ๋ ๋ณํ๋ ๊ฒ๊ณผ ๋ณํ์ง ์๋ ๊ฒ์ ๋ถ๋ฆฌํ๋ ๊ฒ์ด๋ค"
์์ โ ์๊ฐ ์ธก์ ๋ก์ง:
private void logic1() {
long startTime = System.currentTimeMillis(); // โ ๋ณํ์ง ์์
log.info("๋น์ฆ๋์ค ๋ก์ง1 ์คํ"); // โ ๋ณํจ
long endTime = System.currentTimeMillis(); // โ ๋ณํ์ง ์์
log.info("resultTime={}", endTime - startTime); // โ ๋ณํ์ง ์์
}
private void logic2() {
long startTime = System.currentTimeMillis(); // โ ๋ณํ์ง ์์
log.info("๋น์ฆ๋์ค ๋ก์ง2 ์คํ"); // โ ๋ณํจ
long endTime = System.currentTimeMillis(); // โ ๋ณํ์ง ์์
log.info("resultTime={}", endTime - startTime); // โ ๋ณํ์ง ์์
}
โ ์๊ฐ ์ธก์ ์ ๊ฐ์๋ฐ ๋น์ฆ๋์ค ๋ก์ง๋ง ๋ค๋ฆ
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 2.1, 5์ฃผ์ฐจ Unit 5.1
ํต์ฌ ๊ฐ๋
"์ํผํด๋์ค์ ๋ณํ์ง ์๋ ํ ํ๋ฆฟ์ ๋๊ณ , ๋ณํ๋ ๋ถ๋ถ์ ์์ ํด๋์ค์ ๋๋ ํจํด"
public abstract class AbstractTemplate {
public void execute() {
long startTime = System.currentTimeMillis();
call(); // โ ๋ณํ๋ ๋ถ๋ถ (์์์ด ๊ตฌํ)
long endTime = System.currentTimeMillis();
log.info("resultTime={}", endTime - startTime);
}
protected abstract void call();
}
public class SubClassLogic1 extends AbstractTemplate {
@Override
protected void call() {
log.info("๋น์ฆ๋์ค ๋ก์ง1 ์คํ");
}
}
์ต๋ช ๋ด๋ถ ํด๋์ค ํ์ฉ:
AbstractTemplate template = new AbstractTemplate() {
@Override
protected void call() {
log.info("๋น์ฆ๋์ค ๋ก์ง1 ์คํ");
}
};
template.execute();
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 2.2
ํต์ฌ ํ๊ณ
์์์ ๊ฐํ ๊ฒฐํฉ:
extends AbstractTemplate โ ๋ถ๋ชจ์ ๊ฐํ๊ฒ ์์กด๋ณต์กํจ:
ํด๊ฒฐ์ ๋ฐฉํฅ:
extends) ๋์ ํฉ์ฑ(composition)์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 2.3, 5์ฃผ์ฐจ Unit 6.3
ํต์ฌ ๊ฐ๋
"๋ณํ์ง ์๋ ๋ถ๋ถ์ Context์, ๋ณํ๋ ๋ถ๋ถ์ Strategy ์ธํฐํ์ด์ค๋ก"
์์ ๋์ ์์:
public interface Strategy {
void call();
}
public class StrategyLogic1 implements Strategy {
@Override
public void call() {
log.info("๋น์ฆ๋์ค ๋ก์ง1 ์คํ");
}
}
public class ContextV1 {
private Strategy strategy;
public ContextV1(Strategy strategy) {
this.strategy = strategy; // ์์
}
public void execute() {
long startTime = System.currentTimeMillis();
strategy.call(); // โ ์์
long endTime = System.currentTimeMillis();
log.info("resultTime={}", endTime - startTime);
}
}
๋๋ค๋ก ๋ ๊ฐ๊ฒฐํ๊ฒ:
ContextV1 context = new ContextV1(() -> log.info("๋น์ฆ๋์ค ๋ก์ง1"));
context.execute();
ํต์ฌ ํต์ฐฐ:
"Context๋ Strategy ์ธํฐํ์ด์ค์๋ง ์์กด" โ Spring์ DI์ ๊ฐ์ ์ฌ์
์๊ธฐ ์ ๊ฒ
๋ชฉํ: ์ ๋ต ํจํด์ ํ๊ณ๋ฅผ ์ฝ๋ฐฑ ํจํด์ผ๋ก ๊ทน๋ณตํ๊ณ , ๊ทธ๋๋ ๋จ๋ ํ๊ณ๋ฅผ ํ๋ก์๋ก ํด๊ฒฐํ๊ธฐ ์ํ ์ถ๋ฐ์ ์ ์ ๋ค.
์ ์ ์ง์: Unit 2.4
ํต์ฌ ๊ฐ๋
Context V1์ ํ๊ณ:
Context V2 โ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ๊ธฐ:
public class ContextV2 {
public void execute(Strategy strategy) { // โ ๋งค๋ฒ ๋ค๋ฅธ ์ ๋ต
long startTime = System.currentTimeMillis();
strategy.call();
long endTime = System.currentTimeMillis();
log.info("resultTime={}", endTime - startTime);
}
}
// ์ฌ์ฉ
ContextV2 context = new ContextV2();
context.execute(() -> log.info("๋น์ฆ๋์ค ๋ก์ง1"));
context.execute(() -> log.info("๋น์ฆ๋์ค ๋ก์ง2"));
์ ์กฐ๋ฆฝ ํ ์คํ vs ์คํ ์์ ์ ๋ฌ:
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 3.1
ํต์ฌ ๊ฐ๋
์ฉ์ด ๋งคํ:
Context โ TemplateStrategy โ Callbackํ ํ๋ฆฟ ์ฝ๋ฐฑ ํจํด์ GOF๊ฐ ์๋ ์คํ๋ง ์ ์ฉ ์ฉ์ด:
Spring์ XxxTemplate ์๋ฆฌ์ฆ โญ :
JdbcTemplate (6์ฃผ์ฐจ)RestTemplateTransactionTemplateRedisTemplateโ ์ด๋ฆ์ "Template"์ด ๋ถ์ผ๋ฉด ์ด ํจํด
์ฝ๋ฐฑ(Callback)์ ์ ์:
"๋ค๋ฅธ ์ฝ๋์ ์ธ์๋ก์ ๋๊ฒจ์ฃผ๋ ์คํ ๊ฐ๋ฅํ ์ฝ๋"
"์ฝ๋๊ฐ ํธ์ถ(call)๋๋๋ฐ, ์ฝ๋๋ฅผ ๋๊ฒจ์ค ๊ณณ์ ๋ค(back)์์ ์คํ๋จ"
template.execute(() -> log.info("๋น์ฆ๋์ค ๋ก์ง1"));
// โ ์ด ๋๋ค๊ฐ ์ฝ๋ฐฑ
// template ์์์ "๋์ค์" ์คํ๋จ
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Phase 3
ํต์ฌ ํ๊ณ ์ง์
์ง๊ธ๊น์ง์ ๋ชจ๋ ๋ฐฉ๋ฒ(ํ ํ๋ฆฟ ๋ฉ์๋, ์ ๋ต ํจํด, ํ ํ๋ฆฟ ์ฝ๋ฐฑ)์ ๊ณตํต ํ๊ณ:
"๊ฒฐ๊ตญ ์๋ณธ ์ฝ๋๋ฅผ ์์ ํด์ผ ํ๋ค"
100๊ฐ ๋ฉ์๋๋ฉด 100๊ตฐ๋ฐ ์์ . ์ด๊ฑธ ์ ํ๋ ค๋ฉด?
ํ๋ก์(Proxy)์ ๋ฑ์ฅ:
ํ๋ก์๊ฐ ๋๊ธฐ ์ํ ์กฐ๊ฑด:
[Client] โ [Server] โโ๋ณ๊ฒฝโโ> [Client] โ [Proxy] โ [Server]
(DI ํ์ฉ) (์ฝ๋ ๋ณ๊ฒฝ X)
ํ๋ก์์ ์ฃผ์ ๊ธฐ๋ฅ:
์๊ธฐ ์ ๊ฒ
๋ชฉํ: ๊ฐ์ ๋ชจ์์ ๋ ํจํด์ ์๋ ๋ก ๊ตฌ๋ถํ๋ ๋ฒ์ ์ตํ๋ค.
์ ์ ์ง์: Unit 3.3
ํต์ฌ ๊ฐ๋
ํ๋ก์ ํจํด = ์ ๊ทผ ์ ์ด ๋ชฉ์
์บ์ ์์ :
public class CacheProxy implements Subject {
private Subject target;
private String cacheValue;
public CacheProxy(Subject target) {
this.target = target;
}
@Override
public String operation() {
if (cacheValue == null) {
cacheValue = target.operation(); // ์ฒ์๋ง ์ค์ ํธ์ถ
}
return cacheValue; // ๋ ๋ฒ์งธ๋ถํฐ ์บ์ ๋ฐํ
}
}
ํจ๊ณผ:
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 4.1
ํต์ฌ ๊ฐ๋
๋ฐ์ฝ๋ ์ดํฐ ํจํด = ๋ถ๊ฐ ๊ธฐ๋ฅ ์ถ๊ฐ ๋ชฉ์
์์ โ ๋ฉ์์ง ๊พธ๋ฏธ๊ธฐ + ์๊ฐ ์ธก์ :
public class MessageDecorator implements Component {
private Component component;
@Override
public String operation() {
String result = component.operation();
return "*****" + result + "*****"; // ๊พธ๋ฉฐ์ค
}
}
public class TimeDecorator implements Component {
private Component component;
@Override
public String operation() {
long start = System.currentTimeMillis();
String result = component.operation();
log.info("time={}ms", System.currentTimeMillis() - start);
return result;
}
}
์ค์ฒฉ ์ฌ์ฉ:
Component real = new RealComponent();
Component message = new MessageDecorator(real);
Component time = new TimeDecorator(message);
client.execute(time);
// ํธ์ถ ํ๋ฆ: time โ message โ real โ "data"
// โ "*****data*****" โ ์๊ฐ ๋ก๊ทธ
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 4.1, 4.2
ํต์ฌ ํต์ฐฐ
๋ชจ์์ ๊ฐ๋ค, ์๋๊ฐ ๋ค๋ฅด๋ค:
| ํ๋ก์ ํจํด | ๋ฐ์ฝ๋ ์ดํฐ ํจํด | |
|---|---|---|
| ์๋ | ์ ๊ทผ ์ ์ด | ๊ธฐ๋ฅ ์ถ๊ฐ |
| ์ฌ๋ก | ์บ์, ๊ถํ, ์ง์ฐ ๋ก๋ฉ | ๋ก๊น , ๋ฉ์์ง ๊พธ๋ฏธ๊ธฐ, ์๊ฐ ์ธก์ |
| ํด๋ผ์ด์ธํธ ์ธ์ง | ๋ณดํต ๋ชจ๋ฆ | ์ ์๋ ์์ |
| ์ค์ฒฉ ์ฌ์ฉ | ๋๋ฌผ์ | ํํจ |
๊ตฌ๋ถ ๊ธฐ์ค:
"ํ๋ก์๊ฐ ์ ๊ทผ ์ ์ด ๊ฐ ๋ชฉ์ ์ด๋ฉด ํ๋ก์ ํจํด, ์ ๊ธฐ๋ฅ ์ถ๊ฐ ๊ฐ ๋ชฉ์ ์ด๋ฉด ๋ฐ์ฝ๋ ์ดํฐ ํจํด"
์๊ธฐ ์ ๊ฒ
๋ชฉํ: "ํ๋ก์ ํด๋์ค๋ฅผ 100๊ฐ ๋ง๋ค์ด์ผ ํ๋ ๋ฌธ์ "๋ฅผ ์๋ฐ์ ๋์ ๊ธฐ์ ๋ก ํด๊ฒฐํ๋ค.
์ ์ ์ง์: Phase 4
ํต์ฌ ๋ฌธ์
์๋ ํ๋ก์์ ํ๊ณ:
ํด๊ฒฐ์ ์ถ๋ฐ์ โ ๋ฆฌํ๋ ์ :
๋ฆฌํ๋ ์ ์ฌ์ฉ ์ :
target.callA(); // ๋ฉ์๋ ์ด๋ฆ์ด ์ฝ๋์ ๋ฐํ์์
target.callB(); // ๋ค๋ฅธ ๋ฉ์๋๋ ๋ค๋ฅธ ํธ์ถ ์ฝ๋
๋ฆฌํ๋ ์ ์ฌ์ฉ ํ:
Method methodA = classHello.getMethod("callA");
Method methodB = classHello.getMethod("callB");
dynamicCall(methodA, target);
dynamicCall(methodB, target);
private void dynamicCall(Method method, Object target) {
method.invoke(target); // ์ด๋ค ๋ฉ์๋๋ ํธ์ถ ๊ฐ๋ฅ
}
๋ฆฌํ๋ ์ ์ ์ฅ๋จ์ :
์ผ๋ฐ ์ฝ๋์์ ์ฐ์ง ๋ง ๊ฒ โ ํ๋ ์์ํฌ ๊ฐ๋ฐ์ฉ
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 5.1
ํต์ฌ ๊ฐ๋
"ํ๋ก์ ํด๋์ค๋ฅผ ๋ฐํ์์ ์๋ ์์ฑ"
์ ์ ์กฐ๊ฑด: ์ธํฐํ์ด์ค ํ์
InvocationHandler ์ธํฐํ์ด์ค:
public interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
๊ตฌํ ์์ โ ์๊ฐ ์ธก์ ํ๋ก์:
public class TimeInvocationHandler implements InvocationHandler {
private final Object target;
public TimeInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
Object result = method.invoke(target, args); // ์ค์ ํธ์ถ
log.info("time={}ms", System.currentTimeMillis() - start);
return result;
}
}
ํ๋ก์ ์์ฑ:
AInterface target = new AImpl();
TimeInvocationHandler handler = new TimeInvocationHandler(target);
AInterface proxy = (AInterface) Proxy.newProxyInstance(
AInterface.class.getClassLoader(),
new Class[]{AInterface.class},
handler
);
proxy.call(); // ๋์ ํ๋ก์๊ฐ ๊ฐ๋ก์ฑ
// ์ถ๋ ฅ: proxyClass=class com.sun.proxy.$Proxy1
ํต์ฌ ํต์ฐฐ:
$Proxy1, $Proxy2...)์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 5.2
ํต์ฌ ๊ฐ๋
JDK ๋์ ํ๋ก์์ ํ๊ณ:
CGLIB (Code Generator Library):
๋น๊ต:
| JDK ๋์ ํ๋ก์ | CGLIB | |
|---|---|---|
| ์ ์ | ์ธํฐํ์ด์ค ํ์ | ๊ตฌ์ฒด ํด๋์ค๋ง์ผ๋ก OK |
| ๋ฐฉ์ | ์ธํฐํ์ด์ค ๊ตฌํ | ํด๋์ค ์์ |
| ํธ๋ค๋ฌ | InvocationHandler | MethodInterceptor |
| ๋ผ์ด๋ธ๋ฌ๋ฆฌ | JDK ํ์ค | ์ธ๋ถ (Spring ํฌํจ) |
์ค๋ฌด์์ ์ง์ ์ฌ์ฉ?:
์๊ธฐ ์ ๊ฒ
๋ชฉํ: JDK ๋์ ํ๋ก์์ CGLIB์ ๋ถ๊ธฐ๋ฅผ Spring์ด ์ด๋ป๊ฒ ํตํฉํ๋์ง, ๊ทธ๋ฆฌ๊ณ ๊ทธ ์์ ์ด๋ป๊ฒ Pointcut/Advice/Advisor ์ถ์ํ๋ฅผ ์์๋์ง๋ฅผ ๋ณธ๋ค.
์ ์ ์ง์: Phase 5
ํต์ฌ ๊ฐ๋
๋ฌธ์ :
ProxyFactory์ ํด๊ฒฐ:
ServiceInterface target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
proxyFactory.addAdvice(new TimeAdvice());
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
// โ ์๋์ผ๋ก JDK ๋์ ํ๋ก์ ๋๋ CGLIB ์ ํ
์๋ ์ ํ ๊ท์น:
proxyTargetClass=true โ ๊ฐ์ CGLIB)Spring Boot์ ๊ธฐ๋ณธ ์ค์ :
Spring Boot๋ AOP ์ ์ฉ ์ ๊ธฐ๋ณธ์ ์ผ๋ก
proxyTargetClass=trueโ ํญ์ CGLIB
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 6.1
ํต์ฌ ๊ฐ๋
๋ฌธ์ :
InvocationHandler, CGLIB๋ MethodInterceptorAdvice์ ๋ฑ์ฅ:
// org.aopalliance.intercept.MethodInterceptor ๊ตฌํ
public class TimeAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed(); // ์ค์ ํธ์ถ
log.info("time={}ms", System.currentTimeMillis() - start);
return result;
}
}
โ ๏ธ ํจํค์ง ์ฃผ์:
org.aopalliance.intercept.MethodInterceptor (Advice์ฉ โ
)org.springframework.cglib.proxy.MethodInterceptor (CGLIB ์ง์ ์ฌ์ฉ์ฉ)์์ ๊ตฌ์กฐ:
Advice โ Interceptor โ MethodInterceptor (org.aopalliance)
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 6.2
ํต์ฌ ๊ฐ๋
3๋ ๊ฐ๋ ์ ๋ฆฌ โญ :
| ์ฉ์ด | ์๋ฏธ |
|---|---|
| Pointcut | "์ด๋์" ์ ์ฉํ ์ง (ํํฐ๋ง) |
| Advice | "์ด๋ค ๋ก์ง"์ ์ ์ฉํ ์ง (๋ถ๊ฐ ๊ธฐ๋ฅ) |
| Advisor | "Pointcut + Advice" (์ด๋์ + ์ด๋ค ๋ก์ง) |
Pointcut ์ฌ์ฉ ์์:
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedNames("save"); // save ๋ฉ์๋๋ง ๋งค์นญ
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, new TimeAdvice());
proxyFactory.addAdvisor(advisor);
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
proxy.save(); // โ
Advice ์ ์ฉ๋จ
proxy.find(); // โ Advice ์ ๋จ
์คํ๋ง ๊ธฐ๋ณธ Pointcut ์ข ๋ฅ:
NameMatchMethodPointcut: ๋ฉ์๋ ์ด๋ฆ ๋งค์นญJdkRegexpMethodPointcut: ์ ๊ทํํ์TruePointcut: ํญ์ ์ฐธAnnotationMatchingPointcut: ์ด๋
ธํ
์ด์
๋งค์นญAspectJExpressionPointcut: AspectJ ํํ์ โญ (์ค๋ฌด ํ์ค)์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 6.3
ํต์ฌ ๊ฐ๋
์ฌ๋ฌ ๋ถ๊ฐ ๊ธฐ๋ฅ์ ํ ๊ฐ์ฒด์:
ProxyFactory proxyFactory = new ProxyFactory(target);
proxyFactory.addAdvisor(advisor2); // ๋จผ์ ๋ฑ๋ก
proxyFactory.addAdvisor(advisor1); // ๋์ค ๋ฑ๋ก
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
proxy.save();
// ํธ์ถ ํ๋ฆ: proxy โ advisor2 โ advisor1 โ target
์ค์ โ Spring AOP ์ต์ ํ โญ :
"์คํ๋ง AOP๋ target ๋ง๋ค ํ๋์ ํ๋ก์๋ง ์์ฑํ๋ค"
์ฌ๋ฌ AOP๊ฐ ๋์ ์ ์ฉ๋์ด๋ โ 1๊ฐ ํ๋ก์ + N๊ฐ ์ด๋๋ฐ์ด์
์๋ฏธ:
์๊ธฐ ์ ๊ฒ
๋ชฉํ: ์ปดํฌ๋ํธ ์ค์บ๋ ๋น์๋ ํ๋ก์๋ฅผ ์ ์ฉํ๋ ๋ฉ์ปค๋์ฆ์ ์ดํดํ๋ค.
์ ์ ์ง์: Phase 6
๋ ๊ฐ์ง ํฐ ๋ฌธ์
๋ฌธ์ 1 โ ๋๋ฌด ๋ง์ ์ค์ :
๋ฌธ์ 2 โ ์ปดํฌ๋ํธ ์ค์บ:
@Service, @Repository ๋ฑ์ผ๋ก ์๋ ๋ฑ๋ก๋ ๋นโ ๋น ๋ฑ๋ก์ ๊ฐ๋ก์ฑ์ ํ๋ก์๋ก ๋ฐ๊ฟ์น๊ธฐ ํด์ผ ํ๋ค
์๊ธฐ ์ ๊ฒ
@Component ์ปดํฌ๋ํธ ์ค์บ ๋ฉ์ปค๋์ฆ๊ณผ ์ถฉ๋ํ๋๊ฐ?์ ์ ์ง์: Unit 7.1
ํต์ฌ ๊ฐ๋
BeanPostProcessor ์ธํฐํ์ด์ค:
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName);
Object postProcessAfterInitialization(Object bean, String beanName);
}
์์ โ A๋ฅผ B๋ก ๋ฐ๊ฟ์น๊ธฐ:
public class AToBPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof A) {
return new B(); // A ๋์ B ๋ฐํ
}
return bean;
}
}
// ๊ฒฐ๊ณผ
B b = applicationContext.getBean("beanA", B.class); // beanA ์ด๋ฆ์ผ๋ก B ๋ฐํ!
์์:
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 7.2
ํต์ฌ ๊ฐ๋
์คํ๋ง์ด ์ ๊ณตํ๋ ์๋ ํ๋ก์ ์์ฑ๊ธฐ:
spring-boot-starter-aop๋์:
1. ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก๋ ๋ชจ๋ Advisor ๋ค์ ์๋์ผ๋ก ์ฐพ์
2. ๊ฐ ๋น์ ๋ํด Advisor์ Pointcut์ผ๋ก ๋งค์นญ ๊ฒ์ฌ
3. ๋งค์นญ๋๋ฉด ํด๋น ๋น์ ํ๋ก์๋ก ๊ต์ฒด
@Bean
public Advisor advisor3(LogTrace logTrace) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* hello.proxy.app..*(..)) && !execution(* hello.proxy.app..noLog(..))");
LogTraceAdvice advice = new LogTraceAdvice(logTrace);
return new DefaultPointcutAdvisor(pointcut, advice);
}
์ด๋ฆ์ ์๋ฏธ:
@Aspect)์๊ธฐ ์ ๊ฒ
๋ชฉํ: ์ค๋ฌด์์ ๊ฐ์ฅ ๋ง์ด ์ฐ๋ @Aspect ์ด๋ ธํ ์ด์ ๋ฐฉ์์ ๋ง์คํฐํ๊ณ , AOP ์ฉ์ด๋ฅผ ์ ํํ ์ก๋๋ค.
์ ์ ์ง์: Phase 7
ํต์ฌ ๊ฐ๋
@Aspect ์ ์ญํ :
์์:
@Slf4j
@Aspect
public class LogTraceAspect {
private final LogTrace logTrace;
public LogTraceAspect(LogTrace logTrace) {
this.logTrace = logTrace;
}
@Around("execution(* hello.proxy.app..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
TraceStatus status = null;
try {
String message = joinPoint.getSignature().toShortString();
status = logTrace.begin(message);
Object result = joinPoint.proceed(); // ์ค์ ํธ์ถ
logTrace.end(status);
return result;
} catch (Exception e) {
logTrace.exception(status, e);
throw e;
}
}
}
๊ตฌ์ฑ ์์:
@Aspect: "์ด ํด๋์ค๋ ์ด๋๋ฐ์ด์ ๋ณํ ๋์"@Around: ์ด๋๋ฐ์ด์ค + ํฌ์ธํธ์ปทProceedingJoinPoint: MethodInvocation์ AOP ๋ฒ์ joinPoint.proceed(): target ํธ์ถโ ๏ธ @Aspect๋ ์๋ ๋น ๋ฑ๋ก์ด ์๋:
@Bean, @Component, @Import์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 8.1, Phase 1
ํต์ฌ ํต์ฐฐ
OOP์ ํ๊ณ:
AOP์ ๋ต:
"๋ถ๊ฐ ๊ธฐ๋ฅ + ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ด๋์ ์ ์ฉํ ์ง ์ ์ ํ์ ํฉ์ณ์ ํ๋์ ๋ชจ๋๋ก ๋ง๋ ๋ค"
๊ทธ๊ฒ Aspect = ๊ด์
์ค์ํ ๋ช ์ :
AOP๋ OOP๋ฅผ ๋์ฒดํ๋ ๊ฒ์ด ์๋๋ค.
OOP์ ๋ถ์กฑํ ๋ถ๋ถ(ํก๋จ ๊ด์ฌ์ฌ)์ ๋ณด์กฐํ๋ค.
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 8.2
ํต์ฌ ๋น๊ต
AspectJ ํ๋ ์์ํฌ:
Spring AOP:
AOP ์ ์ฉ ์์ 3๊ฐ์ง:
| ์์ | ๋ฐฉ์ | ๋๊ฐ ์ฌ์ฉ? |
|---|---|---|
| ์ปดํ์ผ ์์ | ์ค์ ์ฝ๋์ ๋ถ๊ฐ ๊ธฐ๋ฅ ์ฝ๋ ์ฝ์ | AspectJ ์ง์ |
| ํด๋์ค ๋ก๋ฉ ์์ | ํด๋์ค ๋ก๋ฉ ์ ์ฝ๋ ๋ณ๊ฒฝ | AspectJ ์ง์ |
| ๋ฐํ์ ์์ | ํ๋ก์๋ก ๋ถ๊ฐ ๊ธฐ๋ฅ ์ ์ฉ | Spring AOP โญ |
์ค๋ฌด ๊ฒฐ๋ก :
"Spring AOP๋ง์ผ๋ก ๋๋ถ๋ถ ํด๊ฒฐ ๊ฐ๋ฅ. AspectJ ์ง์ ์ฌ์ฉ์ ์ ํด๋ ๋จ"
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Phase 7~8
ํต์ฌ ์ฉ์ด 7๊ฐ์ง โญ :
| ์ฉ์ด | ์๋ฏธ | ์์ |
|---|---|---|
| ์กฐ์ธ ํฌ์ธํธ(Join point) | ๋ถ๊ฐ ๊ธฐ๋ฅ์ ์ ์ฉํ ์ ์๋ ๋ชจ๋ ์ง์ | ๋ฉ์๋ ํธ์ถ, ์์ฑ์, ํ๋ ์ ๊ทผ |
| ํฌ์ธํธ์ปท(Pointcut) | ์กฐ์ธ ํฌ์ธํธ ์ค ์ค์ ์ ์ฉํ ๊ณณ์ ์ ํ | execution(* hello..*Service.*(..)) |
| ์ด๋๋ฐ์ด์ค(Advice) | ์ ์ฉํ ๋ถ๊ฐ ๊ธฐ๋ฅ ์ฝ๋ | @Around ๋ฉ์๋ |
| ์ ์คํํธ(Aspect) | ํฌ์ธํธ์ปท + ์ด๋๋ฐ์ด์ค์ ๋ชจ๋ | @Aspect ํด๋์ค |
| ํ๊ฒ(Target) | ๋ถ๊ฐ ๊ธฐ๋ฅ์ด ์ ์ฉ๋๋ ์ค์ ๊ฐ์ฒด | OrderService ์ธ์คํด์ค |
| ์๋น(Weaving) | ํฌ์ธํธ์ปท์ ํตํด ์ด๋๋ฐ์ด์ค๋ฅผ ๊ฒฐํฉ | ํ๋ก์ ์์ฑ ์ |
| AOP ํ๋ก์ | AOP ๊ธฐ๋ฅ์ ๊ตฌํํ ํ๋ก์ | JDK ๋์ ํ๋ก์ ๋๋ CGLIB |
Spring AOP์ ํ๊ณ โ ๋ฉ์๋ ์กฐ์ธ ํฌ์ธํธ๋ง:
์๊ธฐ ์ ๊ฒ
๋ชฉํ: ์ค๋ฌด์์ ๋งค์ผ ์ฐ๋ ํจํด๋ค์ ์์ ์ตํ๋ค.
์ ์ ์ง์: Phase 8
ํต์ฌ ๊ฐ๋
@Around โ ๊ฐ์ฅ ๊ฐ๋ ฅํ ์ด๋๋ฐ์ด์ค:
@Aspect
public class AspectV1 {
@Around("execution(* hello.aop.order..*(..))")
public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("[log] {}", joinPoint.getSignature());
return joinPoint.proceed(); // ์ค์ ํธ์ถ
}
}
ProceedingJoinPoint์ ์ฃผ์ ๋ฉ์๋:
proceed(): target ๋ฉ์๋ ํธ์ถgetSignature(): ํธ์ถ๋ ๋ฉ์๋์ ์๊ทธ๋์ฒgetArgs(): ์ ๋ฌ ์ธ์ ๋ฐฐ์ดgetTarget(): ์ค์ ๋์ ๊ฐ์ฒด์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 9.1
ํต์ฌ ๊ฐ๋
๋ฌธ์ : ๊ฐ์ ํฌ์ธํธ์ปท์ ์ฌ๋ฌ ์ด๋๋ฐ์ด์ค์์ ๋ฐ๋ณต ์์ฑ โ ์ค๋ณต
ํด๊ฒฐ โ @Pointcut์ผ๋ก ์๊ทธ๋์ฒ ๋ถ๋ฆฌ:
@Aspect
public class AspectV2 {
@Pointcut("execution(* hello.aop.order..*(..))")
private void allOrder() {} // ์๊ทธ๋์ฒ๋ง (body ์์)
@Around("allOrder()") // ์ฌ์ฌ์ฉ
public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("[log] {}", joinPoint.getSignature());
return joinPoint.proceed();
}
}
๋ณ๋ ํด๋์ค๋ก ๋ถ๋ฆฌ (์ค๋ฌด ํ์ค):
public class Pointcuts {
@Pointcut("execution(* hello.aop.order..*(..))")
public void allOrder() {}
@Pointcut("execution(* *..*Service.*(..))")
public void allService() {}
@Pointcut("allOrder() && allService()") // ์กฐํฉ
public void orderAndService() {}
}
@Aspect
public class AspectV4Pointcut {
@Around("hello.aop.order.aop.Pointcuts.allOrder()")
public Object doLog(ProceedingJoinPoint joinPoint) throws Throwable { ... }
@Around("hello.aop.order.aop.Pointcuts.orderAndService()")
public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable { ... }
}
ํฌ์ธํธ์ปท ํํ์์ ์กฐํฉ:
&&, ||, !์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 9.2
ํต์ฌ ๊ฐ๋
| ์ด๋๋ฐ์ด์ค | ์์ | ์ฉ๋ |
|---|---|---|
@Around | ๋ฉ์๋ ์ ํ | ๊ฐ์ฅ ๊ฐ๋ ฅ, ๋ชจ๋ ๊ฒ ๊ฐ๋ฅ |
@Before | ๋ฉ์๋ ์คํ ์ | ๋จ์ ์ฌ์ ์์ |
@AfterReturning | ์ ์ ์๋ฃ ํ | ๊ฒฐ๊ณผ ์ฒ๋ฆฌ |
@AfterThrowing | ์์ธ ๋ฐ์ ์ | ์์ธ ๋ก๊น |
@After | ์ ์/์์ธ ๋ฌด๊ด (finally) | ์์ ํด์ |
์ ํ ๊ฐ์ด๋:
@Around@Before / @AfterReturning@After@Around์ ๊ฐ๋ ฅํจ:
์ค๋ฌด ๊ฒฐ๋ก :
"๊ณ ๋ฏผ๋๋ฉด @Around ์จ๋ผ. ๋ค๋ฅธ ๊ฑด ๋จ์ ์ผ์ด์ค์์๋ง"
์๊ธฐ ์ ๊ฒ
๋ชฉํ: 7์ฃผ์ฐจ์์ ๋ค๋ฃฌ @Transactional์ 5๊ฐ์ง ํจ์ ์ค internal call ๋ฌธ์ ๋ฅผ ๊น์ด ํ๊ณ , ํธ๋์ญ์ ์ ํ์ 4๊ฐ์ง ์๋๋ฆฌ์ค๋ฅผ ๋ง์คํฐํ๋ค.
์ ์ ์ง์: 7์ฃผ์ฐจ Phase 7, Phase 6 (ProxyFactory)
ํต์ฌ ์ ๋ฆฌ
@Transactional์ ํธ๋์ญ์ AOP ๋ค.
๋์ ํ๋ฆ:
1. ๋น ๋ฑ๋ก ์ ๋น ํ์ฒ๋ฆฌ๊ธฐ๊ฐ ๊ฐ๋ก์ฑ
2. @Transactional ๋ฉ์๋๊ฐ ์์ผ๋ฉด โ ํธ๋์ญ์
ํ๋ก์๋ก ๊ต์ฒด
3. DI ์ ์ค์ ๊ฐ์ฒด ๋์ ํ๋ก์ ์ฃผ์
4. ๋ฉ์๋ ํธ์ถ โ ํ๋ก์๊ฐ ๊ฐ๋ก์ฑ์ ํธ๋์ญ์
์์/์ปค๋ฐ/๋กค๋ฐฑ
@Service
public class CallService {
@Transactional
public void internal() {
// ํธ๋์ญ์
์ ์ฉ๋จ
}
}
// CallService ๋น์ ์ค์ ๋ก๋ ํ๋ก์ ๊ฐ์ฒด
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 10.1
ํต์ฌ ์๋๋ฆฌ์ค
@Service
public class CallService {
public void external() {
log.info("call external");
internal(); // โ ๏ธ ๊ฐ์ ํด๋์ค์ ๋ฉ์๋ ํธ์ถ
}
@Transactional
public void internal() {
log.info("call internal");
}
}
๋ฌธ์ :
callService.external() ํธ์ถinternal() ํธ์ถ โ ์ฌ์ค this.internal()this๋ ํ๋ก์๊ฐ ์๋ target!๋ฌธ์ ์ ๋ณธ์ง:
"ํ๋ก์๋ ์ธ๋ถ ํธ์ถ์ ๊ฐ๋ก์ฑ ์ ์์ง๋ง, ๋ด๋ถ ํธ์ถ์ ๊ฐ๋ก์ฑ์ง ๋ชปํ๋ค"
์์ธ โ ์๋ฐ์ ๋์:
this์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 10.2
ํต์ฌ ํด๊ฒฐ
์๋ฆฌ:
@Service
public class CallService {
private final InternalService internalService; // ๋ค๋ฅธ ๋น ์ฃผ์
public void external() {
log.info("call external");
internalService.internal(); // โ
ํ๋ก์ ๊ฑฐ์นจ
}
}
@Service
public class InternalService {
@Transactional
public void internal() {
log.info("call internal");
}
}
ํธ์ถ ํ๋ฆ:
test โ CallService(์ค์ ๊ฐ์ฒด) โ InternalService(ํ๋ก์) โ InternalService(์ค์ ) โ DB
โ
์ฌ๊ธฐ์ ํธ๋์ญ์
์์
๋ค๋ฅธ ํด๊ฒฐ์ฑ ๋ค (์ฐธ๊ณ ):
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 10.3, 7์ฃผ์ฐจ Phase 7
ํต์ฌ ๊ฐ๋
ํธ๋์ญ์ ์ ํ(Propagation):
"์ด๋ฏธ ์งํ ์ค์ธ ํธ๋์ญ์ ์ด ์์ ๋, ์ ํธ๋์ญ์ ์์ฒญ์ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ๊น?"
REQUIRED (๊ธฐ๋ณธ ์ต์ ):
์์ โ ๋ ๋ค ์ปค๋ฐ:
@Test
void inner_commit() {
log.info("์ธ๋ถ ํธ๋์ญ์
์์");
TransactionStatus outer = txManager.getTransaction(new DefaultTransactionAttribute());
log.info("outer.isNewTransaction()={}", outer.isNewTransaction()); // true
log.info("๋ด๋ถ ํธ๋์ญ์
์์");
TransactionStatus inner = txManager.getTransaction(new DefaultTransactionAttribute());
log.info("inner.isNewTransaction()={}", inner.isNewTransaction()); // false (์ฐธ์ฌ)
txManager.commit(inner); // ์ค์ ๋ก๋ ์ ์ผ์ด๋จ
txManager.commit(outer); // ์ฌ๊ธฐ์ ์ง์ง commit
}
ํต์ฌ ํต์ฐฐ:
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 10.4
ํต์ฌ ์๋๋ฆฌ์ค
@Test
void inner_rollback() {
TransactionStatus outer = txManager.getTransaction(...);
TransactionStatus inner = txManager.getTransaction(...);
txManager.rollback(inner); // ๋ด๋ถ ๋กค๋ฐฑ
assertThatThrownBy(() -> txManager.commit(outer)) // ์ธ๋ถ ์ปค๋ฐ ์๋
.isInstanceOf(UnexpectedRollbackException.class); // โ ๏ธ ์์ธ!
}
๋์ ๋ถ์:
๋ด๋ถ ๋กค๋ฐฑ ์:
1. ์ ๊ท ํธ๋์ญ์
์ด ์๋๋ฏ๋ก ์ค์ ๋กค๋ฐฑ ์ ํจ
2. ํธ๋์ญ์
๋๊ธฐํ ๋งค๋์ ์ rollbackOnly=true ํ์
์ธ๋ถ ์ปค๋ฐ ์:
1. ์ ๊ท ํธ๋์ญ์
์ด๋ฏ๋ก ์ค์ ์ปค๋ฐ ์๋
2. rollbackOnly ํ์ ๋ฐ๊ฒฌ
3. ์ปค๋ฐ ๋์ ๋กค๋ฐฑ ์คํ
4. ๊ฐ๋ฐ์์๊ฒ UnexpectedRollbackException ๋์ง
์ค์ํ ํต์ฐฐ โญ :
"๋ด๋ถ๋ ์ธ๋ถ๋ ๋ ผ๋ฆฌ ํธ๋์ญ์ ์ด ํ๋๋ผ๋ ๋กค๋ฐฑ๋๋ฉด ๋ฌผ๋ฆฌ ํธ๋์ญ์ ์ ๋กค๋ฐฑ"
์ ์์ธ๋ฅผ ๋์ง๋๊ฐ:
์๊ธฐ ์ ๊ฒ
์ ์ ์ง์: Unit 10.5
ํต์ฌ ๊ฐ๋
REQUIRES_NEW:
์์:
TransactionStatus outer = txManager.getTransaction(new DefaultTransactionAttribute());
DefaultTransactionAttribute def = new DefaultTransactionAttribute();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus inner = txManager.getTransaction(def); // โญ ์ ๋ฌผ๋ฆฌ ํธ๋์ญ์
txManager.rollback(inner); // ๋ด๋ถ๋ง ๋กค๋ฐฑ
txManager.commit(outer); // ์ธ๋ถ๋ ์ ์ ์ปค๋ฐ โ
ํจ๊ณผ:
โ ๏ธ ์ฃผ์์ฌํญ:
์ ํ ์ต์ 7๊ฐ์ง:
| ์ต์ | ์ค๋ช |
|---|---|
| REQUIRED | ๊ธฐ๋ณธ. ์์ผ๋ฉด ์ฐธ์ฌ, ์์ผ๋ฉด ์์ |
| REQUIRES_NEW | ํญ์ ์ ํธ๋์ญ์ |
| SUPPORT | ์์ผ๋ฉด ์ฐธ์ฌ, ์์ผ๋ฉด ํธ๋์ญ์ ์์ด |
| NOT_SUPPORT | ์์ผ๋ฉด ๋ณด๋ฅ, ํธ๋์ญ์ ์์ด ์งํ |
| MANDATORY | ๋ฐ๋์ ์์ด์ผ ํจ, ์์ผ๋ฉด ์์ธ |
| NEVER | ํธ๋์ญ์ ์์ผ๋ฉด ์์ธ |
| NESTED | ์ค์ฒฉ ํธ๋์ญ์ (Savepoint) |
์ค๋ฌด์์ ๊ฐ์ฅ ๋ง์ด ์ฐ๋ ๊ฒ: REQUIRED, REQUIRES_NEW
์๊ธฐ ์ ๊ฒ
์ด๋ฒ ์ฃผ์ฐจ๋ ๋ถ๋์ด ๋ง์ ๋งํผ ๋ฐ๋์ ๊น์ด ํ์ผ ํ Unit ์ ๋ช ํํ ํ์:
โ โ โ ๋ฉด์ ยท์ค๋ฌด ๋จ๊ณจ (๋ฐ๋์):
โ โ ๋งค์ฐ ๊ถ์ฅ:
[ Part A โ 8์ฃผ์ฐจ ]
[ ] Phase 1 โ AOP ์
๋ฌธ๊ณผ ๋๊ธฐ (Unit 1.1~1.3)
[ ] Phase 2 โ ๋์์ธ ํจํด์ ์งํ (Unit 2.1~2.4)
[ ] Phase 3 โ ์ฝ๋ฐฑ๊ณผ ํ๋ก์์ ๋ง๋จ (Unit 3.1~3.3)
[ ] Phase 4 โ ํ๋ก์ vs ๋ฐ์ฝ๋ ์ดํฐ (Unit 4.1~4.3)
[ ] Phase 5 โ ๋์ ํ๋ก์ ๊ธฐ์ (Unit 5.1~5.3)
[ ] Phase 6 โ ProxyFactory ํตํฉ ์ถ์ํ (Unit 6.1~6.4) โ
8์ฃผ์ฐจ ์ ์
[ Part B โ 9์ฃผ์ฐจ ]
[ ] Phase 7 โ ๋น ํ์ฒ๋ฆฌ๊ธฐ์ ์๋ ํ๋ก์ (Unit 7.1~7.3)
[ ] Phase 8 โ @Aspect์ AOP ์ฉ์ด (Unit 8.1~8.4)
[ ] Phase 9 โ Spring AOP ์ค์ ํจํด (Unit 9.1~9.3)
[ ] Phase 10 โ @Transactional + ํธ๋์ญ์
์ ํ (Unit 10.1~10.6) โ
9์ฃผ์ฐจ ์ ์
[ ] ์ข
ํฉ ์๊ธฐ ์ ๊ฒ 32๋ฌธํญ ํต๊ณผ
8์ฃผ์ฐจ ์ ์ โ Phase 6 (ProxyFactory):
9์ฃผ์ฐจ ์ ์ โ Phase 10 (ํธ๋์ญ์ ์ ํ):
8-9์ฃผ์ฐจ๋ 1~7์ฃผ์ฐจ์ ํ์ต์ด ๋ชจ๋ ๊ฒฐํฉ ๋๋ ์ง์ :
| ์ถ์ฒ ์ฃผ์ฐจ | 8-9์ฃผ์ฐจ์์์ ์ญํ |
|---|---|
| 1์ฃผ์ฐจ (OOP, ์ธํฐํ์ด์ค) | ํ๋ก์ยท์ ๋ต ํจํด์ ๊ธฐ๋ฐ |
| 3์ฃผ์ฐจ (๋๋ค, ํจ์ํ) | Advice/Callback์ ๋๋ค ํ์ฉ |
| 4์ฃผ์ฐจ (๋ฉํฐ์ค๋ ๋, ThreadLocal) | ๋ก๊ทธ ์ถ์ ๊ธฐ์ ๋์์ฑ ํด๊ฒฐ |
| 5์ฃผ์ฐจ (๋์์ธ ํจํด, OCP) | ํ ํ๋ฆฟ ๋ฉ์๋ โ ์ ๋ต ํจํด ์งํ |
| 5์ฃผ์ฐจ (์ฑ๊ธํค ๋น) | ์๋ ํ๋ก์์ ๋น ๊ต์ฒด ๋ฉ์ปค๋์ฆ |
| 6์ฃผ์ฐจ (JdbcTemplate) | ํ ํ๋ฆฟ ์ฝ๋ฐฑ ํจํด์ ์ค์ ์ฌ๋ก |
| 7์ฃผ์ฐจ (@Transactional) | 8-9์ฃผ์ฐจ ํ์ต์ ๋๊ธฐ + ์ ์ฉ ์ฌ๋ก |
โ 8-9์ฃผ์ฐจ๋ ์๋ฐยทSpring ํ์ต์ ํด๋ผ์ด๋งฅ์ค