JiwonDev

๋กœ๊ทธ ์ถ”์ ๊ธฐ๋กœ ์•Œ์•„๋ณด๋Š” ๋””์ž์ธํŒจํ„ด

by JiwonDev

 

 

์Šคํ”„๋ง ํ•ต์‹ฌ ์›๋ฆฌ - ๊ณ ๊ธ‰ํŽธ - ์ธํ”„๋Ÿฐ | ๊ฐ•์˜

์Šคํ”„๋ง์˜ ํ•ต์‹ฌ ์›๋ฆฌ์™€ ๊ณ ๊ธ‰ ๊ธฐ์ˆ ๋“ค์„ ๊นŠ์ด์žˆ๊ฒŒ ํ•™์Šตํ•˜๊ณ , ์Šคํ”„๋ง์„ ์ž์‹ ์žˆ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค., - ๊ฐ•์˜ ์†Œ๊ฐœ | ์ธํ”„๋Ÿฐ...

www.inflearn.com

 

์„œ๋น„์Šค๋ฅผ ํ™•์žฅํ•˜๊ณ  ์ง€์†๊ฐ€๋Šฅํ•œ ์šด์˜์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ •๋ณด๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

  • ์–ด๋Š ์‹œ์ ์—์„œ ์„ฑ๋Šฅ์ƒ ๋ณ‘๋ชฉ์ด ๋ฐœ์ƒํ•˜๊ณ  ์žˆ๋Š”๊ฐ€?
  • ์–ด๋–ค ์žฅ์• ๊ฐ€ ๋ฐœ์ƒํ–ˆ์œผ๋ฉฐ, ๊ณ ๊ฐ์ด ๊ฐ€์žฅ ๋งŽ์ด ๊ฒช๋Š” ์žฅ์• ๋Š” ๋ฌด์—‡์ธ๊ฐ€?
  • ๊ฐ€์žฅ ํŠธ๋ž˜ํ”ฝ์ด ๋งŽ์€ ์„œ๋น„์Šค / ์ž˜ ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ์„œ๋น„์Šค ๊ธฐ๋Šฅ์€ ๋ฌด์—‡์ธ๊ฐ€?

์ด๋Ÿฌํ•œ ๋กœ๊ทธ๋“ค์„ ์–ด๋–ป๊ฒŒ ๋‚จ๊ธฐ๊ณ  ๊ด€๋ฆฌํ•ด์•ผํ•˜๋Š”์ง€ ์ฐจ๊ทผ์ฐจ๊ทผ ์•Œ์•„๋ณด์ž.

๋ฌผ๋ก  ์ตœ๊ทผ์—๋Š” ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ(ELK๋“ฑ)์„ ์“ฐ๋ฉด ๊ฐ„๋‹จํ•˜์ง€๋งŒ, ์ด๋Ÿฐ ๋„๊ตฌ๊ฐ€ ์—†๋Š” ๊ณผ๊ฑฐ์—๋Š” ์–ด๋–ป๊ฒŒ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ฒผ๋Š”์ง€ ๋ฐฐ์›Œ๋ณด์ž.

 

 

๐Ÿ’ญ ๋กœ๊ทธ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด๋ณด์ž

๐Ÿ“Œ ์–ด๋–ค ๊ธฐ๋Šฅ์ด ํ•„์š”ํ• ๊นŒ

์ •์ƒํ๋ฆ„๊ณผ, ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ์˜ˆ์™ธํ๋ฆ„์„ ๊ตฌ๋ถ„ํ•ด์„œ ๋ณผ ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
์•„๋ž˜์™€ ๊ฐ™์ด ์„œ๋น„์Šค์— ์˜๋ฏธ์žˆ๋Š” ๋ชจ๋“  ๋กœ๊ทธ๋ฅผ ๋‚จ๊ฒจ์•ผํ•œ๋‹ค.

  • ๊ณต๊ฐœ๋œ(Public) ๋ฉ”์„œ๋“œ์˜ ํ˜ธ์ถœ๊ณผ ์‘๋‹ต์ •๋ณด
  • ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ๋ฐ ์ฒ˜๋ฆฌ์— ๊ฑธ๋ฆฐ์‹œ๊ฐ„
  • ๊ฐ HTTP ์š”์ฒญ์— [๊ณ ์œ ํ•œ ํŠธ๋žœ์žญ์…˜ ID]๋ฅผ ๋ถ™์—ฌ์„œ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์ข‹๊ฒŒ ๊ธฐ๋กํ•œ๋‹ค.

์œ„์˜ ์š”์ฒญ์‚ฌํ•ญ์„ ๋งŒ์กฑํ•œ ๋กœ๊ทธ ๊ธฐ๋ก.

 

 

๐Ÿ“Œ ํ•˜๋‚˜์”ฉ ๋งŒ๋“ค์–ด๋ณด์ž.

๊ทธ๋ƒฅ ์„œ๋น„์Šค ๋กœ์ง ์œ„์•„๋ž˜๋กœ trace.begin() ~ trace.end() ํ•ด์„œ ๊ธฐ๋กํ•˜๋ฉด ๋˜๋Š”๊ฑฐ ์•„๋‹๊นŒ?

๋กœ๊ทธ๋ฅผ ์ถ”๊ฐ€, ์œ ์ง€๋ณด์ˆ˜ํ•˜๋Š”๊ฑด ๊ณจ์น˜์•„ํ”ˆ ์ผ์ด๋‹ค. ์™œ ๊ทธ๋Ÿฐ์ง€ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์•Œ์•„๋ณด์ž.

 

1. ๋กœ๊ทธ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ ๋‹ค.

Trace, TraceId ๋“ฑ์˜ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ , ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธธ ๋ฉ”์„œ๋“œ begin() end() ๋ฅผ ๋งŒ๋“ ๋‹ค.

@Slf4j
@Component
public class TraceV1 {

    private static final String START_PREFIX = "-->";
    private static final String COMPLETE_PREFIX = "<--";
    private static final String EX_PREFIX = "<X-";

    public TraceStatus begin(String message) {
        TraceId traceId = new TraceId();
        Long startTimeMs = System.currentTimeMillis();
        log.info("[{}] {}{}", traceId.getId(), addSpace(START_PREFIX, traceId.getLevel()), message);
        return new TraceStatus(traceId, startTimeMs, message);
    }

    public void end(TraceStatus status) {
        complete(status, null);
    }
    ...
}

 

 

2. ์ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธด๋‹ค. ์•„์ง๊นŒ์ง„ ํ• ๋งŒํ•œ๋‹ค.

private final TraceV1 trace; // ์Šคํ”„๋ง ๋นˆ์œผ๋กœ ๋“ฑ๋ก

@GetMapping("/request")
public String request(String itemId) {
    TraceStatus status = trace.begin("OrderController.request()"); // ๋กœ๊ทธ ์‹œ์ž‘
    orderService.orderItem(itemId);
    trace.end(status); // ๋กœ๊ทธ ์ข…๋ฃŒ

    return "ok";
}

 

 

3. ์ด์ œ ์˜ˆ์™ธ์™€ ์ •์ƒ ํ๋ฆ„์„ ๊ตฌ๋ถ„ํ•ด์„œ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๋„๋ก ๋งŒ๋“ ๋‹ค. ์Šฌ์Šฌ ๋ณต์žกํ•ด์ง„๋‹ค

๋กœ๊ทธ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ๊ฐ์ฒด๊ฐ€ ์ง์ ‘ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ฑฐ๋‚˜, ์„œ๋น„์Šค๋‚˜ ๋น„์ฆˆ๋‹ˆ์Šค๋กœ์ง์— ์˜ํ–ฅ์„ ์ฃผ๋ฉด ์•ˆ๋œ๋‹ค.

@GetMapping("/request")
public String request(String itemId) {

    TraceStatus status = null;
    try {
        status = trace.begin("OrderController.request()");
        orderService.orderItem(itemId);
        trace.end(status);
        return "ok";
    } catch (Exception e) {
        trace.exception(status, e); // ์˜ˆ์™ธ์šฉ ๋กœ๊ทธ ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€.
        
        throw e;// ๋กœ๊ทธ๋Š” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ๋ฆ„์„ ์ˆ˜์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์˜ˆ์™ธ๋Š” ๋‹ค์‹œ ๋˜์ง„๋‹ค.
    }
}

 

 

4. ๊ฐ ๋ฉ”์„œ๋“œ๋ณ„๋กœ TraceId๋ฅผ ์ง€์ •ํ•œ๋‹ค. ์–ด๋–ค ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€, ํ˜ธ์ถœ ์Šคํƒ(Level)์„ ๋กœ๊ทธ์— ๋‚จ๊ธด๋‹ค.

์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” Controller ๋ฟ ์•„๋‹ˆ๋ผ Service, Repository ๊นŒ์ง€ ์•„๋ž˜์™€ ๊ฐ™์€ ๋กœ๊น… ๋กœ์ง์„ ์ž‘์„ฑํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ [ํŠธ๋žœ์žญ์…˜ ID]๋ฅผ ๋งŒ๋“ค์–ด ๋ฉ”์„œ๋“œ์˜ ํ˜ธ์ถœํ๋ฆ„, ๊นŠ์ด๋ฅผ ๋กœ๊ทธ์— ๊ธฐ๋กํ•œ๋‹ค.

@Service
public class OrderService {

    private final OrderRepository orderRepository;
    private final Trace trace;

    public void orderItem(TraceId traceId, String itemId) {
        TraceStatus status = null;
        try {
            // ๋ฉ”์„œ๋“œ ๊นŠ์ด๋ฅผ ๊ธฐ๋กํ•˜๊ธฐ์œ„ํ•ด, beginSync() ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. ๊ธฐ์กด ๋กœ๊น…์— ์ถ”๊ฐ€
            status = trace.beginSync(traceId, "OrderService.orderItem()");
            orderRepository.save(status.getTraceId(), itemId);
            trace.end(status);
        } catch (Exception e) {
            trace.exception(status, e);
            throw e;
        }
    }
}
@Repository
public class OrderRepository {

    private final Trace trace;

    public void save(TraceId traceId, String itemId) {
        TraceStatus status = null;
        try {
            status = trace.beginSync(traceId, "OrderRepository.save()");
            /* ...์ €์žฅ ๋กœ์ง... */
            trace.end(status);
        } catch (Exception e) {
            trace.exception(status, e);
            throw e;
        }
    }
}

 

 

5. HTTP ์š”์ฒญ๋‹จ์œ„๋กœ ๊ฐ ๋กœ๊ทธ๋ฅผ ๊ตฌ๋ถ„ํ•œ๋‹ค. ๊ฐ™์€ ์š”์ฒญ์ด๋ฉด ๊ฐ™์€ [Trace ID]๋ฅผ ๊ฐ€์ง€๋„๋ก ๋™๊ธฐํ™”ํ•œ๋‹ค.

์ด๋ฅผ ์œ„ํ•ด์„œ ์„œ๋น„์Šค์—์„œ traceId๋ฅผ ์ง์ ‘ ์ง€์ •ํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ, ์•„๋ž˜์™€ ๊ฐ™์ด ๋กœ๊ทธ ๊ฐ์ฒด๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋„๋ก ๋ฆฌํŒฉํ† ๋งํ•œ๋‹ค.

public void orderItem(String itemId) {
    TraceStatus status = null;
    try {
        // ๊ธฐ์กด ์ฝ”๋“œ status = trace.beginSync(traceId, "OrderService.orderItem()");
    	status = trace.begin("OrderService.orderItem()");
    }
}
  • ๊ฐ™์€ ๋กœ๊น… ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด syncTraceId()๋ฅผ ํ†ตํ•ด [Trace ID]๋ฅผ ๋™๊ธฐํ™”ํ•˜๊ณ  ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ๋ ˆ๋ฒจ์„ +1 ํ•œ๋‹ค.
  • ๋กœ๊น…์ด ๋๋‚  ๋•Œ releaseTraceId()๋ฅผ ํ†ตํ•ด [Trace ID]๋ฅผ ๋™๊ธฐํ™”ํ•˜๊ณ  ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ๋ ˆ๋ฒจ -1 ํ•œ๋‹ค.

@Component
public class FieldLogTrace {

    private TraceId traceIdHolder; // traceId๋ฅผ ๊ฐ์ฒด๊ฐ€ ๋“ค๊ณ ์žˆ๋„๋ก ๋ณ€๊ฒฝ
    
    private void syncTraceId() {
        if (traceIdHolder == null) {
            traceIdHolder = new TraceId();
        } else {
            traceIdHolder = traceIdHolder.createNextId(); // ํ˜ธ์ถœ๋ ˆ๋ฒจ +1
        }
    }
    
    private void releaseTraceId() {
        if (traceIdHolder.isFirstLevel()) {
            traceIdHolder = null; //destroy
        } else {
            traceIdHolder = traceIdHolder.createPreviousId(); // ํ˜ธ์ถœ๋ ˆ๋ฒจ -1
        }
    }
    ...
}
@Component
public class FieldLogTrace {
    ...
    public TraceStatus begin(String message) {
        syncTraceId(); // Trace Id ๋™๊ธฐํ™”
        
        TraceId traceId = traceIdHolder;
        Long startTimeMs = System.currentTimeMillis();
        log.info("[{}] {}{}", traceId.getId(), addSpace(START_PREFIX, traceId.getLevel()), message);
        return new TraceStatus(traceId, startTimeMs, message);
    }
    
    public void end(TraceStatus status) {
        complete(status, null);

    }

    public void exception(TraceStatus status, Exception e) {
        complete(status, e);
    }

    private void complete(TraceStatus status, Exception e) {
        Long stopTimeMs = System.currentTimeMillis();
        long resultTimeMs = stopTimeMs - status.getStartTimeMs();
        TraceId traceId = status.getTraceId();
        if (e == null) {
            log.info("[{}] {}{} time={}ms", traceId.getId(), addSpace(COMPLETE_PREFIX, traceId.getLevel()), status.getMessage(), resultTimeMs);
        } else {
            log.info("[{}] {}{} time={}ms ex={}", traceId.getId(), addSpace(EX_PREFIX, traceId.getLevel()), status.getMessage(), resultTimeMs, e.toString());
        }

        releaseTraceId();
    }
}

 

 

๐Ÿงจ FieldLogTrace๋Š” ๋™์‹œ์„ฑ๋ฌธ์ œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๊ณต๋ถ€ํ–ˆ๋‹ค๋ฉด ์•Œ๊ฒ ์ง€๋งŒ, ๊ธฐ๋ณธ์ ์œผ๋กœ @Component๋Š” ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด๋กœ ๋“ฑ๋ก๋œ๋‹ค.

์ฆ‰ ์•ฑ ์‹คํ–‰์‹œ์ ์— ํ•œ๋ฒˆ ๋งŒ๋“ค์–ด๋†“๊ณ  ์žฌ์‚ฌ์šฉํ•œ๋‹ค. ์—ฌ๋Ÿฌ ํŠธ๋žœ์žญ์…˜์—์„œ ๊ฐ™์€ ๊ฐ’์„ ์ˆ˜์ •ํ•˜๊ธฐ๋•Œ๋ฌธ์— ๋กœ๊ทธ๊ฐ€ ์ด์ƒํ•˜๊ฒŒ ์ฐํžŒ๋‹ค.

์‹ค์ œ ์šด์˜ ๋กœ๊ทธ๋Š” [Trace ID]๋ฅผ ๋ชจ๋‘๊ฐ€ ์‚ฌ์šฉํ•˜๊ณ , ๋กœ๊ทธ๊ฐ€ ๊ฐœํŒ์œผ๋กœ ์ฐํžŒ๋‹ค.

๊ฐ€์žฅ ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ๋Š”, ๊ฐœ๋ฐœํ•  ๋•Œ์—๋Š” ์ด๋Ÿฌํ•œ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ์ธ์ง€ํ•˜๊ธฐ ์–ด๋ ต๋‹ค.  ๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋กœ๋Š” ๋ฐœ๊ฒฌํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

/* ํ•ด๋‹น ํ…Œ์ŠคํŠธ๋Š” ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ์ฐพ์•„์ฃผ์ง€ ์•Š๋Š”๋‹ค. ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜๊ณ , ํ…Œ์ŠคํŠธ์— ์„ฑ๊ณตํ•œ๋‹ค.*/
public class FieldLogTraceTest {

    FieldLogTrace trace = new FieldLogTrace();

    @Test
    void begin_end_level2() {
        TraceStatus status1 = trace.begin("hello1");
        trace.end(status2);
    }

    @Test
    void begin_exception_level2() {
        TraceStatus status1 = trace.begin("hello1");
        trace.exception(status2, new IllegalStateException());
    }
}

๋˜ํ•œ ์šด์˜๋„์ค‘ ์•Œ์•˜๋‹ค๊ณ  ํ•ด๋„, ์–ด๋””์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ๋ชจ๋ฅด๊ธฐ์— ๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ๋‹ค ๋’ค์ ธ๋ณด๋ฉฐ ๋””๋ฒ„๊น… ํ•ด์•ผํ•œ๋‹ค. ๋”์ฐํ•˜๋‹ค.

 

 

6. ๋™์‹œ์„ฑ ๋ฌธ์ œํ•ด๊ฒฐ ๋ฐฉ๋ฒ• - ThreadLocal ์‚ฌ์šฉ

๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ์œ„ํ•ด ์ž๋ฐ”์˜ java.lang.ThreadLocal ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด๋ณด์ž.|
๋ฌผ๋ก  ์‹ฑ๊ธ€ํ†ค ๋นˆ์„ ์“ฐ์ง€์•Š๊ณ  ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜, ์•„์˜ˆ ์ƒํƒœ ๊ฐ’(Thread-Id)์„ ํ•„๋“œ๊ฐ€ ์•„๋‹Œ ๋ฉ”์„œ๋“œ ์ธ์ž๋กœ ์ฃผ๊ณ  ๋ฐ›๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๋ ‡๊ฒŒํ•˜๋ฉด ์„ฑ๋Šฅ๋„ ๊ตฌ๋ ค์ง€๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์ •๋ง ์–ด๋ ค์›Œ์ง„๋‹ค.

์ž๋ฐ”์˜ ThreadLocal ๊ฐ์ฒด๋Š” [ํ•ด๋‹น ์Šค๋ ˆ๋“œ๋งŒ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ ๋ฉ”๋ชจ๋ฆฌ]๋ฅผ ๋งŒ๋“ค์–ด ์ œ๊ณตํ•ด์ค€๋‹ค.

์‚ฌ์šฉ๋ฒ•์€ ๊ฐ„๋‹จํ•˜๋‹ค. ThreadLocal<๊ฐ์ฒด>๋กœ ๊ฐ์‹ธ๊ณ , ์ปฌ๋ ‰์…˜์ฒ˜๋Ÿผ ๋„ฃ๊ณ  ๊บผ๋‚ด ์“ฐ๋ฉด ๋œ๋‹ค.

public class ThreadLocalService {

    private ThreadLocal<String> nameStore = new ThreadLocal<>();
    // ThreadLocal.set("~")
    // ThreadLocal.get()
    // ThreadLocal.remove()
    
    public String logic(String name) {
        nameStore.set(name);
        return nameStore.get();
    }
}

 

 

๐Ÿงจ WAS(ํ†ฐ์บฃ ๋“ฑ)์—์„œ์˜ ThreadLocal ์‚ฌ์šฉ์‹œ ์ฃผ์˜์‚ฌํ•ญ

ThreadLocal ์‚ฌ์šฉ ์ดํ›„ ์Šค๋ ˆ๋“œ ์ข…๋ฃŒ์ด์ „์— ๋ฐ˜๋“œ์‹œ .remove()๋กœ ํ• ๋‹น ํ•ด์ œํ•ด์ค˜์•ผํ•œ๋‹ค.

private void releaseTraceId() {
    TraceId traceId = traceIdHolder.get();
    if (traceId.isFirstLevel()) {
        traceIdHolder.remove(); //destroy
    } else {
        traceIdHolder.set(traceId.createPreviousId());
    }

๊ทธ ์ด์œ ๋Š” ์Šค๋ ˆ๋“œ ํ’€์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์Šค๋ ˆ๋“œ๋Š” ์ œ๊ฑฐ๋˜์ง€ ์•Š๊ณ  ์žฌ์‚ฌ์šฉ๋˜๊ธฐ์— ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฝ๋‹ค.

๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋งŒ ๋ฐœ์ƒํ•˜๋ฉด ๋‹คํ–‰์ธ๋ฐ, ๊ธฐ์กด์˜ ThreadLocal์„ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๊ฐ€ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๋”์ฐํ•œ ๋ณด์•ˆ ๋ฌธ์ œ๋„ ๊ฐ™์ด ๋ฐœ์ƒํ•œ๋‹ค.

๋ฌผ๋ก  ์Šค๋ ˆ๋“œ ํ’€์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋”๋ผ๋„, remove()๋ฅผ ํ†ตํ•ด ํ•ด์ œ์‹œ์ผœ์ฃผ๋Š”๊ฒŒ ์ข‹๋‹ค. ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€์šฉ

 

 

๐Ÿ“Œ ๋ชจ๋“  ์š”๊ตฌ์‚ฌํ•ญ์„ ๋งŒ์กฑํ–ˆ๋‹ค. ์ด์ œ ๋๋‚œ๊ฑธ๊นŒ?

๋กœ๊ทธ๋Š” ์˜ค๋ฅ˜ ์—†์ด ์ •์ƒ์ ์œผ๋กœ ๊ธฐ๋ก๋œ๋‹ค. ํ•˜์ง€๋งŒ ๋กœ๊ทธ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๊ฐ€ ์—„์ฒญ ๋”๋Ÿฌ์›Œ์ง€๊ณ , ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฌธ์ œ์ ์ด ์ƒ๊ฒผ๋‹ค.

  • ๋กœ๊ทธ ๋•Œ๋ฌธ์— ์ •์ƒ ๋กœ์ง์ด ๋ฌด์—‡์ธ์ง€ ์ฐพ๊ธฐ ์–ด๋ ค์›Œ์ง„๋‹ค. ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ๋„ˆ๋ฌด ์–ด๋ ต๋‹ค
  • ๋กœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ต๋‹ค. ์•ฑ ๊ธฐ๋Šฅ์„ ๊ฐœ๋ฐœํ•  ๋•Œ ๋กœ๊ทธ ๋ฉ”์„œ๋“œ (begin, exception, beginSync)๋ฅผ ๊ตฌ๋ถ„์ง€์–ด ํ˜ธ์ถœํ•ด์•ผํ•œ๋‹ค.
  • ์‚ฌ์ด๋“œ์ดํŽ™ํŠธ. ๋ชจ๋“  ์„œ๋น„์Šค ๊ฐ์ฒด์— Log Trace ๊ฐ์ฒด์— ๋Œ€ํ•œ ์˜์กด์„ฑ์ด ์ƒ๊ธด๋‹ค.
  • Log Trace ๊ฐ์ฒด๋ฅผ ์ˆ˜์ •ํ•˜๋ฉด, ์„œ๋น„์Šค ๊ฐ์ฒด์— ๋ณ€๊ฒฝ์ด ์ „ํŒŒ๋œ๋‹ค. ์•ฑ ํ๋ฆ„์— ์˜ํ–ฅ์„ ๋ผ์นœ๋‹ค.
    ๋กœ๊ทธ ๊ธฐ๋Šฅ์€ ์ž˜ ๋ณ€ํ•˜์ง€์•Š์ง€๋งŒ, ์•ฑ ๋กœ์ง์€ ์–ธ์ œ๋“ ์ง€ ๋ณ€ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฐ ์ฝ”๋“œ๊ฐ€ ์„œ๋กœ์—๊ฒŒ ์˜ํ–ฅ์„ ๋ผ์ณ์„œ๋Š” ์•ˆ๋œ๋‹ค.

 

๋กœ๊ทธ๋Š” ๋ง ๊ทธ๋Œ€๋กœ ์—†์–ด๋„ ๋˜๋Š” '๋ถ€๊ฐ€๊ธฐ๋Šฅ'์ด๋‹ค. ์•ฑ, ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ๊ฐ™์€ ํ•ต์‹ฌ๊ธฐ๋Šฅ์— ์˜ํ–ฅ์„ ๋ผ์ณ์„œ๋Š” ์•ˆ๋œ๋‹ค.

์•ฑ ๋กœ์ง์— ์˜ํ–ฅ์„ ์ฃผ์ง€์•Š๊ณ , ์–ด๋–ป๊ฒŒ ์„œ๋น„์Šค์— ๊ด€๋ จ๋œ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธธ ์ˆ˜ ์žˆ์„๊นŒ? ๊ทธ๋Ÿฐ๊ฒŒ ๊ฐ€๋Šฅํ•œ๊ฐ€?

 

 


๐Ÿ’ญ ๋ฌธ์ œํ•ด๊ฒฐ - ๋””์ž์ธ ํŒจํ„ด

๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜์ž๋ฉด, ๊ฐ€๋Šฅํ•˜๋„๋ก ์„ค๊ณ„๋ฅผ ์ž˜ ํ•˜๋ฉด ๋œ๋‹ค. ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ์„ค๊ณ„ ๋ฐฉ๋ฒ•์„ ๋ชจ์•„๋‘” ๊ฒƒ์„ ๋””์ž์ธํŒจํ„ด์ด๋ผ ํ•œ๋‹ค.

 

์„ค๊ณ„์—๋Š” ์–ธ์ œ๋‚˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ •๋‹ต์ด ์—†๋‹ค. ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•ด์•ผํ•œ๋‹ค.

์œ„์—์„œ ๋กœ๊ทธ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค ๋•Œ ์ƒ๊ธด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ๋””์ž์ธ ํŒจํ„ด๋“ค์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž.

๐Ÿงจ๋””์ž์ธ ํŒจํ„ด์„ ์ƒ๊ฐ์—†์ด ๋ฌด์ž‘์ • ๋”ฐ๋ผํ•˜์ง€ ๋ง์ž. ์™œ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€์— ๋Œ€ํ•œ '์ด์œ '๊ฐ€ ๋” ์ค‘์š”ํ•˜๋‹ค.

 

๐Ÿ“Œ ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ ํŒจํ„ด

๋ณ€ํ™”ํ•˜์ง€ ์•Š๋Š” ๊ณตํ†ต๋œ ๊ณจ๊ฒฉ(ํ‹€)์„ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ…œํ”Œ๋ฆฟ, ์ฆ‰ [์ถ”์ƒ ํด๋ž˜์Šค]๋กœ ๋ฝ‘์•„๋‚ด๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

์„ธ๋ถ€๋™์ž‘(์•Œ๊ณ ๋ฆฌ์ฆ˜)์€ ์ƒ์†์„ ๋ฐ›์•„ ์ •์˜ํ•œ๋‹ค.

์„œ๋น„์Šค์— ๋กœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ, ์šฐ๋ฆฌ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ๋ฐ˜๋ณต์ ์œผ๋กœ ์ž‘์„ฑํ–ˆ๋‹ค.

public void log() {
    long startTime = System.currentTimeMillis();
    
    /* ...์„œ๋น„์Šค ๊ธฐ๋Šฅ... */
    
    long endTime = System.currentTimeMillis();
    long resultTime = endTime - startTime;
    log.info("๋กœ๊ทธ resultTime={}", resultTime);
}

 

์ด๋ฅผ ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ๋ฅผ ์ ์šฉ์‹œ์ผœ ์ถ”์ƒํ™”์‹œํ‚ค๋ฉด ๋œ๋‹ค. ์ด์ œ ์„œ๋น„์Šค ๋กœ์ง์—๋Š” ๋” ์ด์ƒ ๋กœ๊ทธ๋ฅผ ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•„๋„ ๋œ๋‹ค. 

public abstract class AbstractTemplate {

    public void execute() {
        long startTime = System.currentTimeMillis();
        //๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์‹คํ–‰
        service();
        //๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ข…๋ฃŒ
        long endTime = System.currentTimeMillis();
        long resultTime = endTime - startTime;
        log.info("resultTime={}", resultTime);
    }

    protected abstract void service();
}
public class OrderService extends AbstractTemplate {
    @Override
    protected void service() {
        /*... ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ...*/
    }
}

 

์•„๋ž˜์™€ ๊ฐ™์ด ๋‹คํ˜•์„ฑ์„ ํ™œ์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
๋กœ๊ทธ๋ฅผ ์ˆ˜์ •ํ•ด๋„ ์„œ๋น„์Šค ๋กœ์ง์„ ๋ณ€๊ฒฝํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ๋‘ ๋ถ€๋ถ„์ด ๊น”๋”ํ•˜๊ฒŒ ๋ถ„๋ฆฌ๋˜์—ˆ๋‹ค.

AbstractTemplate template1 = new OrderService();
AbstractTemplate template2 = new ItemService();

template1.service();
template2.service();

 

 

๋‹จ์ 1. ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ์ƒ์†ํ•ด์„œ ์ƒˆ๋กญ๊ฒŒ ์ž‘์„ฑํ•˜๊ธฐ ๋ฒˆ๊ฑฐ๋กญ๋‹ค.

์ถ”์ƒํ™”ํ•œ๊ฑด ์ข‹์•˜์œผ๋‚˜, ๋งค๋ฒˆ ์ค‘๋ณต๋˜๋Š” ํ…œํ”Œ๋ฆฟ ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•ด์•ผํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์ด ์žˆ๋‹ค.

์ด๋Š” ์ต๋ช…ํด๋ž˜์Šค๋‚˜ Java8์˜ ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค(๋žŒ๋‹ค)๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

AbstractTemplate itemRepository = new AbstractTemplate() {
    @Override
    protected void service() {
        log.info("๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง1 ์‹คํ–‰");
    }
};
itemRepository.service();


// ๊ฐ์ฒด๋กœ ์ „๋‹ฌํ•  ๋•Œ์—๋Š” ์ด๋ ‡๊ฒŒ ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค(๋žŒ๋‹ค)๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
// new ItemService( ItemRepository repository );
var ItemService = new ItemSerivce( () -> log.info("๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง1 ์‹คํ–‰") );

์ฐธ๊ณ ๋กœ ์ต๋ช…์ด๋‚˜ ๋žŒ๋‹ค๋กœ ์ƒ์„ฑ๋œ ํด๋ž˜์Šค๋Š” AbstractTemplate$1, AbstractTemplate$2 ๊ฐ™์€ ์ด๋ฆ„์œผ๋กœ JVM์— ์ƒ์„ฑ๋œ๋‹ค.

 

 

์œ„ ํ…œํ”Œ๋ฆฟ์„ ์ œ๋„ค๋ฆญ๊ณผ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์กฐ๊ธˆ ๋‹ค๋“ฌ์œผ๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

@RestController
public class OrderController {

    private final OrderService orderService;
    private final TraceTemplate template;

    public OrderController(OrderService orderService, LogTrace trace) {
        this.orderService = orderService;
        this.template = new TraceTemplate(trace);
    }

    @GetMapping("/request")
    public String request(String itemId) {
        return template.execute("OrderController.request()", () -> {
            orderService.orderItem(itemId);
            return "ok";
        });
    }
}

 

์—ฌ๊ธฐ์— ์‚ฌ์šฉ๋œ ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

public interface TraceService<T> { // ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค
    T service();
}
public interface LogTrace { // ๋กœ๊ทธ ๊ตฌํ˜„์ฒด๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๊ฒŒ, ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์‚ฌ์šฉํ•œ๋‹ค. DIP

    TraceStatus begin(String message);

    void end(TraceStatus status);

    void exception(TraceStatus status, Exception e);
}
public class TraceTemplate {
    private final LogTrace trace;

    public <T> T execute(String message, TraceService<T> callback) {
        TraceStatus status = null;
        try {
            status = trace.begin(message);
            
            /* ์„œ๋น„์Šค ๋กœ์ง ์‹œ์ž‘ */
            T result = callback.service();
            /* ์„œ๋น„์Šค ๋กœ์ง ์ข…๋ฃŒ */
            
            trace.end(status);
            return result;
        } catch (Exception e) {
            trace.exception(status, e);
            throw e;
        }
    }
}

 

 

๋‹จ์ 2. ์ƒ์†๊ด€๊ณ„๋กœ ์ธํ•ด ์˜์กด์„ฑ์ด ์ƒ๊ธด๋‹ค.

ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ์—์„œ๋Š” ์ƒ์†์„ ํ†ตํ•ด ํด๋ž˜์Šค์˜ ๊ณจ๊ฒฉ(Template)์„ ๋งŒ๋“ ๋‹ค.

์ž์‹ํด๋ž˜์Šค(Log)๋Š” ๋ถ€๋ชจํด๋ž˜์Šค์˜ ๊ธฐ๋Šฅ์„ ์ „ํ˜€ ์‚ฌ์šฉํ•˜์ง€์•Š๋Š”๋ฐ, ์„ค๊ณ„๋ฅผ ์œ„ํ•ด์„œ ๋ถ€๋ชจํด๋ž˜์Šค์— ์˜์กด์„ฑ์„ ๋งŒ๋“ ๋‹ค.

์ƒ์† ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์œ„ ์˜ˆ์ œ์ฒ˜๋Ÿผ TraceService<T> ๊ฐ™์€ ์ถ”๊ฐ€์ ์ธ ํด๋ž˜์Šค๋„ ํ•„์š”ํ•˜๋‹ค๋Š” ๋‹จ์ ๋„ ์žˆ๋‹ค.

 

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๊ฐ„๋‹จํ•˜๋‹ค. ์ƒ์†์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ํ•„๋“œ๋กœ ๋ฐ›์•„์„œ ๋งŒ๋“ค๋ฉด ๋œ๋‹ค. ์˜์กด์„ฑ์„ ์ œ๊ฑฐํ•˜์ž

โžก ์ „๋žตํŒจํ„ด (Strategy Pattern)

 

 

๐Ÿ“Œ ์ „๋žตํŒจํ„ด

ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ ํŒจํ„ด์€, ๋ณ€ํ•˜์ง€ ์•Š๋Š” ๊ณจ๊ฒฉ์„ ๋ถ€๋ชจํด๋ž˜์Šค(Template)๋กœ ๋ฝ‘์•„๋‚ด๊ณ , ์ƒ์†์„ ํ†ตํ•ด ์„ธ๋ถ€๋™์ž‘์„ ๊ตฌํ˜„ํ–ˆ๋‹ค.

์ „๋žต ํŒจํ„ด์€ ์ƒ์†์ด ์•„๋‹Œ ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•œ ์œ„์ž„์œผ๋กœ ์„ธ๋ถ€๋™์ž‘์„ ๊ตฌํ˜„ํ•œ๋‹ค.

public interface Strategy {
    void call();
}
@Test
void test() {
    Strategy myStrategy = new StrategyLogic();
    Context context1 = new Context(myStrategy);
    context1.execute();

    // ํ•จ์ˆ˜ํ˜•์ธํ„ฐํŽ˜์ด์Šค(๋žŒ๋‹ค) ํ™œ์šฉ
    Context context2 = new Context(() -> log.info("๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง1 ์‹คํ–‰"));
    context2.execute();
}
@Slf4j
public class Context {

    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void execute() {
        long startTime = System.currentTimeMillis();
        
        //๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์‹คํ–‰
        strategy.call(); // ์œ„์ž„
        //๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ข…๋ฃŒ
        
        long endTime = System.currentTimeMillis();
        long resultTime = endTime - startTime;
        log.info("๋กœ๊ทธ resultTime={}", resultTime);
    }
}

 

ํ…œํ”Œ๋ฆฟ ๋ฉ”์„œ๋“œ์™€ ๋‹ฌ๋ฆฌ, ํ‹€(Strategy)๊ณผ ๊ตฌํ˜„์ฒด ์‚ฌ์ด์— ์•„๋ฌด๋Ÿฐ ์˜์กด์„ฑ์ด ์—†๋‹ค.

๊ทธ๋ž˜์„œ ๋กœ๊ทธ ๊ธฐ๋Šฅ์ด ์•„๋‹ˆ๋”๋ผ๋„, ํ•„์š”ํ•œ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ์„ ์ ์ ˆํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

๋‹จ์ 1. ์ „๋žต(Strategy)์„ ์„ค์ •ํ•œ ํ›„, ๋‚˜์ค‘์— ๋ณ€๊ฒฝํ•˜๊ธฐ ์–ด๋ ต๋‹ค.

๋ฌผ๋ก  ๋‚ด๋ถ€์— setStrategy(...)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์ง€๋งŒ, ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด์—์„œ ์ƒํƒœ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” Setter๋ฅผ ๋‘๋Š”๊ฑด ๋งค์šฐ ์œ„ํ—˜ํ•˜๋‹ค. ์ฐจ๋ผ๋ฆฌ ๊ฐ์ฒด๋ฅผ ์ƒˆ๋กญ๊ฒŒ ๋งŒ๋“œ๋Š”๊ฒŒ ๋‚˜์„ ์ˆ˜๋„ ์žˆ๋‹ค. ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋‚˜ ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฝ๋‹ค. 

 

ํ•œ ๊ฐ์ฒด(Service)์—์„œ ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์ „๋žต(์•Œ๊ณ ๋ฆฌ์ฆ˜)์„ ์ ์šฉ์‹œํ‚ค๊ณ  ์‹ถ๋‹ค๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฉ”์„œ๋“œ๋กœ ๋ฐ›์œผ๋ฉด ๋œ๋‹ค.
๋‹ค๋งŒ ์‹คํ–‰ํ•  ๋•Œ ๋งˆ๋‹ค ์ „๋žต์„ ์ง€์ •ํ•ด์ฃผ๋Š”๊ฒŒ ๋ฒˆ๊ฑฐ๋กญ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค. ์ƒํ™ฉ์— ๋”ฐ๋ผ ์ ์ ˆํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜์ž.

@Test
void test() {
    ContextV2 context = new ContextV2(); // ์ƒ์„ฑ ์‹œ์ ์—๋Š” ๋น„์›Œ๋‘”๋‹ค.

    // ์‚ฌ์šฉํ•˜๋Š” ์‹œ์ ์— ์ ์ ˆํ•œ Strategy ๊ตฌํ˜„์ฒด๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.
    context.execute(() -> log.info("๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง1 ์‹คํ–‰"));
    context.execute(() -> log.info("๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง2 ์‹คํ–‰"));
    
    // ๊ณ ์œ ํ•œ ์ƒํƒœ ๊ฐ’(ํ•„๋“œ)๊ฐ€ ์—†๋‹ค๋ฉด, ๋งค๋ฒˆ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ  ์žฌ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
    context.execute(myStrategy());
}

 

 

๐Ÿ“Œ ํ…œํ”Œ๋ฆฟ ์ฝœ๋ฐฑํ•จ์ˆ˜(callback function)

์ฐธ๊ณ ๋กœ ํ•จ์ˆ˜๋ฅผ ์ธ์ž(ํŒŒ๋ผ๋ฉ”ํƒ€)๋กœ ๋„˜๊ฒจ์ฃผ๋Š”๊ฑธ callback ํ•จ์ˆ˜(call-after function)๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

๋‹น์žฅ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ , ํ•จ์ˆ˜๋ฅผ ์ธ์ž๋กœ ๋ฐ›์•„ ํ•„์š”ํ•œ ์‹œ์ ์— ์‹คํ–‰ํ•œ๋‹ค๋Š” ์˜๋ฏธ.

 

์ฝœ๋ฐฑ ํ•จ์ˆ˜๋Š” ๋””์ž์ธ ํŒจํ„ด์€ ์•„๋‹ˆ๊ณ , ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋ฒ•์ด๋‹ค.

๋ฐฉ๊ธˆ ์–ธ๊ธ‰ํ•œ [๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•œ ์ „๋žตํŒจํ„ด]๊ณผ ๋น„์Šทํ•˜๋‹ค. ๋‹ค๋ฅธ ์ ์€ ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋Š”๊ฑฐ ์ •๋„

์ž๋ฐ”๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์ผ๊ธ‰์‹œ๋ฏผ (์ธ์ž๋กœ ์ „๋‹ฌ, ๋ฆฌํ„ด ๊ฐ€๋Šฅ)ํ•œ ๋Œ€์ƒ์ด ์•„๋‹ˆ๊ธฐ์— ์ฝœ๋ฐฑํ•จ์ˆ˜ ๊ตฌํ˜„์ด ๋ถˆ๊ฐ€๋Šฅํ–ˆ๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ์œ„ํ•ด JDK8๋ถ€ํ„ฐ๋Š” ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค์™€ ํ•จ์ˆ˜๋ฅผ ์‰ฝ๊ฒŒ ์ „๋‹ฌํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” Function-Class API๋ฅผ ์ œ๊ณตํ•ด์ค€๋‹ค.

์ฐธ๊ณ ๋กœ ์Šคํ”„๋ง์˜ JdbcTemplate, RestTemplate, TransactionTemplate, RedisTemplate ๋“ฑ์€ ์ฝœ๋ฐฑ ํŒจํ„ด์„ ์ด์šฉํ•œ๋‹ค.

 

์•„๋ž˜ ์˜ˆ์ œ์—์„œ๋Š” Runnable์ด๋ผ๋Š” ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ–ˆ์œผ๋‚˜, ํ•„์š”ํ•˜๋‹ค๋ฉด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด๋„ ๋œ๋‹ค.

์˜ˆ) public void execute(MyCallback callback)

public class TimeLogTemplate {

    public void execute(Runnable callback) {
        long startTime = System.currentTimeMillis();

        //๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์‹คํ–‰
        callback.run(); //์œ„์ž„
        //๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ข…๋ฃŒ

        long endTime = System.currentTimeMillis();
        long resultTime = endTime - startTime;
        log.info("resultTime={}", resultTime);
    }
}
@Test
void callback() {
    TimeLogTemplate template = new TimeLogTemplate();
    template.execute(() -> log.info("๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง1 ์‹คํ–‰"));
    template.execute(() -> log.info("๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง2 ์‹คํ–‰"));
}

 

 

๐Ÿ“Œ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†๋Š” ๋ฌธ์ œ์ 

์•„๋ฌด๋ฆฌ ์ถ”์ƒํ™”๋ฅผ ํ•˜๊ณ  ์˜์กด์„ฑ์„ ๋Š์–ด๋‚ธ๋‹ค๊ณ  ํ•œ๋“ค, Template ์ž์ฒด๋ฅผ ์—†์•จ ์ˆ˜๋Š” ์—†๋‹ค.

์‚ฌ์šฉ๋ฒ•์€ ๊ฐ„๋‹จํ•ด์กŒ์ง€๋งŒ ๊ฒฐ๊ตญ ๊ฐ ์„œ๋น„์Šค์—์„œ Template ์ธํ„ฐํŽ˜์ด์Šค๋Š” ํ˜ธ์ถœํ•ด์ค˜์•ผํ•œ๋‹ค.

 

 

๐Ÿค” ์Šคํ”„๋ง์€ ๋˜๋˜๋ฐ์š”?

๋งž๋‹ค. ์Šคํ”„๋ง์€ ๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ๋ฅผ ํ™œ์šฉํ•œ AOP๋ฅผ ํ†ตํ•ด ํ”„๋ก์‹œ ํŒจํ„ด๊ณผ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด์„ ๊ตฌํ˜„ํ•˜์—ฌ ํ•ด๊ฒฐํ–ˆ๋‹ค.

์‚ฌ์‹ค ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด๊ณผ ํ”„๋ก์‹œ ํŒจํ„ด์€ ๊ฑฐ์˜ ๋น„์Šทํ•˜๋‹ค. ๋””์ž์ธํŒจํ„ด์„ '๋ชจ์–‘'์ด ์•„๋‹Œ '์˜๋„'์— ๋”ฐ๋ผ ๊ตฌ๋ถ„ํ•˜์ž. 

  • ํ”„๋ก์‹œ ํŒจํ„ด: ์ ‘๊ทผ์ œ์–ด๊ฐ€ ๋ชฉ์ 
  • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด: ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€๊ฐ€ ๋ชฉ์ 

2021.08.17 - [๐ŸŒฑBackend/Spring Core] - ์ž๋ฐ” AOP์˜ ๋ชจ๋“  ๊ฒƒ(Spring AOP & AspectJ)

 

์ž๋ฐ” AOP์˜ ๋ชจ๋“  ๊ฒƒ(Spring AOP & AspectJ)

์ด ๊ธ€์€ ์‚ฌ์ „์ง€์‹์ด ์—†๋‹ค๋ฉด ์ฝ๊ธฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ๋‹ค. ๋ฐ”์ดํŠธ์ฝ”๋“œ์™€ ๋ฆฌํ”Œ๋ ‰์…˜์„ ๋ชจ๋ฅธ๋‹ค๋ฉด ์•„๋ž˜์˜ ๊ธ€์„ ๊ผญ ์ฝ์–ด๋ณด๋„๋กํ•˜์ž. 2021.08.17 - [๊ธฐ๋ณธ ์ง€์‹/Java ๊ธฐ๋ณธ์ง€์‹] - ๋ฐ”์ดํŠธ์ฝ”๋“œ ์กฐ์ž‘(๋ฆฌํ”Œ๋ ‰์…˜, ๋‹ค์ด๋‚˜๋ฏน

jiwondev.tistory.com

 

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

JiwonDev

JiwonDev

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