스프링 부트 백엔드 개발 강좌, JWT로 로그인 로그아웃 구현, 리프레시 토큰 도메인 구현하기

최근 몇 년간 마이크로서비스 아키텍처와 SPA(Single Page Application)가 점점 더 많은 인기를 끌면서, 웹 애플리케이션에서 보안 방안을 찾는 것이 매우 중요해졌습니다. 이 글에서는 스프링 부트를 활용해 JWT(JSON Web Token)를 이용한 로그인/로그아웃 기능과 리프레시 토큰을 사용하는 도메인을 구현하는 방법에 대해 자세히 설명하겠습니다.

목차

  1. 1. 서론
  2. 2. 스프링 부트란?
  3. 3. JWT란?
  4. 4. 개발 환경 설정
  5. 5. 로그인/로그아웃 기능 구현
  6. 6. 리프레시 토큰 구현
  7. 7. 결론

1. 서론

사용자 인증(authentication)과 인가(authorization)는 현대 웹 애플리케이션에서 중요한 문제입니다. 이러한 문제를 해결하기 위한 여러 가지 기술이 있지만, JWT는 그 중 가장 많이 사용되는 방법 중 하나입니다. 본 강좌에서는 스프링 부트를 활용하여 사용자 로그인 및 로그아웃 기능을 구축하고, JWT와 리프레시 토큰을 이용한 인증 메커니즘을 구현하는 방법을 배우게 됩니다.

2. 스프링 부트란?

스프링 부트(Spring Boot)는 스프링 프레임워크의 확장으로, Spring 기반의 애플리케이션을 쉽게 개발할 수 있도록 도와주는 편리한 도구입니다. 초기 설정이나 복잡한 XML 설정 없이 간단한 어노테이션으로 구현이 가능하여, 개발자들이 자주 사용하는 기능을 미리 구성해놓은 상태로 제공됩니다.

스프링 부트의 주요 특징은 다음과 같습니다:

  • 자체 내장 서버: 톰캣, 제티 등의 웹 서버를 포함하여, 별도의 서버 설치 없이 실행할 수 있습니다.
  • 자동 구성: 프로젝트에 포함된 라이브러리와 빈 설정에 따라 필요한 설정을 자동으로 구성합니다.
  • 쉬운 배포: Jar 파일로 패키징하여 쉽게 배포 가능하며, 클라우드 환경에서도 용이하게 사용할 수 있습니다.
  • 강력한 커뮤니티: 풍부한 문서와 예제, 다양한 플러그인으로 지원되는 큰 커뮤니티를 갖추고 있습니다.

3. JWT란?

JWT(JSON Web Token)는 주로 인증과 인가를 위한 솔루션으로 사용되는 compact, URL-safe means of representing claims to be transferred between two parties. 여기서 ‘claims’는 사용자에 대한 정보로, 이를 통해 서버와 클라이언트 간의 신뢰 관계를 유지할 수 있습니다.

JWT는 크게 세 부분으로 구성됩니다:

  1. Header: 토큰의 타입(typ)과 해싱 알고리즘(alg)의 정보가 포함됩니다.
  2. Payload: 사용자에 대한 정보와 클레임 데이터를 포함합니다. 여기서는 사용자 ID, 권한 등의 정보가 들어갑니다.
  3. Signature: Header와 Payload를 조합한 후, 비밀키를 사용해 서명합니다. 이로써 토큰의 무결성과 진위를 보장할 수 있습니다.

JWT의 장점으로는 다음과 같은 것들이 있습니다:

  • 무상태적 특징: 서버는 JWT를 변조할 수 없으므로 상태를 유지하지 않고, 인증이 필요할 때마다 클라이언트가 JWT를 전송합니다.
  • 보안성: 클라이언트가 JWT를 보유하므로 서버의 데이터베이스를 조회할 필요가 없어, 데이터베이스의 부하를 줄일 수 있습니다.
  • CRUD 작업의 단순화: 인증 정보와 상태를 쉽게 관리할 수 있으며, IAM(Identity and access management)과 같은 복잡한 로직을 단순화할 수 있습니다.

4. 개발 환경 설정

이제 스프링 부트를 이용한 프로젝트를 위한 개발 환경을 설정해보겠습니다. 이를 위해 다음과 같은 도구를 사용합니다:

  • Java 11: JDK 11 이상을 설치합니다.
  • IDE: IntelliJ IDEA 또는 Eclipse를 사용하는 것이 좋습니다.
  • Gradle 또는 Maven: 의존성 관리를 위해 Gradle 또는 Maven을 선택합니다.
  • Postman: API 테스트 도구로 Postman을 이용합니다.

프로젝트를 생성할 때는 Spring Initializr(start.spring.io)를 통해 기본 설정을 해줄 수 있습니다. 필요한 의존성으로는 다음과 같은 것들을 추가합니다:

  • Spring Web
  • Spring Security
  • Spring Data JPA
  • H2 Database (또는 여러분이 선택한 RDBMS)
  • Spring Boot DevTools (개발 편의를 위한 도구)
  • jjwt (Java JWT 라이브러리)

프로젝트 생성 후 Viewer, Controller, Service, Repository 등 필요한 클래스를 생성합니다. `application.yml` 파일에 데이터베이스 관련 설정을 추가해야 합니다.

5. 로그인/로그아웃 기능 구현

로그인 및 로그아웃 기능을 구현하기 위해 다음의 구성 요소가 필요합니다:

5.1. User Entity

사용자 정보를 저장하기 위해 User 엔티티를 생성합니다.


@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String password;
    private String role;

    // Getter, Setter, Constructor
}

5.2. User Repository

User 엔티티에 대한 CRUD 작업을 수행할 UserRepository를 생성합니다.


@Repository
public interface UserRepository extends JpaRepository {
    Optional findByUsername(String username);
}

5.3. User Service

사용자 등록 및 인증 로직을 구현하기 위한 UserService를 생성합니다.


@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public User register(User user) {
        // 여기에 비밀번호 암호화 로직을 추가하고 사용자 정보를 저장합니다.
    }

    public Optional findByUsername(String username) {
        return userRepository.findByUsername(username);
    }
}

5.4. Security Configuration

스프링 시큐리티를 설정하여 JWT 인증을 수행할 수 있도록 합니다.


@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // 사용자 인증 및 보안 관련 설정을 정의합니다.
}

5.5. JWT Utility 클래스

JWT를 생성하고 검증하는 역할을 하는 JWTUtil 클래스를 작성합니다.


@Component
public class JwtUtil {
    private String secretKey = "yourSecretKey"; // 비밀키를 안전하게 관리해야 합니다.

    public String generateToken(String username) {
        // JWT 생성 로직 구현
    }

    public boolean validateToken(String token, String username) {
        // JWT 유효성 검사
    }
}

5.6. Authentication Controller

API 엔드포인트를 정의하여 로그인 및 로그아웃 기능을 제공합니다.


@RestController
@RequestMapping("/api/auth")
public class AuthController {
    @Autowired
    private UserService userService;

    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping("/login")
    public ResponseEntity<String> login(@RequestBody UserCredentials userCredentials) {
        // 로그인 로직
    }

    @PostMapping("/logout")
    public ResponseEntity<String> logout() {
        // 로그아웃 로직
    }
}

6. 리프레시 토큰 구현

리프레시 토큰은 액세스 토큰과 별도로 사용되며, 사용자가 계속해서 로그인 상태를 유지하도록 도와줍니다. 리프레시 토큰을 구현하기 위해 다음과 같은 단계를 수행합니다:

6.1. Refresh Token Entity

새로운 엔티티를 생성하여 리프레시 토큰을 관리합니다.


@Entity
public class RefreshToken {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String token;

    @ManyToOne
    private User user;

    private LocalDateTime expiryDate;

    // Getter, Setter, Constructor
}

6.2. RefreshToken Repository

리프레시 토큰에 대한 CRUD 작업을 수행하기 위한 리포지토리를 생성합니다.


@Repository
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
    Optional<RefreshToken> findByToken(String token);
}

6.3. Refresh Token Service

리프레시 토큰의 생성을 관리하는 서비스를 생성합니다.


@Service
public class RefreshTokenService {
    @Autowired
    private RefreshTokenRepository refreshTokenRepository;

    public RefreshToken createRefreshToken(User user) {
        // 새로운 리프레시 토큰 생성 로직
    }

    public boolean validateRefreshToken(String token) {
        // 리프레시 토큰 유효성 검사
    }
}

6.4. Refresh Token Controller

리프레시 토큰을 이용하여 새로운 액세스 토큰을 생성하는 API를 추가합니다.


@RestController
@RequestMapping("/api/auth")
public class AuthController {
    @Autowired
    private RefreshTokenService refreshTokenService;

    @PostMapping("/refresh-token")
    public ResponseEntity<String> refreshToken(@RequestBody String refreshToken) {
        // 리프레시 토큰을 이용한 새로운 액세스 토큰 생성 로직
    }
}

7. 결론

이 강좌에서는 스프링 부트를 활용하여 JWT 기반의 로그인/로그아웃 기능을 구현하고, 추가적으로 리프레시 토큰을 이용하여 액세스 토큰을 관리하는 방법을 살펴보았습니다. 이런 방식으로 구현한 인증 시스템은 더욱 안전하고 유연하게 다양한 클라이언트 요청을 처리할 수 있습니다.

이제 여러분의 애플리케이션에서 JWT와 리프레시 토큰을 구현하여 보다 향상된 사용자 경험을 제공할 수 있게 될 것입니다. 더 나아가 이 기술을 활용해 다양한 추가적인 보안 기법들과 통합할 수 있습니다. 앞으로의 발전을 기대합니다!