Notice
Recent Posts
Recent Comments
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

Spring & Java

Interceptor 구현 본문

심화 Spring/Interceptor & AOP

Interceptor 구현

dev.hyuck 2026. 1. 27. 03:05

 

이번에는 Interceptor 구현을 해보겠습니다.

 

이번 시간에 연습을 잘 하면 HandlerInterceptor의 구조와 시점을 이해할 수 있고 인증/권한 검증 같은 기능을 Controller 앞단에서 구현할 수 있게 될 것입니다.

 

Interceptor란?

● Spring MVC 에서 Controller 진입 직전/후에 동작 합니다.

● JWT 기반 인증이 끝난 후, 인증된 사용자 정보 ( SecurityContext)를 활용하여 

  ○ 자원 소유자 검증

  ○ 역할 기반 접근 제어

  ○ API 사용 제한

등의 부가 검증을 수행할 수 있음

 

요청흐름

[사용자 요청]
    ↓
[JwtFilter] 커스텀 필터에서 > 인증 처리
    ↓
[SecurityContextHolder에 인증 저장]
    ↓
[Interceptor] 한번 더 추가적인 검증
    ↓
[인증 사용자 검증]
    ↓
[Controller]

 

HandlerInterceptor

Filter와 마찬가지로 처음부터 다 구현하는 것이 아닌 이미 구현되어 있는 HandlerInterceptor를 활용합니다.

 

HandlerInterceptor에서는 3가지의 메서드를 지원합니다.

요즘 개발 환경에서는 preHandle만 사용 하고 있습니다.

preHandle - 컨트롤러 기능 검증, 어떤 동작을 시작 전에 작동 하는 메서드 입니다.

postHandle - 사용을 잘 하지 않아요, RestApi를 사용하게 되면서 사용 안하게 됐습니다.

afgerCompletion - 이 메서드 또한 잘 사용하지 않습니다.

 

게시글 수정 시 작성자 본인인지 확인하는Interceptor 실습을 시작해보고자 합니다.

기능 구현을 위해 몇가지 추가를 해보겠습니다. 앞에 있던 프로젝트 파일을 계속 실습 파일로 사용할 예정이니 따라 오고 계신분들이 있다면 그대로 사용하시면 됩니다.

 

 

UserController에서 Email을 수정하는 기능을 구현해 보겠습니다.

@PutMapping("/username/email")

메서드를 생성하고 각각 클래스에 맞게 생성자를 생성하고 메서드를 구현하는게 목표 입니다.

 

 

 

 

이런식으로 기능 하나가 들어오면 각각의 메서드들을 하나씩 구현해주면 되겠습니다. 참 쉽죠 ? 이건 다 예전부터 만들어오던 것들입니다. 이제는 그냥 뚝딱뚝딱 만들지 않으면 실력이 향상되지 않아요. 

 

이제 무엇을 할 것인가! 이메일을 수정하는 것은 자기 자신만 가능하겠죠? 그 기능을 Interceptor 통해서 제한을 두도록 하겠습니다. 그러기 위해서는 몇가지 설정이 필요한데요 일단은 Interceptor을 하나 만들어줘야겠죠? 만들어 봅시다.

 

common에다가 interceptor 패키지를 하나 만들어 줍니다. 자 여기서 퀴즈 한번 내볼까요 ? 

common은 뭐하는 곳인지 설명할 수 있나요 지금? 이제는 이것을 알아야 됩니다.

여러 도메인(기능)에서 공통으로 사용하는 코드들을 모아두는 공간이라고 했죠 ? 

Interceptor는 특정 도메인의 기능이 아니라 전체 애플리케이션 요청 흐름에 공통으로 적용되는 인프라 계층이기 때문에 common에 사용 되는거에요 이제 기억할 수 있겠죠?

 

앞으로 이 메서드가 실행 될때마다 

해당 Interceptor 실행 됩니다. 

 

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.Authentication;


// 1. 현재 로그인한 사용자 이름을 꺼내야합니다.
// -> JWTFilter에서 넣어준 현재 로그인 사용자 꺼내기
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String currentUsername = auth.getName();
-> userId의 이름을 currentUsername을 꺼내 오도록 합니다. 

JwtFilter에서 Authentication을 꺼내서 사용합니다.

 

// 2. 요청한 URL에서 -> /api/user/{username}/eamil 에서 어떤 username을 넣어줬는지 추출하기
String path = request.getRequestURI(); // api/user/김동현/email

 

일단 여기까지의 기반으로 통과를 시켜보도록 하겠습니다. 포스트맨에서는 어떻게 작동하는지 한번 볼까요 이제 ?

 

@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {

    private final UserOwnerCheckInterceptor userOwnerCheckInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(userOwnerCheckInterceptor)
            .addPathPatterns("/api/user/**/email");

    }
}

userOwnerCheckInterceptor에 따라 정확하게 발동 되었다는 것을 확인할 수 있어야 합니다.

만약 다른 URL을 쓰면 안잡히는지도 봐야될 것입니다. 저희는 지금 userOwnerCheckInterceptor URL에 따라 잡히게 해야 되는데 만약 잡힌다면 이건 잘못된 상황인겁니다. 

자 한번 해볼까요? 일단 브레이크 포인트를 설정 했는데 통과 하는지 안하는지 봐야 알겠죠 ?

조회 해보겠습니다.

 

 

짜잔 걸리지 않고 잘 호출 되는거 보이시죠? Interceptor URL 특정 등록 하지 않으면 통과 되도록 설정이 되었다는 뜻입니다!

이젠 path 여기가 좀 문제이긴 하거든요? 왜냐! 

api/user/ 불라불라/email  이렇게 "장지혁" 이라는 코드가 깨져서 나오는걸 볼수 있죠? 이것을 해결해야 됩니다.

이게 .. 인코딩 문제라 이제 손좀 봐주겠습니다.

String decodePath = URLDecoder.decode(path, StandardCharsets.UTF_8);

코드를 사용해서 인코딩 해보겠습니다 한번더 볼까요 ? 

 

일반 path 를 하니까 깨져 나오던 코드가 decodePath 하니까 잘 나오는것을 확인할 수 있습니다.

이제 이 값을 활용을 해보겠습니다.

 

 

String[] parts = decodePath.split("/");
String username = parts[parts.length - 2]; // 나중에 어떻게 셋팅 되진 모르겠지만 일단 현재는 이렇게 수정

배열에 넣고 호출을 하니 이렇게 잘 담겨져 나오는 모습까지 확인할 수 있었습니다.

 

        // 4. 만약에 일치하면 변경해주면 되고 -> controller 접근을 허용하면 되고
        // 만약에 '현재' 사용자와 소유자가 다른 경우에는 아래 조건문 실행
        
       if(!currentUsername.equals(username)) { // 만약에 아니라면!
            log.info("소유자가 아닙니다. 접근 거부");
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "소유자만 수정할 수 있습니다.");
            return false;
        }

 

문제점 발생 ! 계속 403에러가 발생 합니다. 이 문제를 해결하기 위해 여러 문제를 찾아 봣지만 원인을 찾을수 없고 

db가 업데이트 되지 않는 버그가 있어서 이 원인은 무조건 Controller에 있다고 판단 했습니다.

 

하지만, Controller에서 정확하게 발생하는 원인을 찾지 못했고 Contrroller에 도달하지 못하고 발생하는 원인이라고 생각 하게 되었습니다. 그래서,  WebMvcConfig URL 경로에 문제가 있나 싶어 수정을 하기로 했습니다.

 

기존 

.addPathPatterns("/api/user/**/email");

 

변경

.addPathPatterns("/api/user/*/email");

 

테스트 해보겠습니다.

 

계속 true에서 Controller에 도달하지 못하고 있습니다.계속 동일한 403에러가 발생 합니다. 이 문제를 극복할 수 있는 방법이 무엇이 있을까요 ?  분명 username 안에 "장지혁"이 담겨져 있는것을 확인할 수 있었습니다.

 

그런데 왜 이런 문제가 발생하는지 원인을 추적하기가 쉽지 않았습니다.

2026-01-27T15:12:40.729+09:00  INFO 320 --- [advance] [nio-8080-exec-3] c.e.a.c.i.UserOwnerCheckInterceptor      : currentUsername='장지혁' length=3
2026-01-27T15:12:41.453+09:00  INFO 320 --- [advance] [nio-8080-exec-3] c.e.a.c.i.UserOwnerCheckInterceptor      : pathUsername='장지혁' length=3

그래서 username에 따른 문자열이 불일치 하는가 싶어 로그 검사도 해봣습니다. 그런데 문제가 전혀 없습니다 그렇다면 왜 Controller에 도달하지 못하는 것일까요 ? 

제발 .. URL좀 체크 해야 됩니다 저는, 습관이에요 이거.

@PutMapping("/{username}/email")
public ResponseEntity<String> updateEmail(
        @PathVariable String username,
        @RequestBody UpdateUserEmailRequest request
){
    userService.updateUserEmail(username, request.getEmail());
    return ResponseEntity.ok("수정 완료");
}

 

해결 되었습니다 지금까지 Interceptor 구현 이였습니다 감사합니다.

 

📅 날짜 : 2026-01-27  
📌 주제 : Spring Security 403 Forbidden 원인 분석

 

JWT 인증이 정상적으로 동작하고  
Interceptor에서도 true를 반환했음에도 불구하고  
계속 403 Forbidden 에러가 발생했다.

 

❓ 처음 의심했던 것들
- JWT 토큰 문제
- 권한(Role) 문제
- equals 비교 실패
- 한글 인코딩 문제
- JPA 트랜잭션 문제

✅ 최종 해결

  • PathVariable 정확히 선언
  • Interceptor 경로 제한
  • SecurityConfig에 /api/user/** 명시

🧠 오늘 배운 핵심

  • 403은 예외가 아니라 보안 판단 결과
  • Interceptor는 Controller 이전 단계
  • Security는 URL 매칭이 가장 중요
  • JWT 인증 성공 ≠ 접근 허용
  • URL 하나 빠져도 403이 발생할 수 있다

'심화 Spring > Interceptor & AOP' 카테고리의 다른 글

AOP 구현  (0) 2026.01.27
Filter vs Intetceptor vs AOP  (0) 2026.01.26