← 모든 글

정적 사이트와 동적 페이지 — 같은 서비스에서 분리하는 기준

정적·동적 분리는 아키텍처 취향이 아니라 결제 데이터 정확성과 서버 안정성의 문제다. HEDVION이 정산 대시보드 재설계로 피크 부하 30% 줄인 실전 기준을 공유한다.

결제·정산 현장에서 이 질문이 생사를 가른다

결제와 정산 시스템을 운영하다 보면 "정적이냐 동적이냐"는 질문이 단순한 아키텍처 취향의 문제가 아님을 금방 깨닫는다. 잘못된 판단은 실제 돈과 연결된다. 예를 들어 사용자의 잔액 조회 페이지에 5분짜리 CDN 캐시를 걸어놓으면, 방금 완료된 결제가 화면에 반영되지 않아 "내 돈이 어디 갔냐"는 문의가 쏟아진다. 반대로 완전히 고정된 정산 가이드 문서를 동적으로 서빙하면, 정산 피크 시간대(보통 자정~새벽 2시)에 서버 부하가 불필요하게 치솟는다.

HEDVION처럼 결제 처리, 일별 정산, 자동화 리포트 발송을 한 팀이 직접 운영할 때 이 기준이 명확하지 않으면 두 방향 중 하나로 실수가 생긴다. 캐시를 너무 많이 써서 데이터 일관성을 잃거나, 캐시를 아예 안 써서 서버가 불필요한 부하를 떠안는다. 둘 다 경험했고, 둘 다 운영 중 실제 사고로 이어졌다.

경계를 흐릿하게 만드는 세 가지 요소

가장 먼저 따져야 할 질문은 "이 페이지의 내용이 요청마다 달라지는가"다. 로그인 여부, 사용자 식별, 실시간 잔액 조회가 포함된다면 동적이다. 그런데 결제·정산 서비스에서는 이 경계를 특히 흐릿하게 만드는 요소가 세 가지 있다.

첫째는 개인화 없는 실시간성이다. 오늘의 환율 정보나 카드사 점검 공지처럼 내용은 모든 사용자에게 동일하지만 자주 바뀐다. 이건 정적도 아니고 완전한 동적도 아니다. 1분 TTL의 CDN 캐시로 처리하면 서버 없이도 준실시간으로 서빙 가능하다. 둘째는 감사 추적이 필요한 페이지다. 정산 명세서나 세금계산서 조회 페이지는 데이터가 한번 확정되면 내용 자체는 바뀌지 않는다. 그러나 "언제, 누가, 어떤 IP에서 조회했는가"를 로깅해야 한다면 CDN에서 직접 내려줄 수 없다. 서버를 거쳐야 접근 로그가 남는다. 셋째는 결제 플로우 안의 마케팅 요소다. 결제 완료 페이지에 A/B 테스트 배너를 붙이는 순간, 그 페이지는 더 이상 정적으로 처리할 수 없다. 쿠키 기반 분기가 들어오기 때문이다. 이걸 놓치면 CDN이 특정 사용자에게 캐시된 배너 변형을 다른 사용자에게 그대로 내려보내는 사고가 생긴다.

HEDVION 팀의 실제 분리 기준

반복된 실수 끝에 정착한 기준은 두 개의 질문으로 압축된다.

  1. 이 페이지에서 사용자별 데이터가 렌더링되는가? → Yes면 동적
  2. 이 페이지의 접근 자체가 감사 대상인가? → Yes면 서버를 반드시 거침

두 번째 질문이 결제·정산 도메인에서 특히 중요하다. 일반 서비스는 보통 첫 번째 질문만 따지지만, 금융 데이터를 다룰 때는 "누가 언제 봤는지" 자체가 컴플라이언스 요건이 되는 경우가 있다. 정산 데이터를 CDN에서 바로 내려주면 서버 로그에 접근 기록이 남지 않는다. 이 두 조건을 모두 만족하지 않는 페이지는 적극적으로 정적으로 처리한다. 서비스 소개, 정산 가이드 문서, FAQ, 약관 페이지가 여기에 해당한다. 이 페이지들의 트래픽은 결제 서버로 가지 않는다.

아키텍처 패턴 3가지와 트레이드오프

같은 도메인 안에서 정적과 동적을 분리하는 방법은 크게 세 가지다.

라우트 기반 분리는 Nginx나 CDN의 라우팅 규칙으로 /docs, /guide, /about은 정적 파일을 직접 서빙하고, /dashboard, /api, /payment는 애플리케이션 서버로 프록시한다. 설정이 단순하고 인증 쿠키가 같은 도메인에 있어 공유가 쉽다는 것이 장점이다. 단점은 정적 영역의 독립 배포가 번거롭다는 점이다. 가이드 문서 하나 수정하는 데 전체 배포 파이프라인을 태워야 할 수 있다. 서브도메인 분리www.hedvion.com은 CDN에서, app.hedvion.com은 서버가 처리하는 방식이다. 정적 영역을 외부 CMS로 독립 관리할 수 있지만, 결제 서비스에서는 인증 쿠키가 서브도메인을 넘나들지 못해 SSO 처리가 복잡해진다. app.에서 로그인한 사용자가 www.의 계정 페이지로 이동할 때 세션을 어떻게 전달할 것인가가 반드시 풀어야 할 문제다. 빌드 파이프라인 내 혼용은 Next.js의 SSG/SSR 혼합처럼 같은 코드베이스 안에서 빌드 시점에 정적 생성 여부를 결정하는 방식이다. 유연성이 높지만 ISR 재생성 주기를 팀 전체가 명확히 공유해야 한다. 우리가 현재 쓰는 방식은 라우트 기반 분리를 기본으로, 빌드 파이프라인 혼용을 대시보드 일부에 제한적으로 적용하는 조합이다.

실전 시나리오: 정산 대시보드 재설계에서 배운 것

약 8개월 전, 정산 대시보드를 재설계하면서 이 기준을 처음 체계적으로 적용했다. 기존 대시보드는 전체가 동적으로 서빙되었다. 정산 요약 카드, 거래 내역 테이블, 가이드 문서 링크, 공지사항 배너까지 모두 같은 서버에서 렌더링했다.

문제는 정산 피크 타임(D+1 정산이 일괄 처리되는 자정~새벽 1시)에 서버 CPU가 치솟는다는 것이었다. 정산 배치 작업과 대시보드 렌더링이 같은 서버 자원을 두고 경합했기 때문이다. 이때 대시보드 트래픽의 약 40%가 로그인한 사용자가 아닌, 검색엔진 크롤러와 공유 링크를 타고 들어오는 비인증 트래픽이라는 것을 로그 분석으로 확인했다. 이 비인증 트래픽 40%를 정적 레이어로 분리하는 것만으로 피크 타임 서버 부하가 30% 이상 줄었다. 공지사항 배너, 서비스 안내 페이지, 외부 공개 가이드 문서를 Nginx에서 정적 파일로 직접 서빙하도록 라우팅을 바꿨고, 인증이 필요한 잔액 조회·거래 내역·정산 명세서는 그대로 서버에서 처리했다. 이 과정에서 "지난 30일치 거래 내역은 자주 안 바뀌니 캐시를 길게 걸자"는 의견이 나왔지만 받아들이지 않았다. 취소·환불이 늦게 반영될 수 있고, "내 내역이 틀렸다"는 문의가 들어올 때 캐시 문제인지 실제 데이터 오류인지 구분하는 디버깅 비용이 너무 높다. 정산 데이터는 성능보다 정확성이 우선이다.

바로 써먹을 수 있는 시사점

이 경험에서 뽑아낸 기준을 팀 안에서 바로 쓸 수 있도록 정리한다.

1. 페이지 목록을 두 축으로 분류하라. 가로축은 "사용자별 개인화 필요 여부", 세로축은 "접근 감사 필요 여부"다. 둘 다 No인 페이지는 정적 처리를 검토한다. 하나라도 Yes면 서버를 거친다. 이 매트릭스를 스프린트 초반에 한 번만 그려도 이후 논쟁이 크게 줄어든다.

2. 금융·정산 데이터에는 캐시 TTL을 보수적으로 잡아라. 일반 콘텐츠는 5분 캐시가 허용되더라도, 잔액·거래 내역·정산 금액은 캐시를 쓰지 않거나 최대 30초 이내로 제한한다. 캐시 불일치로 발생하는 CS 처리 비용이 서버 비용보다 비싸다.

3. 분리 기준을 코드 주석과 PR 체크리스트에 남겨라. "왜 이 페이지가 동적으로 되어 있지?"라는 질문이 6개월 후에도 나오지 않으려면, 결정을 코드가 아닌 문서로 남겨야 한다. 각 라우트 설정에 # 이유: 접근 감사 대상 — HEDVION 정책 2025-10 같은 한 줄이면 충분하다.

4. 분리는 트래픽 로그 분석부터 시작해 점진적으로 하라. 비인증 트래픽 비율을 먼저 확인하고, 그 비율이 높은 페이지부터 정적으로 이동시킨다. 한 번에 전부 바꾸다가 캐시 설정 실수로 개인 데이터가 CDN에 올라가는 사고가 생기면 되돌리기도 어렵다. 우리가 확인한 것처럼 비인증 40%를 분리하는 것만으로도 피크 부하 30% 감소라는 의미 있는 결과가 나온다.

결국 정적과 동적을 가르는 기준은 기술 스택의 문제가 아니다. "이 데이터가 틀렸을 때 우리 서비스에 어떤 결과가 생기는가"를 먼저 따지는 것이다. 결제·정산 서비스에서 그 답이 돈과 신뢰에 연결된다면, 캐시 TTL 하나도 신중하게 결정해야 한다.

— by mings

* 위 링크는 인프런 affiliate 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

📚 추천 강의
한 입 크기로 잘라먹는 바이브코딩 (with Claude Code)
Claude Code로 바이브코딩, 개발자라면 꼭 들어야 할 필수 강의
강의 보러가기 →

* 위 추천 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.