세션 방식의 로그인 과정에 대해 설명해 주세요.
세션 방식 로그인은 사용자 인증 후 서버가 세션을 생성하고, 클라이언트에게 Session ID를 발급하여 이후 요청마다 인증 상태를 유지하는 방식입니다.
전체 흐름
[로그인]
1. 클라이언트 → 서버 : POST /login (아이디, 비밀번호)
2. 서버 : DB에서 사용자 조회 + 비밀번호 검증
3. 서버 : 세션 생성 (Session ID + 사용자 정보 저장)
4. 서버 → 클라이언트 : Set-Cookie: JSESSIONID=abc123
[인증이 필요한 요청]
5. 클라이언트 → 서버 : Cookie: JSESSIONID=abc123
6. 서버 : Session ID로 세션 저장소 조회 → 사용자 확인
7. 서버 → 클라이언트 : 응답 반환
[로그아웃]
8. 클라이언트 → 서버 : POST /logout
9. 서버 : 세션 무효화(삭제)
10. 서버 → 클라이언트 : Set-Cookie: JSESSIONID=; Max-Age=0
Java(Spring) 구현 예시
로그인
@PostMapping("/login")
public ResponseEntity<String> login(
@RequestBody LoginRequest request,
HttpSession session) {
// 1. DB에서 사용자 조회
Member member = memberRepository.findByLoginId(request.getLoginId())
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 사용자"));
// 2. 비밀번호 검증 (BCrypt 등 해시 비교)
if (!passwordEncoder.matches(request.getPassword(), member.getPassword())) {
throw new IllegalArgumentException("비밀번호 불일치");
}
// 3. 세션에 사용자 정보 저장 (이 시점에 세션 생성 + JSESSIONID 발급)
session.setAttribute("loginMember", member.getId());
return ResponseEntity.ok("로그인 성공");
}
인증 확인
@GetMapping("/mypage")
public ResponseEntity<MemberResponse> myPage(HttpSession session) {
// 세션에서 사용자 정보 조회
Long memberId = (Long) session.getAttribute("loginMember");
if (memberId == null) {
throw new UnauthorizedException("로그인이 필요합니다");
}
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new IllegalArgumentException("사용자 없음"));
return ResponseEntity.ok(new MemberResponse(member));
}
로그아웃
@PostMapping("/logout")
public ResponseEntity<String> logout(HttpSession session) {
// 세션 무효화 → 서버의 세션 데이터 삭제 + 쿠키 만료
session.invalidate();
return ResponseEntity.ok("로그아웃 성공");
}
HTTP 요청/응답 예시
// 로그인 요청
POST /login HTTP/1.1
Content-Type: application/json
{"loginId": "hong", "password": "1234"}
// 로그인 응답
HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=abc123; Path=/; HttpOnly; Secure
// 이후 인증 요청
GET /mypage HTTP/1.1
Cookie: JSESSIONID=abc123
세션 저장소의 내부 구조
세션 저장소 (서버 메모리 / Redis)
┌──────────────────────────────────────────┐
│ Session ID │ 세션 데이터 │
├──────────────────────────────────────────┤
│ abc123 │ {loginMember: 1} │
│ def456 │ {loginMember: 7} │
│ ghi789 │ {loginMember: 15} │
└──────────────────────────────────────────┘
보안 고려사항
| 위협 | 대응 방법 |
|---|---|
| 세션 하이재킹 (Session ID 탈취) | HttpOnly, Secure, SameSite 쿠키 속성 설정 |
| 세션 고정 공격 (Session Fixation) | 로그인 성공 시 기존 세션 무효화 후 새 Session ID 발급 |
| 무차별 대입 | Session ID를 충분히 길고 랜덤하게 생성 (Tomcat 기본 제공) |
| 세션 만료 미처리 | session.setMaxInactiveInterval(1800) 등 타임아웃 설정 |
// Spring Security - 세션 고정 공격 방어 (기본 활성화)
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.sessionManagement(session -> session
.sessionFixation().changeSessionId() // 로그인 시 새 Session ID 발급
.maximumSessions(1) // 동시 로그인 1개로 제한
.maxSessionsPreventsLogin(false) // 기존 세션 만료 처리
);
return http.build();
}
}
면접 포인트
- 세션 방식은 서버가 인증 상태를 관리하므로 강제 로그아웃, 동시 접속 제한 등 세밀한 제어가 가능하다는 장점이 있습니다.
- 반면 서버에 상태를 저장하므로 scale-out 시 세션 공유 문제가 발생하며, 이를 해결하기 위해 Sticky Session, 세션 클러스터링, Redis 외부 저장소 등을 활용합니다.
- 비밀번호는 반드시 단방향 해시(BCrypt 등) 로 저장하고, 평문 비교가 아닌 해시 비교로 검증해야 합니다.