이전 내용
[FastAPI] 고급인증: JWT
이전 내용 [FastAPI] 인증과 세션이전 내용 [FastAPI] SQLAlchemy와 CRUD (Depends, db.query)이전 내용 [파이썬] FastAPI - ORM 연동하기(SQLAlchemy)이전 내용 [파이썬] FastAPI - 웹소켓이전 내용 [파이썬] FastAPI - 스트리
puppy-foot-it.tistory.com
고급 인증: 세션
HTTP는 자체적으로 상태가 없는(stateless) 프로토콜이라 서버는 클라이언트의 이전 요청을 기억하지 않기 때문에 세션이 등장했다. 클라이언트가 처음 서버에 요청을 보낼 때 서버는 클라이언트에게 고유한 세션 ID를 발급하는데, 이 세션 ID는 쿠키 형태로 클라이언트에 저장된다.
클라이언트가 다시 서버에 요청을 보낼 때마다 이 쿠키를 함께 보내고, 서버는 이 세션 ID를 통해 클라이언트를 식별하고 필요한 정보를 저장하거나 불러온다.
FastAPI에서 이전에 인증을 위해 사용했던 JWT와는 유사한 목적을 갖고 있으나, 몇 가지 차이점이 있다.
◆ JWT와 세션의 차이
1. 상태 유무 (Statefulness)
세션:
- 상태 정보를 서버 측에 저장하므로 상태가 유지되는 시스템이다.
- 사용자가 로그인한 상태를 서버에서 관리하여, 서버에서 복원할 수 있다.
- 서버 측에 클라이언트에 대한 정보를 저장하고 관리해야 하므로 Redis, 데이터베이스 등 추가적인 저장소가 필요하다.
JWT:
- 상태 정보를 클라이언트 측에 저장하므로 상태가 유지되지 않는 (stateless) 시스템이다.
- 서버는 클라이언트로부터 받은 토큰을 통해 사용자의 정보를 확인하며, 별도로 상태를 관리할 필요가 없다.
- 토큰 자체가 모든 정보를 포함하고 있어 서버 측에 별도의 저장소가 필요하지 않다.
2. 보안성 (Security)
세션:
- 서버에서 사용자 정보를 보안적으로 관리하므로 세션 하이재킹의 공격에 대해 어느 정도 보호된다.
- 일반적으로 서버에서는 추가적인 보안 조치(세션 타임아웃, IP 체크 등)를 취할 수 있다.
- HTTPS를 사용하면 세션 쿠키가 암호화되어 전송된다.
JWT:
- JWT는 서명된 토큰이므로 변조를 방지할 수 있지만, 탈취된 경우 악용될 수 있다.
- 토큰 자체가 정보를 포함하고 있기 때문에 토큰 노출 자체로도 문제가 될 수 있다.
- 적절한 보안 구성(예: HTTPS, 짧은 만료 시간 설정 등)이 필수적이며, 암호화 알고리즘을 사용하기 때문에 일정 수준의 보안성이 있다.
3. 확장성 (Scalability)
세션:
- 서버 측에 상태 정보를 저장하므로, 여러 서버가 존재하는 경우 일관된 세션 스토리지를 유지하는 것이 복잡할 수 있다.
- 스케일아웃이 필요할 경우 Redis와 같은 외부 세션 관리 솔루션을 추가해야 할 수도 있다.
JWT:
- 클라이언트 식별을 위한 정보가 토큰 내에 포함되므로, 서버 간 공유 없이 독립적으로 작동할 수 있다.
- 높은 확장성을 제공하며, 여러 서버에서 쉽게 사용할 수 있다.
4. 사용성 (Usability)
세션:
- 사용자의 로그인 상태를 쉽게 관리할 수 있으며, 특히 로그인/로그아웃 기능 구현이 간단하다.
- 한 브라우저에서만 유효하여 다른 브라우저나 장치에서 동일하게 사용할 때 불편할 수 있다.
JWT:
- 다양한 클라이언트(모바일 앱, 웹 앱 등)에서 동일한 토큰을 사용할 수 있어 유연성이 높다.
- 만료된 토큰을 처리하고 로그아웃 기능을 구현하는 것이 다소 복잡할 수 있다.
이를 표로 정리하면 아래와 같다.
특징 | 세션 (Session) | JWT (JSON Web Token) |
상태 유무 | - 상태 정보를 서버 측에 저장 (상태 유지) - 추가 저장소 필요 (Redis, DB 등) |
- 상태 정보를 클라이언트 측에 저장 (무상태) - 별도의 저장소 필요 없음 |
보안성 | - 세션 하이재킹 공격에 대한 보호 실현 - 추가적인 보안 조치 가능 (타임아웃, IP 체크 등) - HTTPS로 안전하게 전송 |
- 서명된 토큰으로 변조 방지 - 탈취 시 악용 가능성 존재 - 적절한 보안 구성이 필요 |
확장성 | - 여러 서버에서 일관된 세션 스토리지 유지 어려움 - 스케일아웃 시 외부 솔루션 필요 (Redis 등) |
- 높은 확장성 제공 - 여러 서버에서 독립적으로 작동 가능 |
사용성 | - 로그인 상태 관리가 용이 - 다른 브라우저나 장치에서 유효성 문제 |
- 다양한 클라이언트에서 동일한 토큰 사용 가능 - 만료 처리 및 로그아웃 구현 복잡성 |
FastAPI와 Starlette
FastAPI는 Starlette 위에 구축된 고급 웹 프레임워크로, 비동기 웹 애플리케이션을 신속하게 개발할 수 있고, 강력한 기능과 유연성을 바탕으로 설계되어, 고성능 RESTful API 개발에 최적화되었다.
Starlette는 ASGI(Asynchronous Server Gateway Interface) 기반의 웹 프레임워크로, 비동기 웹 애플리케이션을 위한 간단하면서도 확장 가능한 툴킷이다. Starlette의 경량성과 비동기 처리 기능 덕분에 FastAPI를 통해 개발자들은 빠르고 효율적인 웹 애플리케이션을 구축할 수 있다.
[FastAPI와 Starlette의 관계]
1. 구조와 디자인
Starlette:
- ASGI 기반의 웹 프레임워크로, 비동기 웹 애플리케이션을 위한 경량화된 툴킷이다.
- 라우팅, 미들웨어, 응답 생성 등 기본적인 웹 애플리케이션 기능을 제공한다.
- HTTP와 WebSocket 프로토콜을 모두 지원하여 다양한 애플리케이션에 적합하다.
FastAPI:
- Starlette 위에 구축된 프레임워크로, RESTful API를 보다 쉽고 우아하게 설계할 수 있도록 지원한다.
- Starlette의 기본 기능을 확장하여 데이터 유효성 검사, 문서화, 경로 및 쿼리 매개변수 처리를 지원한다.
2. 기능상 차별화
- FastAPI는 Starlette의 기본 기능을 기반으로 추가적으로 Pydantic을 사용하여 데이터 모델링과 유효성 검사를 통합한다. 이로 인해 요청 데이터의 검증 및 자동화된 문서화가 가능해져 API 개발의 생산성이 높아진다.
- FastAPI는 OpenAPI를 자동으로 생성하여 API의 문서화 작업을 간소화하고, 이를 통해 Frontend 개발자와의 협업이 용이해진다.
3. 비동기 처리
- FastAPI는 ASGI를 지원하여 비동기 처리를 자연스럽게 활용할 수 있다.
- Starlette의 비동기 기능을 상속받아 데이터베이스와의 비동기 연결을 통해 성능을 최적화하며, 이러한 특징은 높은 동시성을 요구하는 애플리케이션에서 특히 유리하다.
4. 미들웨어와 라우팅
- FastAPI는 Starlette의 미들웨어와 라우터를 활용하여 HTTP 요청 처리 및 응답 생성 시 다양한 기능을 제공한다.
- 사용자 정의 미들웨어를 쉽게 추가할 수 있으며, 라우팅 시스템을 통해 RESTful API를 효율적으로 구성할 수 있다.
세션 관리
SessionMiddleware
Starlette는 세션 관리를 위한 SessionMiddleware 라는 미들웨어를 제공하며, SessionMiddleware를 FastAPI 애플리케이션에 추가하면 FastAPI 기반의 웹 애플리케이션에서도 세션을 쉽게 관리할 수 있다.
※ 미들웨어: 요청과 응답을 중간에서 처리하는 컴포넌트. 로깅, 인증, 세션 관리 등 다양한 기능 수행.
FastAPI에서 SessionMiddleware을 사용하기 위해서는 SessionMiddleware를 애플리케이션에 추가하면 된다. 추가 후에는 각 요청 객체에서 session 속성을 통해 세션 데이터 접근할 수 있다.
from fastapi import FastAPI, Request
from starlette.middleware.sessions import SessionMiddleware
import os
app = FastAPI()
# 실무에서는 환경 변수를 활용하여 secret_key를 관리
SECRET_KEY = os.environ.get("SECRET_KEY")
app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY)
이렇게 한 후에는 각 라우트 함수에서 Request 객체를 통해 세션에 데이터를 저장하거나 읽을 수 있다.
@app.post("/set/")
async def set_session(request: Request):
request.session["username"] = "test_user"
return {"message": "Session value set"}
@app.get("/get/")
async def get_session(request: Request):
username = request.session.get("username", "Guest")
return {"username": username}
@app.post("/set/") 라우트
- HTTP POST 메서드를 사용하는 /set/ 경로
- Request 객체를 인자로 받아 session 속성을 통해 세션에 username 이라는 키와 그 값으로 "test_user" 라는 값을 저장
- async 키워드는 비동기 I/O 작업을 위함
- 클라이언트가 /set/ 엔드포인트에 POST 요청 보냄
- FastAPI 애플리케이션은 이 요청을 받고 set_session() 함수 호출
- set_session() 함수 내에서 request.session["username'] = "test_user" 코드가 실행되면서, 세션에 "username"키와 "test_user" 값 저장
- 함수는 {"message": "Session value set"}을 반환하여 클라이언트에게 세션이 성공적으로 설정되었다는 것을 알림.
@app.get("/get/") 라우트
- HTTP GET 메서드를 사용하는 /get/ 경로
- 세션에서 username 키의 값을 가져와 반환하며, 만약 키가 없으면 "Guest" 반환
- 클라이언트가 /get/ 엔드포인트에 GET 요청 보냄
- 이때 -b cookies.txt 옵션을 통해 이전에 저장한 쿠키(세션 정보)가 요청에 포함
- FastAPI 애플리이션은 이 요청을 받고 get_session() 함수 호출
- Get_session() 함수 내에서 username = request.session.get("username", "Guest") 코드가 실행된다. 이 코드는 세션에서 "username" 키 값을 찾아 username 변수에 저장하고, 만약 키가 없으면 "Guest" 저장
- FastAPI 애플리케이션은 {"username": "test_user"} 라는 응답 반환 (세션에 저장된 "username"의 값을 응답으로 반환)
항목 | 설명 | 예시 값 |
Name (이름) | 쿠키의 이름. 주로 세션이나 로그인된 사용자를 식별할 때 사용. | session |
Value (값) | 쿠키에 저장된 실제 데이터. 암호화된 문자열 (예: 세션 ID, 토큰 등) | eyJ1c2VybmFt... |
Domain (도메인) | 이 쿠키를 사용할 수 있는 웹사이트 도메인. 이 도메인에서만 이 쿠키를 읽고 쓰기 가능. |
127.0.0.1 (로컬 테스트용 주소) |
Path (경로) | 쿠키가 적용되는 웹사이트 경로. /는 사이트 전체를 의미 |
/ |
Expires (만료 시각) | 쿠키가 언제 만료되어 삭제될지를 나타냄. 이 시각이 지나면 자동으로 쿠키가 사라짐. |
2025년 5월 21일 수요일 |
HttpOnly | true이면 자바스크립트에서 이 쿠키에 접근 불가. 보안 목적(예: XSS 방지)에 사용 | true |
Secure | true이면 HTTPS(보안 연결)로만 쿠키가 전송됨. HTTP에서는 전송되지 않음. |
false (테스트 중이므로 비활성화됨) |
실제 웹 환경에서는 웹 브라우저 내에서 cookies.txt 파일을 통해 세션 정보를 확인할 수 있고, 이곳의 정보를 관리한다.
from fastapi import FastAPI, Request, HTTPException
from starlette.middleware.sessions import SessionMiddleware
app = FastAPI()
# session 미들웨어 추가
app.add_middleware(SessionMiddleware, secret_key="my_secret_key")
@app.post("/login/")
async def login(request: Request, username: str, password: str):
# 실제로는 데이터베이스에서 사용자 인증을 수행해야 함
if username != "test_user" or password != "1234":
raise HTTPException(status_code=401, detail="Invalid credentials")
else:
# 세션에 사용자 정보 저장
request.session["username"] = username
return {"message": "Logged in successfully"}
@app.get("/dashboard/")
async def dashboard(request: Request):
# 세션에서 사용자 정보 가져오기
username = request.session.get("username")
if not username:
raise HTTPException(status_code=401, detail="Not authenticated")
return {"message": f"Welcome to the dashboard, {username}!"}
[테스트]
1. 로그인 테스트
로그인 엔드포인트(/login/)에서 사용자명과 패스워드를 입력받아, 유효한 경우 세션에 사용자명 저장하고 로그인 성공 메시지 출력
2. 대시보드 접근 테스트
대시보드 엔드포인트(/dashboard/) 세션에서 사용자명을 조회하여, 로그인된 사용자만 접근하도록 하고, 로그인 성공 시 성공 메시지 출력
[참고]
가장 빠른 풀스택을 위한 플라스크 & FastAPI
다음 내용
[FastAPI] 비동기 처리 (asynchronous processing)
이전 내용 [FastAPI] 고급 인증: 세션이전 내용 [FastAPI] 고급인증: JWT이전 내용 [FastAPI] 인증과 세션이전 내용 [FastAPI] SQLAlchemy와 CRUD (Depends, db.query)이전 내용 [파이썬] FastAPI - ORM 연동하기(SQLAlchemy)이
puppy-foot-it.tistory.com
'[파이썬 Projects] > <파이썬 웹개발>' 카테고리의 다른 글
[파이썬] FastAPI - 파일 업로드 (0) | 2025.05.08 |
---|---|
[파이썬] FastAPI - 비동기 처리 (asynchronous processing) (0) | 2025.05.08 |
[파이썬] FastAPI - 고급 인증: JWT (0) | 2025.05.06 |
[파이썬] FastAPI - 인증과 세션 (0) | 2025.05.01 |
[파이썬] FastAPI - SQLAlchemy와 CRUD (Depends, db.query) (0) | 2025.05.01 |