[삼성청년SW아카데미] 입찰왕
프로젝트 개요
“WebRTC를 사용한 서비스 제작”이 과제였습니다. 저희 팀은 실시간 라이브 경매 서비스를 기획했습니다.
프론트엔드 2명, 백엔드 3명으로 구성된 팀이었습니다. (백엔드 팀원이 4명이었으나 한 명이 취업이 되어 이탈했습니다.)
팀장 및 프론트엔드 리드를 맡았습니다.
GitLab을 미러링한 Github이어서, Merge Request 및 코드리뷰 기록이 없는 상태입니다.
주요 구현 및 기능
라이브 경매 CRUD
라이브 입장 및 판매자 화면 송출 (판매자가 먼저 입장한 후 구매자들이 입장할 수 있음)을 구현했습니다. 라이브 화면 송출은 OpenVidu를 사용했습니다.
라이브 경매 진행
경매 결과에 대한 알림
경매 종료 후 내역 조회
라이브 경매 진행 유튜브 녹화 영상. 위쪽 화면이 판매자, 아래쪽 화면이 구매자 시점입니다.
프로젝트 목표
React의 기본적인 기능을 익히는 것이 주 목표였습니다.
useState, useEffect, useRef 등 기본적인 hooks의 사용법을 익혔습니다.
전역에서 필요한 인증 및 회원 데이터에만 Redux(toolkit)를 사용했습니다.
TypeScript를 프로젝트에 처음 적용했습니다.
프로젝트 종료 이후 담당 부분을 리팩토링했습니다.
가장 아래에 자세한 설명이 있습니다.
프로젝트 기여
1. 와이어프레임을 분해해 컴포넌트 구조를 설계하고, 기본 컴포넌트들을 구현했습니다.
Button,Input같은 작은 컴포넌트를 만들고, 그것들로 더 복잡한 컴포넌트를 조립했습니다.emotion/css를 편의상 css 문자열을 그대로 prop으로 삽입하여 사용했는데, 컴포넌트의 관심사 분리 (UI와 로직 분리 등) 를 하지 못해서 가독성이 많이 떨어지게 됐습니다.유연한 설계란 불확실한 여지를 두는 것이 아니라 치밀하게 설계하는 것이라는 사실을 깨달았습니다.
가장 밑바닥의 컴포넌트가 자주 수정되면서 생각지 못한 에러들이 발생하기도 했습니다.
어떤 니즈가 있어 컴포넌트가 수정될 수는 있지만, 설계 당시에 고려했어야 하는 것들이 많았습니다.
2. 주요 기능 중 라이브 경매에 관련된 부분을 설계하고 구현했습니다.
PDF로 이 문서를 보시는 경우, Notion 링크 또는 아래 링크에서 컴포넌트의 애니메이션을 보실 수 있습니다.
https://youtu.be/iyUf4gSRbaA (분할된 화면 위쪽이 판매자, 아래쪽이 구매자 시점입니다)
라이브 경매 시스템과 채팅 컴포넌트를 구현했습니다.
은어나 전문 용어가 사용되는 복잡한 경매 프로세스를 쉽게 정리했습니다.

사용자는 입찰가를 입력하거나, 현재 가격의 110%로 즉시 입찰할 수 있습니다.
10초의 타이머가 직관적으로 보일 수 있도록 숫자와 바 형태 모두 제공하고, 남은 시간에 따라 색깔로도 구분할 수 있도록 했습니다.
누군가 입찰에 성공하면 잠시 모든 유저의 입찰 버튼이 비활성화됩니다.
sysy가 현재 마우스를 클릭하고 있는 유저입니다.
다음 예외 사항을 필터링했습니다. (백엔드에서도 같이 값을 검증합니다)

현재 가격과 같거나 그보다 작은 입찰가를 입력하는 경우
숫자가 아닌 다른 문자를 입력하는 경우
최대값을 넘는 가격을 입력하는 경우
엔터키를 눌러 입찰을 시도하는 경우
엔터키는 채팅 기능에서만 작동합니다.
10초의 타이머가 0초가 될 때까지 아무도 입찰하지 않으면 다음 경매로 넘어갑니다.

입찰을 했을 때, 사용자가 실제 경매와 비슷한 수준의 만족감과 재미를 느끼도록 하고 싶었습니다. 입찰가를 표시하는 컴포넌트는 직접 구현했습니다.
값이 단순히 바뀌는 것보다는, 애니메이션이 재생되면서 올라갔으면 했습니다.
플랩식 전광판이나 슬롯머신처럼 숫자가 오르내리는 모습이 보고 싶었습니다.
라이브러리를 쓰지 않고, 애니메이션을 직접 구현해 보고 싶었습니다.
(더보기) 디자인을 전공하며 학습한 타이포그래피 지식을 UI에 적용해 보고 싶었습니다.
예를 들어 숫자 ‘1’은 다른 숫자들보다 너비가 좁습니다. 가격을 표시할 때 1이 많이 들어간 숫자는 다른 숫자에 비해 자릿수가 적어 보였습니다.

이 문제를 해결하면서도, 전체 숫자가 자연스럽게 보이도록 하고 싶었습니다. 처음에는
opentype의 고정폭 숫자 옵션을 사용했다가,
전체 숫자의 가독성이 떨어져 동적으로 조정하기로 했습니다. 1이 들어가면 ‘1’의 여백과 그에 따른 전체 너비, 정렬 상태가 애니메이션으로 부드럽게 조정되도록 했습니다.

전체 너비가 조정되기 전에 숫자들이 먼저 조정되면 숫자가 잠시동안 잘려 보이는 문제가 있어 타임라인마다 실행 시간 차이를 두어 자연스럽게 조정되도록 했습니다.
해결하지 못한 문제들이 있어 아쉬운 점도 있었습니다.
애니메이션의 경우 리액트의 컴포넌트 리렌더 로직을 이용한 것이어서, 애니메이션이 부자연스러운 부분이 있습니다.
프레임 드랍이 발생하면 드랍이 발생한 컴포넌트들은 해당 프레임만큼 멈춥니다. 여러 번 입찰가가 올라간다면 숫자들의 세로 위치가 맞지 않는 현상이 발생합니다. 이 현상은 모바일에서 더 자주 발생했습니다.
3. 개발자 경험을 개선했습니다.
docker 환경에서 1분 30초정도 소요되던 빌드 시간을 45초 정도 단축시켰습니다.
storybook을 도입해 협업을 진행함으로써 컴포넌트 개발 환경을 개선했습니다.
아키텍처
전체 아키텍처 및 라이브 경매 흐름도

Spring Boot, Node.js, React, MySQL, Redis는 라이브 경매 중 아래처럼 동작합니다.
Spring서버는 라이브 도메인을 제외한 모든 요청을 처리합니다. (로그인, 회원가입, 경매 CRUD)Express서버는 라이브 경매에 관련된 것 (채팅, 입찰, 낙찰, 유찰, 타이머)만을 처리합니다.MySQL DB와 직접 통신하는 서버는Spring서버 뿐이고,Express.js서버는Redis를 사용합니다.클라이언트에서 입찰 요청(POST)을
Spring서버로 하게 되면, 인증 처리를 한 뒤Redis에서 값 검증 및 동시성 처리를 진행합니다.Spring서버는Redis에서 받은 결과를 가지고Redis의 입찰가를 갱신합니다.Redis는 입찰 이벤트를Express.js서버로 발행하고,Express.js서버는Socket.io를 통해 클라이언트의 타이머를 갱신함과 동시에 입찰가를 갱신합니다.
위의 과정을 라이브 경매를 담당하면서 백엔드 동료들과 함께 설계했습니다.
백엔드 개발에는 참여하지 않았지만, 데이터가 어떻게 오가는지 전체 과정을 살펴보고 개발을 하니 문제가 생겼을 때 빠르게 원인을 찾고 협업해 대처할 수 있었습니다.
포지션이 다르다고 해서 일방적인 의사소통을 하지 않았다는 것이 가장 좋은 경험이었습니다.
테스트 과정에서 문제가 발생했을 때 프론트엔드와 백엔드 따로 원인을 살펴보는 것이 아니라, 함께 전체 과정을 살펴보면서 어디에 문제가 있는지 빠르게 파악할 수 있었습니다.
라이브 경매 흐름도
위의 과정을 그림으로 정리했습니다. 입찰 말고도 경매 입장, 낙찰, 유찰 등 모든 과정의 흐름을 도식화해서 팀원 모두가 공유하고 숙지했습니다.
각자 파트에 대한 기술에 대한 구체적인 지식이 없더라도 디버깅을 할 때 어디에 문제가 있는지 빠르게 예측해 찾을 수 있었습니다.
링크: https://github.com/bid-king/bidking/wiki/아키텍처#5-경매-입찰-시스템-서비스-흐름도
러닝 포인트
첫 프로젝트였던 만큼, 좋은 반면교사가 되었습니다.
타입스크립트는 미숙할 때 생산성을 아주 많이 떨어뜨린다는 사실을 알았습니다.
상태 관리 라이브러리를 사용하지 않으려고 했던 것이 사실상 독이 되었습니다.
컴포넌트 간의 의존성을 강화하는 것은 설계가 제대로 되지 않았을 때나 컴포넌트 추가가 필요할 때 개발 및 유지보수 피로도를 상당히 강화시킨다는 것을 알았습니다.
상태 관리에 대한 제 생각은 https://dev.seungyoon-yu.com/2024/상태-관리-지옥-탈출하기-feat-websocket 에서 더 보실 수 있습니다.
복잡한 구현부를 드러내지 않고 싶어서, 코드를 묶어서 빼내는 식의 custom hooks를 많이 사용했습니다.
결과적으로 해당 부분을 재사용할 수 있게 되거나 읽을 필요가 없어지는 것이 아니었고, 오히려 파일 트리만 복잡해지는 결과를 낳았습니다. 사실상 안티 패턴이었습니다.
단순히 복잡한 구현부를 메서드로 빼기만 하는 것이 좋은 컴포넌트가 아니라, 협업 상황에서 구현부를 읽을 필요가 없도록 짜는 것이 좋은 컴포넌트 설계임을 깨달았습니다.
버그는 견고한 테스트로 잡아야 한다는 것과 좋은 개발 환경을 구축해야 한다는 것을 알았습니다. 좋지 못한 컨벤션과 나쁜 개발 환경이 개발자의 자신감과 안정성을 많이 떨어뜨린다는 것을 알게 되었습니다.