카드 게임 제작 기록 처음부터 보기 : https://helloworld.ai.kr/14
카드 게임 제작 ( 1 ) - 회고
게임 링크 : cardgame.perfect.ai.kr 개발 기간 : 2025.10.28 ~ 2025.11.27총 소요 시간 : 110시간Lovable에게 감탄 ( UI 작업 )https://blog.helloworld.ai.kr/15JWT 토큰은 json web token token이다 ( 로그인 구현 )https://blog.helloworl
helloworld.ai.kr
게임 링크 : cardgame.perfect.ai.kr
Magician Duel
cardgame.perfect.ai.kr
이 시점의 소요 시간 : 19시간 / 110시간
- 프론트엔드 껍데기 구현 완료 (로직 없는 덱 제작, 방 생성 등)
- Lovable이 만들어준 UI 반영
- Supabase 프로젝트 생성
- 구현 방법 및 전체 설계 고민
사서 고생?
게임 엔진 없이 기획부터 풀스택까지 혼자 다 하려니 숨이 턱 막혔다.
솔직히 말하면 중간중간 이런 생각도 들었다.
" 이거 뻘 짓 아닌가? "하지만 동시에 분명히 느꼈다.
하지만 재미가 있기도 하고 성장에 도움이 될 거라는 믿음에 밀고 나갔다.
Lovable 따라잡기
초기 작업에서 가장 인상 깊었던 건 Lovable이었다.
Lovable에게 게임 기획을 넘겨주고 UI를 만들어달라고 했다.
Lovable이 만들어준 UI를 내 코드에 하나씩 이식했는데…
정신차리고 보니 테세우스의 배였다.

그래서 Lovable의 비결을 분석해봤다.
UI 스택 분석
Lovable 결과물을 뜯어보니 구조가 명확했다.
PostCSS
- CSS용 Babel
- 더 높은 수준의 문법으로 CSS 작성 가능
Tailwind
- PostCSS 위에서 작동
- HTML 클래스 기반 빠른 스타일링
Radix UI
- 동작이 보장된 Headless 컴포넌트
shadcn/ui
- Radix UI + Tailwind 스타일 세트
- npx shadcn add {컴포넌트}로 자동 설치
- 착한 사람이 만든 것 같다
Lucide
- SVG 기반 오픈소스 아이콘
- Tailwind, shadcn과 궁합 좋음
밀키트로 요리를 하듯이, shadcn을 적극적으로 사용했기 때문에 Lovable 결과물의 퀄리티가 괜찮아 보였던 것 같다.
애니메이션에 대한 판단
카드게임이니까 애니메이션을 꼭 넣어야 하나 고민했었다.
그런데 Lovable 결과물을 보면서 없어도 괜찮겠다 싶었다.
그래서 우선순위를 이렇게 정리했다.
- 초기: CSS transition / framer motion
- 고도화: GSAP 또는 Pixi.js
Zustand와 React Query — 뒤늦은 깨달음
솔직히 react 커스텀 훅의 역할을 이 때에서야 정확히 이해했다.
React 컴포넌트 함수 본문은 렌더링마다 다시 평가된다.
렌더링되더라도 값을 유지하려면 상태, 즉 훅을 써야 한다.
- useRef
- useMemo
- useState
훅은 일종의 컴포넌트 로컬 상태다.
문제는 여러 컴포넌트에서 상태를 공유하는 것이 불편하다는 것이다.
그래서 등장한 것이 Zustand.
Zustand의 역할
- 전역 스토어 제공
- 여러 컴포넌트에서 동일 상태 공유
- 클라이언트 상태 관리 담당
React Query의 역할
React Query는 Zustand와 유사하게 상태를 관리해주면서도 성격이 다르다.
서버 데이터를 위한 전역 상태 관리 도구
- 주기적 fetch
- 캐시 유지
- 자동 갱신 타이밍 관리
내가 구현한 데이터의 흐름은 이렇게 된다.
React Query → Zustand → hook → UI 렌더링
그리고 persist 설정을 추가하면 Local Storage에 상태가 백업된다.
이 구조는 이후 프로젝트 전반에 그대로 유지됐다.
그때 정리했던 결정 목록
혼자 개발하면서도 문서 작업을 꽤 성실히 했다.
왜냐하면 결정하고 일관성 있게 지켜 나가야 할 것들이 너무 많았기 때문이다.
당시 정리했던 항목들:
- 카드 정보 JSON
- DB 스키마
- API 명세
- Zustand / React Query 상태 정의
- GameState 스키마
이 문서화가 두고두고 도움이 됐다.
당시 가장 큰 난제들
1) 스파게티 코드 방지
카드 효과를 JSON으로 분리한다는 전략을 택했다.
예시:
{
"trigger": "onDestroy",
"effects": [
{ "kind": "draw", "count": 1 },
{ "kind": "damage", "value": 2, "target": "enemy" }
]
}
그리고 일반화가 되지 않는 예외는 핸들러로 따로 분리.
이 방향 덕분에 엔진 메인 코드를 깔끔하게 유지할 수 있었다.
2) 프론트에서도 엔진을 구현할 것인가
프론트 상에서 동일한 엔진을 가지고 있다면 플레이어의 입력에 대해 사전에 유효성 검증을 할 수 있다.
UX 면에서 굉장히 중요한 포인트다.
불가능한 행동을 하려하면 그 행동 '이후에' 불가능하다는 것을 알려주는 것과 애초에 행동이 막히는 효과를 보여주는 것.
후자가 더 유저 친화적이다.
이상적인 구조는 프론트와 백이 동일한 엔진을 공유하는 것이다.
하지만 개발 과정에서 서로 틀어지면 골치 아파지고 구현량도 거의 두 배로 늘어난다.
그리고 이게 '핵심 기능'은 아니라고 판단했다.
최종 결정:
- 프론트: 마나 정도만 체크
- 나머지 유효성 검증: 전부 백엔드 책임
백엔드가 검증 후 불가능 행동이면 경고 반환.
3) 유저 입력 ↔ 효과 처리 반복 문제
이 부분이 까다로웠다.
카드 사용의 단순한 흐름은 이렇다.
카드 사용 → 엔진 처리 → broadcast
문제는 카드 하나 처리 중에도 유저 선택이 필요한 경우가 많다는 것이다.
예를 들어 전광석화라는 카드는 다음의 효과를 가지고 있다.
- 1 피해 공격
- 한 칸 이동
- 2 피해 공격
- 한 칸 이동
- 3 피해 공격
- 한 칸 이동
카드의 효과 처리를 할 때 카드 단위가 아니라 효과 단위로 처리하자고 결론을 내렸다.
그리고 각 효과 사이에서 유저 입력 대기 여부를 판단.
이 구조는 실제 구현에서도 꽤 잘 버텨줬다.
다음 단계
이것으로 UI와 프론트엔드 구현, 주요 기술적 의사결정이 완료되었다.
다음은 데이터베이스 세팅과 로그인 구현이다.
다음 글 보러 가기 : https://helloworld.ai.kr/16
카드 게임 제작 ( 3 ) - JWT 토큰은 json web token token이다
카드 게임 제작 기록 처음부터 보기 : https://helloworld.ai.kr/14 카드 게임 제작 ( 1 ) - 회고게임 링크 : cardgame.perfect.ai.kr 개발 기간 : 2025.10.28 ~ 2025.11.27총 소요 시간 : 110시간Lovable에게 감탄 ( UI 작업 )ht
helloworld.ai.kr
'개발로그' 카테고리의 다른 글
| 카드 게임 제작 ( 4 ) - JS 이벤트 루프와 비동기 처리의 이해 (0) | 2026.02.16 |
|---|---|
| 카드 게임 제작 ( 3 ) - JWT 토큰은 json web token token이다 (0) | 2026.02.16 |
| 카드 게임 제작 ( 1 ) - 회고 (0) | 2026.02.16 |
| 주로 사용하는 기술 스택과 배포 환경 (0) | 2026.02.16 |
| 1인 개발자로서 돈 버는 방법 (0) | 2026.02.16 |
