채팅에 관한 간단한 리서치, JWT 인증 구현
인턴을 하면서 저번에 리더님이 나에게 해보고 싶은 것 있냐고 물어보셨을 때, 웹소켓이나 인증 부분을 다뤄보고 싶다고 말했었다.
그래서 리더님이 그걸 기억하시고, 이번 NIPA 챗봇 외주 개발 프로젝트에 나를 넣어주셨다.
해당 프로젝트는 기존 서비스에 장고로 연결되어 있던 것을 떼와서 FastAPI로 바꾸는 것이었어서 처음부터 끝까지 다뤄보지는 못했다.
그래서 뭔가 아쉬워서 혼자라도 간단한 채팅을 구현해보고 싶어서 이 프로젝트를 시작했다.
NIPA에서는 웹소켓은 Node.js로 이루어져있었고, REST API로 Fast API한테 메시지를 전달하면 AI의 응답을 담아 다시 웹소켓에 쏴주는 형식이었다.
즉, FastAPI 는 socket client 역할을 했었다. 하지만 지금은 FastAPI에서 웹 소켓 서버를 만들고 api까지 1개의 서버에서 하니, REST API 통신이 필요 없지 않을까?
채팅 구조는 어떻게 되어야하는가....
웹 소켓과 api가 같이 있으니 그냥 소켓에서 메시지를 보내기 전에 메시지 저장하고 뿌리면 되는건가?
LINE LIVE 채팅 기능의 기반이 되는 아키텍처 - LINE ENGINEERING
흐음.. 아직 잘 모르겠다...
그리고 파이썬에서 소켓 모듈은 왜이렇게 많은가...? websocket
, websockets
, socket
, socket.io
... 이 중 뭘써야되지...
우선은 FastAPI docs에 나와있는 starlette Websocket
을 사용하여 개발해봐야겠다.
채팅 환경은 우선 공식 문서에서 나와있는 것 그대로 가져오면 작동했다.
그럼 우선 유저를 만들어야하니 모델링이랑 인증을 먼저 구현해봤다.
DB 모델링
DB는 우선 회사에서 쓰던 postgresql을 써보기로 했다.
postgres는 관계형 DB에 NoSQL 느낌이 약간 더해진 DB인데, 아직까지는 그것을 잘 활용을 못하겠다.
MySQL에는 없는 List, Json 필드 타입이 있는데, 이를 외래키와 같은 부분에서 좀 더 편하게 사용할 수 있을 것 같은데 orm 에서 해당 리스트에 객체로 처리가 가능한지를 좀 더 찾아보고 나중에 바꿔봐야겠다.
JWT 구현
이전까지는 로그인이나 인증 부분을 직접 해본적이 없어서 개념적으로만 알고 있었다. 대표적으로 세션을 사용하는 방법을 알고 있었는데, 회사에서는 jwt를 쓰고 있었다.
세션은 서버 메모리에 key,value 값을 저장하여 유저가 로그인을 하면 세션에 유저를 저장하고, 세션 id를 쿠키에 심어서 보내는 것이었다. jwt는 json web token으로 암호화된 json 값을 가지고 통신하는 것이다.
예전에는 인증을 검색하면 세션 방식이 자주 나왔었는데, 요즘은 jwt가 많이 나오는 것 같다.
왜 그런지 생각을 해보니, 요즘은 서버가 여러개로 분산되어 있는 형태가 많다. MSA가 아니더라도 로드밸런싱을 사용한다면, 클라이언트가 api 요청을 할때마다 어느 서버로 갈 지 모른다.
즉, 본인이 인증받았던 서버에 세션에 본인의 정보가 담겨있을텐데, 다음 요청을 보낸다면 다시 거기로 간다는 보장이 없고, 다른 서버에는 본인에게 맞는 세션 id가 없다.
그래서 이럴 경우에는 redis를 사용하여 전역으로 세션 id를 저장하여 요청이 들어올 때 해당 세션이 어느 서버에 있는지 확인을 한 뒤 거기로 요청을 보내는 방식을 사용한다.
하지만 이 경우는 redis를 추가적으로 사용해야하고, 관리 포인트도 늘어난다는 단점이 있다.
반면, jwt는 어느 서버에 가더라도 서버에 같은 secret 암호화 키(보통 환경변수)만 있다면 유저 정보를 어디에서는 얻을 수 있다는 장점이 있다. 추가적으로 redis를 사용할 필요가 없고 어느 서버에서든 인증이 된다는 것이다.
그래서 요즘 jwt를 많이 사용하는 것 같다. 그래서 나도 한 번 jwt를 직접 구현해보기로 했다.
파이썬에는 jwt 모듈이 2개가 있었다... PyJWT, python-jose... 어떤 것이 좋은 지는 명확히 안나와있었다... 그래서 FastAPI에서는 python-jose를 가지고 공식문서에 기술을 해놓아서, 그것을 가지고 구현했다.
이전에 인턴 백오피스 인증 및 인가 관련해서 개발했을 때, jwt 흐름을 봤었기 때문에 구현 자체는 어렵지 않았다.
access : 1시간, refresh: 하루로 우선 구현했음!
고민
- jwt access, refresh 토큰은 어떻게 관리하는가?
- access 토큰을 서버에서 보내줄때 쿠키에 심어주나? 아니면 프론트에서 알아서 하나?
- 회사에서는 프론트에게 응답 body로 주면 프론트가 로컬 스토리지에 저장하는것 같다.
- 이 부분은 프론트와 협의해서 하면 되는 부분인 것 같다.
- refresh 토큰은...?
- redis에 저장하나? db 유저 필드 하나 만들어서 관리하나?
- 유저가 많아질 경우 redis에 저장된 값이 엄청 많아지지 않나? → 사실 지금 단계에서는 고민 안해도 되는 부분이긴 함
- db에 저장하면 disk io 필요 → refresh 토큰으로 요청하는 경우는 그렇게 자주있지는 않으니 괜찮지 않을까?
- 음.. 근데 refresh 토큰 해석하면 유저 정보가 나오는데 exp 시간만 체크하고 다시 발급하면 되지 않나...?
- 좀 더 찾아보는걸로!
- access 토큰을 서버에서 보내줄때 쿠키에 심어주나? 아니면 프론트에서 알아서 하나?
'FastAPI 채팅 개발 일지' 카테고리의 다른 글
21. 05. 12 친구 관계 설정 API, Query logging, jwt 로그아웃 (0) | 2021.05.12 |
---|---|
21.04.28 친구 관계 설정, EC2, RDS, nginx, gunicorn 설정 (0) | 2021.05.01 |
21.04.23 - 소켓 통신 테스트 및 소켓 인증 방식 리서치, Authentication / TrustedHost Middleware 추가 (0) | 2021.04.25 |
21.04.21 - 메시지 조회 및 생성 기능, 다른 api 버그 수정 (0) | 2021.04.25 |
21.04.20 - 채팅방 생성 및 조회 기능, Class Based View (0) | 2021.04.25 |