Level42
42Seoul 게임기기 예약 관리 서비스

- →런칭 후 6개월 월평균 118명 사용
- →프론트엔드 단독 설계 · 구현 · 런칭
- →42 API 연동 로그인 / 예약 / 현황 조회
- →베타테스트 리드 및 피드백 반영
목차
개요
42Seoul 내 공용 게임기기의 사용 현황과 예약이 체계적으로 관리되지 않는 문제를 해결하기 위해 개발한 웹 기반 게임기기 예약 서비스입니다. 42 계정으로 로그인한 뒤 원하는 게임기기와 시간대를 선택해 예약할 수 있으며, 현재 예약 현황과 자신의 예약 내역을 확인하고 관리할 수 있도록 구현했습니다. 초기 목표였던 핵심 기능은 약 두 달 만에 구현했지만, 이후 사용자 검증과 베타테스트, 내부 관계자와의 협의, 정식 런칭과 운영까지 이어지며 실제 서비스로 자리 잡는 과정을 경험했습니다.
기여 범위
Frontend Lead / Full-stack Developer
프론트엔드를 단독으로 설계·구현했으며, 서비스에 필요한 API 연동부터 상태 관리, 실시간 예약 동기화까지 담당했습니다.
- React 기반 프론트엔드 초기 구조 및 라우팅 설계
- 예약 조회·생성·취소 기능 및 예약 모달 구현
- 42 OAuth 기반 로그인 및 사용자 상태 관리
- 사용자 검색 및 프로필 조회 기능 구현
- Recoil 기반 전역 상태 관리
- 날짜·시간 관련 로직 Custom Hook 분리
- Error Boundary, Loading UI, 404 페이지 등 예외 처리 구현
- SSE 기반 예약 현황 실시간 동기화 설계 및 구현
- 백엔드 개발자와 API 스펙 및 데이터 구조 조율
- 베타테스트 및 정식 서비스 런칭 참여
- FC24 토너먼트를 위한 대진표·우승 예측 투표 기능 개발
- 게임기기 관리 및 사용자 대상 이벤트 운영
Tech Stack
Frontend React · JavaScript · Recoil · React Router · Tailwind CSS
Backend / Data Express · MongoDB · Redis · SSE(Server-Sent Events)
Infrastructure Docker · Nginx · GitHub Actions
External API 42 OAuth · 42 API
주요 기술 구현
SSE 기반 실시간 예약 상태 동기화
예약 서비스에서는 여러 사용자가 동시에 동일한 시간대의 예약 현황을 확인할 수 있습니다. 최초 조회 결과만 보여주는 구조에서는 사용자가 특정 슬롯을 예약하려는 사이 다른 사용자가 먼저 예약할 경우, 화면에 표시된 상태와 실제 서버의 상태가 달라지는 문제가 발생할 수 있었습니다. 이를 해결하기 위해 수동 갱신, Polling, WebSocket, SSE를 비교했습니다.
수동 갱신은 구현이 단순하지만 사용자가 직접 새로고침하기 전까지 변경 사항을 확인할 수 없어 예약 경쟁 상황에 적합하지 않았습니다. Polling은 주기적으로 최신 상태를 받을 수 있지만 실제 변경 여부와 관계없이 요청이 반복되고, Polling 주기에 따라 상태 차이가 발생할 수 있었습니다. WebSocket은 실시간 통신에 적합하지만 Level42에서 필요한 통신은 대부분 예약 변경 사항을 서버에서 클라이언트로 전달하는 단방향 흐름이었습니다. 이를 위해 양방향 연결을 관리하는 것은 요구사항 대비 복잡도가 높다고 판단했습니다.
따라서 서버 → 클라이언트 단방향 이벤트 전달에 적합한 SSE를 선택했습니다. 사용자가 예약 서비스를 이용하는 동안 이벤트 스트림을 구독하고, 다른 사용자의 예약 생성·취소가 발생하면 변경된 예약 데이터를 전달받도록 구성했습니다. 프론트엔드에서는 전체 예약 데이터를 다시 Fetch하지 않고 전달받은 변경 사항만 기존 상태에 반영하여, 불필요한 네트워크 요청을 줄이면서 사용자들이 거의 동일한 예약 상태를 확인할 수 있도록 구현했습니다.
예약 데이터와 UI 상태 구조화
예약 화면에서는 선택된 게임기기, 날짜, 예약 내역, 모달 상태 등 여러 상태가 동시에 사용됩니다. 초기 구현에서는 기능 개발 속도를 우선하면서 일부 데이터 처리 로직과 UI 상태가 컴포넌트에 집중되었고, 기능이 확장되면서 유지보수가 어려워졌습니다.
이를 개선하기 위해 전역 상태를 Recoil로 관리하고, 날짜·시간 계산처럼 여러 컴포넌트에서 반복되는 로직을 useDate Custom Hook으로 분리했습니다.
예약 모달 역시 기능 증가에 따라 하위 컴포넌트와 상태를 단계적으로 분리했으며, 데이터 가공 로직도 UI와 분리했습니다.
이를 통해 화면 컴포넌트는 표현에 집중하고 예약 데이터와 공통 로직을 별도로 관리할 수 있도록 구조를 개선했습니다.
이벤트 요구사항에 맞춘 서비스 확장
정식 서비스 개발 과정에서 Level42를 알리고 실제 사용자와 접점을 만들기 위해 FC24 토너먼트를 개최했습니다. 기존 예약 기능과는 다른 요구사항이었지만 별도의 서비스를 새로 구축하기보다 기존 인증 및 사용자 시스템을 활용해 이벤트 기능을 확장했습니다.
- 토너먼트 참가 선수 조회
- 8강 대진표 구성
- 사용자 프로필 조회
- 우승자 예측 투표
- 로그인 여부에 따른 투표 처리
- 경기 결과에 따른 브라켓 시각화
제한된 일정 안에서 기존 서비스의 인증·사용자 기능을 재사용하여 이벤트 페이지를 구현했고, 실제 행사 운영에 사용했습니다.
문제 해결 경험
SSE 도입 후 발생한 상태 동기화 문제
SSE를 처음 적용했을 때 예약 생성 이벤트 자체는 정상적으로 반영되었지만 새로운 문제가 발생했습니다. 사용자가 한 종류의 게임기기 예약 현황을 확인한 뒤 다른 종류로 이동했다 돌아오면 SSE를 통해 갱신된 데이터가 아닌 이전 데이터가 잠시 표시되는 문제가 있었습니다.
더 큰 문제는 예약 삭제 이벤트였습니다. 하나의 예약이 취소됐을 때 해당 예약만 제거되어야 하지만, 화면에 표시되고 있던 예약 전체가 삭제되는 현상이 발생했습니다.
원인을 확인하면서 단순히 SSE 이벤트 처리 함수의 문제가 아니라 현재 선택된 기기의 예약 데이터만 중심으로 보관하던 클라이언트 상태 구조가 실시간 업데이트 방식과 맞지 않는다는 것을 확인했습니다.
이에 예약 상태 구조 자체를 변경했습니다.
기존에는 현재 선택된 기기의 예약 데이터를 중심으로 관리했다면, 이후에는 booksState가 세 종류의 게임기기에 대한 예약 상태를 모두 유지하도록 변경했습니다.
SSE 이벤트가 들어오면 이벤트가 어느 기기 타입에 해당하는지 판단한 뒤 해당 타입의 데이터만 추가·수정·삭제하고, 화면에서는 현재 선택된 타입의 데이터를 렌더링하도록 역할을 분리했습니다.
이를 통해 화면 전환 이후에도 최신 예약 상태가 유지되고, 삭제 이벤트 역시 해당 예약에만 반영되도록 수정했습니다.
이 경험을 통해 실시간 기능을 추가할 때는 통신 방식만 고려하는 것이 아니라 이벤트가 지속적으로 들어오는 상황에 맞춰 클라이언트 상태 구조까지 함께 설계해야 한다는 것을 배웠습니다.
실시간성이 필요한 범위를 정의하고 통신 방식 선택
예약 현황을 실시간으로 반영해야 한다는 요구사항만 보면 가장 먼저 WebSocket을 선택할 수도 있었습니다. 하지만 서비스의 실제 데이터 흐름을 분석해보면 사용자의 예약 요청 자체는 기존 HTTP API로 충분했고, 실시간으로 필요한 부분은 다른 사용자의 예약 변경을 현재 화면에 전달하는 것이었습니다. 따라서 기술이 제공하는 기능의 많고 적음보다,
“현재 서비스가 실제로 필요로 하는 통신 방향은 무엇인가?”
를 기준으로 기술을 비교했습니다. 그 결과 Polling의 반복 요청과 WebSocket의 양방향 연결 관리 대신 SSE를 선택했습니다. 이를 통해 문제를 해결할 수 있는 가장 강력한 기술보다 요구사항을 충족하는 범위에서 가장 단순한 기술을 선택하는 것이 중요하다는 점을 경험했습니다.
42 OAuth 로그인 상태 처리 개선
초기 42 OAuth 로그인 구현에서는 인증 과정에서 전달받은 code를 Recoil 상태 내부에서 처리하는 과정에서 상태 변경과 데이터 요청의 역할이 섞이는 문제가 있었습니다.
단순 selector에서 값을 변경하며 인증 요청을 처리하려던 구조를 수정해 selectorFamily가 OAuth code를 인자로 전달받도록 변경했습니다.
이를 통해 로그인 요청에 필요한 값을 명시적인 입력으로 관리하고, 상태 조회 과정에서 발생하던 부수효과를 줄였습니다.
이후 인증과 관련된 Token 처리 역시 별도의 Hook으로 분리하면서 로그인 여부와 Access Token·Refresh Token 상태를 UI 컴포넌트와 분리했습니다. 초기 설계에서 상태 관리 도구의 사용법 자체에 집중했다면, 이 문제를 해결하면서 상태와 외부 데이터 요청의 책임을 구분하는 구조가 더 중요하다는 점을 배웠습니다.
사용자 반감에서 서비스 필요성 검증까지
개발 초기에는 게임기기를 자주 사용하는 사람들을 대상으로 소규모 대면 설문을 진행했습니다. 당시 예상과 달리 기존 게임기기 사용자 중 일부는 예약 시스템 자체에 부정적이었습니다. 게임기기를 이용하면서 큰 문제를 경험하지 않았던 사용자 입장에서는 예약이라는 절차가 새롭게 추가되는 것을
“기존에는 자유롭게 사용하던 공간인데 왜 예약 때문에 내 사용 시간이 제한되어야 하는가?”
라는 불편으로 받아들였습니다. 이 과정에서 우리가 해결하려는 문제를 개발팀이 중요하다고 생각하는 것과, 기존 사용자가 실제로 문제라고 인식하는 것은 별개라는 사실을 확인했습니다.
단순히 예약 시스템의 장점을 설명하며 사용자를 설득하는 대신, Level42가 게임존 사용자에게 먼저 필요한 존재가 되어야 한다고 방향을 변경했습니다. 게임기기 관리 동아리로 정식 신청하고 기존에 고장 나 있던 컨트롤러를 직접 수리하거나 교체하면서 게임존 환경을 개선했습니다.
동시에 첫 FC24 토너먼트를 개최해 기존 게임기기 이용자뿐 아니라 더 많은 42Seoul 구성원이 게임존과 Level42를 접할 수 있는 계기를 만들었습니다. 토너먼트 과정에서 진행한 설문에는 약 76명이 참여했고, 당시 42Seoul 내부에서 가장 많은 응답을 받은 설문이라는 기록도 만들었습니다. 이처럼 서비스 외적인 문제까지 직접 해결하고 사용자와 반복적으로 접촉하면서 초기의 반감도 점차 줄어들었습니다.
이후 베타테스트에서는 실제로 게임기기 점유 문제를 경험했던 사용자들이 대거 참여했고, 설문 결과 70% 이상의 사용자가 예약 시스템 도입에 찬성했습니다. 이 데이터를 기반으로 내부 운영 담당자인 보컬과 서비스 운영 방식을 협의했고, 최종적으로 Level42를 정식 런칭할 수 있었습니다.
이 경험을 통해 사용자가 제품을 사용하지 않는 문제를 단순히 홍보 부족으로 정의해서는 안 된다는 점을 배웠습니다. 사용자가 왜 제품을 필요 없다고 생각하는지부터 이해하고, 필요하다면 제품 외부의 문제까지 해결해 신뢰와 사용 동기를 만들어야 한다는 것을 실제 서비스 운영을 통해 경험했습니다.
프로젝트를 통해 배운 점
Level42는 기능 구현뿐 아니라 문제 발견 → 사용자 검증 → 개발 → 베타테스트 → 이해관계자 협의 → 런칭 → 운영까지 서비스의 전체 흐름을 처음 경험한 프로젝트였습니다. 특히 초기 사용자 조사에서는 우리가 문제라고 판단했던 상황을 모든 사용자가 동일하게 문제라고 느끼는 것은 아니라는 사실을 경험했습니다.
예약 시스템에 대한 반감을 단순히 설득으로 해결하려 하지 않고 게임기기 관리, 컨트롤러 수리·교체, 이벤트 개최처럼 사용자에게 먼저 가치를 제공하는 방향으로 접근하면서 서비스 개발에서는 기술 외적인 문제 해결 역시 중요하다는 것을 배웠습니다.
기술적으로는 SSE를 도입하면서 요구사항을 기준으로 기술의 복잡도를 판단하는 경험을 했고, 실제 적용 후 발생한 상태 동기화 문제를 해결하면서 새로운 기술을 도입하는 것과 이를 기존 시스템에 안정적으로 통합하는 것은 서로 다른 문제라는 것도 경험했습니다. 또한 개발 이후 베타테스트와 내부 관계자와의 협의에 예상보다 많은 시간이 필요했습니다.
이를 통해 개발 완료는 서비스의 완료가 아니며, 실제 사용자가 받아들이고 지속적으로 사용할 수 있게 만드는 과정까지 서비스 개발의 일부라는 관점을 갖게 되었습니다.
아쉬웠던 점과 개선 방향
사용자 검증 시점이 늦었던 점
초기에는 공용 게임기기의 점유 문제가 존재한다는 경험을 바탕으로 예약 시스템 개발을 먼저 시작했습니다. 하지만 이후 사용자 조사를 통해 해당 문제를 크게 느끼지 않았던 기존 사용자들에게는 예약 시스템 자체가 오히려 새로운 제약으로 받아들여질 수 있다는 사실을 확인했습니다.
결과적으로 제품의 필요성을 검증하는 과정이 개발과 병행되기보다 일정 부분 개발 이후에 이루어졌습니다. 다시 진행한다면 구현을 시작하기 전에 게임기기 이용 빈도와 점유 문제 경험 여부에 따라 사용자를 나누고, 각 그룹이 문제를 어떻게 인식하는지 먼저 검증할 것입니다.
이를 통해 제품에 가장 큰 Pain Point를 가진 사용자를 초기 타깃으로 명확하게 정의하고 개발 우선순위를 결정할 수 있을 것입니다.
초기 프론트엔드 상태 구조의 한계
초기에는 일반적인 API 요청을 중심으로 예약 상태를 설계했기 때문에 현재 화면에 필요한 데이터를 중심으로 관리했습니다. 이후 SSE가 추가되면서 화면 밖의 예약 데이터까지 지속적으로 변경될 수 있게 되었고 기존 상태 구조와 충돌했습니다. 결국 세 종류의 게임기기 예약 데이터를 모두 유지하도록 상태 구조를 변경하면서 문제를 해결했습니다.
다시 설계한다면 실시간 이벤트가 발생할 가능성을 초기 데이터 모델 단계부터 고려하고, 서버 데이터와 UI 상태를 명확하게 분리할 것입니다.
개발 이후 과정에 대한 일정 산정 부족
핵심 기능 개발은 예상보다 빠르게 약 두 달 만에 완료했지만 실제 서비스 런칭까지는 훨씬 많은 시간이 필요했습니다. 사용자 조사, 게임기기 관리, 이벤트, 베타테스트, 피드백 반영, 내부 관계자와의 협의 등 개발 당시에는 충분히 예상하지 못했던 작업들이 지속적으로 발생했습니다. 다시 진행한다면 개발 완료 시점을 프로젝트 완료 시점으로 잡지 않고, 사용자 검증과 운영 준비, 이해관계자 협의까지 별도의 마일스톤으로 정의해 일정에 포함할 것입니다.
회고
사실상 처음으로 직접 배포하고 운영까지 이어간 서비스였기 때문에 저에게 Level42가 갖는 의미는 컸습니다. 초기에는 3개월 내 핵심 기능 개발을 목표로 시작했고, 약 두 달 만에 목표했던 기능을 완성했습니다. 당시에는 가장 큰 산을 넘었다고 생각했지만, 돌이켜보면 진짜 어려운 과정은 그 이후였습니다.
사용자에게 서비스의 필요성을 설명하고, 초기 반감을 이해하고, 베타테스트를 진행하고, 내부 관계자와 협의해 실제 런칭까지 이어가는 데 개발보다 훨씬 긴 시간과 많은 노력이 필요했습니다. 이 과정을 통해 서비스 개발은 기능을 완성하고 배포하는 것으로 끝나는 것이 아니라, 사용자가 받아들이고 실제로 사용하게 만드는 과정까지 포함한다는 것을 체감했습니다.
팀원들과의 소통 방식부터 사용자 조사, 이벤트 운영, 이해관계자와의 협의까지 대부분이 처음 겪는 일이었습니다. 시행착오도 많았지만 그만큼 개발 외적으로도 서비스가 만들어지고 운영되는 전체 과정을 경험할 수 있었습니다.
Level42는 저에게 단순히 첫 배포 프로젝트가 아니라, 코드를 작성하는 개발자에서 실제 사용자를 바라보는 개발자로 관점을 넓히게 만든 프로젝트로 남았습니다.