
“CustomAuthenticationEntryPoint가 없어서 컴파일 실패”라고 나왔고, 실제 GitHub Actions 로그도 아래처럼 터짐.
SecurityConfig.java에서 CustomAuthenticationEntryPoint 타입을 못 찾음
JwtFilter.java import 자체가 깨짐
cannot find symbol: class CustomAuthenticationEntryPoint
package ... does not exist
이건 “빈 주입” 문제가 아니라 자바 컴파일 단계에서 클래스가 클래스패스에 없다고 판단한 상태다.
커밋/푸시 누락
로컬에만 있고 PR/브랜치에 안 올라가면 CI는 당연히 못 봄
src/test/java에 존재
JwtFilter, SecurityConfig는 src/main/java라서 compileJava는 test 소스를 안 봄
→ “존재하지만 main 컴파일 단계에서는 없음”이 됨
import는 com.example.deliveryplatform.common.security.CustomAuthenticationEntryPoint인데
./gradlew clean compileJava
find src -name "CustomAuthenticationEntryPoint.java" -print
git ls-files | grep CustomAuthenticationEntryPoint
doFilterInternal()은 throws ServletException, IOException라서,
잡은 예외를 그대로 전파하되, 타입을 맞춰 주는 패턴이 안전하다.
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
String token = tokenExtractor.extract(request);
if (StringUtils.hasText(token)) {
Authentication authentication = tokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
} catch (AuthenticationException e) {
SecurityContextHolder.clearContext();
authenticationEntryPoint.commence(request, response, e);
return;
} catch (Exception e) {
log.error("예상치 못한 에러",e);
if (e instanceof ServletException se) throw se;
if (e instanceof IOException ioe) throw ioe;
throw new ServletException(e);
}
}
이렇게 하면:
인증 관련은 401로 EntryPoint 처리
예상치 못한 예외는 500 흐름으로 정상 전파(전역 예외 처리에서 잡거나 기본 에러로 처리)
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
ErrorCode errorCode = (authException instanceof JwtAuthenticationException) ?
((JwtAuthenticationException)authException).getErrorCode() :
ErrorCode.AUTHENTICATION_REQUIRED;
ErrorResponse errorResponse = ErrorResponse.from(errorCode);
response.getWriter().write(objectMapper.writeValueAsString(errorResponse));
}
objectMapper를 사용하려는데 tools.jackson을 사용하지말고 보통 SpringBoot가 기본으로 사용하는 com.fasterxml.jackson.databind.ObjectMapper를 사용한다고 하여 사용하려 했더니 발생한 상황이다.
처음엔 “보통은 com.fasterxml.jackson.databind.ObjectMapper가 맞다”는 흐름이었는데,
dependencyInsight를 찍어보니 실제로 프로젝트는 Jackson이 이렇게 들어와 있었음:
tools.jackson.core:jackson-databind:3.0.3
spring-boot-starter-web:4.0.1 → spring-boot-starter-jackson → spring-boot-jackson → tools.jackson...
즉, 현재 환경에서는 tools.jackson.databind.ObjectMapper가 잡히는 게 자연스러운 상태.
dependencyInsight 출력에서 봐야 할 포인트:
tools.jackson.core:jackson-databind:3.0.3가 compileClasspath에 존재
spring-boot-starter-web:4.0.1 경로로 유입됨
Selected by rule / By constraint는 BOM/제약으로 버전이 3.0.3으로 정렬되었다는 신호
(*) repeated occurrences는 동일 트리가 여러 경로로 들어왔지만 Gradle이 한 번만 확장 표시한다는 뜻
-> Spring Framework 7 / Spring Boot 4 계열부터는 Jackson 3이 기본이고, Jackson 3의 databind/core 패키지가 tools.jackson으로 바뀐 게 공식 변경사항
new ObjectMapper()를 매번 만드는 것보다
스프링이 제공하는 ObjectMapper 빈을 주입받아 쓰는 게 정석
(Spring 4.0 프로젝트 기준 import는 tools.jackson...)
삼항 연산자로
JwtAuthenticationException이면 그 안의 ErrorCode 사용
아니면 기본값으로 AUTHENTICATION_REQUIRED
ErrorCode errorCode = (authException instanceof JwtAuthenticationException)
? ((JwtAuthenticationException) authException).getErrorCode()
: ErrorCode.AUTHENTICATION_REQUIRED;
오늘은 공부를 평소처럼 더 하려고 했는데 어제 가족들이 여행간다고 밤새 신나있어서 잠을 제대로 못 자고 하다보니 이해가 너무 안 돼서 조금만 하고 공고 추려서 이력서 넣는 날로 바꿨습니다😭