2022. 5. 31. 11:08γBackend/πΏ Spring
ν νλ¦Ώ λ©μλ ν¨ν΄ - μ¬μ μ μ μ / λͺ©μ
"μμ μμ μκ³ λ¦¬μ¦μ 골격μ μ μνκ³ , μΌλΆ λ¨κ³λ₯Ό νμ ν΄λμ€λ‘ μ°κΈ°νλ€. ν νλ¦Ώ λ©μλλ₯Ό μ¬μ©νλ©΄ νμ ν΄λμ€κ° μκ³ λ¦¬μ¦μ ꡬ쑰λ₯Ό λ³κ²½νμ§ μκ³ λ, μκ³ λ¦¬μ¦μ νΉμ λ¨κ³λ₯Ό μ¬μ μν μ μλ€. [GOF] "
μ΄κ² λ¬΄μ¨ μ리μΈκ°~ μμ보기 μν΄ μ€νλ§ μμ μ½λλ‘ ν νλ¦Ώ λ©μλ ν¨ν΄μ μμλ³΄κ² λ€.
μ€λ³΅λλ λ‘μ§μ λ°λ³΅
μλ μ½λλ 컨νΈλ‘€λ¬μ request λ©μλμ trace λΌλ λ‘κ·Έλ₯Ό μ½μ ν μ½λλ€.
ν΅μ¬ λ‘μ§μ orderService.orderItem(itemId)
μ return "ok"
λΆλΆμ΄ μ λΆμΈλ°, λ‘κ·Έ νΈλνΉμ νλ©΄μ λΆκ°μ μΈ try ~ catch κ΅¬λ¬Έμ΄ λΆμ΄ μ½λκ° λΉκ΅μ μ§μ λΆν΄ 보μΈλ€.
@RestController
@RequiredArgsConstructor
public class OrderControllerV3 {
@GetMapping("/v3/request")
public String request(String itemId) {
TraceStatus status = null;
try {
status = trace.begin("OrderControllerV3.request()");
orderService.orderItem(itemId);
trace.end(status);
return "ok";
} catch (Exception e) {
trace.exception(status, e);
throw e; // μμΈλ₯΄ κΌ λ€μ λμ Έμ£Όμ΄μΌ νλ€.
}
}
}
λ§μ½ νλ‘μ νΈ μ 체 μ½λμ λ‘κ·Έ νΈλνΉμ ν΄μΌνλ€κ³ κ°μ νμ. λͺ¨λ 컨νΈλ‘€λ¬μ μλΉμ€ λ μ΄μ΄, λ ν¬μ§ν 리 λ μ΄μ΄μ λ©μλμ λ‘κ·Έ νΈλνΉμ μν΄, μμ²λΌ try ~ catch ꡬ문μ λ¬μμΌ νλ κ²μ κ°λ°μμ€λ½μ§ λͺ»ν μ²μ¬λ€.
λ² μ€νΈ νλν°μ€λ λ¬Όλ‘ AOP λ₯Ό μ¬μ©ν λ‘κ·Έ νΈλ컀 μ½μ μ΄κ² μ§λ§, μ°μ ν νλ¦Ώ λ©μλ ν¨ν΄μ λμ νμ¬ μ μ½λλ₯Ό μ΄λ»κ² λ³κ²½μν¬ μ μλμ§ λ¨Όμ μμ보μ. κ·Έ νμ μ°¨λ‘λλ‘ μ λ΅ ν¨ν΄κ³Ό ν νλ¦Ώ μ½λ°± ν¨ν΄μ μ μ©νλ©΄μ λ λμ μ€κ³λ‘ λ³κ²½ μμΌλ³΄κ² λ€.
λ³νλ κ²κ³Ό λ³νμ§ μλ κ²μ λΆλ¦¬
μ’μ μ€κ³λ λ³νλ κ²κ³Ό λ³νμ§ μλ κ²μ λΆλ¦¬νλ κ²μ΄λ€. μ μ½λμμ λ³νλ λΆλΆμ ν΅μ¬λ‘μ§μΈ service λ μ΄μ΄λ₯Ό νΈμΆνλ λΆλΆμ΄κ³ , λ³νμ§ μλ λΆλΆμ try ~ catch ꡬ문과 trace.begin()
λ‘κ·Έ νΈλνΉ λΆλΆμ΄λ€. λ³νμ§ μλ λΆλΆμ ν
νλ¦Ών νμ¬ κ³΅ν΅ μμλ‘ λΊ μ μλ€λ©΄, 그리νμ¬ ν΄λΉ ν
νλ¦Ώμ νμν λλ§ νΈμΆνλ λ°©λ²μ μκ°ν΄λ³΄μ.
ν νλ¦Ώ λ©μλ ν¨ν΄ - μμ
ν νλ¦Ώ λ©μλ ν¨ν΄μ μ΄λ¦ κ·Έλλ‘ ν νλ¦Ώμ λ§λ€μ΄ μ¬μ©νλ λ°©μμΌλ‘, ν νλ¦Ώμ΄λΌλ νμ μμμ μΈκΈν λ³νμ§ μλ λΆλΆμ λͺ°μλλ€. κ·Έλ¦¬κ³ λ³νλ λΆλΆμΈ, ν΅μ¬ λ‘μ§μ λ³λλ‘ νΈμΆνλλ‘ μ€κ³ ν΄λ³΄μ. μλ μ½λμμλ λ³νμ§ μλ λΆλΆμ execute() λ©μλμ ꡬννκ³ , λ³νλ λΆλΆμ λ³λλ‘ νΈμΆν μ μλλ‘ (μμ§ μ μλμ§ μμ) μΆμ λ©μλλ‘ μ μΈν΄λμλ€. μ΄λ₯Ό ꡬμ±νλ ν΄λμ€κ° ν νλ¦Ώμ΄ λλ κ²μ΄λ€.
// AbstractTemplate.class
@Slf4j
public abstract class AbstractTemplate {
public void execute() {
long startTime = System.currentTimeMillis();
// λΉμ¦λμ€ λ‘μ§ μ€ν
call(); // μμ
// λΉμ¦λμ€ λ‘μ§ μ’
λ£
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("resultTime = {}", resultTime);
}
// μΆμ λ©μλ
protected abstract void call();
}
κ·Έλ¦¬κ³ λ³νλ λΆλΆμΈ (λΉμ¦λμ€ λ‘μ§1, 2λ₯Ό μ²λ¦¬νλ) μμ ν΄λμ€λ₯Ό SubClassLogic1, SubClassLogic2 μΌλ‘ μ μνλ€. μ΄ λ ν νλ¦Ώμ μΆμλ©μλμΈ call() λ©μλλ₯Ό μ€λ²λΌμ΄λ© νλ€. 그리νμ¬ μ€λ²λΌμ΄λ© λ λ©μλκ° νΈμΆλλ€.
// SubClassLogic1.class
@Slf4j
public class SubClassLogic1 extends AbstractTemplate {
@Override
protected void call() {
log.info("λΉμ¦λμ€ λ‘μ§ 1 μ€ν");
}
}
// SubClassLogic2.class
@Slf4j
public class SubClassLogic2 extends AbstractTemplate {
@Override
protected void call() {
log.info("λΉμ¦λμ€ λ‘μ§ 2 μ€ν");
}
}
SubClassLogic1, 2 ν΄λμ€κ° AbstractTemplate μ΄λΌλ μΆμ ν΄λμ€λ₯Ό μμ λ°λ κ²μ λ³Ό μ μλ€.
μμ ν΄λμ€μΈ SubClassLogic1, 2 λ μμ°μ€λ½κ² μΆμλ©μλμΈ call() λ©μλλ₯Ό μ€λ²λΌμ΄λ νκ³ ,
μ΄ λ μμ λ§μ ν΅μ¬ λ‘μ§(λ³νλ λΆλΆ)μ call() λ©μλμ ꡬννλ€.
λ€νμ±μ΄ μ μ©λλ©΄μ, μμ ν΄λμ€μΈ SubClassLogic1, 2 μ μΈμ€ν΄μ€λ AbstractTemplate μ νμ μ μ§λλ©° λμμ execute() λ©μλλ₯Ό νΈμΆν μ μλ€. execute() λ©μλκ° νΈμΆλλ©΄μ κ·Έ λ΄λΆμ μ μΈλ call() λ©μλκ° μ€νλμ΄, μμ ν΄λμ€μμ μ€λ²λΌμ΄λ ν΄ λ μ½λκ° μ€νλλ€.
@Test
void templateMethodV1() {
AbstractTemplate template1 = new SubClassLogic1();
template1.execute();
AbstractTemplate template2 = new SubClassLogic2();
template2.execute();
}
κ²°κ΅ ν
νλ¦Ώ λ©μλ ν¨ν΄μ, λΆλͺ¨ ν΄λμ€μ μκ³ λ¦¬μ¦μ κ³¨κ²©μΈ ν
νλ¦Ώμ μ μνκ³ , μΌλΆ λ³κ²½λλ λ‘μ§μ μμ ν΄λμ€μ λ³λλ‘ μ μνλ κ²μ΄λ€. 그리νμ¬ μμ ν΄λμ€κ° μκ³ λ¦¬μ¦ μ 체μ 골격μ λ³κ²½νμ§ μκ³ , νΉμ λΆλΆλ§ μ¬μ μ ν μ μλ€. μ¦ μμκ³Ό μ€λ²λΌμ΄λ©μ ν΅ν λ€νμ±
μ νμ©νμ¬ λ¬Έμ λ₯Ό ν΄κ²°νλ ν¨ν΄μΈ κ²μ΄λ€.
μ΅λͺ ν΄λμ€ / λλ€μμ μ΄μ©ν ν νλ¦Ώ λ©μλ ν¨ν΄
ν νλ¦Ώ λ©μλ ν¨ν΄μ μ μ©νμ¬ ν΅μ¬λ‘μ§κ³Ό λΆκ°λ‘μ§μ λΆλ¦¬ν΄λμ§λ§, μ μ½λλ λ§€λ² SubClassLogic μ μμ± ν΄μΌνλ λΆνΈν¨μ΄ μλ€. μ΄ μ μ μ΅λͺ ν΄λμ€λ λλ€μμ μ΄μ©ν΄ κ°μν ν μ μλ€.
@Test
void templateMethodV2() {
// μ΅λͺ
ν΄λμ€ μ¬μ©
AbstractTemplate template = new AbstractTemplate() {
@Override
protected void call() {
log.info("λΉμ¦λμ€ λ‘μ§1 μ€ν");
}
};
log.info("ν΄λμ€μ΄λ¦1 = {}", template.getClass());
template.execute();
// λλ€μ μ¬μ©
AbstractTemplate template2 = { log.info("λΉμ¦λμ€ λ‘μ§2 μ€ν"); };
log.info("ν΄λμ€μ΄λ¦2 = {}", template2.getClass());
template2.execute();
// 볡μ‘ν΄λ³΄μ΄μ§λ§, λ³λμ SubClassLogic1 κ°μ λ³λμνμΌμ λ§λ€μ§ μμλ λλ€.
}
ν νλ¦Ώ λ©μλ ν¨ν΄μ μ μ©
μ΄μ κΈ°μ‘΄ μ½λμ ν νλ¦Ώ λ©μλ ν¨ν΄μ μ μ©ν΄λ³΄μ. μ°μ ν νλ¦Ώμ΄ λ AbstractTemplate μΆμ ν΄λμ€λ₯Ό μμ±νλ€. ν νλ¦Ώμλ μ°λ¦¬κ° λΆκ° λ‘μ§μ΄λΌ μ¬κΈ΄, λ³νμ§ μλ λΆλΆμ ν΄λΉλλ try ~ catch μ½λλ₯Ό μ½μ νλ€. κ·Έλ¦¬κ³ κ·Έ λ΄λΆμ call() μΆμ λ©μλλ₯Ό μ μΈνμ¬, μμ ν΄λμ€μμ μ€λ²λΌμ΄λ λ λΉμ¦λμ€λ‘μ§μ΄ μ€νλλλ‘ νμ.
// λΆλͺ¨ μν (= ν
νλ¦Ώ)
public abstract class AbstractTemplate<T> {
private final LogTrace trace;
public AbstractTemplate(LogTrace trace) {
this.trace = trace;
}
// type μμ± μμ μ κ°μ²΄ μμ±μμ μΌλ‘ 미룬λ€.
public T execute(String message){
TraceStatus status = null;
try {
status = trace.begin(message);
// logic νΈμΆ
T result = call();
trace.end(status);
return result;
} catch (Exception e) {
trace.exception(status, e);
throw e;
}
}
protected abstract T call();
}
@RestController
@RequiredArgsConstructor
public class OrderControllerV4 {
private final OrderServiceV4 orderService;
private final LogTrace trace;
@GetMapping("/v4/request")
public String request(String itemId) {
// μ¬κΈ°μ logκ΄λ ¨ μ½λλ₯Ό λͺ¨λ λͺ°μλ£κ³ , μΆμλ©μλ ꡬνλΆμ ν΅μ¬λ‘μ§μ λ£λλ€
AbstractTemplate<String> template = new AbstractTemplate<>(trace) {
@Override
protected String call() {
orderService.orderItem(itemId);
return "ok";
} };
return template.execute("OrderController.request()");
}
}
μ΄λ€κ°? λ¨μν try ~ catch κ΅¬λ¬Έλ§ extract ν κ²μ΄ μλλΌ, λ‘κ·Έλ₯Ό λ¨κΈ°λ λΆλΆμ λ¨μΌ μ±
μ μμΉ(SRP)
μ μ§ν¨ μ½λλ₯Ό μ§°λ€. λ‘κ·Έμ κ΄λ ¨λ λ³κ²½ μ§μ μ νλλ‘ λͺ¨μμ
λ³κ²½μ μ½κ² λμ²ν μ μλ ꡬ쑰λ₯Ό μ€κ³ν κ²μ΄λ€ < template method pattern μ μ₯μ > . μ΄μ λ‘κ·Έλ₯Ό λ¨κΈ°λ λ‘μ§μ λ³κ²½ν΄μΌ νλ©΄, λͺ¨λ λ©μλμ ν©λΏλ €μ§ μ½λλ₯Ό μΌμΌμ΄ μμ νμ§ μκ³ ν
νλ¦Ώ μ½λ
λ§ μμ νλ©΄ λλ€.
ν νλ¦Ώ λ©μλμ νκ³
μμ μ€λͺ νλλ‘, ν νλ¦Ώ λ©μλ ν¨ν΄μ μμκ³Ό μ€λ²λΌμ΄λ©, λ€νμ±μ νΉμ§μ μ΄μ©νμ¬ λ¬Έμ λ₯Ό ν΄κ²°νλ€. νμ§λ§ μμ보λ€λ μ»΄ν¬μ§μ μ νμ©νλΌλ λ§μ΄ μλ―μ΄, μμμΌλ‘ μΈν΄ μμ ν΄λμ€κ° λΆλͺ¨ ν΄λμ€μ μ»΄νμΌ μμ μ κ°νκ² κ²°ν©λλ λ¬Έμ κ° μ‘΄μ¬νλ€. μμ ν΄λμ€μ μ μ₯μμλ call() μ΄λΌλ μΆμλ©μλλ§ μ€λ²λΌμ΄λ© ν λΏ λΆλͺ¨ ν΄λμ€μ κΈ°λ₯μ μ ν μ¬μ©νμ§ μμ μ± μμλ§ λ°κ³ μλ μν©μ΄λ€.
μμμ λ°λ κ²μ, μμ ν΄λμ€κ° λΆλͺ¨ ν΄λμ€λ₯Ό μμ‘΄
νλ€λ μλ―Έμ΄λ©°, μμ ν΄λμ€μ μ½λμ λΆλͺ¨ ν΄λμ€μ μ½λκ° λͺ
ννκ² μ νμλ€λ μλ―Έμ΄κΈ°λ νλ€. μ΄κ² μ μ’μ§ λͺ»ν μν©μΌκΉ? μμ‘΄μ λμμ΄ λλ ν΄λμ€κ° λ³κ²½λλ©΄ μμ‘΄νλ ν΄λμ€λ λ³κ²½μ μν₯μ λ°κΈ° λλ¬Έμ΄λ€.
κ·Έλ λ€λ©΄ μμμ λ¨μ μ μ κ±°νλ©΄μ, μμ λ¬Έμ λ₯Ό ν΄κ²°ν μ μλ λ°©λ²μ μμκΉ? λ°λ‘ μμμ΄ μλ μμμ νμ©νλ©΄ λλ€. μ΄λ₯Ό νμ©ν μ μλ μ λ΅ ν¨ν΄μ λ€μ ν¬μ€ν μμ μμ ν΄λ³΄κ² λ€.
μ½λ λ° μλ£ μΆμ² :https://inf.run/5BUY