본문 바로가기

반응형

전체 글

(121)
4편: plays 테이블의 회수 — 주간/월간 랭킹과 유령 기록 1편에서 plays(판 단위 원천)와 player_game_stats(집계)를 둘 다 만들어 뒀다고 했습니다. 당시엔 집계만 있어도 서비스가 돌았어요. 레벨도, 전체 랭킹도 집계만 읽으면 되니까요. plays는 "언젠가 쓰겠지" 하고 넣어둔 테이블이었습니다.그 언젠가가 왔습니다. 주간·월간 랭킹입니다.회귀 위험 0으로 얹기기존 랭킹 API는 잘 돌고 있었습니다. 여기에 기간 개념을 넣으면서 제일 피하고 싶었던 건, 기존 랭킹이 미묘하게 달라지는 것이었어요. 순위 하나가 바뀌어도 유저 입장에서는 버그로 보입니다.그래서 규칙을 이렇게 잡았습니다. GET /rankings?period=daily|weekly|monthly|all (기본 all)period가 없거나 all이면 기존 함수를 그대로 호출합니다...
3편: FOREIGN KEY constraint failed — 크래시를 두 번 잡은 이야기 SQLite 전환 배포 다음날, 서버가 이따금 죽었습니다. 도커가 알아서 재시작해 줘서 서비스가 오래 멈추진 않았지만, 접속해 있던 사람들의 소켓은 전부 끊기죠. 로그에 남은 건 한 줄이었습니다. FOREIGN KEY constraint failed파일 저장소에는 없던 종류의 사고입니다. JSON은 아무 키나 써도 받아주니까요. 외래 키를 켠 순간, 그동안 조용히 넘어가던 잘못된 쓰기가 전부 예외로 바뀐 겁니다. 1차: 두 줄의 순서 문제역추적해 보니 점수 제출(submit_score) 경로였습니다. 코드는 대충 이런 모양이었어요. jssubmitScore(...) // score_rankings INSERT — player_key가 players를 FK 참조recordPlayRow(...) ..
2편: 배포 전 스크립트를 끼울 자리가 없다 — 마이그레이션을 서버 기동에 밀어 넣기 스키마를 다 짰으니 이제 데이터를 옮길 차례입니다. 처음엔 평범하게 CLI 스크립트를 만들었습니다. node src/migrate.js # DB 파일이 이미 있으면 중단node src/migrate.js --force # 덮어쓰기이걸 배포 직전에 한 번 돌리면 끝 — 이라고 생각했는데, 여기서 막혔습니다.배포 파이프라인에 손댈 자리가 없었다PlayTalk은 집에 둔 미니 PC에서 셀프호스티드 러너로 배포됩니다. main에 머지되면 러너가 알아서 빌드하고 컨테이너를 갈아 끼우는 구조예요. 문제는 이 파이프라인이 "main 머지 = 배포 완료" 하나로 굴러가게 만들어져 있어서, 중간에 "이번 배포만 이 스크립트를 먼저 돌려주세요" 같은 단계를 끼워 넣기가 마땅치 않았다는 겁니다.수동으로 서..
1편: users.json이 한계에 온 날 — 신원 키 하나로 정리한 스키마 PlayTalk은 처음부터 DB 없이 시작한 사이드 프로젝트입니다. 회원 정보는 users.json, 게스트 전적은 guestStats.json, 친구 관계는 friends.json, 점수 랭킹은 rankings.json. 파일 네 개에 JSON을 통째로 읽고 통째로 쓰는 방식이었어요. 게임이 두세 개일 때는 이게 제일 빨랐습니다. 스키마 마이그레이션도 없고, 서버 켜면 바로 돌아가니까요.그런데 게임이 열 개를 넘고, 로그인 회원과 게스트가 섞이고, 친구 기능까지 붙으면서 슬슬 이상해지기 시작했습니다.파일 저장소가 아팠던 지점첫째, 같은 사람이 파일마다 다른 이름으로 존재했습니다. 로그인 사용자는 users.json 안의 id, 게스트는 guestStats.json의 uuid. 그래서 "친구 목록을 보여..
6편: "대부분 지고 가끔 크게 이긴다"를 숫자로 만들기 — 차트 모델 삽질기 이 게임의 심장은 차트입니다. 플레이어는 이 차트를 보고 "지금이 바닥이다" 판단해서 매수하니까요. 그래서 차트가 어떻게 움직이느냐가 게임의 재미를 좌우했는데, 여기서 제법 삽질을 했습니다. 문제 → 원인 → 재설계 과정이 꽤 깔끔하게 나와서 정리해봅니다.목표: 대부분 지고, 이길 땐 크게1편에서 말했듯 이 게임의 감성은 "역발상으로 바닥 잡겠다고 덤비지만 대부분 죽는다"입니다. 이걸 차트로 옮기면 이런 요구사항이 됩니다.승률은 낮아야 한다 (대부분 크아악).대신 이길 때는 크게 올라야 한다 (하락분을 만회할 만큼).계속 우하향해서 0에 처박히면 안 된다 (차트가 깨짐).말은 쉬운데, 이걸 숫자로 만드는 게 만만치 않았습니다.1차 시도: 하락 편향 랜덤워크 → 실패처음엔 단순하게 갔습니다. 매 순간 가격..
5편: 페페는 왜 못 쓰고 와작은 되나 — 밈 저작권과 AI 생성물의 오해 이 게임을 만들면서 가장 오래 고민한 주제가 사실 코드가 아니라 저작권이었습니다. 원본 짤은 페페(개구리)인데, 이걸 그대로 쓸 수가 없었거든요. 이 이야기는 밈을 소재로 뭔가 만들려는 분들에게 도움이 될 것 같아 따로 정리합니다.페페는 주인이 있고, 그 주인이 단속한다먼저 팩트부터. 페페 더 프로그(Pepe the Frog)는 명확한 저작권자가 있습니다. 원작자는 맷 퓨리(Matt Furie)라는 만화가고, 이 사람은 자기 캐릭터가 무단으로 상업적으로 쓰이는 걸 실제로 적극적으로 막아온 사람입니다. 경고장을 보내고, 플랫폼에 삭제 요청을 넣고, 소송까지 갑니다.즉 페페는 "인터넷에 굴러다니는 공짜 밈"이 아니라 주인이 있고, 그 주인이 권리를 행사하는 캐릭터입니다. 개인이 SNS에 짤로 공유하는 정도야..
4편: 도메인 사기 부담스러워서, 게임을 기존 사이트에 하위 페이지로 붙였다 게임이 완성됐습니다. 이제 배포할 차례인데, 여기서 아주 현실적인 벽에 부딪혔어요.도메인이 부담스러웠다처음엔 이 게임을 단독 서비스로 배포하려고 했습니다. 그럴듯한 도메인 하나 사서, 거기에 올리고요. 그런데 막상 하려니 마음이 걸렸어요.재미로 만든 밈 게임입니다. 얼마나 많은 사람이 할지도 모르고, 대부분은 한두 번 해보고 말 단발성 콘텐츠일 가능성이 높죠. 그런데 여기에 도메인 값을 매년 내는 게 아깝더군요. 도메인 등록하고, DNS 붙이고, 호스팅 잡고… 배보다 배꼽이 큰 느낌이었습니다.그러다 생각했어요. "어차피 나 플레이톡 운영하고 있잖아?"플레이톡은 제가 만들어서 운영 중인 랜덤 매칭 미니게임 사이트입니다. 이미 도메인도 있고, 배포 환경도 다 갖춰져 있죠. 그러니 이 게임을 굳이 따로 낼 게..
3편: 클로드코드와 SPEC 문서 하나로 게임 만들기 애셋이 준비됐으니 이제 코딩입니다. 그런데 이번엔 제가 직접 짜지 않고 **클로드코드(Claude Code)**에 맡겼습니다. 여기서 제가 나름 정착시킨 워크플로우가 있는데, 꽤 잘 굴러가서 공유해봅니다.프롬프트를 매번 쓰는 건 비효율적이다AI 코딩 도구를 쓰다 보면 흔히 하는 실수가, 매번 "이거 해줘 저거 해줘"를 즉흥적으로 프롬프트로 던지는 겁니다. 이러면 몇 가지 문제가 생겨요.이전에 뭘 요청했는지 기록이 안 남는다.AI가 맥락을 잃어버리면 다시 설명해야 한다.프로젝트가 커질수록 "지금 이게 어떤 상태였지?"가 헷갈린다.그래서 저는 SPEC.md라는 사양 문서 하나를 만들어서 그걸 단일 기준으로 삼았습니다. 게임의 모든 것 — 게임 루프, 상태 머신, 애셋 목록, 차트 로직, 색상 처리 방식, 심..

반응형