From 3a96ba0199bdc353e908bb9a3075f699d3edea47 Mon Sep 17 00:00:00 2001 From: dmori Date: Sun, 22 Feb 2026 21:17:04 +0900 Subject: [PATCH 01/19] =?UTF-8?q?feat:=20MDC=20=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/filter/MdcLoggingFilter.java | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/main/java/com/techfork/global/filter/MdcLoggingFilter.java diff --git a/src/main/java/com/techfork/global/filter/MdcLoggingFilter.java b/src/main/java/com/techfork/global/filter/MdcLoggingFilter.java new file mode 100644 index 0000000..5265f1c --- /dev/null +++ b/src/main/java/com/techfork/global/filter/MdcLoggingFilter.java @@ -0,0 +1,68 @@ +package com.techfork.global.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.MDC; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.UUID; + +/** + * HTTP 요청마다 MDC에 트레이싱 컨텍스트를 설정하는 필터. + * 요청 종료 시 MDC를 정리하여 스레드 풀 재사용 시 누수를 방지한다. + *

+ * MDC 필드: + * - requestId: 요청 단위 고유 ID (UUID) + * - userId: 인증된 사용자 ID (비인증 요청은 "anonymous") + * - method: HTTP 메서드 + * - uri: 요청 URI + * - clientIp: 클라이언트 IP (X-Forwarded-For 우선) + */ +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class MdcLoggingFilter extends OncePerRequestFilter { + + private static final String REQUEST_ID = "requestId"; + private static final String USER_ID = "userId"; + private static final String METHOD = "method"; + private static final String URI = "uri"; + private static final String CLIENT_IP = "clientIp"; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + try { + MDC.put(REQUEST_ID, UUID.randomUUID().toString().replace("-", "").substring(0, 12)); + MDC.put(METHOD, request.getMethod()); + MDC.put(URI, request.getRequestURI()); + MDC.put(CLIENT_IP, resolveClientIp(request)); + MDC.put(USER_ID, "anonymous"); + + filterChain.doFilter(request, response); + } finally { + MDC.clear(); + } + } + + private String resolveClientIp(HttpServletRequest request) { + String ip = request.getHeader("X-Forwarded-For"); + if (isValid(ip)) { + return ip.split(",")[0].trim(); + } + ip = request.getHeader("X-Real-IP"); + if (isValid(ip)) { + return ip; + } + return request.getRemoteAddr(); + } + + private boolean isValid(String ip) { + return ip != null && !ip.isBlank() && !"unknown".equalsIgnoreCase(ip); + } +} From be6de7a1bb19404eaf6309ae2f9d3430d90f8441 Mon Sep 17 00:00:00 2001 From: dmori Date: Sun, 22 Feb 2026 21:17:35 +0900 Subject: [PATCH 02/19] =?UTF-8?q?feat:=20=EC=9D=B8=EC=A6=9D=EB=90=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=EB=8A=94=20JwtAuthenticationFilter?= =?UTF-8?q?=EC=97=90=EC=84=9C=20userId=20=EA=B0=B1=EC=8B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/filter/JwtAuthenticationFilter.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/techfork/global/security/filter/JwtAuthenticationFilter.java b/src/main/java/com/techfork/global/security/filter/JwtAuthenticationFilter.java index cd920a5..0ea40db 100644 --- a/src/main/java/com/techfork/global/security/filter/JwtAuthenticationFilter.java +++ b/src/main/java/com/techfork/global/security/filter/JwtAuthenticationFilter.java @@ -14,6 +14,7 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.slf4j.MDC; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; @@ -66,6 +67,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse securityContext.setAuthentication(authentication); SecurityContextHolder.setContext(securityContext); + MDC.put("userId", String.valueOf(userId)); log.debug("Set authentication for user: {}", userId); } } catch (Exception e) { From 38d529c73afd3477442e3315166118241ae81912 Mon Sep 17 00:00:00 2001 From: dmori Date: Sun, 22 Feb 2026 21:17:57 +0900 Subject: [PATCH 03/19] =?UTF-8?q?chore:=20logback-spring.xml=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-dev.yml | 7 +- src/main/resources/application-local.yml | 7 -- src/main/resources/logback-spring.xml | 120 +++++++++++++++++++++++ 3 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 src/main/resources/logback-spring.xml diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 9329f70..9fffb2d 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -38,11 +38,8 @@ spring: uris: http://elasticsearch:9200 logging: - level: - com.techfork: INFO - org.springframework.batch: INFO - org.hibernate.SQL: INFO - org.hibernate.type.descriptor.sql.BasicBinder: WARN + file: + path: /app/logs apple: private-key-path: /app/keys/AuthKey_${APPLE_KEY_ID}.p8 \ No newline at end of file diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index d127e59..c7cf4c6 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -37,13 +37,6 @@ spring: elasticsearch: uris: http://elasticsearch:9200 -logging: - level: - com.techfork: DEBUG - org.springframework.batch: INFO - org.hibernate.SQL: DEBUG - org.hibernate.type.descriptor.sql.BasicBinder: TRACE - # Actuator 설정 - Resilience4j 모니터링 management: endpoints: diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..9f21060 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + %d{HH:mm:ss.SSS} %highlight(%-5level) [%cyan(%thread)] [rid=%X{requestId:-?} uid=%X{userId:-?}] %boldWhite(%logger{36}) - %msg%n + + UTF-8 + + + + + + ${LOG_DIR}/${APP_NAME}.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] [rid=%X{requestId:-?} uid=%X{userId:-?} ip=%X{clientIp:-?} %X{method:-?} %X{uri:-?}] %logger{36} - %msg%n + UTF-8 + + + + ${LOG_DIR}/archive/${APP_NAME}.%d{yyyy-MM-dd}.%i.log.gz + 100MB + + 30 + 3GB + + + + + + ${LOG_DIR}/${APP_NAME}-error.log + + ERROR + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] [rid=%X{requestId:-?} uid=%X{userId:-?} ip=%X{clientIp:-?} %X{method:-?} %X{uri:-?}] %logger{36} - %msg%n + UTF-8 + + + ${LOG_DIR}/archive/${APP_NAME}-error.%d{yyyy-MM-dd}.%i.log.gz + 100MB + 30 + 1GB + + + + + + + 0 + 1024 + false + + + + + 0 + 256 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a7a53db4b3b6f4378a96a8772ccbb7e5273357b0 Mon Sep 17 00:00:00 2001 From: dmori Date: Sun, 22 Feb 2026 21:19:50 +0900 Subject: [PATCH 04/19] =?UTF-8?q?chore:=20shutdown=20Hook=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/logback-spring.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index 9f21060..a5d85e9 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -1,6 +1,10 @@ + + 2000 + + From 863893be68286598d00ea1e689d92e11348ee123 Mon Sep 17 00:00:00 2001 From: dmori Date: Sun, 22 Feb 2026 21:29:08 +0900 Subject: [PATCH 05/19] =?UTF-8?q?chore:=20root=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=83=81=EC=86=8D=ED=95=98=EC=97=AC=20=EC=A4=91=EB=B3=B5?= =?UTF-8?q?=EC=9D=84=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/logback-spring.xml | 58 +++++++++------------------ 1 file changed, 18 insertions(+), 40 deletions(-) diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index a5d85e9..f20b0a9 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -12,20 +12,16 @@ + - - %d{HH:mm:ss.SSS} %highlight(%-5level) [%cyan(%thread)] [rid=%X{requestId:-?} uid=%X{userId:-?}] %boldWhite(%logger{36}) - %msg%n - + %d{HH:mm:ss.SSS} %highlight(%-5level) [%cyan(%thread)] [rid=%X{requestId:-?} uid=%X{userId:-?}] %boldWhite(%logger{36}) - %msg%n UTF-8 - ${LOG_DIR}/${APP_NAME}.log @@ -33,18 +29,13 @@ UTF-8 - ${LOG_DIR}/archive/${APP_NAME}.%d{yyyy-MM-dd}.%i.log.gz 100MB - 30 3GB - ${LOG_DIR}/${APP_NAME}-error.log @@ -62,11 +53,7 @@ - - 0 1024 false @@ -81,23 +68,26 @@ + + + + + + + + - + + + - - - - - - - - - @@ -107,18 +97,6 @@ - - - - - - - - - - - - - + \ No newline at end of file From 795e8c4639f583da64d378f68551816f43e4c8a0 Mon Sep 17 00:00:00 2001 From: dmori Date: Sun, 22 Feb 2026 21:31:13 +0900 Subject: [PATCH 06/19] =?UTF-8?q?chore:=20=ED=81=AC=EB=A1=A4=EB=A7=81=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EB=8A=94=20=EB=B3=84=EB=8F=84=EB=A1=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/logback-spring.xml | 29 ++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index f20b0a9..a70894e 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -53,6 +53,21 @@ + + + ${LOG_DIR}/${APP_NAME}-crawler.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] [rid=%X{requestId:-?}] %logger{36} - %msg%n + UTF-8 + + + ${LOG_DIR}/archive/${APP_NAME}-crawler.%d{yyyy-MM-dd}.%i.log.gz + 100MB + 7 + 1GB + + + 0 1024 @@ -66,12 +81,24 @@ true + + + 0 + 512 + false + + - + + + + + + From a85f8f3a3d6c5f20c0d4c28e8f869db74f183fd2 Mon Sep 17 00:00:00 2001 From: dmori Date: Sun, 22 Feb 2026 21:31:58 +0900 Subject: [PATCH 07/19] =?UTF-8?q?chore:=20=EC=97=90=EB=9F=AC=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EB=8A=94=2060=EC=9D=BC=EB=A1=9C=20=EB=B3=B4=EA=B4=80?= =?UTF-8?q?=EA=B8=B0=EA=B0=84=20=EC=A6=9D=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/logback-spring.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index a70894e..19ba981 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -48,7 +48,7 @@ ${LOG_DIR}/archive/${APP_NAME}-error.%d{yyyy-MM-dd}.%i.log.gz 100MB - 30 + 60 1GB From 754b5ed4acc28e6e441ff2744f66fe2cd88a3b6d Mon Sep 17 00:00:00 2001 From: dmori Date: Sun, 22 Feb 2026 21:49:30 +0900 Subject: [PATCH 08/19] =?UTF-8?q?chore:=20Actuator=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EB=8A=94=20=EC=95=88=EC=B0=8D=ED=9E=88=EB=8F=84=EB=A1=9D=20WAR?= =?UTF-8?q?N=20=EB=A0=88=EB=B2=A8=EB=A1=9C=20=EB=82=AE=EC=B6=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/logback-spring.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index 19ba981..e36ec08 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -103,6 +103,7 @@ + @@ -22,83 +22,10 @@ - - ${LOG_DIR}/${APP_NAME}.log - - %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] [rid=%X{requestId:-?} uid=%X{userId:-?} ip=%X{clientIp:-?} %X{method:-?} %X{uri:-?}] %logger{36} - %msg%n - UTF-8 - - - ${LOG_DIR}/archive/${APP_NAME}.%d{yyyy-MM-dd}.%i.log.gz - 100MB - 30 - 3GB - - - - - ${LOG_DIR}/${APP_NAME}-error.log - - ERROR - - - %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] [rid=%X{requestId:-?} uid=%X{userId:-?} ip=%X{clientIp:-?} %X{method:-?} %X{uri:-?}] %logger{36} - %msg%n - UTF-8 - - - ${LOG_DIR}/archive/${APP_NAME}-error.%d{yyyy-MM-dd}.%i.log.gz - 100MB - 60 - 1GB - - - - - - ${LOG_DIR}/${APP_NAME}-crawler.log - - %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] [rid=%X{requestId:-?}] %logger{36} - %msg%n - UTF-8 - - - ${LOG_DIR}/archive/${APP_NAME}-crawler.%d{yyyy-MM-dd}.%i.log.gz - 100MB - 7 - 1GB - - - - - 0 - 1024 - false - - - - - 0 - 256 - true - - - - - 0 - 512 - false - - - - - - - - - @@ -106,10 +33,10 @@ - + @@ -120,11 +47,86 @@ + + + ${LOG_DIR}/${APP_NAME}.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] [rid=%X{requestId:-?} uid=%X{userId:-?} ip=%X{clientIp:-?} %X{method:-?} %X{uri:-?}] %logger{36} - %msg%n + UTF-8 + + + ${LOG_DIR}/archive/${APP_NAME}.%d{yyyy-MM-dd}.%i.log.gz + 100MB + 30 + 3GB + + + + + ${LOG_DIR}/${APP_NAME}-error.log + + ERROR + + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] [rid=%X{requestId:-?} uid=%X{userId:-?} ip=%X{clientIp:-?} %X{method:-?} %X{uri:-?}] %logger{36} - %msg%n + UTF-8 + + + ${LOG_DIR}/archive/${APP_NAME}-error.%d{yyyy-MM-dd}.%i.log.gz + 100MB + 60 + 1GB + + + + + ${LOG_DIR}/${APP_NAME}-crawler.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] [rid=%X{requestId:-?}] %logger{36} - %msg%n + UTF-8 + + + ${LOG_DIR}/archive/${APP_NAME}-crawler.%d{yyyy-MM-dd}.%i.log.gz + 100MB + 7 + 1GB + + + + + 0 + 1024 + false + + + + + 0 + 256 + true + + + + + 0 + 512 + false + + + + + + + + + + + \ No newline at end of file From 19e6c61d2636b046760f0a3b59173abac961bce6 Mon Sep 17 00:00:00 2001 From: dmori Date: Mon, 23 Feb 2026 01:33:43 +0900 Subject: [PATCH 18/19] =?UTF-8?q?feat:=20MDC=20=ED=95=84=ED=84=B0=EC=97=90?= =?UTF-8?q?=20API=20=EC=86=8C=EC=9A=94=EC=8B=9C=EA=B0=84=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/techfork/global/filter/MdcLoggingFilter.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/techfork/global/filter/MdcLoggingFilter.java b/src/main/java/com/techfork/global/filter/MdcLoggingFilter.java index cdcc926..0d600e2 100644 --- a/src/main/java/com/techfork/global/filter/MdcLoggingFilter.java +++ b/src/main/java/com/techfork/global/filter/MdcLoggingFilter.java @@ -1,10 +1,11 @@ package com.techfork.global.filter; +import com.techfork.global.constant.MdcKey; import jakarta.servlet.FilterChain; +import lombok.extern.slf4j.Slf4j; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import com.techfork.global.constant.MdcKey; import org.slf4j.MDC; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; @@ -25,14 +26,15 @@ * - uri: 요청 URI * - clientIp: 클라이언트 IP (X-Forwarded-For 우선) */ +@Slf4j @Component @Order(Ordered.HIGHEST_PRECEDENCE) public class MdcLoggingFilter extends OncePerRequestFilter { - @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + long startTime = System.currentTimeMillis(); try { MDC.put(MdcKey.REQUEST_ID, UUID.randomUUID().toString().replace("-", "").substring(0, 12)); MDC.put(MdcKey.METHOD, request.getMethod()); @@ -41,6 +43,12 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse MDC.put(MdcKey.USER_ID, "anonymous"); filterChain.doFilter(request, response); + + log.info("{} {} {} {}ms", + request.getMethod(), + request.getRequestURI(), + response.getStatus(), + System.currentTimeMillis() - startTime); } finally { MDC.clear(); } From 8ae06547e23862ca53b9bb1c621e6cdcfffff8ad Mon Sep 17 00:00:00 2001 From: dmori Date: Mon, 23 Feb 2026 01:42:06 +0900 Subject: [PATCH 19/19] =?UTF-8?q?fix:=20RecommendationService=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9E=90=20=EC=9D=B8=EC=9E=90=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../evaluation/RecommendationEvaluationService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/techfork/domain/recommendation/evaluation/RecommendationEvaluationService.java b/src/test/java/com/techfork/domain/recommendation/evaluation/RecommendationEvaluationService.java index cf61246..92fc00a 100644 --- a/src/test/java/com/techfork/domain/recommendation/evaluation/RecommendationEvaluationService.java +++ b/src/test/java/com/techfork/domain/recommendation/evaluation/RecommendationEvaluationService.java @@ -28,6 +28,8 @@ import java.io.IOException; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; /** * 추천 시스템 성능 평가를 위한 전용 서비스 @@ -60,7 +62,8 @@ public RecommendationEvaluationService( ) { super(elasticsearchClient, userProfileDocumentRepository, recommendedPostRepository, recommendationHistoryRepository, readPostRepository, postRepository, - mmrService, timeDecayStrategy, properties, vectorQueryBuilder); + mmrService, timeDecayStrategy, properties, vectorQueryBuilder, + Executors.newSingleThreadExecutor()); this.elasticsearchClient = elasticsearchClient; this.userProfileDocumentRepository = userProfileDocumentRepository; this.vectorQueryBuilder = vectorQueryBuilder;