WARDOGTAG — 워독스 한국 커뮤니티 사이트
한국어 정보 채널이 없던 FPS 게임 WARDOGS를 위해, 서버 현황·패치노트 요약·장비 DB·클랜 편제를 다루는 커뮤니티 사이트를 이틀 만에 설계·구현·배포했습니다.
개요
2026년 9월 10일 한국어 얼리 액세스로 출시된 WARDOGS에는 한국어 정보 채널이 없었습니다. 패치노트는 영문이고, 접속할 서버를 고르려면 게임을 켜야 했습니다. 그 두 가지를 웹에서 해결하는 사이트를 기획부터 운영까지 혼자 맡아 이틀 만에 올렸습니다. 커뮤니티 글은 검색 노출과 초기 로딩이 중요한데 SPA로 얻을 이익이 없어 EJS 서버 사이드 렌더링을 택했고, 상호작용이 탭·복사·카운터 수준이라 클라이언트 스크립트는 바닐라 326줄로 끝냈습니다. 비용을 들이지 않으려고 개인 미니PC를 서버로 쓰고 기존 포트폴리오 도메인의 서브패스(/wardogtag)로 붙였습니다. nginx가 HTTPS를 받아 pm2로 띄운 Express에 넘기고, 세션은 Redis를 더 올리지 않도록 MySQL에 저장해 재시작 후에도 로그인이 유지되게 했습니다. 로그인은 Steam OpenID라 비밀번호를 아예 받지 않습니다.
가장 손이 많이 간 곳은 외부 API였습니다. 서버 목록 API는 전 세계 2,100대가 넘어 한 번에 받으면 홈 화면이 2초 가까이 멈췄습니다. 제공자가 안내한 갱신 주기(60초)를 지키면서 ETag 조건부 요청으로 변경이 없으면 304에서 끝내고, 지역별 집계는 stale-while-revalidate로 직전 값을 즉시 주면서 뒤에서 갱신하고, 서버가 뜨는 동안 미리 받아두는 예열을 붙여 20~50ms로 내렸습니다. 외부 API가 죽어도 페이지는 떠야 하므로 모든 호출에 실패 기본값을 두고, 장애를 강제로 재현해 홈과 서버 목록이 정상 렌더되는지 확인했습니다.
게임 정보 사이트는 정확성이 생명이라 보고 규칙을 먼저 정했습니다. 공식 공지·패치노트·스토어 설명과 대조한 값만 올리고 항목마다 출처를 표시하며, 공개되지 않은 값은 추정하지 않고 '미확인'으로 남기고, 매체마다 다른 수치는 아예 쓰지 않습니다. 실제로 초기 가이드에 출시 전 홍보 자료에서 온 문장이 섞여 있어서, 공지 27건과 용어집, DB 35항목을 원문과 한 문장씩 대조해 9곳을 고쳤습니다. 검증은 브라우저 자동화 대신 실제 운영 서버에서 도는 스크립트로 했습니다. 세션 테이블에 행을 넣고 서명된 쿠키를 만들어 로그인 상태를 그대로 재현하고, 권한별 계정을 임시로 만들어 시나리오를 돌린 뒤 데이터를 전부 지우는 방식입니다.
혼자 운영하는 서비스라 손이 많이 가는 일부터 자동화했습니다. 배포는 명령 하나로 git archive → 전송 → 의존성 설치 → 마이그레이션 → pm2 재기동 → 응답 확인까지 이어지고, 스키마는 순번 마이그레이션 9개를 적용 이력 테이블로 관리합니다. 백업은 매일 새벽 덤프와 업로드 압축을 14일 보관하되, 만들기만 해서는 소용이 없으므로 덤프 안에 테이블 25개가 모두 들어 있고 정상 종료되었는지까지 확인하는 절차를 넣었습니다. 회원 탈퇴는 계정을 즉시 지우되 글과 댓글은 남기고 작성자를 '탈퇴한 회원'으로 바꾸며, 대대장은 인계 전까지 탈퇴를 막아 조직 데이터가 깨지지 않게 했습니다. 남은 한계도 분명합니다 — 전적 API가 공개되지 않아 킬 기록 랭킹은 만들 수 없어 도전과제·기여도로 대체했고, 단일 서버라 대규모 트래픽에 취약하며, 재기동 방식이라 배포 시 수 초간 끊깁니다.
원인 — 넣은 방어가 등록 순서 때문에 꺼져 있었다
CSRF 방어를 구현해 넣었는데도 외부 사이트에서 비회원 글쓰기가 그대로 통했습니다. 코드 자체는 맞았고, 미들웨어 등록 순서가 어긋나 요청이 검증을 거치기 전에 라우터에 닿고 있던 것이 원인이었습니다. 공격 시나리오를 돌리지 않았다면 '방어를 넣었다'고 믿은 채 베타를 열었을 겁니다. 그 뒤로는 방어를 추가할 때마다 그 방어를 뚫는 요청을 함께 작성했습니다. 보안은 구현과 검증이 한 쌍이어야 성립합니다.
핵심
- 이틀·커밋 50개로 서버 6,094줄, 뷰 51개, DB 테이블 25개, 라우트 16개를 구현하고 개인 미니PC에 배포. 런타임 의존성 12개, 프런트엔드 프레임워크 없음
- 전 세계 2,100대를 한 번에 받느라 홈이 2초 가까이 멈추던 외부 서버 목록 API를, ETag 조건부 요청 + stale-while-revalidate + 부팅 예열로 20~50ms로 단축
- 넣어둔 CSRF 방어가 미들웨어 등록 순서 때문에 동작하지 않던 것을 검증 테스트로 발견·수정 — 외부 사이트에서 비회원 글쓰기가 가능한 상태였습니다
- 기능 E2E 109개 + 공격 시나리오 86개(SQLi·XSS·CSRF·경로탐색·권한우회·프로토타입 오염·ReDoS·업로드 위장) 통과, npm audit 취약점 0건
- 245페이지 전수 크롤링에서 5xx·깨진 링크 0건, 동시 접속 50 부하에서 초당 166요청·실패 0건·p95 0.57초, 응답 중앙값 42ms
- 정확성을 규칙으로 고정 — 공식 공지·스토어 설명과 대조한 값만 싣고 미공개 값은 추정하지 않고 '미확인'으로 남김. 이 기준으로 수치 오류와 추측 표현 9곳을 걷어냄