티스토리 뷰

서비스가 성장하다 보면 반드시 마주치는 순간이 있습니다. 어제까지 멀쩡하던 API가 오늘은 유난히 느려지고, 서버 팀에서는 "데이터가 너무 많아서 인덱스를 타도 한계가 있다"거나 "리소스 증설이 필요하다"는 이야기가 들려오기 시작하죠.
하지만 모든 문제의 해답이 서버에만 있는 것은 아닙니다. 때로는 프론트엔드에서 사용자의 기다림을 정의하는 방식을 바꾸거나, 서버로 향하는 요청의 성격을 조정하는 것만으로도 시스템 전체의 가용성을 드라마틱하게 높일 수 있습니다.
제가 실무에서 겪었던 두 가지 '프론트엔드에서 시작한 최적화' 사례를 통해, 우리가 어떻게 서버의 압박을 덜어내고 사용자의 경험을 지켜냈는지 공유하고자 합니다.
1. "기다리게 하지 말고, 과정을 공유하세요"
엑셀 대량 작업 생성의 비동기 전환
로봇 물류 현장에서는 관리자가 수백, 수천 개의 작업을 엑셀로 한 번에 등록하곤 합니다. 초기에는 데이터가 적어 파일 업로드 후 몇 초면 응답이 왔지만, 운영 규모가 커지자 상황이 변했습니다.
- 문제: API 응답 시간이 30초를 넘어가며 브라우저 타임아웃이 발생했습니다. 사용자는 화면의 로딩 스피너만 멍하니 바라보다가, 결국 창을 새로고침하며 중복 요청을 보내는 악순환이 반복되었습니다.
이 상황에서 단순히 "서버 응답을 빠르게 해달라"고 요청하는 대신, 프론트엔드에서 먼저 비동기 처리를 제안했습니다. "서버가 물리적으로 처리하는 시간을 당장 줄이기 어렵다면, 클라이언트가 먼저 '생성 중' 상태를 정의해 사용자의 흐름을 열어주고 상태를 구독하는 방식은 어떨까요?"라는 아이디어에서 논의가 시작되었습니다.
핵심은 서버의 실제 작업 완료 여부와 무관하게, 클라이언트가 '생성 중'이라는 상태를 먼저 정의하여 UI에 노출하고, 이후 변화하는 상태를 실시간으로 구독하는 비동기 흐름의 도입이었습니다.
핵심 아이디어: '생성 중' 상태 노출과 실시간 작업 상태 구독
과거에는 '요청-응답'이 완료될 때까지 화면이 멈춰있어야 했습니다. 하지만 새로운 구조에서는 요청 즉시 '생성 중' 상태를 가진 작업 객체를 UI에 먼저 렌더링합니다. 이후 클라이언트는 해당 작업의 ID를 기반으로 실시간 상태를 구독하며, 서버에서 처리되는 과정에 따라 UI를 유연하게 업데이트합니다.
sequenceDiagram
participant Admin as 관리자
participant FE as 프론트엔드
participant BE as 서버 (API/Worker)
Admin->>FE: 엑셀 파일 업로드
FE->>BE: Bulk 작업 생성 요청
BE-->>FE: 202 Accepted (Request ID 반환)
Note over FE: [상태 추상화] UI상에 '생성 중' 상태 객체 즉시 생성
FE->>Admin: "작업을 생성 중입니다. 목록에서 확인하세요."
rect rgb(245, 245, 245)
Note over FE, BE: 특정 작업 ID에 대한 상태 구독 (Subscription)
BE->>FE: [Event] Status: Creating (생성 중 상태 전송)
FE->>Admin: UI: [생성 중...]
BE->>BE: 서버측 실제 생성 완료
BE->>FE: [Event] Status: Ready (최종 완료 데이터 전송)
FE->>Admin: UI: [준비 완료] 상태 전환 및 기능 활성화
end
사용자가 정말 싫어하는 것은 '오래 걸리는 것' 그 자체보다 '내 요청이 증발했을까 봐 불안한 상태' 입니다. 프론트엔드에서 '생성 중' 이라는 상태를 먼저 보여주고, 실제 진행 상황을 실시간으로 구독하여 시각화함으로써 사용자의 불안감을 해소했습니다. 이를 통해 서버의 물리적인 처리 시간을 줄이지 않고도 '수용 가능한 경험'을 만들어냈습니다.
2. "보이지 않는 것은 부하를 일으키지 않아야 합니다"
대시보드 RPS(Request Per Second) 최적화
우리 서비스의 핵심인 실시간 대시보드는 로봇의 상태를 초 단위로 폴링(Polling)합니다. 어느 날, 특정 고객사의 작업 단위가 매우 작아지면서 대시보드에 표시해야 할 데이터가 수천 개로 늘어나는 상황이 발생했습니다. 서버 팀에서는 급증하는 API 호출 부하를 해결하기 위해 쿼리 최적화와 서버 인프라 증설을 고민하고 있었습니다.
옆에서 이 고민을 함께 듣고 있다가, "API 성능 개선도 중요하지만, 클라이언트에서 호출의 절대량을 줄여보는 건 어떨까요?"라고 먼저 제안을 던졌습니다. 작업의 총량은 많아도 사용자가 한 번에 보는 데이터는 한정적이라는 점에 착안한 것이죠.
이때 우리가 활용한 것은 브라우저의 Intersection Observer API였습니다.
최적화 전략: Viewport 기반 타겟팅 폴링
수천 개의 데이터가 있더라도 사용자가 한 화면(Viewport)에서 볼 수 있는 데이터는 고작 10~20개 내외라는 점에 주목했습니다.
- 관찰: 모든 리스트 아이템을 관찰 대상으로 등록합니다.
- 활성화: 화면에 들어온 아이템만 '활성 상태'로 표시하고 폴링 큐에 넣습니다.
- 비활성화: 화면에서 나가는 순간 즉시 폴링 대상에서 제외합니다.
부하량 추이 비교
graph LR
A[데이터 개수] --> B{기존 방식}
A --> C{최적화 방식}
B --> B1[부하가 선형적으로 무한 증가]
C --> C1[Viewport 크기에 맞춰 부하 수렴]
style B1 fill:#ffcccc,stroke:#ff0000
style C1 fill:#ccffcc,stroke:#00ff00
이 개선 이후, 데이터가 100개일 때나 10,000개일 때나 서버가 받는 RPS는 일정하게 유지되었습니다. 서버 팀은 성능 개선을 위한 시간을 벌 수 있었고, 클라이언트는 방대한 데이터를 부드럽게 렌더링할 수 있게 되었습니다. "보이는 것에만 집중한다" 는 단순한 원칙이 서버 인프라 비용을 절감하는 강력한 도구가 된 셈입니다.
마치며: 우리에겐 '해결사'의 마인드셋이 필요합니다
문제가 발생했을 때 "이건 API가 느린 거니까 서버 팀에서 고쳐야 해요"라고 말하는 것은 쉽습니다. 하지만 비즈니스는 기다려주지 않죠.
프론트엔드 엔지니어는 사용자와 가장 가까운 곳에 있는 사람입니다. 서버의 한계를 UX로 감추거나, 브라우저의 자원을 활용해 서버의 짐을 덜어주는 시도는 서비스의 생존력을 높이는 아주 중요한 엔지니어링 역량입니다.
지금 여러분의 서비스가 서버 부하로 고통받고 있다면, 혹시 클라이언트에서 '보지 않아도 될 것을 굳이 물어보고 있지는 않은지', 혹은 '사용자를 막연한 기다림 속에 방치하고 있지는 않은지' 점검해 보시는 건 어떨까요?
'Dev > 기타' 카테고리의 다른 글
| 프론트엔드 격변기 회고: 2021년부터 현재까지 (0) | 2026.05.08 |
|---|---|
| 글로벌 서비스를 위한 다국어(i18n) 개발기: 번역 자동화부터 타임존 대응까지 (0) | 2026.04.14 |
| 개발자의 마지막 보루: 네트워크 지옥에서 빛나는 로깅 이야기 (0) | 2026.04.09 |
| PC 1대로 로봇 20대를 움직이다: 지속 가능한 E2E 테스트 인프라 구축기 (0) | 2026.04.07 |
| 정말 그 기능이 필요할까요? : 요구사항 너머의 진짜 고민 (0) | 2026.04.06 |
- Total
- Today
- Yesterday
- 드림코딩
- NomadCoder
- 그림판 만들기
- Class
- frontend
- Build your own React
- 생활코딩
- object
- JavaScript
- redux-toolkit
- Firebase
- 트위터 클론
- Python
- html
- TypeScirpt
- github
- nodejs
- hooks
- 프론트엔드
- css
- 바닐라js
- 오버라이딩
- Django
- RUBY
- React
- project
- nrc
- instagram CSS
- 기능추가
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |