VITA - 1
VITA - 2
VITA - 3
VITA - 4
VITA - 5
VITA - 6

실시간 게임 메이트 매칭 서비스 - WebSocket 기반 실시간 통신과 토스페이먼츠 결제 시스템을 통합한 플랫폼

ReactTypeScriptTailwind CSSZustandTanstack QueryWebSocketOAuth 2.0토스페이먼츠

주요 기능 개발

1. 메인 · 카테고리 · 유저 페이지

  • 메인 배너/추천 카테고리/오늘의 메이트/후기 등 주요 섹션 UI 및 Swiper 캐러셀 구현
  • 카테고리 필터·무한스크롤·게임메이트 카드·게임 등급/레벨 표시 등 탐색 경험 전반 개발
  • 유저 상세 페이지에서 메이트 정보, 게임 정보, 리뷰, 등록 게임 전환 UI 구현
  • 주요 화면(메인·카테고리·유저)의 Skeleton UI 및 1280/1024/768/640px 반응형 설계

2. 의뢰 · 코인 · 결제 플로우

  • 주문 확인 모달, 나의 주문/주문을 받았습니다, 의뢰 수락·거절·취소, 리뷰 작성 등 의뢰 전 과정 구현
  • 의뢰/취소/거절/수락 시 코인 차감·환불 로직 연동 및 UI 실시간 반영
  • 코인 충전 페이지 및 카드 클릭 시 코인 업데이트 처리
  • 토스 SDK 기반 결제 플로우(결제 방법 선택, 결제 요청, 성공/실패 페이지, 결제 정보 출력) 구현

3. 프로필 · 마이페이지 · 인증

  • 프로필 편집(사진 업로드, 닉네임, 소개글 등) 및 관련 반응형 UI 구현
  • 마이페이지 사이드바(프로필, 게임메이트 등록, 코인, 의뢰, 결제 내역, 로그아웃) 네비게이션 개발
  • 헤더에서 로그인/로그아웃 상태별 UI(코인·메시지·프로필·채팅 모달 진입) 처리
  • 카카오 로그인/로그아웃 연동 및 Auth 상태에 따른 화면 제어

트러블 슈팅 경험

Tanstack Query로 실시간 데이터 갱신 문제 해결

Tanstack Query로 캐싱된 데이터를 실시간으로 보여주려 했으나, 특정 데이터가 즉시 갱신되지 않는 문제가 있었습니다. 기본 캐시 전략만으로는 상태가 자동 갱신되지 않아 캐시된 데이터에 새로운 값이 반영되지 않았습니다.

원인 분석

  • 불변성 유지와 캐싱 구조: Tanstack Query는 얕은 비교를 사용하기 때문에 기존 데이터를 깊은 복사로 교체하지 않으면 상태 변경을 감지하지 못합니다.
  • 깊은 복사의 필요성: React처럼 Tanstack Query에서도 불변성을 지켜야 하므로, setQueryData 시 기존 객체를 복사한 뒤 변경된 데이터만 삽입해야 합니다.

해결 방법

불변성을 유지하면서 필요한 데이터만 교체하도록 setQueryData 로직을 재구성하고, 라이브러리 대신 JavaScript 내장 메서드로 깊은 복사를 수행했습니다.

if (value) {
// INFO: 불변성 유지 및 깊은 복사를 통한 데이터 갱신
const index = value.results.findIndex(
(v) => order.game_request_id === v.game_request_id,
);
const updateResults = value.results.filter((_, i) => i !== index);
queryClient.setQueryData(["orders"], { ...value, results: updateResults });
}

이렇게 변경한 뒤에는 새로운 데이터가 캐시에 즉시 반영되어 실시간 갱신이 정상적으로 동작했습니다.


CI/CD 과정에서 발생한 문제와 트러블슈팅

CI/CD 파이프라인 트러블슈팅 흐름

Github Actions로 CI/CD 파이프라인을 구축하면서 순차적으로 여러 단계가 실패했습니다. 각 단계를 디버깅하고 CI와 CD의 원인을 분리해 해결했습니다.

원인 분석

  • 1. CI 단계 실패 — 의존성 설치 누락, Github Actions 권한 부족, TypeScript 경고/에러로 인해 빌드가 중단되었습니다.
  • 2. CD 단계 실패 — Github Secrets에 잘못 입력된 AWS 인증 정보와 CloudFront 캐시 무효화 누락으로 최신 배포본이 노출되지 않았습니다.

해결 방법

  • CI: npm install 단계 명시, 필요한 권한 설정, TypeScript & Lint 오류를 정리해 빌드 성공률을 확보
  • CD: AWS 자격 증명을 재발급 후 Secrets 갱신, 배포 스크립트에 CloudFront 캐시 무효화 단계를 추가

표준 웹소켓 통신에서 헤더를 직접 설정할 수 없는 문제 해결

실시간 온·오프라인 전달을 위해 WebSocket을 사용했지만, 서버에서 인증을 위해 필요한 access token을 헤더로 보낼 수 없어 초기 연결이 차단되는 문제가 발생했습니다.

원인 분석

  • WebSocket 헤더 제한: HTTP 요청과 달리 커스텀 헤더를 추가할 수 없어 기본 인증 방식이 동작하지 않음
  • 토큰 전달 경로 필요: 서버가 토큰으로 클라이언트를 식별해야 하므로 다른 전달 채널이 요구됨

해결 방법

access token을 URL 쿼리 스트링으로 전달하도록 수정해 서버가 연결 시 즉시 인증할 수 있도록 했습니다.

useEffect(() => {
const accessToken = localStorage.getItem("accessToken");
const socket: WebSocket = new WebSocket(
`wss://resdineconsulting.com/ws/status/?token=${accessToken}`,
);
socket.onopen = () => {
console.log("소켓 연결 성공");
};
return () => {
socket.close();
console.log("소켓 연결 종료");
};
}, [user.id]);

이 방식으로 WebSocket 연결 시 인증이 정상적으로 수행되어 실시간 온·오프라인 기능이 안정적으로 작동했습니다.