한 화면 = 한 API로 바꾸니 모바일 로딩이 정리됐다
화면 하나에 API를 여러 번 부르던 구조를 집계 엔드포인트 하나로 합친 과정과, 어디까지 합치고 어디서 멈춰야 하는지 판단 기준을 정리했어요.
모바일 화면 하나를 여는데 API를 네 번 부르고 있었어요. 프로필, 최근 기록, 요약 통계, 알림 개수. 각각은 빠른데 화면은 느렸습니다.
왜 각각 빠른데 화면은 느릴까
측정해 보니 원인이 명확했어요. 서버 처리 시간이 아니라 왕복 횟수였습니다.
모바일 네트워크에서 요청 하나의 왕복 지연은 서버 처리보다 훨씬 큽니다. 서버가 20ms에 응답해도 왕복에 200ms가 붙으면 한 번에 220ms예요. 이걸 4번 하면 880ms. 브라우저·앱이 병렬로 쏴도 인증 헤더 검증, 커넥션 수 제한, 그리고 무엇보다 의존 관계가 발목을 잡습니다.
실제로 제 코드에는 이런 순서가 있었어요.
두 번째 요청이 첫 번째 결과를 기다립니다. 병렬이 아니라 순차예요. 이 구조에서는 서버를 아무리 튜닝해도 왕복 2번은 그대로 남습니다.
집계 엔드포인트로 합치기
플랜비(plan-b) 대시보드를 손보면서 화면 하나에 엔드포인트 하나를 대응시켜 봤어요. GET /api/dashboard 하나가 필요한 데이터를 전부 모아서 내려주는 방식입니다.
서버 안에서는 여전히 네 개의 조회를 합니다. 다만 그게 같은 프로세스, 같은 DB 커넥션 안에서 일어나요. 네트워크 왕복이 아니라 함수 호출이라 지연이 밀리초 단위로 떨어집니다.
돌려 보니 체감이 확실히 달랐어요. 순차 의존 2단계가 사라지니 첫 화면 진입이 눈에 띄게 정리됐습니다.
어디까지 합칠지 — 판단 기준 4가지
여기서 멈추지 않으면 만능 엔드포인트가 됩니다. 그래서 기준을 정해뒀어요.
1. 화면 단위로만 자른다
엔드포인트 이름을 도메인이 아니라 화면으로 붙여요. /api/dashboard, /api/member-detail. 도메인 기준(/api/users + /api/stats)으로 쪼개면 결국 클라이언트가 조합해야 합니다.
2. 캐시 수명이 다르면 분리한다 이게 가장 중요한 기준이에요. 프로필은 몇 시간 유효한데 알림 개수는 30초도 못 갑니다. 한 응답에 묶으면 전체 캐시 수명이 가장 짧은 쪽에 맞춰져요. 수명 차이가 크면 따로 두는 게 맞습니다.
3. 화면 안에서도 지연 로딩할 부분은 뺀다 스크롤을 내려야 보이는 영역, 탭을 눌러야 나오는 영역은 첫 응답에 넣지 않아요. 첫 페인트에 필요한 것만 집계합니다.
4. 무거운 하나가 전체를 잡아먹지 않게 집계 응답은 가장 느린 조회만큼 느려요. 조회 하나가 유독 느리면 그건 합치기 전에 쿼리부터 고쳐야 할 신호입니다. 합치는 건 왕복을 줄이는 기술이지 느린 쿼리를 숨기는 기술이 아니에요.
- 화면 단위로 자르기
- 캐시 수명 다르면 분리
- 지연 로딩 영역 제외
- 느린 쿼리는 먼저 수정
응답 설계에서 놓치기 쉬운 것
부분 실패를 어떻게 다룰지 먼저 정하세요. 네 개 중 하나가 실패하면 전체를 500으로 던질지, 나머지를 내려줄지. 저는 화면이 최소한 뜨는 쪽을 택했어요. 실패한 영역은 명시적으로 표시하고, 조용히 빈 값으로 넘기지 않습니다. 빈 값과 조회 실패는 다른 상태예요.
권한 검증은 필드별로 확인하세요. 여러 데이터를 한 응답에 묶으면 각 조각의 접근 권한이 다를 수 있어요. 회비 내역은 운영진만 봐야 하는데 집계 응답에 묶여 전부 나가버리는 사고가 나기 쉽습니다. 조회를 합쳐도 권한 판정은 조각별로 서버에서 해야 해요.
응답 크기를 재보세요. 왕복을 줄이려다 페이로드가 커지면 모바일에서 손해입니다. 필요 없는 필드는 빼고, 목록은 개수 상한을 두세요.
정리
한 화면 = 한 API는 만능이 아니라 왕복 지연이 병목일 때 쓰는 도구예요. 순차 의존이 있는 화면에서 효과가 크고, 캐시 수명이 제각각인 데이터를 억지로 묶으면 오히려 손해입니다.
같은 고민을 플랜씨(plan-c)의 계산기 화면에도 적용해 봤는데, 거기는 애초에 요청이 하나라 합칠 게 없었어요. 구조가 단순하면 굳이 만들지 않는 게 맞습니다. 먼저 재보고, 병목이 왕복인지 쿼리인지 확인한 다음에 결정하세요.
관련 글
댓글
아직 댓글이 없어요. 첫 댓글을 남겨주세요.