우아한테크코스 레벨 2 자료를 참고하여 학습한 내용을 정리한 글입니다.
💭 들어가며
인증과 인가라는 용어는 자주 들어봤지만, 그 차이를 정확히 이해하고 있지는 않았다. 세션과 토큰 방식의 차이점 역시 명확하지 않았다. 이번 기회에 관련 개념들을 학습하면서 많은 의문이 생겼고, 그 과정을 통해 오히려 더 깊이 이해할 수 있었다. 특히 이론적으로만 알고 있었던 Stateless와 Stateful 개념을 실제로 적용해보며 학습할 수 있었던 점이 전체 구조를 파악하는 데 큰 도움이 되었다. 이번 학습을 통해 인증과 인가에 대한 전반적인 개념을 명확히 정리하고자 한다.
✅ 인증과 인가
▶ 인증(Authentication)
하나의 애플리케이션을 만들었을 때, 아무나 내부 정보에 접근할 수는 없다. 이때 사용자의 신원을 확인하는 과정이 바로 인증이다.
▶ 인가(Authorization)
인증이 완료된 후, 모든 사용자에게 동일한 권한을 부여해서는 안 된다. 예를 들어, 일반 사용자가 관리자 페이지에 접근해서는 안 된다. 따라서 인증된 사용자에게 허용된 범위 내에서만 접근을 허용하는 과정을 인가라고 한다.
✅ Stateless vs Stateful
▶ Stateless
- 서버가 클라이언트의 이전 요청 상태를 저장하지 않는 방식이다.
- 각 요청은 완전히 독립적이며, 요청 하나만으로 필요한 모든 정보를 포함해야 한다.
- 장점
- 서버 간 세션을 공유하지 않아도 되어 서버 확장이 용이하다.
- 중앙 세션 관리가 필요하지 않아서 구현이 단순하다.
- 단점
- 요청마다 필요한 모든 정보를 포함하기 때문에 네트워크 비용이 증가할 수 있다.
- 인증 유지가 어렵기 때문에 토큰 방식이 필요하다.
▶ Stateful
- 서버가 클라이언트의 이전 상태 정보를 유지하는 방식이다.
- 한 번 로그인하면 그 이후 요청에서 사용자의 정보를 기억한다.
- 장점
- 상태를 보존하는 서비스에는 UX가 상대적으로 좋을 수 있다.
- 로그아웃, 세션 만료 등 구현이 쉽다.
- 단점
- 서버 확장 어렵다. (세션 복제, 공유 필요)
- 로드밸런서나 캐시 서버 사용 시 주의가 필요하다.
- 상태가 날아가면 복구가 어렵다.
- 서버 확장 어렵다. (세션 복제, 공유 필요)
✅ 인증의 역사
애플리케이션에서 특정 정보에 접근하려면 “누가 요청했는지” 알아야 한다. 이러한 인증 방식은 다음과 같은 과정을 거쳐 발전해 왔다.
▶ 관리자가 예약 관리 페이지에 접근하려는 경우
- 서버는 요청을 보낸 사용자가 정말 관리자인지 어떻게 알 수 있을까?
- 이를 알기 위해 요청을 보내는 사람은 “내가 누구인지”에 대한 정보를 포함해서 요청해야 한다.
- 사용자는 아이디와 비밀번호를 포함해서 서버에 요청을 보낸다.
- 그러나 이 방식에는 사용성에 문제가 있다. HTTP는 상태를 저장하지 않는 Stateless 하기 때문에 요청마다 아이디와 비밀번호를 반복해서 전송해야 한다.
- 이를 매번 입력하는 것은 번거롭기 때문에, 클라이언트에 아이디와 비밀번호 값을 저장해 둔다.
- 그러나 이 방식에는 보안에 문제가 있다. 클라이언트에 아이디와 비밀번호를 저장하면 쉽게 탈취당할 수 있다.
- 웹에서 관리자 도구를 켜서 확인해 보면 쿠키에 바로 접근이 가능한 것을 볼 수 있다.
- 클라이언트에서는 아이디와 비밀번호를 매번 보내는 대신, 처음에만 아이디와 비밀번호를 서버에 보내고 다음부터는 서버가 외부 사람들은 알 수 없게끔 암호화한 다른 정보를 보내는 방법(세션 ID, 토큰)이 있다.
- 이렇게 하면 반복적인 입력을 하지 않아도 되고, 정보를 보호할 수 있다.
✅ 인증 방법의 종류
▶ Basic Auth
- 기본적으로 아이디와 비밀번호를 요청에 포함해 인증하는 방식이다.
- Authorization 헤더에 아이디와 비밀번호를 Base64로 인코딩해 전송한다.
- 예: Authorization: Basic YWRtaW46cGFzc3dvcmQ=
Base64는 암호화가 아니라 단순 인코딩 방식이므로, 누구나 쉽게 디코딩할 수 있다.
이로 인해 보안에 취약하며, 현대의 시스템에서는 거의 사용되지 않는다.
▶ 세션
- 클라이언트가 아이디와 비밀번호로 최초 인증 요청을 보낸다.
- 서버는 인증에 성공하면 세션 저장소에 사용자 정보를 저장한다.
- 서버는 세션 ID를 응답에 담아 클라이언트에 전달하고, 클라이언트는 이를 쿠키에 저장한다.
- 이후 요청마다 클라이언트는 쿠키에 저장된 세션 ID를 서버에 함께 보낸다.
- 서버는 세션 ID를 통해 저장소에서 사용자 정보를 조회하고 인증을 처리한다.
🔽 세션 관련 개념
Q. 세션 ID는 어떻게 발급되는지?
A. 서버가 인증 성공 시 고유한 식별자인 세션 ID를 생성한다.
Q. 쿠키란?
A. 클라이언트가 저장하고 서버와 자동으로 주고받는 key-value 형식의 데이터이다.
Q. 세션 저장소란?
A. 서버에서 세션 ID에 대응되는 사용자 정보를 보관하는 저장소이다. (메모리, Redis 등)
▶ 토큰
- 클라이언트가 아이디와 비밀번호로 인증 요청을 보낸다.
- 서버는 인증 성공 시 토큰(JWT 등)을 생성해 응답한다.
- 클라이언트는 해당 토큰을 로컬스토리지 또는 메모리에 저장한다.
- 이후 요청 시 Authorization 헤더에 토큰을 담아 전송한다.
- 예: Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR...
- 서버는 토큰의 유효성을 검사하고, 내부 정보를 디코딩해 사용자를 확인한다.
▶ 인증 방식 비교
Basic Auth | 세션 | 토큰 | |
인증 정보 저장 위치 | 매번 ID/PW 전송 | 서버의 세션 저장소 | 클라이언트(LocalStorage 등) |
서버 상태 유지 여부 | Stateless | Stateful | Stateless |
보안 위험 | ID/PW 유출 위험 | 세션 ID 탈취 위험 | 토큰 탈취 위험 (위조는 어려움) |
확장성 | 낮음 (인증 유지 어려움) | 낮음 (세션 공유 필요) | 높음 (서버 확장 용이) |
로그아웃 처리 | 어려움 (상태 없음) | 쉬움 (세션 삭제) | 복잡함 (블랙리스트, 만료시간 등) |
위조 방지 | 없음 (그냥 ID/PW) | 세션 서버에서 관리 | 서명(Signature)으로 위조 방지 가능 |
적합한 사용처 | 테스트, 내부 서비스 | 전통적인 웹앱 | 모바일 앱, 분산 시스템 등 |
✅ 의문 점
1️⃣ - 1️⃣ 왜 Basic Auth를 현재 사용하지 않는가?
- 클라이언트가 매번 ID/PW를 전송해야 하므로, 요청마다 자격 증명을 함께 보내야 해서 불편하다.
- 매번 ID/PW를 포함해 전송하기 때문에, 탈취 위험이 상대적으로 더 크다.
- HTTPS가 적용되지 않으면 ID/PW가 쉽게 탈취될 수 있다.
- 사용자 상태를 추적할 수 없어 권한 관리가 어렵고, 확장성이 떨어진다.
-> 실무에서는 거의 사용되지 않고, 간단한 내부 테스트나 인증이 중요하지 않은 곳에서만 쓰인다.
1️⃣ - 2️⃣ 모든 인증은 결국 ID/PW를 보내야 하지 않는가?
- 세션 방식은 서버가 세션을 저장하고 세션 ID를 발급한 뒤, 클라이언트는 이후 요청에 해당 ID만 전송한다.
- 토큰 방식은 서버가 JWT를 생성해 클라이언트에 전달하고, 클라이언트는 이후 JWT만 전송한다.
- 반면 Basic Auth는 매 요청마다 ID와 PW를 함께 전송해야 하므로, 탈취 위험이 상대적으로 더 크다.
- 이외에도 Basic Auth에 비해 세션/토큰 방식은 보안성, 로그인 상태 유지, 로그아웃 처리, 확장성 측면에서 더 많은 장점이 있어 사용된다.
2️⃣ - 1️⃣ 왜 세션 방식은 Stateful인가?
🔽 세션 ID 구조
- 세션 ID 자체는 그냥 랜덤 문자열로, 아무런 정보도 담고 있지 않다.
- 예: 세션 ID = "a91b234f8e90c7..."
🔽 인증 정보는 어디에 있는가
세션 ID는 Redis 또는 서버 메모리의 세션 저장소에 key처럼 사용되고, 실제 사용자 정보는 그 저장소에 있는 value에 저장되어 있다.
세션 ID | 세션 저장소 내부 데이터 |
abc123xyz | { userId: 42, role: "ADMIN", exp: ... } |
🔽 왜 Stateful인가
- 로그인 상태가 서버에 저장되어 있기 때문이다.
- 서버가 세션 상태(로그인 상태)를 관리해야 하고, 서버가 죽으면 로그인 정보도 사라진다.
2️⃣ - 2️⃣ 왜 토큰 방식은 Stateless인가?
- JWT는 세 부분으로 구성된 문자열 토큰이다.
- xxxxx.yyyyy.zzzzz
- (Header).(Payload).(Signature)
🔽 Header
- 토큰 타입(JWT)과 서명 알고리즘
{
"alg": "HS256",
"typ": "JWT"
}
🔽 Payload
- 실제 인증 정보, 권한, 유저 식별자(userId, role 등)
{
"sub": "user123",
"role": "ADMIN",
"exp": 1710000000
}
- 서버는 이 정보를 보고 사용자 정보를 복원할 수 있다.
- exp(만료시간)도 포함되어, 서버가 별도 저장 없이 판단이 가능하다.
🔽 Signature
- 위의 Header + Payload를 비밀키로 서명한 값
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
- 서버는 이 서명을 보고 토큰이 위조되지 않았는지 검증한다.
- Header와 Payload는 단순히 Base64Url로 인코딩된 문자열이며, 서명(Signature)은 이 둘을 비밀키로 서명한 값이다.
- 서버는 Header와 Payload를 디코딩한 후, 동일한 방식으로 서명을 다시 생성하여 기존 서명과 비교함으로써 토큰의 위변조 여부를 검증한다.
🔽 왜 Stateless인가
- 토큰에 사용자 정보와 만료 정보가 직접 포함되어 있어, 세션처럼 따로 저장하지 않고 서버는 이 서명만 검증하면 사용자 인증을 완료해서 사용자를 식별할 수 있기 때문이다.
3️⃣ 인증 방식에 따라 인가 방식이 어떻게 달라지는가?
🔽 세션 방식의 인가
- 서버가 세션을 조회해 사용자 정보를 찾고,
- 사용자 권한(role)을 세션 저장소에서 확인하여 인가한다.
session = redis.get(sessionId)
if (session.role == "ADMIN") {
allowAccess()
}
🔽 토큰(JWT) 방식의 인가
- 토큰을 디코딩하면 payload 안에 role, userId 같은 정보가 포함되어 있다.
- 서버는 별도 조회 없이 토큰 안의 role 정보만으로 인가 판단 가능하다.
{
"sub": "user123",
"role": "USER"
}
claims = jwt.decode(token)
if (claims.role == "ADMIN") {
allowAccess()
}
4️⃣ - 1️⃣ 토큰 방식에서 Refresh Token을 사용하면 세션보다 이점이 있는가?
세션 기반 인증 | 토큰 기반 인증 (Access + Refresh) | |
인증 유지 방식 | 서버 세션 저장소에 사용자 정보 저장 | Access는 클라이언트가 보관, Refresh는 서버 선택적 저장 |
상태 저장 여부 | Stateful, 세션 저장소 필요 | Access Token은 Stateless |
서버 확장성 | 낮음 (세션 저장소 공유 필요) | 높음 (서버는 토큰만 검증) |
모바일 적합성 | 쿠키 의존, 제약 있음 | 상대적으로 적합 |
로그아웃 | 간단함 (서버에서 세션 삭제) | Refresh만 저장하면 비슷하게 가능 |
복잡도 | 단순함 | 복잡함 (보안, 만료, 저장 등 고려 필요) |
모바일 환경에서는 브라우저가 없거나, 쿠키를 자동으로 저장하고 전송하는 기능이 제한적이다.
4️⃣ - 2️⃣ 그렇다면 세션도 쿠키를 사용하지 않고, 헤더에 담아 전송하면 되지 않을까?
- 이론적으로는 가능하나, 세션은 원래 쿠키를 전제로 설계되었다.
- 많은 웹 프레임워크는 세션 ID를 쿠키로 주고받는 구조로 되어 있다.
- 즉, JSESSIONID 같은 쿠키가 없다면 자동으로 동작하지 않고, 개발자가 세션을 수동으로 관리해야 한다.
4️⃣ - 3️⃣ 리프레시 토큰이 사용자 편의성과 보안을 위해 도입된 걸로 알고 있는데, 오히려 탈취되면 더 위험한 거 아닌가?
- 토큰에서 리프레시 토큰을 사용한다면, 무상태의 이점을 완전히 누릴 수 없다.
- 토큰이 세션보다 범용적으로 더 자주 쓰이는 이유를 솔직히 잘 모르겠다.
- 그리고 리프레시 토큰은 탈취되면 오히려 더 위험한 거 아닌가?
- 사실 세션이든 토큰이든 탈취되면 똑같이 위험하다.
- 결국 인증 방식과 보안은 별개의 문제다.
- 그래서 보안은 방식과 상관없이 따로 신경 써야 한다.
- HttpOnly, Secure 쿠키 사용
- 세션이면 세션 ID 삭제, 토큰이면 리프레시 토큰 무효화
- 그렇다면 토큰은 좋은 점이 무엇일까?
- 토큰은 서버가 오토스케일링되거나, MSA 환경처럼 서버가 여러 개인 구조에서 유리하다.
- 세션은 클라이언트 상태를 서버가 기억해야 하므로 서버가 많아질수록 세션 공유/동기화가 필요하다.
- 여러 기기에서 동시에 로그인 상태를 유지하려면 토큰이 훨씬 낫다. 세션은 기기별로 상태가 나뉘고, 관리가 어렵다.
- 모바일 앱에서는 쿠키 기반 세션은 제약이 많고 번거롭다. 반면 토큰은 헤더에 담아 전송하면 되기 때문에 간편하다.
- 토큰은 서버가 오토스케일링되거나, MSA 환경처럼 서버가 여러 개인 구조에서 유리하다.
- 그래서 토큰은 이런 환경에서 큰 이점이 있어서, 쓸 수밖에 없다.
- 요즘은 모놀리스보다는 MSA가 더 흔해지면서 자연스럽게 토큰도 더 범용적으로 쓰이게 되었다.
- 그리고 UX를 생각하면 리프레시 토큰은 무조건 필요하다.
- 15분마다 다시 로그인하라고 하면, 나 같아도 그냥 탈퇴한다.
- 결론은 상황에 맞게 적절한 방식을 선택하는 것이 중요하다.
- 세션은 소규모 프로젝트나 서버 하나로 운영할 때 적합하고,
- 토큰은 서버가 오토스케일링되거나, MSA 구조일 때 유리하다.
4️⃣ - 4️⃣ Access Token이 더 탈취되기 쉬운가?
주로 저장되는 위치 | JavaScript 접근 가능 여부 | |
Access Token | LocalStorage, Memory 등 | 가능 (XSS 공격에 취약) |
Refresh Token | HttpOnly + Secure 쿠키 | 불가능 (XSS 방어 가능) |
- "그렇다면 Access Token도 HttpOnly + Secure 쿠키에 저장하면 되는 것 아닌가?"와 같은 의문이 생길 수 있는데,
- 앞서 살펴본 것처럼, 그렇게 되면 결국 세션 방식과 큰 차이가 없어지게 된다. (모바일 앱에서 사용 어려움 등)
📍 참고 자료
- 우아한테크코스 레벨 2 - 미션 2 자료
'Programming > CS' 카테고리의 다른 글
[CS] 동기/비동기, 블로킹/논블로킹 (0) | 2025.04.19 |
---|---|
[CS] 프로그래밍 에러 종류 (2) | 2024.11.28 |