[Spring] ์คํ๋ง ์ฝ์ด 2 - ๋ก๊ทธ ์ถ์ ๊ธฐ
by rlaehddnd0422๋ก๊ทธ ์ถ์ ๊ธฐ๋ฅผ ๋ง๋ค์ด ๋ณด๋ฉด์ ์คํ๋ง ํต์ฌ์๋ฆฌ์ ๋ํด ์กฐ๊ธ ๋ ๊น๊ฒ ๋ฐฐ์๋ด ์๋ค.
์๊ตฌ์ฌํญ
- public ๋ฉ์๋์ ํธ์ถ์ ๋ํ ์๋ต ์ ๋ณด ๋ก๊ทธ ์ถ๋ ฅ
- ๋น์ฆ๋์ค ๋ก์ง์ ๋์์ ์ํฅ์ ์ฃผ์ง ์๊ฒ.
- ๋ฉ์๋ ํธ์ถ์ ๊ฑธ๋ฆฐ ์๊ฐ๋ ํจ๊ป ์ถ๋ ฅ
- ์ ์ ํ๋ฆ, ์์ธ ํ๋ฆ ๊ตฌ๋ถ - ์์ธ ๋ฐ์ ์ ์์ธ ์ ๋ณด ์ถ๋ ฅ
- ๋ฉ์๋ ํธ์ถ์ ๊น์ด ํํ : level์ด ์ฆ๊ฐ ํ ์๋ก ๊น์ด๋ฅผ ์ง๊ด์ ์ผ๋ก ํํ
- HTTP ์์ฒญ ๊ตฌ๋ถ : HTTP ์์ฒญ ๋จ์๋ก ํน์ ID๋ฅผ ๋จ๊ฒจ์ Client๋ฅผ ๊ตฌ๋ถ
- ํ๋์ HTTP ์์ฒญ์ ์์๊ณผ ๋์ ํ๋์ ID๋ก ๋ฌถ๋๋ก
์์
๋ก๊ทธ๋ฅผ ์ ์ฉํ ๋จ์ ๋ก์ง ์์ฑ
์ถํ ๋ก๊ทธ์์ฑ๊ธฐ๋ฅผ ๋ง๋ค๊ณ ์ ์ฉํด๋ณผ ๋ก์ง์ ๊ฐ๋จํ๊ฒ ๋ง๋ค์ด์ฃผ์์ต๋๋ค. ์ฝ๋์ ๋ํ ์ค๋ช ์ ์๋ตํ๋๋ก ํ๊ฒ ์ต๋๋ค.
๋ก๊ทธ์ ๋ํ ์ ๋ณด๋ฅผ ์ ์ฅํ ๊ฐ์ฒด ์์ฑ - TraceId - ( id, level)
@Getter
public class TraceId {
private String id;
private int level;
์ฌ๊ธฐ์ ID๋ Http ์์ฒญ์ ๊ตฌ๋ถํ ๊ณ ์ ํ ID์ ๋๋ค. ์์ฑ์์์ createId ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ๊ณ ์ ํ 8๊ธ์์ UUID๋ฅผ ๋ถ์ฌํด์ค๋๋ค.
Level์ ๋ฉ์๋ ํธ์ถ์ ๊น์ด๋ฅผ ํํํ๊ธฐ ์ํด ์ฌ์ฉ๋์์ต๋๋ค. ์ถํ ๋ก๊ทธ์ ์ํ๋ฅผ ์ ์ฅํ ๊ฐ์ฒด TraceStatus์์ level์ ์ฌ์ฉ ๊น์ด๋ฅผ ์ง๊ด์ ํํํ ๊ฒ์ ๋๋ค.
๋ก๊ทธ์ ๋ํ ์ํ๋ฅผ ์ ์ฅํ ๊ฐ์ฒด ์์ฑ - TraceStatus ( traceId, startTimeMs, message )
TraceStatus๋ ๋ก๊ทธ์ ๋ํ ์ ๋ณด์ธ TraceId, ๋ก๊ทธ์ ์์์๊ฐ์ธ startTimeMills, ๋ก๊ทธ ๋ฉ์์ง์ธ message๋ฅผ ํ๋๋ฅผ ๊ฐ์ง๋๋ค.
์ ์ด์ , ๋ก๊ทธ ์ถ์ ๊ธฐ๋ฅผ ๋ฎ์ ๋ฒ์ ๋ถํฐ ์์ฐจ์ ์ผ๋ก ๋ง๋ค์ด๋ณด๋ฉด์ ์ฝ๋๋ฅผ ๊ฐ์ ํด๋ด ์๋ค.
HelloTraceV1
์ฒซ ๋ฒ์งธ ๋ฒ์ ์ ๋๋ค.
์ฒซ ๋ฒ์งธ ๋ฒ์ ์์์ ๋ก๊ทธ ์ถ์ ๊ธฐ๋ ์๋์ ๊ฐ์ ๋ก์ง์ผ๋ก ์งํ๋ฉ๋๋ค.
๋ก๊ทธ์ ์์(begin) -> 1) ์์ธ ๋ฐ์ X (end) -> ๋ก๊ทธ ์ถ๋ ฅ (complete)
->2) ์์ธ ๋ฐ์ (exception) -> ๋ก๊ทธ ์ถ๋ ฅ (complete)
๊ณตํต์ ์ผ๋ก ๋ก๊ทธ์ ์์์์ TraceStatus๋ฅผ ์์ฑํด์ ๋ฆฌํดํ๊ณ ,
1) ๋ก์ง์์ ์์ธ๊ฐ ๋ฐ์ํ์ง ์๊ณ ๋ฌด์ฌํ ํต๊ณผํ๋ฉด ํต๊ณผ ๋ฉ์์ง์ PREFIX์ ๋ฉ์๋ Depth Level์ ๋ก๊ทธ๋ก ์ถ๋ ฅํฉ๋๋ค.
2) ๋ก์ง์์ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด ๋ก๊ทธ๋ฅผ ์ถ๋ ฅํ ๋์ ์์ธ ๋ฉ์์ง์ PREFIX์ ๋ฉ์๋ Depth Level, ์์ธ ๋ฉ์์ง๋ฅผ ๋ก๊ทธ๋ก ์ถ๋ ฅํฉ๋๋ค.
@Slf4j
@Component
public class HelloTraceV1 {
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 startTimeMills = System.currentTimeMillis();
log.info("[{}] {}{}", traceId.getId(), addSpace(START_PREFIX, traceId.getLevel()), message);
return new TraceStatus(traceId, startTimeMills, message);
}
// 1) ์์ธ ๋ฐ์ X
public void end(TraceStatus status) {
complete(status, null);
}
// 2) ์์ธ ๋ฐ์ O
public void exception(TraceStatus status, Exception e) {
complete(status, e);
}
- end() : ์์ธ๊ฐ ๋ฐ์ํ์ง ์๊ณ ๋ฌด์ฌํ ํต๊ณผํ ์ฝ๋์์ ์คํํ๋ ๋ฉ์๋, complete๋ฉ์๋์ parameter๋ก status์ ํจ๊ป ์์ธ๊ฐ ์์ผ๋ฏ๋ก Exception์๋ null์ ๋๊ฒจ์ค๋๋ค.
- exception() : ์์ธ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ ์คํํ๋ ๋ฉ์๋, complete ๋ฉ์๋์ paramter๋ก status์ ์์ธ e๋ฅผ ๋๊ฒจ์ค๋๋ค.
private void complete(TraceStatus status, Exception e) {
Long stopTimeMs = System.currentTimeMillis();
long resultTimeMs = stopTimeMs - status.getStartTimeMs();
TraceId traceId = status.getTraceId();
// 1) ์์ธ ๋ฐ์ X
if (e == null) {
log.info("[{}] {}{} time={}ms", traceId.getId(),
addSpace(COMPLETE_PREFIX, traceId.getLevel()), status.getMessage(),
resultTimeMs);
} else { // 2) ์์ธ ๋ฐ์
log.info("[{}] {}{} time={}ms ex={}", traceId.getId(),
addSpace(EX_PREFIX, traceId.getLevel()), status.getMessage(), resultTimeMs,
e.toString());
}
}
private static String addSpace(String prefix, int level) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < level; i++) {
sb.append( (i == level - 1) ? "|" + prefix : "| ");
}
return sb.toString();
}
- complete() : ๋ก๊ทธ๋ฅผ ์ค์ง์ ์ผ๋ก ์ถ๋ ฅํ๋ ๋ฉ์๋
- ์์ธ๊ฐ ์๋ ๊ฒฝ์ฐ addSpace ๋ฉ์๋๋ฅผ ํตํด ๋ฉ์๋ ๊น์ด์ COMPELETE_PREFIX๋ฅผ ๋๊ฒจ์ฃผ์ด ๋ฉ์๋ ๊น์ด์ ๋ก์ง์ด ์์ธ์์ด ์งํ ๋์์์ ๋ก๊ทธ๋ก ์ง๊ด์ ์ผ๋ก ํํํฉ๋๋ค.
- ์์ธ๊ฐ ์๋ ๊ฒฝ์ฐ addSpace ๋ฉ์๋๋ฅผ ํตํด ๋ฉ์๋ ๊น์ด์ EX_PREFIX๋ฅผ ๋๊ฒจ์ฃผ์ด ๋ฉ์๋ ๊น์ด์ ์์ธ๊ฐ ๋ฐ์ํ์์ ๋ก๊ทธ๋ก ์ง๊ด์ ์ผ๋ก ํํํฉ๋๋ค.
ํ ์คํธ ์ฝ๋๋ก ํ ์คํธ ํด๋ณด๋ฉด ์ ์๋ํ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
๊ฐ๋ฐํ ๋ก๊ทธ ์ถ์ ๊ธฐ๋ฅผ ์์ ์์ฑํ ๋จ์๋ก์ง์ ์ ์ฉํด๋ด ์๋ค.
๋ก๊ทธ ์ถ์ ๊ธฐ ๋ก์ง ์ ์ฉ
OrderControllerV1.java
๋จ์ ์๊ตฌ์ฌํญ
public ๋ฉ์๋์ ํธ์ถ์ ๋ํ ์๋ต ์ ๋ณด ๋ก๊ทธ ์ถ๋ ฅ๋น์ฆ๋์ค ๋ก์ง์ ๋์์ ์ํฅ์ ์ฃผ์ง ์๊ฒ.๋ฉ์๋ ํธ์ถ์ ๊ฑธ๋ฆฐ ์๊ฐ๋ ํจ๊ป ์ถ๋ ฅ์ ์ ํ๋ฆ, ์์ธ ํ๋ฆ ๊ตฌ๋ถ - ์์ธ ๋ฐ์ ์ ์์ธ ์ ๋ณด ์ถ๋ ฅ- ๋ฉ์๋ ํธ์ถ์ ๊น์ด ํํ : level์ด ์ฆ๊ฐ ํ ์๋ก ๊น์ด๋ฅผ ์ง๊ด์ ์ผ๋ก ํํ
- HTTP ์์ฒญ ๊ตฌ๋ถ : HTTP ์์ฒญ ๋จ์๋ก ํน์ ID๋ฅผ ๋จ๊ฒจ์ Client๋ฅผ ๊ตฌ๋ถ
- ํ๋์ HTTP ์์ฒญ์ ์์๊ณผ ๋์ ํ๋์ ID๋ก ๋ฌถ๋๋ก
๋จ์ ์๊ตฌ์ฌํญ(= Level ํํ, ์์ฒญ ๊ตฌ๋ถ)์ ํด๊ฒฐํ๊ธฐ ์ํด์๋ ์ด์ ๋ก๊ทธ์ ๋ํ ๋ฌธ๋งฅ ์ ๋ณด๋ฅผ ๋๊ฒจ์ฃผ์ด์ผ ํฉ๋๋ค.
์ฝ๊ฒ ๋งํด์ Controller์์ ํ์ฌ ๋ก๊ทธ ์ํ ์ ๋ณด์ธ ID์ LEVEL์ ๋ค์ Service์ ๋๊ฒจ์ฃผ๊ณ Service๋ ์ด ์ ๋ณด๋ฅผ Repository์๊ฒ ๋๊ฒจ์ฃผ๋ ๋ฐฉ์์ผ๋ก.
๋ก๊ทธ ์ถ์ ๊ธฐ V2 - ํ์ฌ ๋ก๊ทธ ์ ๋ณด ํ๋ผ๋ฏธํฐ ๋๊ธฐํ
TraceV2
public TraceStatus beginSync(TraceId beforeTraceId, String message) {
TraceId traceId = beforeTraceId.createNextId();
Long startTimeMills = System.currentTimeMillis();
log.info("[{}] {}{}", traceId.getId(), addSpace(START_PREFIX, traceId.getLevel()), message);
return new TraceStatus(traceId, startTimeMills, message);
}
public TraceId createNextId() {
return new TraceId(id, level + 1);
}
- beginSync์์๋ ์ด์ TraceId๋ฅผ ์๋ก ๋ง๋ค์ง ์๊ณ (์ด์ TraceId).createNextId() ํ์ฌ ์๋ก์ด TraceId๋ฅผ ์์ฑํฉ๋๋ค.
- ์๋ก์ด TraceId = ( ํ์ฌ Trace Id, level + 1 )
TestCode
class HelloTraceV2Test {
@Test
public void begin_end() throws Exception {
HelloTraceV2 trace = new HelloTraceV2();
TraceStatus status = trace.begin("hello1");
TraceStatus status1 = trace.beginSync(status.getTraceId(), "hello2");
trace.end(status1);
trace.end(status);
}
@Test
public void begin_exception() throws Exception {
HelloTraceV2 trace = new HelloTraceV2();
TraceStatus status = trace.begin("hello");
TraceStatus status1 = trace.beginSync(status.getTraceId(), "hello2");
trace.exception(status1, new IllegalStateException());
trace.exception(status, new IllegalStateException());
}
}
V2 ๋ก์ง์ ์ ์ฉ
Controller
@GetMapping("/v2/request")
public String request(@RequestParam String itemId) {
TraceStatus status = trace.begin("OrderController.request()");
try {
orderService.orderItem(status.getTraceId(), itemId);
trace.end(status);
return "ok";
} catch (Exception e) {
trace.exception(status, e);
throw e;
}
}
- Service์ orderItem์ ํ์ฌ TraceId๋ฅผ ๋๊ฒจ์ค๋๋ค.
Service
public void orderItem(TraceId traceId, String itemId) {
TraceStatus status = trace.beginSync(traceId, "OrderService.orderItem()");
try {
orderRepository.save(status.getTraceId(), itemId);
trace.end(status);
} catch (Exception e) {
trace.exception(status, e);
throw e;
}
}
- Controller์์ ๋๊ฒจ๋ฐ์ traceId๋ฅผ begin์ด ์๋ beginSync๋ก ์คํํจ์ผ๋ก์จ ์ด์ ์ TraceId๋ฅผ ๋๊ธฐํ ํ์ฌ ์ฌ์ฉํฉ๋๋ค.
- ๋ง์ฐฌ๊ฐ์ง๋ก Repository์๋ ๋๊ธฐํ๋ traceId๋ฅผ ๋๊ฒจ์ค๋๋ค.
Repository
public void save(TraceId traceId, String itemId) {
TraceStatus status = trace.beginSync(traceId, "OrderRepository.save()");
try {
if (itemId.equals("ex")) {
throw new IllegalStateException("์์ธ ๋ฐ์");
}
sleep(1000);
trace.end(status);
} catch (IllegalStateException e) {
trace.exception(status, e);
throw e;
}
}
- Repository๊ณ์ธต์์๋ Service์์ ๋๊ฒจ๋ฐ์ TraceId๋ฅผ beginSync๋ก ์ด ์ ๊ณ์ธต์ TraceId๋ฅผ ๋๊ธฐํ ํ์ฌ ์ฌ์ฉํฉ๋๋ค.
<์ ๋ฆฌ>
- V1์์ ์ฒ๋ฆฌํ์ง ๋ชปํ ์๊ตฌ์ฌํญ๋ค์ ํ๋ผ๋ฏธํฐ๋ก ๋๊ธฐํ ํ์ฌ ์ฒ๋ฆฌํจ์ผ๋ก์จ ํด๊ฒฐํด๋ณด์๋๋ฐ, ๊ฐ๋
์ฑ์ด๋ ์ ์ง๋ณด์๋ฉด์์ ์ข์ ์ฝ๋๋ผ๊ณ ๋ณด๊ธด ์ด๋ ค์ ์ต๋๋ค.
- ๋ก๊ทธ๋ฅผ ์ฒ์ ํธ์ถํ ๋ - begin(), ์ฒ์์ด ์๋ ๋ - beginSync() ๋ฅผ ํธ์ถํด์ผ ํจ.
- try/catch๋ฌธ ๋๋ฌธ์ ์ฝ๋ ๊ฐ๋ ์ฑ ๋จ์ด์ง.
- TraceId๋ฅผ ๋๊ธฐํํด์ฃผ๊ธฐ ์ํด ๋ฉ์๋์ ํ๋ผ๋ฏธํฐ๋ฅผ ์์ ํด์ผ ํจ. (์ ์ง๋ณด์์ ์ด๋ ค์)
- ๋ง์ฝ OrderController๊ฐ ์๋ ๋ค๋ฅธ ์ปจํธ๋กค๋ฌ์์ OrderService๋ฅผ ํธ์ถํ๋ ์ํฉ์ด๋ผ๋ฉด, ํ๋ผ๋ฏธํฐ๋ก ๋๊ธธ TraceId๋ฅผ ๋ ๋ง๋ค์ด์ฃผ์ด์ผํจ.
- ์ด์ด์ ๋ค์ ํฌ์คํ ์์ ํ๋ผ๋ฏธํฐ ๋๊ธฐํ๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ์ฌ๋ฌ ๋์์ธ ํจํด๋ค์ ๋์ ํด๋ด์ผ๋ก์จ ์ฝ๋๋ฅผ ์กฐ๊ธ์ฉ ๊ณ ์ณ๋๊ฐ ๋ณด๊ฒ ์ต๋๋ค.
<์ฐธ๊ณ ์๋ฃ>
'๐ Backend' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋ธ๋ก๊ทธ์ ์ ๋ณด
Study Repository
rlaehddnd0422