프로젝트/레트로 테트리스

테트리스 레트로 개발 회고 — 성능 개선부터 배포까지

ksc-dev 2026. 6. 10. 20:09

개요

테트리스 레트로 프로젝트를 진행하면서

  • 백엔드 연동
  • 풀스택 배포
  • 성능 최적화
  • 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