주말에 약간의 시간을 활용해서 진도를 살짝 나가보았다.
1️⃣ Session
✅ Session ?
⚡ 서버에서 중요한 정보를 보관하며 로그인 연결을 유지하는 방법이다.
Cookie를 사용한 방식은 중요한 정보를 Client에서 보관하고 있기 때문에 이를, 서버 쪽에서 관리를 하여야한다.

- 로그인에 성공하면 Server에서 임의로 만든 Session ID를 생성한다.
- Session ID는 예측이 불가능해야 한다.
- UUID와 같은 값을 활용한다.
- 생성된 Session ID와 조회한 User 인스턴스를 서버의 Session 저장소에 저장한다.
- 서버에 유저와 관련된 중요한 정보를 저장한다.
※ Session 동작 순서
1. 로그인
- 상태유지를 위해 Cookie를 사용한다.
- 서버는 클라이언트에 Set-Cookie: SessionId=임의생성값 을 전달한다.
- 클라이언트는 Cookie 저장소에 전달받은 SessionId 값을 저장한다.
- Sessions을 사용하면 유저와 관련된 정보는 클라이언트에 없다.
결론적으로 웹 브라우저에는 id값만 저장 된다는 것이다.

2. 로그인 이후

- 클라이언트는 모든 요청에 Cookie 의 SessionId를 전달한다.
- 서버에서는 Cookie를 통해 전달된 SessionId로 Session 저장소를 조회한다.
- 로그인 시 저장하였던 Session 정보를 서버에서 사용한다.
- Session 특징
- Session을 사용하여 서버에서 민감한 정보들을 저장한다.
- 예측이 불가능한 세션 ID를 사용하여 쿠키값을 변조해도 문제 ❌
- 세션 ID에 중요한 정보는 들어있지 않다.
- 시간이 지나면 세션이 만료되도록 설정
- 해킹이 의심되는 경우 해당 세션을 제거
- Session은 단지 Cookie를 사용하여
클라이언트가 아닌서버에서 데이터를 저장해두는 방법이다. - Servlet은 Session 을 자체적으로 지원한다.
- Session은 Memory를 사용하기 때문에 리소스를 낭비하면 안된다. 💥
- Session을 사용하여 서버에서 민감한 정보들을 저장한다.
✅ HttpSession
Servlet이 공식적으로 지원하는 Session인 HttpSession은 Session 구현에 필요한 다양한 기능들을 지원한다.
Servlet을 통해 HttpSession 을 생성하게되면 SessionId 가 JSESSIONID로 생성되고 JSESSIONID의 Value는 예측 불가능한 랜덤값으로 생성된다.
@PostMapping("/session-login")
public String login(
@Valid @ModelAttribute LoginRequestDto dto,
HttpServletRequest request
) {
LoginResponseDto responseDto = userService.login(dto.getUserName(), dto.getPassword());
Long userId = responseDto.getId();
// 실패시 예외처리
if (userId == null) {
return "session-login";
}
// 로그인 성공시 로직
// Session의 Default Value는 true이다.
// Session이 request에 존재하면 기존의 Session을 반환하고,
// Session이 request에 없을 경우에 새로 Session을 생성한다.
HttpSession session = request.getSession();
// 회원 정보 조회
UserResponseDto loginUser = userService.findById(userId);
// Session에 로그인 회원 정보를 저장한다.
session.setAttribute(Const.LOGIN_USER, loginUser);
// 로그인 성공시 리다이렉트
return "redirect:/session-home";
}
@PostMapping("/session-logout")
public String logout(HttpServletRequest request) {
// 로그인하지 않으면 HttpSession이 null로 반환된다.
HttpSession session = request.getSession(false);
// 세션이 존재하면 -> 로그인이 된 경우
if(session != null) {
session.invalidate(); // 해당 세션(데이터)을 삭제한다.
}
return "redirect:/session-home";
}
- request.getSession();
- request.getSession(true);
- Default 설정
- Request 객체 내에 Session이 존재한다면 기존 Session을 반환
- Request 객체 내에 Session이 없으면 새로운 Session을 생성해서 반환
- request.getSession(false);
- Request 객체 내에 Session이 존재한다면 기존 Session을 반환
- Request 객체 내에 Session이 없으면 null을 반환
- request.getSession(true);
- session.setAttribute(Const.LOGIN_USER, responseDto);
- Session에 Data를 저장하는 방법으로 request.setAttribute(); 와 비슷
- 하나의 Session에 여러개의 데이터를 메모리에 저장할 수 있다.
✅ Spring의 Session
📣 Spring에서 Session 쉽게 다루도록 @SessionAttribute 라는 어노테이션 제공함.
@SessionAttribute
- Session을 새로 생성하는 기능 ❌
- 이미 로그인이 완료된 사용자를 찾는 경우 즉, Session이 있는 경우에 사용
🔴 처음 로그인을 시도하는 경우
- 완전히 처음 로그인을 시도하는 경우 URL 뒤에 JSSESSIONID=값이 함께 전달된다.
- 해당 값은 굳이 필요가 없다. 내부적으로 Set-Cookie로 SessionID와 값을 넣어주기 때문
- Cookie를 지원하지 않으면 URL을 통해 Session을 유지하는 방법에 사용된다.
- 하지만, 사용하려면 모든 요청 URL에 jsessionId값이 전달 되어야한다.
- Template Engine을 사용하면 jsessionId를 URL에 자동으로 포함
✅ Session 정보
HttpSession은 Session을 간편하게 사용할 수 있도록 다양한 기능을 지원
@Slf4j
@RestController
public class SessionController {
@GetMapping("/session")
public String session(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
return "세션이 없습니다.";
}
// session 정보 조회
log.info("session.getId()={}", session.getId());
log.info("session.getMaxInactiveInterval()={}", session.getMaxInactiveInterval());
log.info("session.getCreationTime()={}", session.getCreationTime());
log.info("session.getLastAccessedTime()={}", session.getLastAccessedTime());
log.info("session.isNew()={}", session.isNew());
return "세션 조회 성공!";
}
}
- session 정보
- session.getId();
- jsessionId 값을 조회할 수 있다.
- session.getMaxInactiveInterval();
- 세션의 유효시간
- second 단위 default는 30분(1800초)이다.
- session.getCreationTime();
- 세션 생성시간
- session.getLastAccessedTime();
- 해당 세션에 마지막으로 접근한 시간
- session.isNew();
- 새로 생성된 세션인지 여부
- session.getId();
✅ 문제점, 한계
📚 Session은 logout 기능을 사용하여 session.invalidate(); 가 되어야 삭제되지만 대부분의 사용자들은 로그아웃을 굳이 하지않고, 브라우저를 종료한다.
- Session의 문제점
- HTTP는 Connectionless 특성을 가지고 있어서 서버가 브라우저 종료 여부를 판별하지 못한다.
- 서버에서 Session을 언제 삭제해야 하는지 판단하기 힘들다.
- JSESSIONID의 값을 탈취 당한 경우 해당 값으로 악의적인 요청을 할 수 있다.
- 세션은 서버 메모리에 생성되고 자원은 한정적이기 때문에 꼭 필요한 경우만 생성해야 한다.
- Session 생명주기 ❗ ❗
- 기본적으로 30분을 기준으로 세션을 삭제한다.
- 실제 로그인 후 30분 이상의 시간동안 사용중인 사용자의 세션 또한 삭제된다.
- 다시 로그인 해야하는 경우가 발생한다.
- HttpSession 사용
- 세션 생성시점 30분이 아닌 서버에 최근 Session을 요청한 시간을 기준으로 30분을 유지한다.
- HttpSession은 기본적으로 해당 방식으로 세션의 생명주기를 관리한다.
- Session 정보에서 LastAccessedTime 을 기준으로 30분이 지나면 WAS가 내부적으로 세션을 삭제한다.
- 마치, 고용24, 금융 웹 페이지에서 사용자가 아무런 동작이 없으면 30분후 자동으로 로그아웃되는 것.
✅ 인증/인가, 쿠키, 세션 정리

- 인증(Authentication)
- 사용자가 누구인지 확인하는 과정
- 인가(Authorization)
- 사용자가 어떤 권한을 가지고 있는지 결정하는 과정
- 반드시 인증이 선행되어야 한다.
- 쿠키(Cookie)
- 정의
- 웹 브라우저(Client 측)에 저장되는 데이터
- 용도
- 사용자의 방문 기록, 로그인 상태 유지, 개인 맞춤 설정 등을 저장, HTTP 특성 극복, 광고 정보
- 특징
- 클라이언트 측에 저장되며, 서버에 요청을 보낼 때마다 포함되어 전송됨.
- 수명
- 만료 날짜 설정할 수 있음, 세션 쿠키(브라우저 종료 시 삭제)와 영속 쿠키(지정된 기간 동안 유지)로 구분됨.
- 정의
- 세션(Session)
- 정의
- 사용자와 서버 간의 상태를 유지하기 위한 방법.
- 용도
- 로그인 정보, 사용자 활동 등을 서버 측에서 관리.
- 특징
- 서버 측에서 저장되며, 세션 ID가 쿠키에 저장되어 클라이언트와 연결됨.
- 수명
- 브라우저를 닫거나 일정 시간이 지나면 만료됨.
- Session 관리
- 세션은 메모리를 사용한다.
- 세션이 많아지면 서버의 장애가 발생할 수 있다.(메모리 리소스 부족)
- 최소한의 데이터만 저장해야 한다.
- HttpSession은 LastAccessedTime을 기준으로 30분의 생명주기를 가지고 있다.
- 세션의 시간을 너무 오래 유지하여도 메모리 리소스가 부족할 수 있다.
- 적당한 시간 설정이 꼭 필요하다.(Default 30분)
- 세션은 메모리를 사용한다.
- 정의
2️⃣ Token
✅ Token
Web Application이나 API에서 인증(Authentication)과 인가(Authorization) 과정에서 사용되며 사용자 또는 시스템의 신원과 권한을 증명하고 요청의 유효성을 검증하는 데 사용되는 디지털 문자열
- Token을 사용하는 이유
- Token은 서버가 아닌 클라이언트에 저장되어 서버의 부담을 덜 수 있다.
- Cookie는 웹 브라우저에만 존재하여 모바일 앱 등의 다양한 클라이언트에서 인증을 처리할 수 없다.
- Token 방식은 Stateless를 기반으로 하여 확장성이 뛰어나다.
- 인증된 사용자임을 확인하기 위한 고유한 서명을 포함하여 위조된 요청인지 확인할 수 있다.
- Token의 단점
- Cookie/Session 방식보다 Token 자체의 데이터 용량이 많다.
- 요청이 많아지면 그만큼 트래픽이 증가한다.
- Payload(전송되는 데이터)는 암호화되지 않아서 중요한 데이터를 담을 수 없다.
- Token을 탈취당하면 대처하기 어려워 만료 시간(30분)을 설정한다.
- Cookie/Session 방식보다 Token 자체의 데이터 용량이 많다.
3️⃣ JWT ( JSON Web Token )
✅ JWT
인증에 필요한 정보들을 암호화시킨 JSON 형태의 Token을 의미.
JSON 데이터 포맷을 사용해 정보를 효율적으로 저장하고 암호화로 서버의 보안성을 높임.
JWT 구조
XXXXXX.YYYYYY.ZZZZZZ
(Header).(Payload).(Signature)
1. Header
- 토큰의 타입과 해싱 알고리즘을 정의한다.
{
"alg": "HS256",
"typ": "JWT"
}
2. Payload
- 실제로 인증과 관련된 데이터(Claims)를 담고 있다.
- Claims의 종류
- Registered Claims : 미리 정의된 Claims
- iss(issuer) : 발행자
- exp(expiration time) : 만료시간
- sub(subject) : 제목
- iat(issued At) : 발행 시간
- jti(JWT ID) : 토큰의 고유 식별자
- Public Claims : 사용자가 정의할 수 있는 클레임, 공개용 정보 전달 목적
- Private Claims : 사용자 지정 클레임, 당사자들 간에 정보를 공유하기 위한 목적
- Registered Claims : 미리 정의된 Claims
{
"sub": "1234567890",
"name": "Sparta",
"exp": 1682563600
}
3. Signature ( 서버 )
- Header와 Payload를 서버의 Secret Key로 서명하여 암호화 한다.
- 암호화는 Header에서 정의한 알고리즘(alg)을 활용한다.
- 서명을 통해 서버는 Token이 변조되지 않았음을 확인할 수 있다.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
💡 base64UrlEncode는 값을 URL에서 사용할 수 있도록 +, / 를 각각 -, _로 표기한다.
💡 Header와 Payload는 Encoding된 값이기 때문에 복호화 혹은 값을 수정할 수 있지만 Signature는 서버에서 관리하는 값이기 때문에 Secret Key가 유출되지 않는 이상 복호화 할 수 없다.
✅ JWT 인증
JWT는 Base64로 인코딩되어 쉽게 복호화 할 수 있다.
Payload가 그대로 노출되기 때문에 비밀번호나 민감한 정보를 저장하지 않는다.
JWT 인증 과정

- 클라이언트의 로그인 요청
- 로그인에 성공했다면 Header, Payload에 Secret Key를 사용하여 Signature를 만든다.
- 이후 Base64로 Encoding 한다.
- 일반적으로 Cookie에 담아 클라이언트에게 JWT를 발급한다.
- 발급받은 JWT를 저장 후 서버에 요청할 때 Authorization Header에 JWT를 담아 보낸다.
- 서버에서 JWT의 유효성 검사를 통해 통과한다면 인증에 성공하여 요청을 처리해준다.
- JWT 만료, 위변조 여부를 검사한다.
JWT 유효성 검사

- A의 JWT를 B가 탈취
- B가 탈취한 JWT를 임의로 수정
- B가 수정한 JWT로 Server에 요청
- 서버는 Signature를 사용하여 유효성 검사(Signature 불일치)
- Header, Payload를 서버의 Secret Key값을 이용해 Signature를 다시 만들어 비교한다.
- 임의로 조작된 데이터를 판별할 수 있다.
💡 JSON Web Token의 목적은 정보 보호가 아닌, 위조 방지에 있다.
✅ JWT 장단점
- JWT 장점
- Signature로 서버의 보안성이 증가한다.
- Token 자체가 필요한 정보(유저 및 검증 정보)들을 모두 가지고 있다.
- 서버는 인증 정보와 관련된 별도의 저장소를 사용하지 않는다.
- 서버의 수평 확장성(Scale Out)이 높아진다.
- Cookie가 없는 다른 환경에서도 인증/인가를 적용할 수 있다.
- DB를 조회하지 않아도 된다.
💡 Mobile의 경우 App을 자주 닫거나 백그라운드로 전환하여 Session 방식을 사용하지 않는다.
- JWT 단점
- Payload는 암호화 된 것이 아니라 민감한 정보를 다루지 못한다.
- Token의 길이가 길어서 트래픽이 증가하면 네트워크에 부하가 증가한다.
- 클라이언트 측에서 Token을 관리하기 때문에 탈취당하면 대처하기 어렵다.
✅ Access Token, Refresh Token
Token은 클라이언트에서 관리하여 탈취당할 위험성이 높기 때문에 만료 시간 설정이 필요
➡ 발생하는 단점을 극복하기 위해 Access Token과 Refresh Token을 사용
- Token의 유형
- Access Token
- 사용자 인증 후 서버가 발급하는 유저 정보가 담긴 토큰이다.
- 유효 기간 동안 API나 리소스에 접근할 때 사용한다.
- Refresh Token
- Access Token은 보안을 위해 짧은 수명을 가진다.
- Access Token이 만료된 경우 재발급 받기위해 사용한다.
- 주로 데이터베이스에 유저 정보와 같이 저장한다.
- Access Token
⚡ Access Token, Refresh Token 인증

- 클라이언트의 로그인 요청
- 로그인에 성공했다면 Header, Payload에 Secret Key를 사용하여 Signature를 만든다.
- 발급받은 JWT를 저장 후 서버에 요청할 때 Authorization Header에 JWT(Access Token)를 담아 보낸다.
- 서버에서 JWT의 유효성 검사를 통해 통과한다면 인증에 성공하여 요청을 처리해준다.
- Access Token이 만료 되었다면 Refresh Token 으로 토큰 재발급을 요청한다.
- 서버로부터 Access Token을 재발급 받는다.
💡 JWT를 Access Token만을 사용하여 인증한다면 탈취되어 보안에 취약 💥 . 유효 시간을 부여해 보안 문제를 해결하지만 유효 시간이 짧다면 로그인을 자주 해야하기 때문에 Refresh Token을 적용한다.
Access Token + Refresh Token을 둘다 사용해주는게 바람직하다.
'[내일배움캠프-Sparta] > Spring 6기' 카테고리의 다른 글
| TIL 30 [ Spring ( Data JPA ), 스탠다드반 ( Optional ) ] (0) | 2025.04.01 |
|---|---|
| TIL 29 [ Spring ( Filter, 객체와 RDB, JPA , 영속성 컨텍스트, Entity 제작 ) ] (0) | 2025.03.31 |
| TIL 28 [ Spring ( Bean Validation, Cookie ) ] (0) | 2025.03.28 |
| TIL 27 [ Spring ( SOLID 객체지향, Container와 Bean, 싱글톤 ) + 스탠다드반 (DI / IOC) ] (0) | 2025.03.27 |
| TIL 26 [ Spring 일정관리 앱 과제 해설 + 학습법 & 동기부여 세션 ] (0) | 2025.03.26 |