지난 한달간 열심히 개발한 실시간 퀴즈 사이트입니다. django, django drf, jwt 기반 인증, websocket, ticket으로 받아오는 jwt websocket 인증, celery, 등을 모두 사용한 예시이기도 합니다. 구경와주세요~

제이미
@theeluwin@hackers.pub · 61 following · 53 followers
스콜라아이돌교수버튜버
django에서 test를 할 때, async/await 처리를 성공적(?)으로 하려면 TransactionTestCase를 사용해야하는데 이러면 classmethod인 setUpTestData를 사용하지 못합니다. 그래서 매 테스트 함수마다 데이터를 넣어주는 setUp으로 도로 바꿔야했고... 2분 걸리던 테스트를 최적화해서 10초로 줄여놨다가 다시 30초로 늘어났네요 뭔가... 하루종일 뭐한거지 싶지만,,, 꼭 한번 알고 갔어야하는 느낌이기도 합니다. 요컨대, 추상화/최적화는 정말 해봐야만 보이는 문제점이라는게 있고 제가 아직 경험이 부족해서 예측이 잘 안되네요(
@theeluwin제이미 보일러플레이트는 원래...... 충분히 성숙기까지 만들고 나서 그때 생각해야하는...
@kodingwarriorJaeyeol Lee 를 배웠네요 이번 기회에...
서비스 A를 개발하기 위해 boilerplate B를 만들기 위한 boilerplate C를 만들고 시작했더니 A에서 뭐 하나 바꾸면 B, C까지 전부 다 바꾸고 있는 상황... 이게 실제로 운영을 해봐야만 발견할수 있는게 너무 많네요 하 당연한 얘기긴 한데ㅠ
간만에 SSL 다는 작업을 했는데 (그동안은? 그동안은 로컬에서만 개발했죠) 곳곳에 제가 'http'나 'ws'로 박아둔 string들이 도처에... 하...
@theeluwin제이미 돈은 모든 것을 해결해줍니다
빌드용 서버를 구매하시면...
@akastoot악하 어찌어찌 로컬에서 빌드하고 빠르게 업로드 하는 스크립트를 열심히 짜서 해결 했습니다,,,
아니 멀티 아키텍처 빌드라는게 있었네요
잘 해결 되었습니다
서버에서 도커 빌드가 안됨 (메모리 부족)
로컬과 서버 CPU 아키텍처가 다름 (호환 안됨)
쉬운 해결책: 메모리를 돈 주고 늘린다
귀찮은 해결책: 빌드용 서버를 잠시 빌려서... 하ㅠ...
아니 멀티 아키텍처 빌드라는게 있었네요
서버에서 도커 빌드가 안됨 (메모리 부족)
로컬과 서버 CPU 아키텍처가 다름 (호환 안됨)
쉬운 해결책: 메모리를 돈 주고 늘린다
귀찮은 해결책: 빌드용 서버를 잠시 빌려서... 하ㅠ...
무겁고 큰 메인 기능 구현 착수하기가 겁나서 가볍고 작은 서브 기능부터 구현하면서 차일피일 미루다가 더이상 서브 기능 남은게 없어서 드디어 메인 기능 구현을 했는데, 소요된 노력이 서브와 유사한 수준이라 뭔가... 알 수 없는 불안감이 느껴지는 이 기분... 아시나요... 중요한걸 미루지 맙시다(?
@theeluwin제이미 실례지만 어떤 요구사항이 있어서 이것을 구현하는지 알 수 있을까요?
@akastoot악하 어떤 링크로 갈 수 있는 버튼을 사용할수 있는 상태인지 아닌지 (사용할수 없는 상태면 링크로 들어가도 무효화 처리까지) 입니다. 사실 누가 요구한건 아니고 제가 이렇게 한번 구현해보고 싶어서... 입니다.
요즘 웹을 많이 하고 있는데 정말 단순한 기능 하나도 뭐 이리 구현할게 많은지요...
- 버튼 하나 누르면 어떠한 상태 변경이 전파 되는 기능임
- 버튼을 누르는 유저는 그걸 누를 수 있는 권한이 있는지 체크
- 모든 유저가 websocket으로 전파 받을 수 있도록 해야함
- 로그에 JWT가 남으면 싫으니까 ticket을 발행해서 연결
- 해주는 API를 짜고 ticket에서 user 알아내서 컨텍스트에 붙여주는 미들웨어 구현
- 을 하기 위해 이제 redis로 캐시를 붙이고
- 백엔드 다 했으면 프론트에서 이제 어떤 component가 websocket에 연결 되어 있을지
- 이걸 받아서 또 다른 component한테 어떻게 뿌리지 (event emit으로...
- 등등등...
- 따라서 일단 야크 털을 깎아야한다 라는
클로드 데스크탑에서 사용할 수 있는 알라딘 MCP 서버를 DXT 파일로 공유합니다. 베스트셀러나 신간 정보, 도서 정보를 조회할 수 있습니다. 알라딘 API만 발급 받으면 편하게 쓰실 수 있습니다.
드디어 야크 털을 다 깎았습니다. 이번엔 진짜 진짜 pocket-galaxy
라는 이름에 걸맞는 boilerplate입니다. 온갖 기능을 다 집어 넣어봤습니다. 실제 서비스에서 쓰는 용도라기보단, 내부 툴이나 간단한 1회용 서비스 개발용에 가깝습니다.
하 근데 정말 이거 문서 쓰기가 어렵네요. 이 많은 기능과 설정 방법과 응용 방법을 언제 다...
드디어 야크 털을 다 깎았습니다. 이번엔 진짜 진짜 pocket-galaxy
라는 이름에 걸맞는 boilerplate입니다. 온갖 기능을 다 집어 넣어봤습니다. 실제 서비스에서 쓰는 용도라기보단, 내부 툴이나 간단한 1회용 서비스 개발용에 가깝습니다.
@theeluwin제이미 거기에 x100 정도 해서 ARPAnet 개발자들이 느꼈을 감동을 시뮬레이션해봅시다
@bglbgl gwyng 오..... 그러게요 wao....
아마 다들 비슷한 경험이 있으실겁니다. 태어나서 처음으로 쌍방향 연결에 성공해서 두 클라이언트가 대화할 수 있게 했을때의 기쁨... 저는 딱 15년만에 하는거라(...) 처음 해본것처럼 기쁘네요
디버깅할게 정말 많고 복잡한 뭐시기인것 같습니다. 참고로 가장 마지막 실패 원인은 django가 ASGI로 실행되지 않았기 때문이었습니다. test 환경에서는 이런 문제가 발생하지 않죠...
아마 다들 비슷한 경험이 있으실겁니다. 태어나서 처음으로 쌍방향 연결에 성공해서 두 클라이언트가 대화할 수 있게 했을때의 기쁨... 저는 딱 15년만에 하는거라(...) 처음 해본것처럼 기쁘네요
오늘 한것: 웹소켓 써서 채팅방 구현하기. . . . . . . 대체 왜였을까. . . (재밌기 때문
잘 동작합니다! 드디어 야크 털을 좀 얻었어요
(이미 저 멀리 와버린 야크 털 깎기 - 어디서 출발했는지도 이젠 잊어버림)
django에서 django rest framework에서 jwt로 authentication을 하기 위해 simple jwt를 쓸 때 (아이고 길다) access, refresh token을 cookie에 두고 사용하고 싶다면 약간의 수제(?) 코드가 필요합니다. 누군가 구현 해놨을까 싶었는데 있네요!
https://velog.io/@kimjihong/simple-jwt-login
https://velog.io/@kimjihong/issue-drf-jwt-header#solution---custom-middleware
하... 하려던건 이게 아닌데 이걸 적용 할지말지 또 고민... (결국 하겠죠
사-도파티
구체적으로 내가 원하는 스펙이 따로 있어서 직접 구현하기로 했습니다... 근데 이거를 다시 오픈소스로 만들면 또 관리 안되는 오픈소스C가 탄생하는 것일까요?
django channels에서 jwt authentication을 쓰고 싶은데
- 직접 만들긴 뭔가 싫다
- 오픈소스A는 5년 전에 마지막 커밋이 있다...
- 오픈소스B는 2년 전이 마지막이긴 한데 .DS_Store가 같이 커밋 되어있어서 불안하다...
어카지
야크 털 깎기 안하기가 쉽지 않습니다.
- 지금 필요한것: 일정 시간 동안만 설문을 수집하여 결과 확인하기
- 올바른 구현: 구글 폼
- 실제로 하고 있는것: 일정 시간만 공개되는 설문을 실시간 퀴즈 풀기 사이트와 유사하므로 웹소켓으로 퀴즈 내용을 알려주는 풀스택 웹서비스를 구현하기 위한 boilerplate에 혹시 모를 task 관리를 위해 redis 붙이고 celery 붙이고 모니터링 붙일지 말지 고민하기
- 설문을 -> 설문은
야크 털 깎기 안하기가 쉽지 않습니다.
- 지금 필요한것: 일정 시간 동안만 설문을 수집하여 결과 확인하기
- 올바른 구현: 구글 폼
- 실제로 하고 있는것: 일정 시간만 공개되는 설문을 실시간 퀴즈 풀기 사이트와 유사하므로 웹소켓으로 퀴즈 내용을 알려주는 풀스택 웹서비스를 구현하기 위한 boilerplate에 혹시 모를 task 관리를 위해 redis 붙이고 celery 붙이고 모니터링 붙일지 말지 고민하기
코딩 할 거 없어서 심심하신 분들
@theeluwin제이미 어쩔 수 없습니다 너무 편해서 돌이킬 수 없어요
@kodingwarriorJaeyeol Lee 진짜... 거의 이거 때문에 django를 포기 못하는 느낌입니다
django admin 그만 쓰는 법
Pelican을 기준으로,
- docker 개발(hot-reload), 프로덕션(nginx) 환경 구축
- jinja2 tag, filter, macro 사용하기 (+ context)
- GitHub Action으로 build 테스트 + PR merge시 GitHub Pages로 deploy하기
하는 내용이 동봉되어있습니다. 약간 튜토리얼을 겸한달까요...
이왕 이렇게 된거 후기 글도 쓰고 정말로 튜토리얼도 만들고 할까 싶었지만... 코딩 하느라 좀 지쳐버린ㅠ
대학원 연구실 홈페이지 생성기를 만들었습니다. Jekyll은 Ruby니까, 이번엔 Python을 좀 써보자 싶어서 Pelican으로 구현했습니다. Article을 일종의 DB처럼 사용해서 멤버나 논문, 뉴스, 강의 등의 데이터를 관리하는 방식입니다.
솔직히는 하루이틀이면 끝날줄 알았는데 만드는데 거의 full-time으로 일주일이 걸렸네요. macro에서 왜 context가 전달이 안되는지, filter에서는 왜 안되는지, GitHub Action은 왜 맨날 뻑이 나는지... branch 규칙도 여러번 수정하고 github pages로 내보낼때만 fork me 리본 달아주고 등등... 왤케 자잘하게 할게 많은지ㅠ
Pelican 자체는 쓸만하더라구요. 필요한 기능이 거의 다 있습니다. 근데 없는것처럼 보여요. 근데 다 있긴 합니다.
암튼... 구경와주세요
GitHub에서 PR 할 때 Copilot한테 리뷰 부탁하는거 나쁘지 않네요. 자명한 오타나 실수를 AI로 체크 한다는 느낌.
웹코딩 왤케 재밌는지, 약간 게임에 빠져있는것과 같은 경험임
- 현재 내게 중요하지 않은 작업 (즉, 딴짓)
- 책임을 지지 않아도 되는 서비스 (상업용이 아님)
- 오랜만에 함
- 원래 좀 재밌긴 함
이렇게 네가지가 겹침.
방금은 최적화도 좀 했다. 대충 O(n^3)쯤 되는 코드를 O(n)으로 바꿈. 근데 n은 대충 10쯤 되고, 기존 0.01초 걸리던게 이제는 0.01초 걸림.
@hongminhee洪 民憙 (Hong Minhee) 오ㅋㅋㅋㅋ 바로 한번 써보겠습니다 감사합니다ㅎㅎ
연구실 홈페이지를 쉽게 만들고 관리 할 수 있는 pelican 기반 bolierplate를 만들고 있습니다...만, 이건 말이 bolierplate지 사실상 theme도 포함인거라 디자인이 좀 들어가있어야하는데... 여기서 막혔습니다,,, 다른 부분은 완전 완성인데ㅜㅜ
테스트 작성 할 때 마다 약간 테스트 자체를 자꾸 디버깅 하고 있느라 시간을 다 쓴다는 느낌이었는데... 오늘은 그래도 Cursor의 도움으로 94개나 되는 테스트를 순식간에 작성했고 (엄청난 디버깅이 있었지만) 그래도 딱 한개, 정말로 테스트에 의해서만 잡을 수 있는 문제를 발견해서 올바르게 수정 할 수 있었다.
어제~오늘의 잡도리 일기
첫 MCP 경험 is like
오픈소스에서 아쉬운 점 발견 → 내가 기여해야지! → 혹시 모르니 issue, PR 확인해서 중복이 아닌지 체크 → 이미 해결된 문제였고 내가 사용법을 몰랐을 뿐 (그럼 접근성이 부족하니 문서라도 업데이트 할까? → 문서에도 적혀있었고 그저 내가 게을렀을 뿐)
jekyll 대신 pelican을 써보고 있습니다. Cursor에게 알아서 좀 짜라고 맡겨놨더니 링크가 모조리 깨지고 화면 템플릿도 제대로 안보이고 난리더라구요. 4시간동안 잡도리를 한 결과, 그냥 제가 처음부터 다시 다 짰습니다. 젠장... 암튼 pelican 쓸만 하네요. 어차피 대부분의 기능은 직접 구현해야해서, 최소한의 세팅만을 원했는데 충분히 제공하는것 같습니다. 공개할 수 있게되면 use case로써 공유해볼게요.
@theeluwin제이미
korean
같은 패키지를 활용하셔야 하지 않을까요?
@hongminhee洪 民憙 (Hong Minhee) transifex를 통해서 할 방법은 없나보네요,,, django 자체에 저 기능을 추가해야할지,,,
transifex로 django 번역을 좀 더 하고 있는데요, 을를이가를 어떻게 처리하는게 좋을까요? 조사를 전부 빼는것도 능사는 아닌것 같은데...
@theeluwin제이미 축하드립니다~!!
@arkjunJuntai Park 감사합니다ㅎㅎ!!!
(user, quiz, option)을 response의 필드로 두고 (user, quiz)에 unique constraint를 걸면, option이 quiz 소속인지를 보장 할 수 없는데, 이거까지 보장하는건 쉽지 않습니다.
@theeluwin제이미
option
테이블의 기본 키를 (quiz_id, index)
정도의 복합 키로 만든 뒤에, response
테이블의 (quiz_id, option_index)
를 option
키의 외래 키로 만들면 option
이 어느 quiz
의 소속인지 강제할 수 있지 않을까요?
@hongminhee洪 民憙 (Hong Minhee) 아 그렇겠네요..! 이제 django orm 구현이 걱정인데.. (실은 지금은 다중선택 퀴즈도 만들어야해서 구조를 완전 바꿨습니다(...
퀴즈엔 여러개의 보기가 있습니다. 사용자는 퀴즈당 한개의 보기만 고를 수 있습니다. 이를 어떻게 강제 할 수 있을까요? Django ORM에서 말이죠.
답은, ‘쉽지 않다’ 입니다.
(user, quiz, option)을 response의 필드로 두고 (user, quiz)에 unique constraint를 걸면, option이 quiz 소속인지를 보장 할 수 없는데, 이거까지 보장하는건 쉽지 않습니다.
(user, option)만 필드로 둘 경우엔 (user, option__quiz)를 unique로 둬야하는데 이런 기능은 존재하지 않죠.
복합키, trigger 등을 사용하기엔 너무 복잡하고, 결국 어플리케이션 로직이 필요합니다.
그래서, 어떡하지 이제
생각해보니 어차피 정답 여러개인 경우도 만들어야하니까 (user, option)으로 가고, 채점 할 때 로직으로 땜빵하는 방식으로 가야겠다.
퀴즈엔 여러개의 보기가 있습니다. 사용자는 퀴즈당 한개의 보기만 고를 수 있습니다. 이를 어떻게 강제 할 수 있을까요? Django ORM에서 말이죠.
답은, ‘쉽지 않다’ 입니다.
(user, quiz, option)을 response의 필드로 두고 (user, quiz)에 unique constraint를 걸면, option이 quiz 소속인지를 보장 할 수 없는데, 이거까지 보장하는건 쉽지 않습니다.
(user, option)만 필드로 둘 경우엔 (user, option__quiz)를 unique로 둬야하는데 이런 기능은 존재하지 않죠.
복합키, trigger 등을 사용하기엔 너무 복잡하고, 결국 어플리케이션 로직이 필요합니다.
그래서, 어떡하지 이제
새롭게 다시 태어난, 또 만들어버린 boilerplate. 이제는 진짜 monolithic 하고 Pocket Galaxy라는 이름에 걸맞는 boilerplate입니다.
Django + Vue(Vuetify) 조합이구요, nginx가 이것저것을 다 처리합니다.
백엔드는 /api
에서 서빙하고, 기타 기본적인 static 캐싱이나 로깅 등 전부 기초적인건 제공합니다.
간단한 웹사이트 하나 만들겠다는게 어쩌다 여기까지 왔는지.... 암튼 이제는 진짜 최소한의 웹사이트 만들때 뚝딱 하면 만들수 있을것 같습니다 제발...
내부용 툴 만들때 애용해보세요.
@theeluwin제이미 축하드립니다!!
@bglbgl gwyng 감사합니다ㅎㅎ!!!
@theeluwin제이미 오… 축하드립니다! 축하드려야 하는 거 맞죠?
@hongminhee洪 民憙 (Hong Minhee) 앗 넵ㅋㅋㅋㅋㅋ 감사합니다ㅎㅎㅎ 어려서부터 꿈이었습니다ㅎㅎ
아 9월부터 강의를 합니다 (교수가 되었습니다). 무슨 과목을 맡게될지는 모르겠지만 꿈에 그리던 기깔난... 마치 IoT로 도배된 집과도 같은 강의를 해보겠습니다 기대해주세요. 그리고 언젠가 이 경험들이 쌓여서 파이콘에서라도 발표하면 좋겠네요.