개요
테트리스 레트로 프로젝트를 진행하면서
- 백엔드 연동
- 풀스택 배포
- 성능 최적화
- UI 개선
까지 이어지는 전체 흐름을 정리한 회고다.
단순 기능 추가가 아니라
👉 “로컬 게임 → 실서비스 구조”로 발전시키는 과정이었다.
1. 백엔드 연동 & 풀스택 배포
1-1. API 클라이언트 구조 설계
프론트엔드에서는 axios 기반 API 레이어를 분리했다.
핵심 설계
- VITE_API_URL 환경변수 사용
- withCredentials: true (쿠키 인증)
- X-Requested-With 헤더 추가 (score API 요구사항 대응)
API 목록
- getRanking
- getMe
- recordScore
- getMyRecords
- restoreCode
👉 프론트에서 fetch 직접 호출을 제거하고
👉 “API 계층 구조”로 분리
1-2. 게임오버 자동 기록 시스템
게임 종료 시 자동으로 기록이 저장되도록 개선했다.
- 레벨 5 이상만 랭킹 등록 가능
- 닉네임 저장 시 자동 제출
- useEffect 기반 자동 처리
중요한 구조 문제 발견
useRecoveringCode()를 GameOverlay에서 직접 사용
👉 문제:
- 게임 tick마다 리렌더 발생
- 성능 저하
해결
- 상태를 GamePage로 이동
- props로 전달
👉 Zustand + React 구조 최적화 포인트
1-3. 랭킹 & 개인 기록 시스템
RankingPage
- LV.5 이상만 등록
- level → lines → score 정렬
MyRecordPage
- recovery code 기반 조회
- 다른 기기에서도 데이터 복원 가능
1-4. 배포 구성 (Render + Vercel)
구조
- Frontend: Vercel
- Backend: Render
- DB: Supabase
- Cache: Redis
주요 환경 변수
Render
- DATABASE_URL
- REDIS_URL
- HMAC_SECRET
- CORS_ORIGIN
- NODE_OPTIONS
Vercel
- VITE_API_URL
배포 이슈 해결
- Prisma 7 adapter 문제
- IPv6 연결 실패
- migration deploy 속도 문제
- CORS preview URL 문제
👉 결과적으로 “실서비스 구조” 완성
2. 성능 개선 & UI 업데이트
2-1. 입력 렉 문제 (조작 지연)
문제
- 키 입력 후 블록이 즉시 움직이지 않음
- 최대 800ms 지연 발생
원인
input → state 변경 only
render → setInterval tick에서만 발생
👉 입력과 렌더링이 분리되지 않음
해결
function moveWithRender(fn: () => boolean): () => boolean {
return () => {
const moved = fn()
if (moved) syncRender()
return moved
}
}
결과
- 즉시 반응 구조 완성
- 입력 UX 개선
2-2. 렌더링 성능 문제 (200개 객체 생성)
문제
- 10×20 = 200 셀
- 매 tick마다 style 객체 생성
return { backgroundColor, border, boxShadow }
👉 React diff 비용 과다 발생
해결
CSS 기반 구조로 변경
<div
className={`board-cell ${cell ? 'cell-filled' : 'cell-empty'}`}
style={cell ? { '--cell-color': cell } as React.CSSProperties : undefined}
/>
.cell-empty { background: #111; }
.cell-filled { background: var(--cell-color); }
핵심
- JS 객체 제거
- CSS 변수만 동적 처리
👉 렌더링 비용 크게 감소
3. UI 개선
3-1. 랭킹 페이지 UX 개선
문제
- LV.5 이상만 등록되지만 설명 없음
해결
- 필터 조건 안내 추가
👉 “왜 내 점수가 안 보이는지” 문제 해결
3-2. 내 기록 페이지 개선
개선 내용
- 복구 코드 안내 강화
- 2줄 → 4줄 확장
- 캡쳐 안내 추가
📸 이 코드를 캡쳐해서 백업하세요
🔄 복구 코드는 주기적으로 재발급 권장
3-3. 업데이트 모달 개선
문제
- 7px 폰트 → 가독성 낮음
- 색상 대비 부족
해결
- 8px로 증가
- #666 → #bbb 변경
👉 레트로 감성 유지 + 가독성 확보
4. 전체 배운 점
4-1. React 게임 구조 핵심
- tick 기반 루프
- 입력 즉시 반응
👉 둘이 섞이면 반드시 구조 충돌 발생
해결 패턴
- 입력 → 즉시 렌더
- 자동 루프 → 별도 처리
4-2. React 렌더링 비용
핵심 문제
- 인라인 style 객체 생성
👉 React diff 비용 증가
최적화 패턴
방식사용
| CSS class | 정적 스타일 |
| CSS variable | 동적 값 |
4-3. Zustand 구조 문제
- store를 잘못 위치시키면
- 게임 루프에서 리렌더 폭발 가능
👉 상태 위치 설계 중요
결론
이번 프로젝트는 단순한 테트리스가 아니라
“풀스택 구조 + 실시간 게임 + 배포 + 성능 최적화”까지 포함된 실전 프로젝트였다.
특히 핵심은:
- 백엔드 구조 설계
- Redis 캐싱
- Prisma 7 마이그레이션
- React 게임 루프 설계
- 렌더링 최적화
- 배포 환경 문제 해결
한 줄 요약
로컬 게임이 아니라 “서비스로 동작하는 게임 구조”를 완성한 과정
🔗 직접 플레이해보기
이번 글에서 정리한 내용은 실제 서비스에 반영되어 있습니다.
아래 링크에서 확인할 수 있습니다.
https://tetris-retro-web.vercel.app/
tetris_retro_web
tetris-retro-web.vercel.app
'프로젝트 > 레트로 테트리스' 카테고리의 다른 글
| Redis 제거와 복구코드 재발급 기능 구현 기록 (TDD + NestJS 트러블슈팅) (4) | 2026.06.15 |
|---|---|
| Claude Code로 테트리스 만들기 4부 - 모바일 터치 조작 구현 (0) | 2026.04.21 |
| Claude Code로 테트리스 만들기 3부 - 반응형 레이아웃 구현 (0) | 2026.04.20 |
| Claude Code로 테트리스 만들기 2부 - 줄 삭제와 점수 시스템 구현 (0) | 2026.04.19 |
| Claude Code로 테트리스 만들기 1부 - 충돌 감지 구현 (0) | 2026.04.19 |