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

Filter와 같이 사용하는 JWT 본문

심화 Spring/JWT & Filter

Filter와 같이 사용하는 JWT

dev.hyuck 2026. 1. 22. 13:35

 

우리는 앞전에  JWT를 통해서 인증 인가/인가 처리하는 것을 해봤습니다.

API가 요청이 들어오면 JWT가 있는지 없는지 검사하고 없다면 해당 토큰이 유효햔지를 검사 했습니다.

유효하면 Controller로 접근을 허용하고 그렇지 않다면 접근을 막았습니다.

이런 작업을 모든 요청에 일괄적으로 적용한다면 Controller에서 일일이 요청할 필요가 없어지는 겁니다

 

무슨 말이냐면 모든 컨트롤러 메서드 마다 url 마다 매번 넣어줫어야 했습니다.

이렇게 되면 똑같은 코드의 무한한 반복, 코드를 수정을 할 때 코드를 계속 바꿔야 하고 코드도 길어지는 불편함이 생깁니다.

@GetMapping("/get")
public String api1(){
	// JWT 인증/인가 검사
    // 실행하고 싶은 로직
}

@GetMapping("/get")
public String api2(){
	// JWT 인증/인가 검사
    // 실행하고 싶은 로직
}

@GetMapping("/get")
public String api3(){
	// JWT 인증/인가 검사
    // 실행하고 싶은 로직
}

 

그래서 Filter를 통해서 일괄 적용이 가능합니다. 스프링에서 가장 앞쪽에 있기 때문에 Controller 접근전에 모든 요청을 한 곳에서 처리가 가능합니다.

Filter와 JWT를 같이 활용하면서 인증/인가를 필터에서 진행하게 되는 흐름을 설계 하도록 하겠습니다.

 

< 실습 코드 >

우선 JwtFilter를 하나 생성해 봅시다 그리고 저는 어노테이션 방법을 아직 익숙하게 여기지 못해서 하나하나 기억하는 식으로 학습 할려고 합니다 그러니까 어노테이션 캡쳐해서 올린다고 이상하게 생각하진 말아주세요 ( 구도 파악, 흐름 파악, 기능 파악 의도 )

 

자 이제 Filter 클래스를 extends 해야겠죠 ? 앞에서 학습 했듯 OncePerRequestFilter을 상속 시킵니다.

왜 이럴까? OncePerRequestFilter는 가장 많이 쓰이는 방식이라고 앞에서도 학습 했고, 지금도 우리는 이 필터만 쓸 것이기 때문입니다.

 

 

그럼 이렇게 할수 있겠죠? 자 이제부터 하나씩 만들어 봅시다. 어떻게 구현하는게 올바른 방법일까? 한번쯤 고민해보고 시작해도 나쁘지 않을것 같습니다.

 

 

이 3가지에 대한 얘기를 먼저 다루면서 다음으로 넘어가 볼게요.

 

너 토큰 있어? 없어? 

여기에 Athorization이 있는지 없는지 확인 하자!

//!!유효성 검사도 필수 입니다.
        // 토큰을 발급 받는 로그인의 경우에는 토큰 검사를 하지 않아도 통과
        String requestURL = request.getRequestURI();

        // ' 로그인은 토큰 검사 제외 '
        if(requestURL.equals("/api/user/login")){
            filterChain.doFilter(request,response);
            return;
        }
        
해석  : 만약에 requestUrl이랑 동일하면 암호 따로 필터를 통과!
해석2 : 토큰이 있는지 없는지 검사
									
                                    ↓
// 너 토큰 있어? 없어?
        String authorizationHeader = request.getHeader("Authorization");

" 해석 : 만약에 Authorizatio의 값이 null이거나 공백이라면 에러를 출력한다. "

        if(authorizationHeader == null || authorizationHeader.isBlank()){
            log.info("JWT 토큰이 필요합니다.");
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "JWT 토큰이 필요 합니다.");
            return;

        }

 

토큰이 있어? 그럼 토큰은 유효해?

        // 토큰이 있어? 그럼 그 토큰 유효해?
        // 여기까지 통과 했다면 토큰이 유효하다!
        String jwt = authorizationHeader.substring(7);
        // 만약에 jwtUtil이 유효하지 않다면 error를 던져 줘라.
        if(!jwtUtil.validateToken(jwt)) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("{\"error\" : \"Unauthorized\"}");

        }

 

유효해? 그럼 어떤 정보를 가지고 있어?

        // 만약에 유효해 그럼 어떤 정보를 가지고 있어 ?
        String username = jwtUtil.extractUsername(jwt);
        // username을 꺼내긴 했는데 어떻게 사용하는게 좋을까?
        // 여기서 나오는게 바로 HttpServletRequest가 등장한다!

 

HttpServletRequest 알아보기

요청을 보내면 Postman 기준 윗쪽 화면과 아랫쪽 화면을 각각 request, response 라는 공간에 나눠 담고 servlet 이라는 객체에 담아서 Spring으로 전달 하는것입니다.

 

근데 확실히 알고 가야 될 부분이 바로 요청과 응답 영역입니다. 이것을 알아야 왜 Request를 쓰는지 왜 Response를 쓰는지 알수 있는거니까요 

 

이미지 기준 왼쪽이 Request 오른쪽이 Response 가 되는 것이죠.

 

postman 요청을 보낼 때 응답 부분은 비어 있고 API 통신이 완료 되면 채워지는 것 처럼 1번의 요청당 1개의 servlet이 위의 그림 처럼 흐름을 돌면서 빈칸을 채워가고 사용자에게 전달을 한다고 생각하면 됩니다.

 

 

HttpServletRequest 안에는 postman 위쪽 공간만 존재 하는게 아니라 별도의 attribute 라는 저장 공간이 따로 또 있습니다. 우리는 그 저장 공간을 활용하고 필요할때마다 스프링MVC에서 꺼내 쓸 생각입니다.

어떻게 쓸 것인가 ?

 

    // 만약에 유효해 그럼 어떤 정보를 가지고 있어 ?
    String username = jwtUtil.extractUsername(jwt);
    // username을 꺼내긴 했는데 어떻게 사용하는게 좋을까?
    // 여기서 나오는게 바로 HttpServletRequest가 등장한다!

    request.setAttribute("username", username);
    //  불러오긴 했는데 이걸 컨트롤러에 쓰는 방법을 좀 알아야겠지?

    filterChain.doFilter(request,response);
}

@GetMapping("/get")
public String getUserInfo(HttpServletRequest request) {
    String username = (String) request.getAttribute("username");
    log.info(username);
    return username;
}

 

이렇게 써보는게 좋겠죠 ? 이제 디버깅 해보겠습니다. 결과 값이 어떻게 출력 되는지 궁금해지네요.

 

잘 불러와지네요 토큰이 유효한지 유효성 검사를 끝으로 Token 값이 king4 라는 값을 확인할 수 있게 되었습니다.

 

필터는 모든 스프링 요청중 맨 앞에서 요청을 받으니까 로깅(요청/응답) , 공통 인증 / 권한 체크 (JWT)는 가장 먼저 하는게 올바른 로직이기 때문에 필터에서 JWT를 수행 하는것이 좋습니다. 왜? 가장 먼저 수행하는 곳이니까 그렇다고 생각하면 아주 좋습니다.

필터가 없다면 컨트롤러마다 검증 로직을 다 채워줘야 되는 비효율적인 문제가 생기기 때문에 필터를 무조건 쓰는게 맞다고 생각 하면 됩니다. 

 

오늘의 TIL 

📌 날짜 : 2026 01 22

📌 주제 : Filter와 JWT를 같이 사용하자

✅ 오늘 학습한 내용

Filter와 JWT 인증 방식을 함께 사용하는 구조를 학습 했는데, 기존에는 Controller나 Service 단에서 인증을 처리 했지만, 이번 학습을 통해 HTTP 요청이 Controller에 도달하기 전 단계인 Filter에서 JWT 인증을 처리하는 방식을 이해하게 되었습니다.

 

Filter를 사용하는 이유는 모든 HTTP 요청/응답을 가장 맨 처음 사용하고 Controller 진입 전,후 공통 로직 처리가 가능하기 때문에 그래서 인증, 인가, 로깅 같은 공통 기능에 매우 적합해서 안쓸 이유가 없는 것이죠.

JWT 인증은 Controller가 실행되기 전에 검증되어야 하므로 Filter에서 처리하는 것이 가장 적절하다.

클라이언트 요청
   ↓
JwtFilter 진입
   ↓
로그인 요청 여부 확인
   ↓
Authorization Header 존재 여부 확인
   ↓
JWT 토큰 검증
   ↓
유효하면 Controller로 요청 전달

 

❌ 발생한 문제

JWT 로직에는 문제가 없었지만 jwtUtil을 사용할 수 없다는 컴파일 에러가 발생했습니다.

강의에서는 생성자를 사용하지 않았고 그대로 코드를 흐름대로 설계 했는데 따라가다 보니

jwtUtil을 해결할 수 없는 에러를 직면 했습니다.

private final JwtUtil jwtUtil;

 

강의에서 생성자를 주입하지 않아 생성자를 직접 강제로 주입 했더니 에러는 해결이 되었습니다. 어노테이션 문제가 있는지 검색 해보고 원인을 찾기 위해 여러 방법을 찾아 봤지만 1시간? 이상 고생 했던것 같다 이것 때문에.

그래서 생성자를 직접 주입하고 사용해보니 문제 없이 로그인이 되는 것을 확인 할 수 있었다.

 

✍️ 느낀 점

단순히 JWT를 구현하는 것보다 왜 Filter에서 인증을 처리하는지, 의존성 주입이 어떤 방식으로 동작하는지를 깊게 이해할 수 있었습니다. 강의 코드를 그대로 따라 치는것과 직접 에러를 겪으며 구조를 이해하는 것은 확실히 차이가 크고 이번 경험을 통해
" 어떤 흐름대로 설계하고 왜 이렇게 설계 하는것인지 " 를 생각하는 습관이 중요해졌습니다.

 

 

이번에는 필터와 JWT를 같이 써보고 로그인 했을때 POSTMAN에서 어떻게 작동하고 토큰을 어떻게 활용 하는지 배워봤습니다. 왜 필터가 중요한지 왜 JWT와 필터를 같이 쓰는지 핵심을 아주 잘 이해할 수 있는 좋은 시간이였습니다.

'심화 Spring > JWT & Filter' 카테고리의 다른 글

JWT 이해  (0) 2026.01.21
Filtet 개념과 동작 원리  (0) 2026.01.20