예약 가능하다고 믿었는데… 이미 예약 완료? 😟
이제 실시간으로 바뀌는 좌석 상태를 한눈에 확인하세요! 🚀
🎟️
끊김 없는 서비스 제공: 많은 사용자가 동시에 접근해도 안정적으로 실시간 서비스 제공.
- 기존에 선택한 WebSocket을 통한 실시간 통신은 서버가 클라이언트의 요청을 계속해서 수신해야 하기에 서버의 리소스 부담으로 이어진다.
- 서버가 클라이언트에게 일방적으로 데이터를 푸시하는 Server Sent Event로 실시간 통신 방식을 변경한다.
- 서버가 더 이상 클라이언트 요청 수신에 대한 리소스를 낭비하지 않게 된다.
- 기존 방식은 클라이언트마다 각각 데이터가 발행돼, 구독자가 늘어남에 따라 오버헤드가 커지는 문제가 있었다.
- 동일한 데이터 소스를 여러 클라이언트가 참조하는 형태의 브로드캐스팅을 구현했다.
- 구독자의 수에 상관 없이 데이터 발행에 드는 비용을 고정적으로 줄일 수 있었다.
- 다수의 사용자를 좌석 선택 화면에 머물게 하면 서버에 부하가 점차 가중된다.
- 서버에 몰리는 사용자 수에 제한을 걸어 트래픽을 조절하기 위해 대기열 시스템을 도입한다.
- 대기열 도입을 통해서 좌석 선택 페이지에 몰리는 SSE 연결을 조절하여 서버의 안정성을 확보한다.
- 대기열 화면과 좌석 선택 화면 사이에서 클라이언트의 갖은 예외적 움직임이 발생했고, 대기열 운영에 오류를 일으켰다.
- 변수를 통제하는 방향으로 유저의 상태를 세분하게 정의, 추적, 관리했다.
- 예외적 상황에도 누수 없이 정확한 인원 추적으로 서비스의 무결성을 유지할 수 있게 되었다.
- 예매 오픈 시 다수의 클라이언트가 동시에 좌석 점유 페이지로 이동해 서버에 과도한 부하가 발생한다.
- 매크로 방지 기능을 사용해 사용자 입력을 요구하여 의도적으로 지연을 발생시킨다.
- 동시에 들어오는 요청을 분산시켜 서버의 안정성을 확보한다.
- 클라이언트에게 SVG 데이터 전송하는 것은 용량이 커져서 효율적이지 못했다.
- viewbox 데이터와 모서리 좌표 데이터를 전송하는 방식 도입했다.
- 데이터의 볼륨을 줄이고 네트워크 비용을 감소시켰다.
- 좌석 배치 데이터 저장을 위해 좌석 하나당 하나의 행을 차지했다. 또한 좌석 조회시 JOIN을 통해 데이터를 조회해야 했다.
- 좌석 배치 데이터의 정적인 특성을 활용해, 저장 형태를 압축하는 반정규화를 했다.
- 좌석 배치 데이터의 관리, 조회에 드는 오버헤드를 해소하였다.
- 좌석 현황의 저장 형태가 길어 수정/조회에 큰 오버헤드가 있었다. 또한 좌석 현황에 대한 동시적 수정/조회 때문에 동시성 이슈가 발생했다.
- 좌석 현황의 저장 타입을 변환하여 부분 수정을 가능케 하고, 조회/수정 명령에는 원자성을 부여했다.
- 가장 요청이 많고 핵심 병목 구간인 좌석 현황에서, 동시성을 제어하고 쿼리 성능을 개선해 병목을 완화했다.
- 여러 페이지에서 동일한 데이터에 대한 API 반복해서 호출하고 있었다.
- query키와 staleTIme설정을 통해서 캐시 데이터를 사용했다.
- API 호출 감소로 서버 부하를 감소시켰다.
- 여러 사용자의 좌석 동시 선택으로 인한 충돌 문제가 있었다.
- 클라이언트에서는 pending 상태를 표시, 서버에서 실패 응답시에는 사용자 행동을 방해하지 않으면서 실패 사실을 알렸다.
- 사용자는 동시 선택 충돌이 일어나더라도 다음 행동을 방해받지 않으며, 즉시 인지하여 다른 좌석을 취할 수 있다.
- 개발 편의성을 위해 도입한 TypeORM이 불필요한 쿼리 요청을 만들어내고 있었다.
- 옵션을 통한 선택적 EAGER 로딩 정책으로 필요한 데이터만 불러오도록 한다.
- 최소한의 쿼리로 필요한 데이터만 불러오도록 해 효율적으로 TypeORM을 사용한다.
모두 1호선에 살고 있어요
J05_곽희상 1호선 당정역 |
J033_김동현 1호선 동암역 |
J102_박노철 1호선 대방역 |
J144_신성규 1호선 주안역 |