Post

JWT란? 왜 사용할까?

JWT란? 왜 사용할까?

🎯 오늘의 목표

Spring Boot로 로그인 기능을 구현하려고 하면 대부분 JWT(Json Web Token)를 이용한 인증 방식에 도달하게 된다.
JWT의 개념부터 실제 로그인 기능 구현 전까지, 꼭 이해하고 넘어가야 할 부분들을 정리해보려고 한다.


📌 1. JWT란?

JWT는 JSON Web Token의 줄임말로, 사용자의 인증 정보를 JSON 형식으로 담아서 암호화한 토큰이다.

  • Stateless한 인증 방식을 제공하며, 서버가 세션을 저장할 필요가 없다.
  • 주로 Access TokenRefresh Token으로 나뉘어 사용된다.

🧱 JWT 구조

JWT는 크게 세 부분으로 구성된다.

1
Header.Payload.Signature

예시:

1
2
3
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiJ1c2VySWQiLCJpYXQiOjE2ODAwMDAwMDAsImV4cCI6MTY4MDA4NjQwMH0.
HkT7O6l2lZq5P8J1N_RL65HxqB34e2bB3KkPQBz8MXM
파트설명
Header토큰 타입(JWT)과 해싱 알고리즘 (예: HS256)
Payload사용자 정보 (sub, iat, exp 등)
Signature서명을 통해 위변조 방지 (비밀키 기반)

🙋‍♂️ 2. 왜 JWT를 사용할까?

세션 방식과의 차이

항목세션 방식JWT 방식
상태 유지서버가 세션 저장서버가 상태 저장 안 함
확장성서버 간 세션 공유 필요토큰만 있으면 어느 서버든 인증 가능
클라이언트 저장세션 ID만 저장전체 인증 정보 저장

언제 유리할까?

  • 모바일 앱, SPA(단일 페이지 어플리케이션)
  • 마이크로서비스, 서버리스 환경
  • 서버에 상태를 저장하지 않아야 하는 시스템

📌 예제

🔧 1. JWT 의존성 추가

Gradle을 사용하는 경우 build.gradle에 의존성을 추가한다:

1
2
3
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

🔐 2. JWT 유틸 클래스 생성

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
31
32
33
34
35
36
37
38
39
40
41
42
43
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.util.Date;

@Component
public class JwtUtil {

    private final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
    private final long expireTime = 1000 * 60 * 60; // 1시간

    // 토큰 생성
    public String generateToken(String email) {
        return Jwts.builder()
                .setSubject(email)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expireTime))
                .signWith(key)
                .compact();
    }

    // 토큰 검증
    public boolean validateToken(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
            return true;
        } catch (JwtException e) {
            return false;
        }
    }

    // 사용자 정보 추출
    public String getEmailFromToken(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }
}

📦 3. 테스트 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
@RequestMapping("/api/test")
public class JwtTestController {

    @Autowired
    private JwtUtil jwtUtil;

    @GetMapping("/generate")
    public String generateToken() {
        return jwtUtil.generateToken("test@example.com");
    }

    @GetMapping("/validate")
    public String validateToken(@RequestParam String token) {
        return jwtUtil.validateToken(token) ? "유효한 토큰입니다." : "유효하지 않은 토큰입니다.";
    }
}

📌 Spring Security와 JWT 연동

🧱 1. 기본 Security 설정

SecurityConfig.java 파일을 생성하여 Spring Security 설정을 작성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    private final JwtFilter jwtFilter;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
            .build();
    }
}

🧱 2. JWT 필터 구현

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
@Component
@RequiredArgsConstructor
public class JwtFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;
    private final CustomUserDetailsService userDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        String token = resolveToken(request);

        if (token != null && jwtUtil.validateToken(token)) {
            String email = jwtUtil.getEmailFromToken(token);
            UserDetails userDetails = userDetailsService.loadUserByUsername(email);
            UsernamePasswordAuthenticationToken authentication =
                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

            SecurityContextHolder.getContext().setAuthentication(authentication);
        }

        filterChain.doFilter(request, response);
    }

    private String resolveToken(HttpServletRequest request) {
        String bearer = request.getHeader("Authorization");
        return (bearer != null && bearer.startsWith("Bearer ")) ? bearer.substring(7) : null;
    }
}

👤 3. 로그인 요청 처리 API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {

    private final AuthenticationManager authenticationManager;
    private final JwtUtil jwtUtil;

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {
        Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword()));

        String token = jwtUtil.generateToken(request.getEmail());
        return ResponseEntity.ok(new JwtResponse(token));
    }
}

🧾 4. DTO 예시

1
2
3
4
5
6
7
8
9
10
11
@Data
public class LoginRequest {
    private String email;
    private String password;
}

@Data
@AllArgsConstructor
public class JwtResponse {
    private String token;
}

✅ 5. 결과 확인

이제 프론트에서 로그인 요청 시 /api/auth/login 으로 이메일/비밀번호를 전송하면,
JWT 토큰이 응답으로 내려오고, 이후 API 요청 시 Authorization: Bearer <token> 헤더로 인증이 가능해진다.


🔚 마무리

JWT 개념부터 토큰 발급, 검증, Spring Security 연동까지 정리해봤다.

백엔드 서버가 인증 상태를 직접 저장하지 않고도 JWT만으로 안정적인 사용자 인증 처리를 수행할 수 있는 구조를 만들 수 있다.

보안이나 인증 관련 문제는 가장 중요하게 다뤄야 한다,, 개념을 확실히 이해하고 개발할 수 있도록 하자.

This post is licensed under CC BY 4.0 by the author.