[기술블로그] EP7.놀고 있는 웹 서버 활용하기: WAS에 집중된 정적 리소스 서빙 구조 개선기

2026-04-09 | 개발 이야기, 사이냅 이야기

안녕하세요! 키냅스(Kynapse)에서 백엔드 및 인프라를 담당하고 있는 개발자, 위성영입니다.

이번 글에서는 웹 서버(Nginx)의 역할을 확장하여, 시스템 효율을 높인 실제 업무 경험을 공유합니다.

기존에는 정적 리소스 서빙까지 WAS(Node.js)에 집중되어 있었고, 웹 서버는 단지 리버스 프록시 역할에 머물러 있었습니다.

이 구조를 개선하여 정적 리소스 서빙을 웹 서버로 이관하는 과정과, 그 과정에서 겪은 시행착오를 정리해보았습니다.

[EP7. 핵심 요약]

  • WAS 부하 분산 (Nginx 역할 확대)
    비즈니스 로직에 집중해야 할 Node.js(WAS)가 처리하던 이미지·폰트 등 정적 리소스를 Nginx로 이관. 각 서버가 잘하는 일에 집중하게 하여 시스템 전체 효율을 증가

  • S3 기반 리소스 분리
    Nginx와 WAS 파드가 분리된 구조를 해결하기 위해, 정적 리소스를 빌드 시점에 S3로 업로드하고 Nginx가 이를 직접 서빙하도록 개선. WAS의 리소스 의존성을 완벽히 제거

  • 해시(Hash) 기반 버전 관리
    배포 시 발생하는 404 오류와 버전 충돌을 막기 위해 Docker 이미지 해시값을 경로에 도입. Webpack 설정에 해시를 주입해 클라이언트가 항상 현재 버전에 맞는 리소스를 참조하도록 보장

  • 통합 배포 파이프라인 구축
    단순 설정 변경을 넘어 CI/CD, 인프라, 클라이언트 빌드가 유기적으로 맞물리는 구조를 설계. 이를 통해 서비스 중단 없는 안정적인 정적 리소스 배포 체계 완성

| 생각보다 바빴던 WAS, 그리고 여유 있던 웹 서버

서비스를 운영하면서 API 요청 로그를 살펴보던 중, 생각보다 요청 수가 많다는 점이 눈에 들어왔습니다.
그래서 로그를 조금 더 자세히 살펴보았고, 이상한 점을 발견했습니다.

API 요청뿐만 아니라 이미지, 폰트 같은 정적 리소스 요청까지 모두 WAS를 거쳐 전달되고 있었던 것입니다.
이미 앞단에는 웹 서버(Nginx)가 있었지만, 실제로는 대부분의 요청이 그대로 WAS까지 전달되는 구조였습니다.

결과적으로는 웹 서버가 처리할 수 있는 작업까지 WAS에 집중되면서 불필요한 부하가 발생하고 있던 상황이었습니다. 이 글은 이런 구조에서 출발해, 웹 서버와 WAS의 역할을 다시 나누고 시스템 효율을 개선해나간 과정을 정리한 내용입니다.

| 우리가 쓰던 구조, 왜 WAS가 이렇게 바빴을까

저희는 처음부터 웹 서버(Nginx)를 사용하고 있었던 것은 아니었습니다.
Nginx를 도입하게 된 계기는 여러 사내 서비스와의 연동을 위해
로그인과 같은 인증 처리를 하나의 인증 서버에서 담당하도록 구성해야 했기 때문입니다.

서비스(Kynapse) 도메인에서 벗어나지 않으면서 인증 페이지를 제공하고,
그 안에서 발생하는 회원가입이나 로그인 API 역시 인증 서버로 전달해야 했습니다.

이를 해결하려고 Nginx를 도입하긴 했지만,
당시에는 특정 경로의 요청을 인증 서버로 넘겨주는 리버스 프록시 역할에만 급급했습니다.
사실상 정적 리소스는 Nginx에서 직접 서빙해야 한다는 점을 아예 간과한 채 사용하기 시작했던 것이죠.

문제는 여기였습니다.
Nginx가 단순히 인증 서버로 요청을 넘겨주는 ‘통로’ 역할만 수행하다 보니, 정적 리소스를 직접 처리해야 한다는 점을 간과한 것이죠.

결국 이미지, CSS, JS, 폰트 같은 모든 정적 데이터 요청이 고스란히 WAS로 향하게 되었습니다. 
비즈니스 로직에 집중해야 할 WAS가 무관한 요청들까지 떠안게 되면서, 
불필요한 리소스 낭비는 물론 서버 부하가 급격히 늘어나는 결과를 초래했습니다.


| 그래서 이렇게 바꿔봤습니다

문제를 확인한 이후, 구조를 어떻게 개선할지에 대해 먼저 기준을 세웠습니다.
핵심은 단순했습니다. 각 서버가 잘하는 일에 집중하게 하자는 것이었습니다.

정적 리소스 서빙에 강점이 있는 Nginx(웹 서버)와 비즈니스 로직 처리에 특화된 WAS,
각자의 역할에 충실한 구조를 만드는 것이 적절하다고 판단했습니다.
이 기준에 따라 WAS가 부담하던 정적 리소스 요청을 웹 서버로 과감히 이관하기로 했습니다.

다만 여기서 문제가 있었습니다.
현재 Nginx는 별도의 파드로 구성되어 있었고, WAS가 포함된 서비스 파드와는 분리된 구조였기 때문에
서비스 파드 내부에 존재하는 정적 리소스를 직접 서빙할 수 없는 상태였습니다.

이를 해결하기 위해 배포 구조까지 함께 수정하기로 했습니다.
CI/CD 파이프라인에서 정적 리소스를 빌드 시점에 추출하여,
해시 기반 파일명으로 S3에 업로드하고 버전을 관리하는 방식을 도입했습니다.

그리고 Nginx는 정적 리소스 요청에 대해 특정 prefix를 기준으로, 해당 리소스를 S3에서 직접 가져와 서빙하도록 구성했습니다.
이 구조를 통해 WAS는 정적 리소스 처리에서 완전히 분리되었고,
웹 서버는 정적 리소스 서빙 역할을 수행하도록 명확하게 역할이 나뉘게 되었습니다.
적용 과정 자체는 생각보다 순탄했고, 금방 마무리되는 듯 보였습니다.

하지만 진짜 문제는 예상치 못한 곳에서 시작되었습니다…

| 바꿔보니 바로 터진 문제들

구조를 정리하고 테스트 환경에 적용까지 마쳤을 때, 모든 것이 잘 동작할 것이라 기대했습니다.
하지만 현실은 그렇지 않았습니다.
배포 약 3시간 전하나씩 모습을 드러내기 시작했습니다.

가장 먼저 마주한 문제는 정적 리소스의 버전 관리였습니다.
당시 배포 프로세스는 빌드 결과물의 해시값을 기반으로 인프라 설정을 갱신하는 선언적 구조를 취하고 있었습니다.
하지만 정작 S3에 업로드되는 정적 리소스는 버전 관리가 제대로 고려되지 않은 상태였습니다.
즉 이전 버전으로 되돌리더라도, 해당 버전에 맞는 정적 리소스가 S3 내 어디에 있는지 구분할 수 없는 구조였던 것입니다.

결국 파이프라인을 빠르게 수정하였고,
정적 리소스를 업로드할 때 빌드된 Docker 이미지의 해시 값을 기준으로 S3 경로를 분리하도록 변경했습니다.

s3://bucket/static/{image-hash}/…

이렇게 하여 배포 버전과 정적 리소스를 1:1로 매핑할 수 있도록 구조를 보완했습니다.
하지만 문제는 여기서 끝나지 않았습니다.

S3 경로 구조가 변경되면서, 기존에 배포되어 있던 정적 리소스 요청들이 대량의 404 오류를 발생시키기 시작했습니다.
원인은 요청 URL은 그대로였지만, 실제 리소스의 위치는 이미 변경된 상태였기 때문입니다.

이 문제를 해결하기 위해, 클라이언트에서 참조하는 정적 리소스 경로에도 동일한 변경이 필요했습니다.
Webpack 설정을 수정하였고, 빌드 결과 해시 값을 prefix로 포함하도록 코드를 변경했습니다.

const hashCode = process.env.HASH_CODE ? `${process.env.HASH_CODE}/` : “”;

output: {
     publicPath: `/static/${hashCode}`,
     ..
     ..
}

또한 Docker 빌드 과정에서 생성된 해시 값을 환경 변수로 주입하여,
클라이언트가 항상 현재 배포 버전에 맞는 정적 리소스를 참조하도록 맞춰주었습니다.

이 과정을 통해 정적 리소스의 버전 관리와 경로 문제를 해결할 수 있었지만,
단순한 구조 변경으로 보였던 작업에도 예상보다 많은 고려 사항이 필요했습니다.

|마무리

이번 작업을 통해 단순히 정적 리소스 서빙 위치를 변경하는 것을 넘어
웹 서버와 WAS의 역할을 어떻게 나누는 것이 더 효율적인지 다시 한 번 고민해볼 수 있었습니다.

처음에는 “정적 리소스를 Nginx로 옮기자”는 비교적 단순한 생각에서 시작했지만
실제로 적용하는 과정에서는 배포 구조, 버전 관리, 클라이언트 설정까지 함께 고려해야 했습니다.

특히 정적 리소스를 외부 저장소(S3)로 분리하면서 단순히 서빙 위치만 바꾸는 것이 아니라 배포 방식 전체를 함께 고려해야 한다는 점을 체감할 수 있었습니다.

처음에는 구조 개선 자체에만 집중했지만,
실제로는 버전 관리, 경로 설계, 클라이언트 설정 등 다양한 요소들이 유기적으로 맞물려 있었습니다.

이러한 과정을 거치며 WAS는 불필요한 정적 리소스 요청의 굴레에서 벗어나, 본연의 비즈니스 로직 처리에 전념할 수 있는 여유를 되찾았습니다.
이번 트러블슈팅을 통해 얻은 가장 큰 교훈은 배포 과정을 변경하는 일은 생각보다 훨씬 많은 고려가 필요하다는 것입니다.

혹시 비슷한 구조 개선을 고민하고 계신가요? 그렇다면 단순한 설정 변경에 그치지 말고, 전체 배포 파이프라인의 흐름과 리소스 정합성까지 함께 점검해 보시길 권해 드립니다.

이런 고민들과 개선 과정을 바탕으로, 더 나은 서비스를 만들어가고 있습니다.
앞으로도 지속적으로 발전해 나갈 키냅스의 모습을 지켜봐 주세요!

👉 [kynapse.ai 더 알아보기]

사이냅 문서뷰어

어디서 어떻게 사용되고 있을까요?

사이냅 문서뷰어의 적용사례를 만나보세요

[개인정보 수집, 이용에 대한 동의 절차]

사이냅 문서뷰어 적용사례를 만나보세요

차원이 다른 HTML5 웹에디터

사이냅 에디터

사이냅 에디터가 어디에 활용될 수 있을까요?
다양한 적용사례를 만나보세요

[개인정보 수집, 이용에 대한 동의 절차]

한 차원 높은 HTML5 웹에디터를 만나보세요