컴퓨터 프로그래밍/FastAPI

[FastAPI] Redis 를 활용해 otp 기능 구현

한33 2025. 3. 17. 15:12

💡 개요

FastAPI 프로젝트에 Redis 를 연결한 후 email OTP 인증 기능을 추가하고자 했다.

key : email, value : otp 로 설정을 해놓고 만료시간을 3분으로 설정을 했다.

 

💡 실행

docker run -p 6379:6379 --name redis -d --rm redis

 

위 명령어를 통해 docker 로 부터 redis 최신 버전을 다운 받는다.

 

pip install redis

 

tests > cache.py

import redis

redis_client = redis.Redis(
    host="127.0.0.1", port=6379, db=0, encoding="UTF-8", decode_responses=True
)

 

host 와 port, db 는 0번째 등등 옵션을 설정해준다.

 

✔️ OTP 발급

api > user.py

@router.post("/email/otp")
def create_otp_handler(
        request: CreateOTPRequest,
        _ = Depends(get_access_token),
        user_service: UserService = Depends(),
):
    otp: int = user_service.create_otp()

    redis_client.set(request.email, otp)
    redis_client.expire(request.email, 3*60)

    return {"otp": otp}
redis_client.set(request.email, otp)
redis_client.expire(request.email, 3*60)

 

앞서 선언한 redis_client 를 이용해서 set 함수를 통해 key 와 value 를 설정해주고,

expire 함수를 통해 key 와 number * seconds 로 만료시간을 설정해주었다.

 

✔️ OTP 인증

api > user.py

@router.post("/email/otp/verify")
def verify_otp_handler(
        request: VerifyOTPRequest,
        access_token: str = Depends(get_access_token),
        user_service: UserService = Depends(),
        user_repo: UserRepository = Depends()
) -> UserSchema:
    otp: str | None = redis_client.get(request.email)
    if not otp:
        raise HTTPException(status_code=400, detail="Bad Request")

    if request.otp != int(otp):
        logging.error(
            "OTP 불일치 - 요청된 OTP: %s (타입: %s), 저장된 OTP: %s (타입: %s)",
            str(request.otp), type(request.otp), str(otp), type(otp)
        )
        raise HTTPException(
            status_code=400,
            detail="Bad Request ( otp 불일치 ) " + str(request.otp) + " != " + str(otp)
        )

    username: str = user_service.decode_jwt(access_token=access_token)

    user: User | None = user_repo.get_user_by_username(username=username)
    if not user:
        raise HTTPException(status_code=404, detail="User Not Found")

    user: User = user.update_email(request.email)

    return UserSchema.from_orm(user)

 

1. otp 를 받은 후에 /verify api 에서 email 과 함께 otp 를 입력하면,

2. redis 에 저장된 입력한 email 과 동일한 key  값의 otp 를 가져오고

3. 입력한 otp 와 비교해서 일치하면 

4. 입력한 email 을 access token 으로 부터 조회해 접근한 User Table 에 빈 값으로 설정되어있을 email Column 에 업데이트한다.

 

❌ 타입 불일치 문제

redis 에 저장되어있는 otp 와 입력한 otp 가 동일한데 계속해서 otp 가 동일한데 일치하지 않다는 문제가 발생했다.

integer 타입으로 저장되더라도 redis  에 저장될 때에는 string 타입으로 저장된다는 점을 까먹고 get 으로 가져와서 비교할 때 입력한 int 타입과 불러온 string 타입을 비교하게 되니 숫자가 같아도 계속 불일치 결과가 나왔었다.

 

int(otp) otp 를 integer 타입으로 강제변환 함으로써 해결해주었다.

'컴퓨터 프로그래밍 > FastAPI' 카테고리의 다른 글

[Alembic] Alembic  (0) 2025.03.15
[FastAPI] Repository 패턴 및 orm 적용  (0) 2025.03.15
[FastAPI] DB 연결 및 orm 설정  (0) 2025.03.14
[FastAPI] Status_code Error 처리  (0) 2025.03.14
[FastAPI] CRUD  (0) 2025.03.13