Skip to content
@Grande-Horse

그런데 말입니다

🐴 그런데 말입니다는 실제 한국마사회의 경주마 데이터에 기반한 2D 도트 그래픽 경마 게임입니다 👾

🐎 그런데 말입니다

실제 한국마사회의 경주마 데이터에 기반한 2D 도트 그래픽 경마 게임 👾

🚀 주요 기능

➊ 경마 게임 및 실시간 채팅 💬

  • 경주마 능력치와 클릭 이벤트에 기반한 멀티 레이싱 게임
  • 경마방을 생성하고 다수의 유저와 실시간 소통
Race Room Screen Race Screen
race room race screen

➋ 말 카드 획득 및 합성 기능 🐴

  • 실제 마사회의 경주마 데이터를 바탕으로 말의 등급과 능력치 측정
  • 말 카드를 합성하여 일정 확률로 더 높은 등급의 말 카드 획득
Horse Stat Screen Horse Combine Success Screen
horse stat Horse Combine Success

➌ 카드팩 구매 및 유저 간 거래 시스템 💰

  • 포트원 기반 결제 및 코인 충전 시스템
  • Chart.js 기반 특정 말 거래 변동 시세 시각화
Coin Market Screen Horse Purchase Screen Horse Sell Screen
coin Image Image

➍ 목장 관리 시스템 🌳

  • 경주에 출전할 후보말을 최대 6마리까지 등록
  • 후보말은 목장 페이지에서 랜덤한 위치로 지속적으로 이동
Pasture Screen Horse Management Screen
pasture 스크린샷 2025-04-10 오후 5 21 16

➎ SSAFY, Kakao 로그인 기능 🔐

  • 싸피 혹은 카카오 계정으로 손쉽게 로그인
Landing Screen
landing

🌟 성과

  • 이용자 수 200명 이상 달성 📈

🫂 팀원 소개

박경범 오상하 우성문 김서로 신성우 이지운
박경범
BE Leader
총괄 팀장
GitHub
오상하
BE
Data Analysis
GitHub
우성문
BE
Infra
GitHub
김서로
FE Leader
Design
GitHub
신성우
FE
Design
GitHub
이지운
FE
Mobile
GitHub

🛠️ 기술 스택

BE Java Spring Boot Gradle MySQL Redis Apache Hadoop
FE TypeScript React Vite pnpm TanStack Query TailwindCSS MSW Flutter
Infra Docker nginx Jenkins
Cooperation GitLab Jira Figma Notion

💡 구현 방식

➊ WebSocket 기반 실시간 통신

멀티 레이싱 게임 및 실시간 채팅 기능을 구현하기 위해 단순한 폴링 방식으로 사용자 경험을 만족시키기 어렵다고 판단하였습니다.
따라서 WebSocket 통신 방식을 도입하고 메시지 프로토콜을 효율적으로 관리할 수 있는 SockJSStompJS을 사용하였습니다.

  • SockJS: 웹소켓 미지원 브라우저에 대한 폴백(fallback)을 제공
  • StompJS: pub/sub 기반의 구조를 통해 메시지 발행과 구독을 명확히 분리

STOMP의 subscribe 함수는 구독 시점이 중요하기 때문에 컴포넌트 단위에서 적절한 구독 시점을 찾는 것이 중요했습니다.
사용자가 접속한 페이지에 따라 필요한 구독 정보가 달라지기 때문에,
컴포넌트 단위에서 subscribe를 일괄적으로 처리하면 불필요한 구독이 발생하는 문제가 있었습니다.

이를 해결하기 위해 전역 상태로 구독 목록을 관리하는 구조를 도입하여 필요한 채널만 동적으로 구독하고,
페이지 전환 시 구독 해제 및 재구독이 자연스럽게 이루어지도록 설계하였습니다.

한편, 라우팅 시에도 실시간 정보를 유지하기 위해 라우터 쿼리나 상태를 통해 정보를 전달하는 구조를 도입했습니다.
이를 통해 페이지 간 이동하거나 재접속 할 때도 실시간 연결 상태와 필요한 데이터를 유지할 수 있었습니다.

➋ 말 무작위 움직임

목장 페이지 속 살아 있는 말을 나타내기 위해 Image Sprite을 활용하여 여러 마리의 말이 랜덤한 위치로 이동하도록 구현했습니다.
상하좌우 방향으로 이동하거나 정지하는 말 객체를 표현하기 위해 총 8가지 말의 상태를 Tailwind CSS 전역 클래스로 정의했습니다.
이를 통해 다른 페이지에서 동일한 클래스를 불필요하게 정의하지 않고 말 클래스를 재사용할 수 있도록 설계하였습니다.

HorseRun Right HorseRun Left HorseRun Up HorseRun Down HorseIdle Right HorseIdle Left HorseIdle Up HorseIdle Down
horseRunRight horseRunLeft horseRunUp horseRunDown horseIdleRight horseIdleLeft horseIdleUp horseIdleDown

각각의 말이 이동하면서 서로 부딪히지 않도록 모든 말의 좌표와 바라보는 방향을 Context API을 통해 전역적으로 관리하였습니다.
random, setTimeout 함수를 재귀적으로 활용하여 각 말이 랜덤한 주기로 이동하도록 구현하였으며,
주변에 다른 말이나 장애물이 감지되면 정지하거나 방향을 전환하도록 설계하였습니다.
이러한 방식으로 목장페이지에서 모든 말에 자연스러운 움직임을 부여할 수 있었습니다.

➌ Suspense & Error Boundary 도입

사용자 경험 개선과 효율적인 에러 처리를 위해 Suspense와 ErrorBoundary을 도입하였습니다.
또한 체계적으로 오류를 식별하고 다양한 예외 상황에 대처하기 위해 다양한 커스텀 에러 코드를 정의하였습니다.

이러한 개발 과정에 있어서 다음과 같은 문제가 발생하였습니다.

  • 컴포넌트 마운트 후 실행되는 Data Fetching은 React의 생명 주기와 직접적인 연관이 없음
  • 이로 인해 Suspense와 ErrorBoundary가 로딩 상태와 에러를 감지하지 못함

따라서 렌더링과 동시에 Data Fetching을 진행할 수 있도록 TanStack Query의 useSuspenseQuery hook을 사용하였습니다.
해당 hook은 내부적으로 렌더링 중에 로딩과 에러 상태를 throw하기 때문에
부모 컴포넌트인 Suspense와 ErrorBoundary가 이를 catch하게 됩니다.

위 과정을 통해 애플리케이션 전반에 걸쳐 일관된 로딩과 에러 상태를 표시하고 사용자 경험을 크게 향상시켰습니다.

Error Screen
image

➍ Mock Service Worker을 활용한 API 요청 테스트

Backend API 개발이 완료되기 전까지 Frontend 개발을 대기해야 하는 비효율적인 문제를 해결하기 위해
클라이언트 사이드에서 API을 모킹할 수 있는 도구인 MSW을 도입하였습니다.

MSW는 브라우저에서 제공하는 API인 Service Worker을 이용하여 클라이언트 측에서 네트워크 요청을 가로채고 모의 응답을 전송합니다.

실제 API 명세서에 기반한 모의 응답을 설계함으로써 네트워크 지연이나 서버 오류와 같은 예외 상황에 대한
통신 테스트를 수행했고 이를 통해 실제 Backend API 연동 과정에서 발생할 수 있는 문제를 사전에 최소화했습니다.

Popular repositories Loading

  1. grande-horse grande-horse Public

    🐴 그런데 말입니다는 실제 한국마사회의 경주마 데이터에 기반한 2D 도트 그래픽 경마 게임입니다 👾

    TypeScript 1

  2. .github .github Public

Repositories

Showing 2 of 2 repositories

Top languages

Loading…

Most used topics

Loading…