인증 워크플로우
- 회원가입과 로그인 과정에서 어떤 흐름이 이루어지는지 단계별로 이해하기
- 인증 상태 유지의 필요성, 구현 방법(세션방식, 토큰방식) 이해하기
- 실제 워크플로우 다이어그램과 예제를 통해 이해하기
- 단순 기능 구현 + 보안적 고려 사항까지 함께 학습
- 실무 환경에서 발생할 수 있는 문제상황, 대응방법
회원가입(Registration) 워크플로우
새로운 사용자가 시스템에 계정을 등록하는 첫 단계
올바른 회원가입 절차로 이후 로그인, 인증을 올바르게 작동시킴
1) 회원가입의 전체 흐름
사용자(브라우저) - > 서버 - > 검증 - > 비밀번호 암호화 or 에러 응답 - > 회원가입 완료
2) 디렉토리 구조 예시
src/main/java/user
User.java
UserRepository.java
UserService.java
UserController.java
3) 회원가입 예제
public class UserService {
private UserRepository userRepository = new UserRepository();
public void register(String email, String rawPassword, String nickname) {
// 이메일 중복 확인
if(userRepository.findById(email) != null) { // userRepository에서 email을 찾는데 있다면,
throw new IllegalArgumentException("이미 존재하는 이메일입니다.");
}
// 비밀번호 정책 검증
if(rawPassword.length() < 8) { // rawPassword의 길이가 8을 넘는다면,
throw new IllegalArgumentException("비밀번호는 8자리 이상!");
}
// 간단한 비밀번호 해시처리(BCrypt 등 사용해야함)
String passwordHash = Integer.toHexString(rawPassword.hashCode()); // 해시코드로 바꿈
User user = new User(email, passwordHash, nickName);
userRepository.save(user);
System.out.println("회원가입 성공!");
}
}
public class User {
private String email; // 이메일
private String passwordHash; // 암호화된 비밀번호
private String nickname; // 닉네임
// setter
public User(String email, String passwordHash, String nickname) {
this.email = email;
this.passwordHash = passwordHash;
this.nickname = nickname;
}
}
* 비밀번호는 반드시 BCrypt, Argon2와 같은 강력한 해시 알고리즘을 사용해야함
* 이메일 인증(Verification)을 통해 계정 활성화 절차를 두는게 안전하다
비밀번호 분실 대비 기능(비밀번호 재설정 이메일 발송??)도 반드시 고려해야함
로그인 워크플로우
회원가입된 사용자가 본인임을 증명하는 과정
1) 로그인 전체 흐름
2) 로그인 예제
public class UserService {
private UserRepository userRepository = new UserRepository();
// 로그인 처리
public boolean login(String email, String rawPassword) {
User user = userRepository.findByEmail(email);
if(user == null) {
System.out.println("존재하지 않는 사용자입니다.");
return false;
}
// 입력받은 비밀번호를 해시처리
String passwordHash = Integer.toHexString(rawPassword.hashCode());
if(user.getPasswordHash().equals(passwordHash)) {
System.out.println("로그인 성공!");
return true;
} else {
System.out.println("이메일 혹은 비밀번호가 일치하지 않습니다.");
return false;
}
}
}
* 로그인 실패 횟수를 기록해서 계정 잠금 정책을 사용할 수 있다.
* 로그인 성공 시 반드시 세션(Session) 혹은 토큰(Token)을 발급해야함!
* 보안을 위해서 로그인 로그를 남겨야하며, 이상 징후(짧은 시간 내 다수의 로그인 실패 등..)를 탐지하는게 중요하다고 함
인증 상태 유지의 필요성
인증 상태 유지는 HTTP 프로토콜의 속성(무상태성) 때문에 필수임
기본적으로 HTTP는 무상태성을 띄며, 연결 유지가 보장되지 않는(connection agnostic) 요청-응답 모델임
따라서 사용자가 한번 로그인했다고 해서 이후 요청에서도 자동으로 신원이 보장되는건 아님
따라서 세션 혹은 토큰같은 매커니즘으로 '누가 요청했는가??'를 매 요청마다 증명해야함
1) 왜 필요한가?
- 무상태성(Stateless) : 서버는 각 HTTP 요청을 독립적 사건으로 취급함.
이전 요청의 맥락(로그인 여부, 사용자 정보 등)을 자동으로 기억하지 않음
- 연결 재사용과는 무관함 : HTTP 1.1에서 keep-alive가 있더라도,
이는 TCP 소켓의 재사용일 뿐 응용 계층의 인증 상태를 보장하지 않음.
로드밸런서가 요청을 다른 서버로 보낼수도 있고, 연결이 끊겼다가 재연결될 수도 있음
- 분산 인프라 특성 : 실제 서비스는 로드밸런서, 다수의 서버, 캐시, 프록시가 개입한다.
서버 로컬 메모리에만 의존한다면 다른 서보로 라우팅되는 순간 인증 상태를 잃어버리게 된다.
중앙 저장소(세션 스토어) 또는 클라이언트가 들고 다니는 증표(토큰) 같은게 필요하다.
- 보안 경계의 일관성 : TLS 종료 지점, 역프록시, API 게이트웨이가 바뀌어도 요청 그 자체에 신원 증거가 포함되어있어야 안전함
즉, 매 요청마다 신원확인이 가능해야한다
- > HTTP의 기본은 "요청은 서로 무관하다" 임. 따라서 인증 상태는 프로토콜 바깥에서 별도 장치로 명시적으로 유지해야함
별도 장치는 세션, 토큰을 사용한다
2) 상태를 유지하는 대표 전략
(1) 세션 기반 방식(쿠키)
로그인 성공시 서버가 세션 ID를 생성하고, 이를 쿠키로 클라이언트에 전달함
이후 요청마다 쿠키로 세션을 찾아서 사용자의 신원을 복원한다.
- 구성 요소 : 세션 저장소(메모리, Redis, DB), 쿠키 설정(HttpOnly, Secure, SameSite)
- 장점 : 구현이 비교적 단순하다, 서버가 세션을 제어하므로 즉시 강제 로그아웃이 가능함(세션 삭제)
- 단점 : 서버 측 상태를 유지하기 때문에 확장할 때 저장소 일관성이 필요함(공유 저장소가 필수임)
| 옵션 | 설명 | 권장값 |
| HttpOnly | JS의 접근을 차단함(XSS 완화) | true |
| Secure(아마도 Spring Secure) | HTTPS에서만 전송함 | true |
| SameSite | 크로스 사이트 전송 제어 | Lax 또는 Strict(필요시 None + Secure) |
| Path | 쿠키가 전송될 경로 | / |
| Domain | 쿠키 적용 도메인 | 서비스 도메인으로 제한함 |
확장성 : 다중 서버 환경에서는 스티키 대신 공유 세션 저장소(Redis 같은것) 사용을 권장한다고 함
(2) 토큰 기반 방식(JWT 등등...)
로그인 성공시 서명이 포함된 토큰을 발급함.
클라이언트는 이후 요청마다 토큰을 Authorization 헤더로 전송하고, 서버는 서명 검증으로 신원을 확인함
- 구성 요소 : Access Token(짧은 수명), Refresh Token(긴 수명, 재발급용), 키 관리(비밀키 또는 공개키)
- 장점 : 서버가 인증 상태를 저장하지 않아서 수평 확장이 쉬움
- 단점 : 발급된 토큰은 유효기간 동안 기본적으로 유효함.
따라서 강제 만료가 필요하면 블랙리스트나 토큰 버전 필드 등을 도입해야만 함
JWT 핵심 클레임
| 클레임 | 의미 | 비고 |
| sub | 주체 ID(유저 식별자) | 필수에 가깝게 사용함 |
| iss | 발급자 | 서비스 식별 |
| aud | 대상자 | 특정 클라이언트 제한 |
| exp | 만료 시간 | 반드시 짧게 설정하기(15분정도) |
| iat | 발급시간 | 토큰 생성 시각 |
| jti | 토큰 식별자 | 재사용 감지, 블랙리스트 키로 활용 |
* Access Token은 짧게, Refresh Token은 회전(Rotation) 혹은 원장 기록으로 관리함
기본 흐름 다이어그램
1) 세션 기반
2) 토큰 기반
3) 세션 vs 토큰
| 방식 | 설명 | 장점 | 단점 | 적용사례 |
| 세션 | 서버 메모리에 인증정보 저장 | 단순하고, 안정적임 | 서버 부하 증가 | 소규모 웹 서비스 |
| 토큰 | 클라이언트에 토큰을 저장하고 매 요청마다 전달 |
확장성이 높음 | 토큰이 탈취되면 위험함 | 모바일, 분산환 |
* HTTPS를 반드시 적용해서 세션 ID나 토큰이 탈취되지 않도록 해야함
* 토큰에는 민감한 정보를 직접 담지 말고, 필요한 최소한의 정보만 넣어야함
* 로그아웃시 세션/토큰을 무효화하는 절차를 반드시 포함해야함
'Spring Boot' 카테고리의 다른 글
| 쿠키와 세션 기반 인증 : 세션 기반 인증 (0) | 2025.09.24 |
|---|---|
| 쿠키와 세션 기반 인증 : 쿠키의 개념, 특성 (0) | 2025.09.24 |
| 유저 관리 기능 : 유저 기능의 필요성과 활용 (0) | 2025.09.23 |
| 로깅 : 로그 분석과 활용 (1) | 2025.08.21 |
| 로깅 : 로그 레벨별 활용전략 (1) | 2025.08.21 |