카테고리 없음

[Spring Boot] tave 스터디 5주차 JWT 로그인 구현

밍들레밍 2026. 4. 19. 12:13

세션 기반 로그인과 토큰 기반 로그인(JWT) 차이

세션 기반

  1. 사용자가 로그인 요청
  2. 서버가 아이디/비밀번호 확인
  3. 서버가 세션 생성
  4. 서버가 세션 ID를 브라우저에 쿠키로 전달
  5. 이후 요청마다 브라우저는 쿠키의 세션 ID 전송
  6. 서버는 세션 저장소를 조회해서 로그인 여부 확인

토큰 기반(JWT)

  1. 사용자가 로그인 요청
  2. 서버가 아이디/비밀번호 확인
  3. 서버가 JWT 발급
  4. 클라이언트가 JWT 저장
  5. 이후 요청마다 Authorization: Bearer <토큰> 형태로 전송
  6. 서버는 JWT 서명, 만료시간 등을 검사해서 인증

 

 

JWT( JSON Web Token )란? 

JWT

: 사용자 정보와 만료시간 같은 내용을 담고, 위조되지 않았는지 검증할 수 있게 만든 문자열 토큰

 

로그인 후에는 서버가 매 요청마다 요청 보낸 사람이 누구인지를 확인해야 한다.

예전엔 세션 방식으로 많이 했고, JWT 방식에서는 로그인 성공 후 서버가 토큰을 하나 발급해준다.

그 다음부터 사용자는 요청할 때마다 이 토큰을 함께 보내고, 서버는 그 토큰을 보고 사용자를 확인한다.

  • 로그인 성공 → JWT 발급
  • 이후 요청 → JWT 같이 보냄
  • 서버 → JWT 검사 후 인증 처리

 

JWT의 3부분

1) Header

토큰의 정보가 들어 있다 (어떤 알고리즘으로 서명했는지, 이게 JWT인지)

 

2) Payload

실제 데이터가 들어 있다

  • 사용자 id
  • username
  • 권한(role)
  • 만료시간(exp)

3) Signature

이 토큰이 위조되지 않았는지 확인하는 서명

  • header + payload를 바탕으로 서버의 secret key로 서명 생성
  • 서버는 나중에 이 서명을 다시 계산해서 비교함

 

JWT는 암호화가 아니라 “서명”이 핵심이다

JWT는 기본적으로 내용을 숨기는 용도가 아니다.

보통 header와 payload는 디코딩하면 읽을 수 있기 때문
대신 중요한 건

  • 내용을 읽을 수는 있어도
  • 마음대로 바꾸지는 못한다

바꾸면 signature 검증에서 걸림

그래서 JWT 안에는 민감한 개인정보를 넣으면 안된다

 

 

로그인에서 JWT가 쓰이는 흐름

  1. 사용자가 아이디/비밀번호로 로그인
  2. 서버가 사용자 확인
  3. 서버가 JWT 발급
  4. 클라이언트가 JWT 저장
  5. 이후 API 요청 때 JWT를 함께 보냄

보통 이렇게 보냄

Authorization: Bearer eyJhbGciOiJIUzI1Ni...

 

서버가 JWT 검증해서 성공하면 “이 유저는 인증된 사용자”라고 판단

 

 

JWT의 장점

  • 서버가 세션 저장을 안 해도 됨
    • 세션 방식은 서버가 로그인 상태를 저장해야 했는데,
      JWT는 토큰 자체에 정보가 있으니까 상대적으로 관리가 단순
  • 프론트/백 분리에 잘 맞음
    • React, React Native, 모바일 앱, REST API 구조에서 많이 씀.
  • 확장성에 유리
    • 서버 여러 대를 운영할 때 세션 공유 부담이 줄어듦.

JWT의 단점

  • 탈취되면 위험
  • 발급 후 강제 무효화가 까다로움
    • 세션은 서버에서 지우면 끝인데, JWT는 이미 클라이언트가 가지고 있으니까 바로 끊기 어렵다.
  • 토큰 안에 민감한 정보 넣으면 안 됨

 


 

 

JWT 구현해보기

1. 목표

  • Spring Boot 기반 프로젝트에 JWT 기반 로그인 기능을 구현하고,
    Postman을 통해 다음 흐름을 검증하는 것이 목표였다.
    • 회원가입
    • 로그인 (JWT 발급)
    • JWT를 이용한 인증 요청

2. 구현 전 상태

  • 기존 프로젝트는 post/comment 중심
  • 로그인 기능 없음
  • Spring Security, JWT 라이브러리 미적용

3. 구현 내용

3-1. 의존성 추가

표시된 부분이 추가된 의존성
추가된 파일들

  • security
  • jjwt
  • validation
  • jpa, mysql

3-2. 회원가입 기능

  • Member 엔티티
  • MemberRepository
  • SignupRequest, SignupResponse
  • POST /api/members/signup

요청

POST /api/members/signup

요청 Body

{
  "username": "minju",
  "password": "1234"
}

결과

  • DB에 회원 정보 저장
  • 응답으로 회원 정보 반환

 

3-3. 로그인 기능

  • LoginRequest, LoginResponse
  • POST /api/members/login
  • username/password 검증 후 JWT 발급

요청

POST /api/members/login
 

요청 Body

{
"username": "minju",
"password": "1234"
}

처리 과정

  1. username으로 사용자 조회
  2. password 비교
  3. 일치하면 JWT 생성

응답

{
"accessToken": "eyJhbGciOiJIUzI1NiJ9..."
}

이 토큰이 앞으로 인증에 사용된다.

3-4. JWT 유틸

  • JwtTokenProvider
  • 토큰 생성
  • 토큰 검증
  • username 추출

3-5. JWT 필터

모든 요청은 JwtAuthenticationFilter를 통과한다.

String bearerToken = request.getHeader("Authorization");

if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
    String token = bearerToken.substring(7);

    if (jwtTokenProvider.validateToken(token)) {
        String username = jwtTokenProvider.getUsername(token);

        SecurityContextHolder.getContext().setAuthentication(
            new UsernamePasswordAuthenticationToken(
                username, null, AuthorityUtils.NO_AUTHORITIES
            )
        );
    }
}
  • JwtAuthenticationFilter
  • Authorization 헤더 확인
  • Bearer 토큰 추출
  • SecurityContext에 인증 정보 저장

3-6. Security 설정

  • /signup, /login은 허용
  • 나머지는 인증 필요
  • 세션 대신 STATELESS

3-7. 인증 확인 API

  • GET /api/members/me
  • 토큰 없으면 401
  • 토큰 있으면 인증 성공

4. Postman 테스트 결과

회원가입
로그인 성공

 

  • 회원가입 성공
  • 로그인 성공, accessToken 발급
  • /me 요청 성공

 

5. 트러블슈팅..

트러블슈팅 1. 401 Unauthorized

  • 원인: 회원가입 시도하자 Security 기본 설정 때문에 회원가입 API가 막힘
  • 해결: SecurityConfig에서 permitAll 설정
.requestMatchers("/api/members/signup", "/api/members/login").permitAll()

 

트러블슈팅 2. Could not resolve placeholder 'jwt.secret'

  • 원인: application.yaml에서 jwt 설정 위치 문제, spring 아래에 두었었음
  • 해결: 공통 영역으로 이동

트러블슈팅 3. cannot find symbol JwtTokenProvider

  • 원인: 파일/인식 문제 또는 빌드 동기화 문제
  • 해결:캐시 초기화로도 해결이 안 됐는데 rebuild를 하니 해결! 

 

6. 이번 구현으로 이해한 점

  • JWT는 로그인 후 발급받는 인증 토큰
  • 서버는 세션을 저장하지 않고, 매 요청마다 토큰을 검증
  • 필터가 인증의 핵심 연결 지점
  • Spring Security는 기본 설정만으로도 요청을 막기 때문에 허용 경로를 명시해야 함

 

7. 다음 개선 방향

  • 비밀번호 암호화 (PasswordEncoder)
  • 권한 처리 (작성자만 수정/삭제)
  • 예외 처리 구조 개선
  • 게시글(Post)/댓글(Comment) 작성자와 사용자 연결