Skip to content

Commit a481a7c

Browse files
authored
[dev -> main] 2025.10.12 05:30
[dev -> main] 2025.10.12 05:30
2 parents a6495a5 + 39be5f5 commit a481a7c

File tree

11 files changed

+160
-4
lines changed

11 files changed

+160
-4
lines changed

build.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ dependencies {
5858
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
5959
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
6060

61+
// Redis 체크
62+
implementation 'io.lettuce:lettuce-core'
63+
64+
// AOP
65+
implementation 'org.springframework.boot:spring-boot-starter-aop'
66+
67+
6168
}
6269

6370
tasks.named('test') {

src/main/java/DiffLens/back_end/domain/members/controller/AuthController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import DiffLens.back_end.domain.members.dto.auth.AuthRequestDTO;
44
import DiffLens.back_end.domain.members.dto.auth.AuthResponseDTO;
5-
import DiffLens.back_end.domain.members.service.AuthService;
5+
import DiffLens.back_end.domain.members.service.auth.AuthService;
66
import DiffLens.back_end.global.responses.exception.ApiResponse;
77
import io.swagger.v3.oas.annotations.Operation;
88
import io.swagger.v3.oas.annotations.tags.Tag;

src/main/java/DiffLens/back_end/domain/members/service/AuthService.java renamed to src/main/java/DiffLens/back_end/domain/members/service/auth/AuthService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package DiffLens.back_end.domain.members.service;
1+
package DiffLens.back_end.domain.members.service.auth;
22

33
import DiffLens.back_end.domain.members.auth.StrategyFactory;
44
import DiffLens.back_end.domain.members.auth.strategy.interfaces.AuthStrategy;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package DiffLens.back_end.domain.members.service.auth;
2+
3+
import DiffLens.back_end.domain.members.entity.Member;
4+
import DiffLens.back_end.global.responses.code.status.error.AuthStatus;
5+
import DiffLens.back_end.global.responses.exception.handler.ErrorHandler;
6+
import org.springframework.security.core.context.SecurityContextHolder;
7+
import org.springframework.stereotype.Service;
8+
9+
@Service
10+
public class CurrentUserService {
11+
12+
public Member getCurrentUser() {
13+
var authentication = SecurityContextHolder.getContext().getAuthentication();
14+
if (authentication == null) {
15+
throw new ErrorHandler(AuthStatus.AUTHENTICATION_FAILED);
16+
}
17+
18+
var principal = authentication.getPrincipal();
19+
if (principal == null) {
20+
throw new ErrorHandler(AuthStatus.AUTHENTICATION_FAILED);
21+
}
22+
23+
if (!(principal instanceof Member)) {
24+
throw new ErrorHandler(AuthStatus.AUTHENTICATION_FAILED);
25+
}
26+
27+
return (Member) principal;
28+
}
29+
30+
public Long getCurrentUserId() {
31+
Member user = getCurrentUser();
32+
return user != null ? user.getId() : null;
33+
}
34+
35+
public String getCurrentUserEmail() {
36+
Member user = getCurrentUser();
37+
return user != null ? user.getEmail() : null;
38+
}
39+
40+
41+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package DiffLens.back_end.global.aop;
2+
3+
import DiffLens.back_end.domain.members.service.auth.CurrentUserService;
4+
import jakarta.servlet.http.HttpServletRequest;
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.aspectj.lang.ProceedingJoinPoint;
8+
import org.aspectj.lang.annotation.Around;
9+
import org.aspectj.lang.annotation.Aspect;
10+
import org.springframework.stereotype.Component;
11+
import org.springframework.web.context.request.RequestContextHolder;
12+
import org.springframework.web.context.request.ServletRequestAttributes;
13+
14+
@Aspect
15+
@Component
16+
@Slf4j
17+
@RequiredArgsConstructor
18+
public class ApiRequestLogAspect {
19+
20+
private final CurrentUserService authService;
21+
22+
/**
23+
* 전/후 처리 모두 가능
24+
* CommonPointcut.restControllerEndpoints() Pointcut 지정하여
25+
* API 호출 시 호출 전후로 로그 출력하도록 하는 Aspect
26+
*/
27+
@Around("CommonPointCut.restControllerEndpoints()")
28+
public Object logApiRequest(ProceedingJoinPoint jp) throws Throwable {
29+
long start = System.currentTimeMillis();
30+
31+
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
32+
HttpServletRequest request = attrs.getRequest();
33+
String uri = request.getRequestURI();
34+
String httpMethod = request.getMethod();
35+
36+
Object currentUser = null;
37+
try { currentUser = authService.getCurrentUser(); } catch (Exception ignored) {}
38+
39+
String methodName = jp.getSignature().getName();
40+
String args = jp.getArgs() != null ? String.join(", ", java.util.Arrays.stream(jp.getArgs())
41+
.map(String::valueOf).toArray(String[]::new)) : "";
42+
43+
String userInfo = currentUser != null ? "[User: " + currentUser + "]" : "[User: Anonymous]";
44+
String requestInfo = "[" + httpMethod + ": " + uri + " - " + methodName + "(" + args + ")]";
45+
46+
log.info("⏳ [API 호출 시작] {} {}", userInfo, requestInfo);
47+
48+
try {
49+
Object result = jp.proceed();
50+
long end = System.currentTimeMillis();
51+
log.info("✅ [API 호출 종료] {} {} - 실행시간: {}ms", userInfo, requestInfo, (end - start));
52+
return result;
53+
} catch (Throwable ex) {
54+
long end = System.currentTimeMillis();
55+
log.error("❌ [API 호출 예외] {} {} - 실행시간: {}ms - 예외: {}", userInfo, requestInfo, (end - start), ex.getMessage());
56+
throw ex; // 예외를 다시 던져서 컨트롤러에게 전달
57+
}
58+
}
59+
60+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package DiffLens.back_end.global.aop;
2+
3+
import org.aspectj.lang.annotation.Pointcut;
4+
5+
public class CommonPointCut {
6+
7+
// API 호출 시 로그 찍도록 @RestController 어노테이션에 PointCut 설정
8+
@Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
9+
public void restControllerEndpoints() {}
10+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package DiffLens.back_end.global.redis;
2+
3+
import jakarta.annotation.PostConstruct;
4+
import lombok.RequiredArgsConstructor;
5+
import lombok.extern.slf4j.Slf4j;
6+
import org.springframework.data.redis.core.RedisTemplate;
7+
import org.springframework.stereotype.Component;
8+
9+
@Slf4j
10+
@Component
11+
@RequiredArgsConstructor
12+
public class RedisHealthChecker {
13+
14+
private final RedisTemplate<String, Object> redisTemplate;
15+
16+
@PostConstruct
17+
public void checkRedisConnection() {
18+
log.info("[Connection Check] Redis 연결 체크...");
19+
try {
20+
String ping = redisTemplate.getConnectionFactory() != null ? redisTemplate.getConnectionFactory()
21+
.getConnection()
22+
.ping() : "null";
23+
if(!"PONG".equals(ping)) {
24+
log.error("[Connection Check] Redis 연결 실패 ❌ - 응답값이 'PONG' 이 아님");
25+
throw new IllegalStateException("Redis 연결 실패: PING 응답 이상. 응답=" + ping);
26+
}
27+
}catch (Exception e) {
28+
log.error("[Connection Check] Redis 연결 실패 ❌ - {}", e.getMessage());
29+
throw new IllegalStateException("Redis 연결 실패: " + e.getMessage(), e);
30+
}
31+
log.info("[Connection Check] Redis 연결 성공 ✅");
32+
}
33+
34+
}

src/main/java/DiffLens/back_end/global/responses/code/status/error/AuthStatus.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
@AllArgsConstructor
1111
public enum AuthStatus implements BaseErrorCode {
1212

13+
AUTHENTICATION_FAILED(HttpStatus.UNAUTHORIZED, "AUTH400", "인증에 실패했습니다."),
14+
1315
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "AUTH401", "존재하지 않는 사용자입니다."),
1416
ALREADY_EXISTS(HttpStatus.BAD_REQUEST, "AUTH402", "이미 존재하는 사용자입니다."),
1517
INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "AUTH403", "올바르지 않은 비밀번호입니다."),
@@ -22,7 +24,7 @@ public enum AuthStatus implements BaseErrorCode {
2224

2325
SOCIAL_USERINFO_API_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "AUTH504", "소셜 사용자 정보를 가져오지 못했습니다."),
2426
SOCIAL_USERINFO_NETWORK_ERROR(HttpStatus.SERVICE_UNAVAILABLE, "AUTH505", "소셜 사용자 정보 요청 중 네트워크 오류가 발생했습니다."),
25-
SOCIAL_USERINFO_UNKNOWN_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "AUTH506", "소셜 사용자 정보 처리 중 오류가 발생했습니다.")
27+
SOCIAL_USERINFO_UNKNOWN_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "AUTH506", "소셜 사용자 정보 처리 중 오류가 발생했습니다."),
2628

2729
;
2830

src/main/java/DiffLens/back_end/global/responses/exception/ExceptionAdvice.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public ResponseEntity<Object> handleMethodArgumentNotValid(
106106
});
107107

108108
return handleExceptionInternalArgs(
109-
e, HttpHeaders.EMPTY, ErrorStatus.valueOf("_BAD_REQUEST"), request, errors);
109+
e, HttpHeaders.EMPTY, ErrorStatus.valueOf("BAD_REQUEST"), request, errors);
110110
}
111111

112112
@ExceptionHandler

src/main/resources/application-dev.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ spring:
2020
redis:
2121
host: ${REDIS_HOSTNAME}
2222
port: ${REDIS_PORT}
23+
timeout: 2000ms
2324

2425

2526

0 commit comments

Comments
 (0)