1. 서론
현대 웹 애플리케이션에서 보안은 가장 중요한 요소 중 하나입니다. 사용자 인증 및 권한 부여는 이러한 보안을 유지하는 핵심 기능입니다.
이번 강좌에서는 스프링 부트를 이용한 백엔드 개발에서 JWT(JSON Web Token)를 사용하여 로그인 및 로그아웃 기능을 구현하는 방법을 자세히 알아보겠습니다.
이 강좌에서는 기본적인 스프링 부트 애플리케이션을 설정하고, JWT 기반 인증 시스템을 구현하며, 컨트롤러를 추가하여 RESTful API를 완성하는 과정을 다룹니다.
2. 스프링 부트란?
스프링 부트는 자바 기반의 프레임워크인 스프링(SPRING)을 보다 쉽게 사용할 수 있도록 도와주는 도구입니다.
이를 통해 개발자는 설정과 구성을 최소화하고, 빠르게 애플리케이션을 구축할 수 있습니다.
스프링 부트는 독립 실행 가능한 JAR 파일로 패키징되어 실행될 수 있으며, RESTful 서비스를 효율적으로 개발할 수 있습니다.
스프링 부트의 주요 특성은 다음과 같습니다:
- 자동 구성: 스프링 부트는 개발자가 필요로 하는 기본 설정을 자동으로 구성해 줍니다.
- 스타터 패키지: 개발자는 필요한 기능을 빠르게 추가할 수 있는 스타터 패키지를 사용할 수 있습니다.
- 내장 서버: 스프링 부트는 톰캣, 제트티, 언더타우 등과 같은 내장 서버를 제공하여, 쉽게 애플리케이션을 실행할 수 있습니다.
- 의존성 관리: Maven 또는 Gradle을 사용해 의존성을 소스 코드 내에서 간편하게 관리할 수 있습니다.
3. JWT란?
JWT( JSON Web Token)는 안전한 정보 전송을 위한 인터넷 표준 RFC 7519입니다. JWT는 JSON 객체를 사용하여 주체(sub), 발행자(iss), 만료 시간(exp) 등의 정보를 암호화하여 전달합니다.
JWT는 세 부분으로 구성됩니다:
- 헤더(header): JWT의 유형과 사용할 서명 알고리즘을 지정합니다.
- 페이로드(payload): 전송할 정보와 그 정보를 설명하는 메타데이터를 포함합니다.
- 서명(signature): 헤더와 페이로드를 안전하게 하여 변조를 방지합니다. 비밀 키를 사용하여 생성됩니다.
JWT는 트래픽이 높은 환경에서 API 인증으로 많이 사용됩니다. 세션을 서버에 저장할 필요가 없기 때문에 효율적이며, 클라이언트에 상태 정보를 보관할 수 있어
서버의 부하를 줄일 수 있습니다.
4. 프로젝트 설정
4.1. 스프링 부트 프로젝트 생성
스프링 부트 프로젝트를 생성하기 위해 Spring Initializr를 사용합니다.
필요한 설정을 다음과 같이 입력합니다:
- Project: Maven Project
- Language: Java
- Spring Boot: 2.6.6 (가장 최신 버전)
- Project Metadata:
- Group: com.example
- Artifact: jwt-demo
- Name: jwt-demo
- Description: JWT Authentication Demo
- Packaging: Jar
- Java: 11
그리고 다음의 의존성을 추가합니다:
- Spring Web
- Spring Security
- Spring Data JPA
- H2 Database
- jjwt (Java JWT)
4.2. 프로젝트 구조
프로젝트 생성 후, 기본 패키지 구조는 다음과 같습니다:
└── src └── main ├── java │ └── com │ └── example │ └── jwt_demo │ ├── JwtDemoApplication.java │ ├── controller │ ├── model │ ├── repository │ ├── security │ └── service └── resources ├── application.properties └── static
5. 데이터베이스 설정
H2 데이터베이스를 사용하여 사용자 정보를 저장할 수 있습니다.
application.properties 파일에 다음과 같이 설정합니다:
spring.h2.console.enabled=true spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password= spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
6. 사용자 모델 만들기
사용자의 정보를 담기 위한 User 모델 클래스를 생성합니다.
package com.example.jwt_demo.model; import javax.persistence.*; @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String username; @Column(nullable = false) private String password; // Getters and Setters... public User() {} public User(String username, String password) { this.username = username; this.password = password; } }
7. 사용자 리포지토리 생성하기
사용자 정보를 데이터베이스에서 관리하기 위해 JPA 리포지토리 인터페이스를 생성합니다.
package com.example.jwt_demo.repository; import com.example.jwt_demo.model.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends JpaRepository{ User findByUsername(String username); }
8. 보안 설정
스프링 시큐리티를 통해 JWT 인증을 구현하겠습니다. 이를 위해 WebSecurityConfigurerAdapter을 상속하는 SecurityConfig 클래스를 생성합니다.
package com.example.jwt_demo.security; import com.example.jwt_demo.filter.JwtRequestFilter; import com.example.jwt_demo.service.UserDetailsServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsServiceImpl userDetailsService; @Autowired private JwtRequestFilter jwtRequestFilter; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/authenticate").permitAll() .anyRequest().