본문 바로가기
개발로그

카드 게임 제작 ( 2 ) - Lovable에게 감탄

by ddony8128 2026. 2. 16.

카드 게임 제작 기록 처음부터 보기 : 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