UkiDelly / Django_Chat_bot_Server

Django DRF로 만든 챗봇 어플리케이션 서버
0 stars 0 forks source link

이슈 및 문제들 #14

Closed UkiDelly closed 11 months ago

UkiDelly commented 11 months ago

개발하면서 발생한 이슈 및 문제들

UkiDelly commented 11 months ago

Websocket에서 JWT 인증 구현

웹소켓에 인증을 구현하기 위해 다음과 같이 AuthMiddlewareStack 미들웨어를 사용

# asgi.py
application = ProtocolTypeRouter(
    {
        "http": get_asgi_application(),
        "websocket": AuthMiddlewareStack(
            AllowedHostsOriginValidator(
                URLRouter([re_path(r"chat/(?P<room_name>\w+)/$", ChatConsumer.as_asgi())])
            )
        )
    }
)

하지만 해당 미들웨어는 세션 방식을 사용하기에 JWT를 사용하는 현재 상황에 알맞지 않은 방식, 그래서 구글링을 통해 다음과 같이 다른사람의 코드를 긁어옴


# user_id로 유저를 찾아서 유저 객체를 반환
@database_sync_to_async
def get_user(user_id):
    try:
        return User.objects.get(id=user_id)
    except User.DoesNotExist:
        return AnonymousUser()

class WebSocketJWTAuthMiddleware(BaseMiddleware)::
    def __init__(self, inner):
        self.inner = inner

    async def __call__(self, scope, receive, send):
        authorization_header = next(
            (header for header in scope["headers"] if header[0] == b"cookie"), None
        )

        if authorization_header:
            token = (
                authorization_header[1].decode("utf-8").split("=")[-1]
            )
        else:
            token = None

        try:
            access_token = AccessToken(token)
           # 에러 발생 ~~ user_id 값이 존재하지 않음
            scope["user"] = await get_user(access_token["user_id"])
        except TokenError:
            scope["user"] = AnonymousUser()

        print("user : ", scope["user"])

        return await super().__call__(scope, receive, send)

하지만 위 방식을 사용해도 JWT를 가져오지 못함, 디버그 모드와 브레이크 포인트를 사용해서 확인해 본 결과

        authorization_header = next(
            (header for header in scope["headers"] if header[0] == b"cookie"), None
        )

여기서부터 문제가 시작됨. 위 코드는 b"cookie"에서 JWT를 추출하지만, JWT는 b"authorization"에 존재했음 그래서 해당 코드를 다음과 같이 수정함

        authorization_header = next(
            (header for header in scope["headers"] if header[0] == b"authorization"), None
        )

정상적으로 JWT가 있는 헤더값을 추출 그런 다음,

            # ("authorization","Bearer 토큰")
            token = (
                authorization_header[1].decode("utf-8").split("=")[-1]
            )

이 코드를 다음으로 수정

            # ("authorization","Bearer 토큰")
            token = (
                authorization_header[1].decode("utf-8").split(" ")[-1]
            )

token에 정상적으로 jwt 토큰이 담겨지는걸 확인하고 검증을 통해 user_id를 추출하게 되어 문제 해결

UkiDelly commented 11 months ago

Open AI Api Key 문제

개인키를 발급 받아서 사용하려고 했지만 다음과 같은 에러가 발생

openai.RateLimitError: Error code: 429 - {'error': {'message': 'You exceeded your current quota, please check your plan and billing details.', 'type': 'insufficient_quota', 'param': None, 'code': 'insufficient_quota'}}

Api Key를 삭제하고 여러번 발급해도 계속 같은 문제가 발생해서 Api Key 요청해서 사용하기로 결정함

UkiDelly commented 11 months ago

Flutter Web 새로고침시 에러 발생

플러터 웹을 사용하면, 네트워크 통신을 담당 하는 dio와 상태관리를 담당하는 riverpod에서 오류 발생

지속적으로 해결방법을 찾겠지만 현재로썬 새로고침을 봉인

UkiDelly commented 11 months ago

WebSocket 인증

서버에서 JWT를 header로 받아 인증처리하는 로직을 작성하고, Postman같은 http 테스트 프로그램을 사용할때는 정상적으로 작동, 하지만 프론트에서 웹소켓의 header에 값을 추가할수 있는 방법이 존재 하지 않아서 인증이 되지 않아 접속이 불가능,

사용자 인증은 프론트에게 인가하고 서버에서는 인증 없이 웹소켓에 연결할 수 있도록 서버를 수정

UkiDelly commented 11 months ago

WebSocket json 처리 문제

AsyncJswonWebSocket을 사용해서 웹소켓을 처리하고 있었는데, Postman에서 json 형식으로 데이터를 전송할때는 문제 발생 X 프론트에서 json형식으로 전송하면, {'message' : 'message'} 이렇게 큰 따옴표가 아닌 작은 따옴표로 전송되어 서버에서 변환 했을때 문제가 발생, 프론트쪽에서 큰 따옴표를 사용하도록 여러가지 방법을 썻지만, 실패하고 서버를 AsyncJsonWebSocket에서 AsyncWebSocket으로 수정하여 json 형식이 아닌 단순 문자열 형식으로 데이터를 주고 받도록 수정하여 문제 해결

UkiDelly commented 11 months ago

하루 5회 요청 제한 구현 문제

유저당 하루 요청 가능한 횟수를 제한하기 위해 User모델에 채팅 횟수를 기록하는 기능을 구현했지만, 자정이 되었을때 이를 초기화를 할 방법에 대해 모색함.

crontab이라는 모듈을 발견하고 사용법을 알게 되었고, 다음과 같은 함수를 cron.py에 추가

def reset_chat_count():
    MyUser.objects.update(chat_count=0)

그리고 settings.py 파일에 다음과 같은 항목을 추가

# Cron Jobs
CRONJOBS = [
    ('* 0 * * *', 'chat.cron.reset_chat_count')
]

그리고 다음과 같은 명령어를 입력하여 cron 태스크를 추가함

python manage.py crontab add

이로써 서버가 돌아가고 있을때 자정이 넘어가면 자동으로 cron 태크스를 시작해서 모든 유저의 요청 가능 횟수를 0으로 업데이트 하는 코드가 동작하도록 하여 해결했지만, 테스트는 진행하지 못함