본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 06. 06. 13:25

프로그래밍 지식 제로 ~ 본격 게임 개발 기록

요약

실시간 일본어 타이핑 게임 '카케루 타이핑'의 대전 모드를 구현하기 위해 Socket.IO와 Render를 활용한 아키텍처를 소개합니다. 서버리스 환경의 한계를 극복하기 위해 대전 서버를 분리하고, 봇 투입 및 서버 슬립 방지 노하우를 다룹니다.

핵심 포인트

  • Socket.IO를 활용한 실시간 룸 관리 및 진척도 동기화
  • Vercel과 Render를 분리하여 WebSocket 상시 연결 유지
  • 정규화된 값을 이용한 실시간 레이스 시각화 구현
  • WPM 기반의 자연스러운 봇(Bot) 로직 설계
  • UptimeRobot을 이용한 Render 무료 플랜 슬립 방지

개인 개발로, 여러 명이 동시에 같은 문장을 입력하며 속도를 겨룰 수 있는 일본어 타이핑 연습 사이트 카케루 타이핑 (Kakeru Typing) (https://kakeru-typing.com)을 만들고 있습니다.

이 기사에서는 그 핵심 기능인 「모두와 대전 (VS) 모드」를 뒷받침하는 **실시간 통신 (Real-time Communication)**을, Socket.IO와 무료 Render만으로 어떻게 구현했는지 해설합니다. WebSocket을 처음부터 직접 작성하는 것이 아니라, 룸 관리 · 진척도 동기화 · 봇 대전 · 무료 운영의 노하우까지 구현의 핵심 포인트를 정리합니다.

카케루 타이핑은 「화면 (Next.js)」과 「대전 서버 (Socket.IO)」를 별도 프로세스로 나누고 있습니다.

[브라우저] ──HTTP──> [Next.js / Vercel] … 화면 · 인증 · DB
│
└──WebSocket──> [Socket.IO / Render] … 대전의 실시간 동기화

Vercel은 기본적으로 서버리스 (Serverless) 방식이며, 상시 연결 상태인 WebSocket을 계속 유지하는 용도로는 적합하지 않습니다. 그래서 대전 서버만을 Render의 상주 프로세스로 분리했습니다. 프론트엔드에서는 환경 변수를 통해 접속 대상을 전달합니다.

// 접속 대상은 빌드 시 환경 변수로 교체 (로컬은 localhost)
const SERVER_URL =
process.env.NEXT_PUBLIC_BATTLE_SERVER_URL ?? "http://localhost:3002";

대전은 「룸 (Room)」 단위로 이루어집니다. 참가자가 입장하고, 전원이 모이면 카운트다운 → 동일한 문장을 배포하고, 각자의 타건 진척도를 브로드캐스트 (Broadcast) 합니다.

import { Server } from "socket.io";
const io = new Server(PORT, {
cors: { origin: process.env.CORS_ORIGIN?.split(",") ?? [] },
...

포인트는 진척도를 0..1 사이의 정규화된 값 (Normalized Value)으로 보내는 것입니다. 글자 수나 난이도에 의존하지 않고, 상대방의 「자동차」를 바(Bar)나 트럭 위에서 움직이는 묘사에 그대로 사용할 수 있습니다. 카케루 타이핑에서는 진척도를 레이스 거리로 변환하여 상대방과의 차이를 실시간으로 보여줍니다.

매칭 상대가 없으면 이용자가 적어 보일 수 있으므로, 인원이 부족할 때는 봇 (Bot)을 투입하도록 했습니다. 봇의 핵심은 「자연스러운 속도」입니다. 실제 플레이어의 체감에 맞춰 WPM 150~300 사이에서 랜덤하게 작동하도록 했습니다.

// WPM(=1분당 타건 수)을 「1초당 타건 수 (CPS)」로 변환하여 봇을 실행
const KEYSTROKES_PER_KANA = 1.9; // 가나 1글자당 평균 로마자 키 수 (실측값)
const targetWpm = 150 + Math.random() * 150; // 150〜300 WPM
...

KEYSTROKES_PER_KANA (가나 1글자당 로마자 타건 수)를 거침으로써, 「WPM」이라는 인간이 이해하기 쉬운 단위 그대로 봇의 강도를 조정할 수 있습니다. 이 값을 산출하는 방법은 별도 기사인 일본어 타이핑 엔진 편에서 자세히 다루겠습니다.

Render의 무료 플랜은 15분 동안 액세스가 없으면 슬립 (Sleep) 상태가 되며, 다음 액세스 시 기동하는 데 십수 초가 걸립니다. 대전에 들어가려는 순간 멈추면 치명적이므로, UptimeRobot으로 /health를 5분 간격으로 호출하여 계속 깨워두고 있습니다.

// 대전 서버 측에 헬스 체크 (Health Check)용 엔드포인트 준비
httpServer.on("request", (req, res) => {
if (req.url === "/health") {
...

CORS도 잊기 쉬운 포인트입니다. 프론트엔드 (Vercel)와 대전 서버 (Render)는 오리진 (Origin)이 다르기 때문에, CORS_ORIGIN에 운영 도메인과 localhost를 모두 넣어둡니다.

  • WebSocket 상주가 필요한 부분만 Render로 분리하면, Vercel과 무료로 공존할 수 있다
  • 진척도는 0..1의 정규화된 값으로 보내면 묘사가 쉽다
  • 봇은 「WPM」 기준으로 설정하면 강도 조정이 직관적이다
  • 무료 플랜의 슬립 상태는 UptimeRobot으로 회피한다

실제 동작은 카케루 타이핑 (Kakeru Typing) (https://kakeru-typing.com)의 「다 함께 대전」에서 테스트할 수 있습니다. 혼자서 접속하더라도 봇이 참여하므로, 실시간 동기화 (Real-time synchronization)의 체감을 즉시 확인할 수 있습니다.

다음 기사에서는 이 위에서 동작하는 **일본어 타이핑 엔진 (로마자 입력 · 후리가나 · WPM 산출)**의 내부 구조를 해설합니다.

완성품으로 플레이하기 → 카케루 타이핑 (Kakeru Typing) https://kakeru-typing.com

↑ 공개 후,

URL0

URL4

를 Qiita의 기사 URL로 교체해 주세요.

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0