원래 token 관리를 DB에서 했는데, Redis에서 해보기로 하였당

그림으로 표현하면

# 로그인
@router.post("/login")
async def login(
req: AuthSigninReq,
db=Depends(get_db_session),
jwtUtil: JWTUtil = Depends(),
authService: AuthService = Depends(),
redis_db=Depends(get_redis),
redisService: RedisService = Depends(),
):
user = authService.signin(db, req.login_id, req.pwd)
if not user:
raise HTTPException(status_code=401, detail="Login failed")
access_token = jwtUtil.create_token(user.model_dump())
# redis 에 토큰넣기
await redisService.add_token(redis_db, access_token, req.login_id)
return AuthResp(
message="로그인 되었습니다.",
user=user,
access_token=access_token, # front에 토큰을 응답으로 반환
)
SECRET_KEY = "1234" # 임시값
ALG = "HS256"
def create_token(
self, payload: dict, expires_delta: timedelta | None = timedelta(minutes=30)
):
payload_to_encode = payload.copy()
expire = datetime.now(timezone.utc) + expires_delta
payload_to_encode.update({"exp": expire})
return jwt.encode(payload_to_encode, SECRET_KEY, algorithm=ALG)
# 실제
access_token = jwtUtil.create_token(user.model_dump())
from pydantic import BaseModel
# 응답 class 정의
class AuthResp(BaseModel):
message: str
user: User
access_token: str | None = None
# login API return 값
return AuthResp(
message="로그인 되었습니다.",
user=user,
access_token=access_token, # front에 토큰을 응답으로 반환
)
const result = await response.json();
if (response.ok) {
alert("로그인 성공!");
localStorage.setItem("token", result.access_token); // JWT 토큰 저장
<참고>
front 에서 localstorage 에 저장하는게 맞는지 좋은지?
쿠키에 저장하는게 좋은지?
# redis 에 토큰넣기
await redisService.add_token(redis_db, access_token, req.login_id)
from fastapi import HTTPException
# ✅ 토큰 유지시간 3600초 == 1시간
TTL = 3600
class RedisService:
async def add_token(
self,
redis_db,
token: str,
login_id: str,
):
await redis_db.set(token, login_id, ex=TTL)
async def delete_token(self, redis_db, token):
value = await redis_db.get(token)
if value is None:
raise HTTPException(status_code=404, detail="Redis token expired")
await redis_db.delete(token)
✅ redis_db.set / redis_db.delete 는 비동기 함수 이므로 await를 사용
✅ redis 에 jwt token 저장시 key : value 형식을 token : login_id으로 함. ➡️ 이유는 밑에서 설명
# 로그아웃
@router.post("/logout")
async def auth_logout(
authorization: str = Header(None),
redis_db=Depends(get_redis),
redisService: RedisService = Depends(),
): # 헤더에서 토큰을 받음
if not authorization:
raise HTTPException(status_code=401, detail="인증 토큰이 필요합니다.")
token = authorization.split(" ")[1] # "Bearer <토큰>"에서 토큰만 추출
await redisService.delete_token(redis_db, token)
return {"message": "로그아웃 되었습니다."}
로그아웃은 front의 Reqest를 통해 token을 받고, 이후에 redis 의 token 과 비교해서 로그아웃을 시켜준다.
이때, redis 는 key 값을 기반으로 delete를 할 수 있기 때문에, ⬆️위에서 말한 token값을 key값으로 설정하게 되었다.
login_id : token 형식으로 저장되는게 이상적이라고 생각하지만.. 이러한 이유로 token : login_id 형식으로 저장하게 되었다.
원래 DB 에서 Token을 관리하던 코드를 바꾸는 상황이었다
Redis 에 Token을 넣는건 까지는 순조로웠으나, 로그아웃을 해도 토큰이 삭제가 안되는 문제가 계속 발생했다..

^
로그아웃 하면 나온 alert
delete_token 함수문제 인가 싶어서
async def delete_token(self, redis_db, token):
value = await redis_db.get(token)
if value is None:
raise HTTPException(status_code=404, detail="Redis token expired")
await redis_db.delete(token)
redis_db.get 함수의 return 값을 받아보기 시도..
하지만 계속 동일한 문제가 발생했다.
그래서 Token 자체는 제대로 가져오는지 확인을 해보았는데, 프론트에서 Token을 제대로 가져오지 못하고 있는 사실을 발견했다!!!
프론트 코드는 건들인 것이 없기때문에, 그럼 애초에 프론트에서 Token값을 받지 못한게 아닐까?
하고 로그인API의 return 값을 살펴보았지만.. 아무런 문제가 없어보였다.

토큰 반환 하잖아!!!!
await 문제인가..??? 싶었음..
그렇게 1시간 정도 삽질을 하다가 AuthResp 의 내용을 바꾸지 않은걸 깨달았다..
-> 근데 원래 DB 에서 Token보내줄때랑 차이가 없는데, 왜 AuthResp가 바껴잇었지??