스프링 부트 백엔드 개발 강좌, JWT로 로그인 로그아웃 구현, 의존성 추가하기

안녕하세요! 이번 블로그 포스트에서는 스프링 부트를 활용한 백엔드 개발 과정에서 JSON Web Token(JWT)을 사용하여 로그인 및 로그아웃 기능을 구현하는 방법에 대해 자세히 알아보겠습니다. 웹 애플리케이션 개발에서 인증과 권한 부여는 중요한 요소이며, JWT는 이를 효과적으로 관리하는 데 도움을 줍니다. 본 강좌에서는 JWT의 개념, 스프링 부트 설정, 의존성 추가 방법 등을 단계별로 설명하겠습니다.

1. JWT란 무엇인가?

JWT는 JSON Web Token의 약자로, 사용자 인증 정보를 안전하게 전송하기 위한 표준입니다. JWT는 세 가지 부분으로 구성되어 있습니다:

  • Header: 토큰의 타입과 해싱 알고리즘 정보를 담고 있습니다.
  • Payload: 사용자 정보와 같은 클레임을 포함합니다. 이 클레임은 공개된 정보(API 호출 시 필요한 정보)를 포함할 수 있습니다.
  • Signature: 헤더와 페이로드 정보를 기반으로 비밀 키를 사용하여 서명합니다. 이는 데이터의 무결성을 보장하며, 누군가 토큰을 위조했는지를 확인하는 데 사용됩니다.

JWT의 가장 큰 장점은 스테이트리스(stateless) 특성을 가진다는 것입니다. 서버에서 세션 상태를 유지할 필요가 없기 때문에, 확장성과 성능이 뛰어납니다.

2. 스프링 부트 프로젝트 생성

스프링 부트를 사용하여 새로운 프로젝트를 생성합시다. 먼저, Spring Initializr(https://start.spring.io/)를 방문합니다. 아래의 설정을 적용하여 프로젝트를 생성합니다:

  • Project: Maven Project
  • Language: Java
  • Spring Boot: 2.6.0 (최신 버전 선택)
  • Group: com.example
  • Artifact: jwt-demo
  • Dependencies: Spring Web, Spring Security, Spring Data JPA, H2 Database

프로젝트를 생성한 후, IDE에서 해당 프로젝트를 열고 필요한 디렉토리 구조를 설정합니다.

3. 의존성 추가하기

먼저, pom.xml 파일에 필요한 의존성을 추가합니다:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jwt</artifactId>
    <version>0.9.1</version>
</dependency>

이외에도 스프링 시큐리티와 데이터 JPA는 이미 추가되어 있으므로 추가 의존성은 필요하지 않습니다. H2 데이터베이스는 개발과 테스트 환경에 유용하게 사용할 수 있습니다.

4. 스프링 시큐리티 설정

JWT를 사용하기 위해 스프링 시큐리티를 설정해야 합니다. 먼저 SecurityConfig 클래스를 생성합니다:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests().antMatchers("/api/auth/**").permitAll()
            .anyRequest().authenticated();
    }
}

위의 설정에서, 모든 사용자는 /api/auth/** 경로에 접근할 수 있습니다. 다른 모든 요청은 인증을 요구합니다.

5. JWT 생성 및 검증

JWT를 생성하고 검증하는 클래스를 작성합니다. 여기에 필요한 메서드를 정의합니다:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Service;

import java.util.Date;

@Service
public class JwtUtil {

    private String secretKey = "YourSecretKey"; // 비밀 키는 외부에 노출되지 않게 관리해야 합니다

    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10시간
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }

    public boolean validateToken(String token, String username) {
        final String extractedUsername = extractUsername(token);
        return (extractedUsername.equals(username) && !isExpired(token));
    }

    public String extractUsername(String token) {
        return extractAllClaims(token).getSubject();
    }

    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
    }

    private boolean isExpired(String token) {
        return extractAllClaims(token).getExpiration().before(new Date());
    }
}

위의 JwtUtil 클래스는 토큰 생성, 유효성 검증 및 사용자 이름 추출을 위한 메서드를 포함하고 있습니다.

6. 인증 및 로그아웃 구현

이제 인증 및 로그아웃을 처리할 컨트롤러를 작성해보겠습니다. AuthController 클래스를 생성하세요:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping("/login")
    public ResponseEntity login(@RequestBody AuthRequest authRequest) {
        // 사용자 인증 로직 추가
        String token = jwtUtil.generateToken(authRequest.getUsername());
        return ResponseEntity.ok(token);
    }

    @PostMapping("/logout")
    public ResponseEntity logout() {
        // JWT를 블랙리스트에 추가하는 등의 로직 추가
        return ResponseEntity.ok("로그아웃 성공");
    }
}

위의 코드에서 AuthRequest는 사용자 이름과 비밀번호를 담는 DTO 클래스이며, 사용자 인증 로직은 구체적으로 구현해야 합니다. 일반적으로는 데이터베이스에서 인증 정보를 확인합니다.

7. 최종 테스트

이제 모든 설정을 마쳤습니다. Postman과 같은 도구를 사용하여 API를 테스트할 수 있습니다:

  • 로그인: POST /api/auth/login
  • 로그아웃: POST /api/auth/logout

로그인 API의 요청 본문에서 사용자 이름과 비밀번호를 전달하면, 성공적으로 JWT를 반환받을 수 있습니다. 반환된 JWT는 이후 API 호출 시 Authorization 헤더에 포함하여 전달해야 합니다.

8. 마무리

스프링 부트를 사용한 JWT 기반의 로그인 및 로그아웃 기능 구현에 대해 알아보았습니다. 인증 및 권한 부여는 웹 애플리케이션 개발에서 중요한 부분이며, JWT는 이를 간편하고 안전하게 처리할 수 있도록 돕습니다. 추가 기능으로는 JWT 블랙리스트 처리, 리프레시 토큰 구현 등을 고려해볼 수 있습니다.

이번 강좌가 스프링 부트 백엔드 개발에 도움이 되었기를 바랍니다. 더 많은 정보와 자료는 공식 문서와 커뮤니티를 통해 확인할 수 있습니다. 감사합니다!