Bootcamp/Implementation

JWT 기반 로그인 구현과 Access Token 발급

개발자 오리 2026. 7. 7. 17:51

💡  오늘 학습 키워드

  • JWT 기반 로그인 구현 & Access Token 발급

 

 

🎯  학습 내용 정리

로그인은 어떻게 동작할까?

Spring Security를 처음 사용하면 로그인 API를 직접 구현하는 것이 다소 낯설 수 있다.

기존 Session 기반 인증에서는 Spring Security가 /login 요청을 자동으로 처리하지만, JWT 기반 인증에서는 로그인 API를 직접 구현해야 한다.

 

로그인 과정은 다음과 같은 순서로 진행된다.

Client
  ↓ ID / Password
Controller
  ↓
AuthenticationManager
  ↓
UserDetailsService
  ↓
PasswordEncoder
  ↓
Authentication 생성
  ↓
JwtProvider
  ↓
Access Token 발급
  ↓
Client
 
 
 

먼저 로그인 요청을 받을 DTO를 작성한다.

public record LoginRequest(

        @NotBlank
        String email,

        @NotBlank
        String password

) {
}
 

로그인 응답은 Access Token을 반환하도록 구성한다.

public record LoginResponse(

        String accessToken

) {
}
 
 
 

Controller에서는 로그인 요청을 Service에 전달하는 역할만 수행한다.

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

    private final AuthService authService;

    @PostMapping("/login")
    public ApiResponse<LoginResponse> login(

            @Valid @RequestBody LoginRequest request

    ) {

        return ApiResponse.success(
                authService.login(request)
        );

    }

}
 

Controller는

  • JWT 생성
  • 비밀번호 비교
  • 사용자 조회

등의 인증 로직을 직접 수행하지 않는다.

요청을 Service에 전달하고 결과를 반환하는 역할만 담당한다.

 

 

Service에서 실제 인증은 AuthenticationManager를 이용한다.

@Service
@RequiredArgsConstructor
public class AuthService {

    private final AuthenticationManager authenticationManager;
    private final JwtProvider jwtProvider;

    public LoginResponse login(LoginRequest request) {

        Authentication authentication =
                authenticationManager.authenticate(

                        new UsernamePasswordAuthenticationToken(
                                request.email(),
                                request.password()
                        )

                );

        String accessToken =
                jwtProvider.createAccessToken(authentication);

        return new LoginResponse(accessToken);

    }

}

 

 

AuthenticationManagerSpring Security에서 로그인 인증을 담당하는 핵심 컴포넌트이다.

다음 코드를 보면

authenticationManager.authenticate(
    authentication
);
 

Spring Security가

  • 사용자 조회
  • 비밀번호 비교
  • 인증 성공 여부 판단

을 모두 수행한다.

즉, Service에서 직접 비밀번호를 비교할 필요가 없다.

 

 

내부 동작 과정은 다음과 같다.

AuthenticationManager
  ↓
DaoAuthenticationProvider
  ↓
UserDetailsService
  ↓
User 조회
  ↓
PasswordEncoder.matches()
  ↓
Authentication 생성
 

 

 

UsernamePasswordAuthenticationToken은 무엇일까?

로그인 요청으로 전달된 아이디와 비밀번호를 Spring Security에 전달하기 위한 객체이다.

new UsernamePasswordAuthenticationToken(

        request.email(),
        request.password()

)
 

이 시점에는 아직 인증이 완료되지 않았다.

AuthenticationManager가 인증을 성공하면 새로운 Authentication 객체를 반환한다.

 

즉, 로그인 요청 시에는 다음과 같지만

Principal : email
Credentials : password
Authenticated : false
 

 

 

인증 성공 후에는 다음과 같이 상태가 변경된다.

Principal : CustomUserDetails
Credentials : null
Authenticated : true
 

 

 

PasswordEncoder는 언제 사용될까?

AuthenticationManager는 내부적으로 PasswordEncoder를 이용하여 비밀번호를 비교한다.

우리가 직접

passwordEncoder.matches()
 

를 호출하지 않아도 된다.

 

SecurityConfig에서 등록한 Bean이 자동으로 사용된다.

@Bean
public PasswordEncoder passwordEncoder() {

    return new BCryptPasswordEncoder();

}
 

Spring Security는

DB Password
  ↓
BCrypt Hash
 

사용자가 입력한 Password
 

를 비교하여 인증 여부를 결정한다.

 

 

인증이 성공하면 Authentication 객체가 반환된다.

Authentication authentication =
        authenticationManager.authenticate(
                token
        );
 

이 객체에는

  • 로그인한 사용자 정보
  • 권한(Role)
  • 인증 여부

등이 저장된다.

 

예를 들면

Principal
  ↓
CustomUserDetails
  ↓
UserEntity
 

와 같은 구조를 가진다.

 

이 Authentication을 이용하여 JWT를 생성한다.

String accessToken =
        jwtProvider.createAccessToken(authentication);
 

 

 

왜 UserRepository를 직접 조회하지 않을까?

로그인을 직접 구현하면 다음과 같은 코드를 작성하는 경우가 많다.

User user = userRepository.findByEmail(email);

if (!passwordEncoder.matches(
        password,
        user.getPassword()
)) {
    throw new LoginException();
}
 

동작에는 문제가 없지만, Spring Security가 제공하는 인증 체계를 우회하게 된다.

AuthenticationManager를 사용하면

  • UserDetailsService
  • PasswordEncoder
  • AuthenticationProvider
  • 인증 예외 처리

등을 Spring Security가 일관된 방식으로 수행한다.

Service는 인증을 요청하고 결과만 활용하면 된다.

 

 

이번 글에서 구현한 흐름은 다음과 같다.

Client
  ↓
Controller
  ↓
AuthenticationManager
  ↓
UserDetailsService
  ↓
PasswordEncoder
  ↓
Authentication 생성
  ↓
JwtProvider
  ↓
Access Token 발급
  ↓
Client
 

로그인 API는 사용자 인증을 직접 구현하는 것이 아니라, Spring Security의 인증 체계에 인증을 위임하고 인증이 성공한 결과를 기반으로 JWT를 발급하는 과정이다.

AuthenticationManager를 사용하면 사용자 조회, 비밀번호 검증, 인증 객체 생성이 일관된 방식으로 처리되며, Service는 JWT 발급과 같은 비즈니스 흐름에 집중할 수 있다.

 

 

이번 글에서는 JWT 기반 로그인 API를 구현하고, Spring Security가 제공하는 AuthenticationManager를 이용해 인증을 처리하는 방법을 살펴보았다.

로그인 과정에서 직접 사용자 정보를 조회하거나 비밀번호를 비교하지 않고 Spring Security에 인증을 위임함으로써 인증 로직과 비즈니스 로직을 명확하게 분리할 수 있었다.

 

 

📚  한줄 정리

JWT 로그인은 Service가 직접 인증하는 것이 아니라, AuthenticationManager에 인증을 위임하고 성공한 Authentication을 기반으로 Access Token을 발급하는 과정이다.

'Bootcamp > Implementation' 카테고리의 다른 글

UserDetails와 UserDetailsService  (0) 2026.07.09
Authentication과 SecurityContextHolder  (0) 2026.07.08
Spring Security 구조 설계  (0) 2026.07.06
OAuth  (0) 2026.06.28
Spring Security + JWT  (0) 2026.06.27