Reverse Proxy가 뭐길래 전부 다 쓸까

Nginx는 왜 웹서버의 필수품이 되었나

Reverse Proxy가 뭐길래 전부 다 쓸까
Photo by Scott Rodgerson / Unsplash

보통 처음 웹 서비스를 배포할 때는 애플리케이션 서버를 그대로 외부에 노출하거나, Vercel이나 Netlify 같은 플랫폼에 프로젝트를 올린다. 후자의 경우 Reverse Proxy 계층이 없는 것이 아니라, 플랫폼이 이를 대신 관리하기 때문에 개발자에게 보이지 않을 뿐이지만, 결국 Reverse Proxy라고 하는 것을 개발자가 경험하지 못하게 된다. 실제 운영 환경에서는 대개 애플리케이션 앞에 요청을 받아 처리하는 별도의 계층이 존재한다.

최초의 웹

1990년대 초반의 웹은 오늘날의 애플리케이션 플랫폼이라기보다, 서로 연결된 하이퍼텍스트 문서를 공유하고 열람하는 시스템에 가까웠다. 웹 서버의 역할도 지금보다 훨씬 단순했다. 브라우저가 다음과 같은 요청을 보내면,

GET /index.html

서버는 디스크에서 index.html을 읽어 그대로 반환했다. HTML 문서와 이미지 같은 정적 리소스를 전달하는 것이 웹 서버의 주된 역할이었다.

당시에는 오늘날처럼 사용자 계정, 데이터베이스, 결제 시스템을 중심으로 동작하는 웹 애플리케이션이 일반적이지 않았다. 여러 시스템이 복잡하게 연결된 서비스도 드물었다. 초기 웹의 핵심은 프로그램을 실행하는 것보다, 하이퍼텍스트를 통해 문서를 연결하고 이동할 수 있게 만드는 데 있었다.

웹 애플리케이션의 등장

인터넷이 보급되면서 웹은 단순한 문서가 아니라 서비스를 제공하기 시작했다. 로그인 기능이 생기기 시작했고, 게시판이 만들어졌고, 쇼핑몰에서 상품을 구매해 배송받게 되었으며 은행 업무를 모니터와 키보드로 처리하게 되었다.

이러한 웹 서비스를 작동 시키기 위해서는 단순히 파일을 제공하는 정적 웹 서버 (Static Web Server) 따위로는 부족했다. 이제 웹 서비스는 여러가지 프로그램, 데이터베이스, 백엔드 서버, 프론트엔드 서버 프로그램 등이 유기적으로 동작하게 되었다.

데이터베이스를 조회하고, 비즈니스 로직을 수행하고, HTML을 동적으로 생성하는 것을 단일 웹 서버 혼자서는 처리하기 힘들었다. 이를 위해 먼저 CGI가 등장했고, 이후에는 매 요청마다 프로세스를 생성하는 방식의 부담을 줄이기 위해 FastCGI와 PHP-FPM 같은 구조가 사용되기 시작했다.

웹 서버는 요청을 받아 애플리케이션에게 전달하고, 애플리케이션은 결과를 다시 웹 서버로 돌려주는 구조가 만들어졌다.

여기서부터 웹 서버와 애플리케이션이 분리되기 시작했다. 여기서 서비스 규모가 커질수록 새로운 요구사항이 나타나게 되었다. HTTPS, 압축, 캐싱, 가상 호스트, 여러 서버로 요청 분산, 접근 제어, DDoS 방어 등등.

웹 서버는 외부 요청을 받고 정적 콘텐츠와 공통 HTTP 기능을 처리하며, 별도의 애플리케이션 서버는 비즈니스 로직과 동적 응답 생성을 담당하는 구조가 확산되었다. Java 생태계에서는 이러한 애플리케이션 실행 계층을 흔히 WAS라고 부른다. 이 글에서는 설명의 편의를 위해 다른 생태계의 유사한 구성 요소도 애플리케이션 서버라고 부르겠다.

이것을 계기로 웹 서버는 정적 콘텐츠와 HTTP 처리에 집중하고, 애플리케이션은 비즈니스 로직을 담당하는 구조가 자리 잡기 시작했다. 이후 HTTPS, 캐싱, 로드 밸런싱 등의 요구가 늘어나면서 오늘날의 Reverse Proxy 아키텍처가 발전하게 된다.

Forward Proxy

프록시 서버는 클라이언트가 리소스를 요청하고 서버가 해당 리소스를 제공하는 중간에서 동작하는 애플리케이션이다. 여기서 Forward Proxy는 우리의 관심사인 Reverse Proxy와 다르게 정방향으로 흐르는 Proxy로 보통 소개된다.

즉, 클라이언트가 파일이나 웹 페이지와 같은 리소스 요청을 처리할 수 있는 서버에 직접 연결하는 대신, 클라이언트는 요청을 프록시 서버로 보내면 요청을 평가하고 필요한 네트워크 트랜잭션을 수행한다. 이는 요청의 복잡성을 단순화하거나 제어하거나, 로드 밸런싱, 개인 정보 보호 또는 보안과 같은 추가적인 이점을 제공할 수 있다.

Forward Proxy는 클라이언트 앞단에 위치하여 클라이언트의 요청을 평가해 다른 웹서버에 연결해주는 애플리케이션이다. 즉, 주 목적이 클라이언트 보호 및 관리이다. 예를 들어 기업/학교 등에서 특정 사이트 접근을 제한하거나 캐싱을 통해 데이터를 빠르게 불러올 때 사용된다.

반면 Reverse Proxy는 웹 서버 앞단에 위치하여 들어오는 요청들에 대해 평가를 수행한다. 클라이언트가 웹 서버에 요청을 보낼 때, 실제 서버가 아닌 리버스 프록시 서버와 먼저 통신하면 리버스 프록시가 이 요청을 받아 내부의 실제 서버로 전달한다.

특히 사람들이 리버스 프록시를 입문하게 되는 일반적인 계기는 한 서버 컴퓨터에서 여러 웹 서비스를 제공하고자 할 때이다. 서버 A와 서버 B를 동시에 한 컴에서 80번 포트에 서빙하고 싶으면 포트 80번을 NginX에게 맡기고, A를 8001, B를 8002에 켜서 요청 도메인 주소를 통해 필터링하여 두개의 서버에 각각 요청을 보낼 수 있다.

간단한 구조도

Reverse Proxy

앞서 본 예시처럼, Reverse Proxy 기술을 잘 사용하면 꽤나 유용한 결과물을 얻을 수 있다.

여러 서비스 운영

Reverse Proxy가 가장 먼저 해결한 문제 중 하나는 요청을 적절한 애플리케이션으로 전달하는 것이다. 서비스 규모가 커질수록 하나의 서버에서 API 서버, 관리자 페이지, 블로그, 인증 서버 등 여러 애플리케이션을 함께 운영하는 경우가 많아졌다. 이들을 하나의 진입점으로 묶기 위해서는 들어오는 HTTP 요청을 분석해 어느 애플리케이션으로 전달할지 결정하는 계층이 필요했다.

Reverse Proxy는 요청의 Host 헤더, URL 경로, HTTP 헤더 등을 기반으로 적절한 Backend를 선택해 요청을 전달한다. 이러한 과정을 Routing이라고 한다. 덕분에 사용자는 하나의 도메인만 알면 되고, 내부에서는 애플리케이션이 몇 개가 실행되는지, 어떤 포트를 사용하는지, 심지어 어떤 서버에서 동작하는지조차 알 필요가 없다.

TLS Termination

오늘날 대부분의 웹 서비스는 HTTPS를 사용한다. 하지만 모든 Backend 애플리케이션이 직접 TLS 인증서를 관리하고 암호화를 처리하는 것은 비효율적이다. 인증서는 주기적으로 갱신해야 하고, 암호화 알고리즘도 계속 발전하며, 여러 개의 Backend가 있다면 동일한 인증서를 각각 관리해야 하는 문제도 발생한다. Reverse Proxy는 이러한 TLS 처리를 전담할 수 있는 기술이다. 클라이언트와는 HTTPS로 안전하게 통신한 뒤, 내부 네트워크에서는 복호화된 HTTP 요청을 Backend로 전달한다. 이를 TLS Termination이라고 한다. 덕분에 Backend는 비즈니스 로직에만 집중할 수 있으며, 인증서 교체나 HTTPS 설정 변경도 Reverse Proxy 한 곳에서 관리할 수 있다. (다만 내부 구간이 별도의 서버나 신뢰할 수 없는 네트워크를 통과한다면 Backend까지 HTTPS나 mTLS를 유지하기도 한다.)

Static File Serving

웹 페이지는 단순히 HTML 한 장만 내려받지 않는다. CSS, JavaScript, 이미지, 폰트 등 수십 개의 정적 파일을 함께 요청한다. 이러한 파일은 요청할 때마다 데이터베이스를 조회하거나 복잡한 비즈니스 로직을 수행할 필요가 없다. 그런데도 Backend가 모든 정적 파일을 직접 제공한다면 불필요한 리소스를 소비하게 된다. Reverse Proxy는 정적 파일 제공에 최적화되어 있으며, 파일 캐싱이나 효율적인 파일 전송 기능도 함께 제공한다. 따라서 Backend는 API와 같은 동적인 요청만 처리하고, 정적 리소스는 Reverse Proxy가 전담하는 구조가 일반적이다. 이는 애플리케이션의 부담을 줄이는 동시에 응답 속도도 향상시킨다.

Compression

API 응답이나 HTML 문서는 대부분 텍스트 기반이기 때문에 압축 효율이 매우 높다. gzip이나 Brotli와 같은 압축 방식을 적용하면 전송해야 하는 데이터 크기를 크게 줄일 수 있으며, 이는 곧 페이지 로딩 속도 향상으로 이어진다. 물론 Backend에서도 직접 압축을 구현할 수 있지만, 모든 프레임워크와 애플리케이션이 동일한 기능을 반복해서 구현하는 것은 비효율적이다. Reverse Proxy는 응답 내용을 분석해 적절한 압축 방식을 적용하고, 클라이언트가 지원하는 압축 형식에 맞춰 자동으로 응답을 변환한다. Backend는 응답 데이터만 생성하면 되고, 네트워크 전송 최적화는 Reverse Proxy가 담당하는 것이다.

Caching

모든 요청을 Backend까지 전달하는 것은 생각보다 큰 비용이 든다. 자주 요청되는 이미지나 CSS 파일은 물론이고, 변경 주기가 긴 API 응답 역시 매번 애플리케이션에서 생성할 필요는 없다. Reverse Proxy는 이러한 응답을 일정 시간 동안 메모리나 디스크에 저장해 두었다가 동일한 요청이 들어오면 Backend를 거치지 않고 즉시 반환할 수 있다. 이를 캐싱(Cache)이라고 한다. 캐싱은 응답 속도를 크게 향상시키는 동시에 Backend의 CPU와 데이터베이스 부하를 줄여준다. 특히 트래픽이 많은 서비스에서는 캐싱만으로도 상당한 성능 향상을 얻을 수 있다.

Load Balancing

서비스 규모가 커지면 Backend 서버 한 대만으로는 모든 요청을 처리하기 어렵다. 동일한 애플리케이션을 여러 대 실행한 뒤 요청을 적절히 분산해야 한다. Reverse Proxy는 클라이언트에게는 하나의 서비스처럼 보이지만, 내부적으로는 여러 Backend 서버에 요청을 분배한다. Round Robin, Least Connections 등 다양한 알고리즘을 사용해 트래픽을 균등하게 분산할 수 있으며, 특정 서버에 장애가 발생하면 해당 서버를 제외하고 나머지 서버로 요청을 전달하는 것도 가능하다. 사용자는 서버가 몇 대인지조차 알지 못한 채 하나의 도메인만 이용할 수 있다.

Security

Reverse Proxy는 인터넷과 Backend 사이의 가장 앞단에 위치하기 때문에 보안 기능을 적용하기에 가장 적합한 계층이다. 악성 요청을 Backend까지 전달한 뒤 차단하는 것보다, 아예 입구에서 걸러내는 편이 훨씬 효율적이기에 많은 Reverse Proxy는 Rate Limiting을 통한 요청 수 제한, IP 기반 접근 제어, User-Agent 필터링, 봇 차단, WAF와 같은 기능을 제공한다. 이러한 보안 정책을 Reverse Proxy에 집중시키면 Backend는 애플리케이션 로직에 집중할 수 있고, 모든 서비스에 동일한 보안 정책을 일관되게 적용하는 것도 쉬워진다.

Web Server vs WAS

사람들이 가장 헷갈리는 부분은 바로 WAS도 결국 웹 서버라는 것이다. Express도 웹 서버이고, Spring도 웹 서버이며, ASP.NET도 웹 서버이다. 그러나 아까 보았듯 Nginx와 같은 Reverse Proxy 서버는 HTTP 요청이 들어오는 것을 PreProcessing해주는 것이 핵심 기능이고, 그 뒤에 실제 로직을 구현하는 것이 WAS의 역할이다. 둘 다 HTTP를 처리하지만, 주 목적과 사용 범위가 다르다.

마무리하며

웹은 처음부터 Reverse Proxy를 염두에 두고 설계된 기술이 아니었다. 처음에는 HTML 파일만 전달하면 충분했고, 웹 서버 하나만으로도 대부분의 요구사항을 해결할 수 있었다.

하지만 웹이 애플리케이션으로 발전하면서 동적인 페이지를 생성하기 시작했고, HTTPS가 기본이 되었으며, 여러 서비스를 동시에 운영하고, 트래픽을 분산하고, 보안을 강화해야 하는 요구사항이 계속 추가되었다. 그 과정에서 HTTP 처리와 애플리케이션 로직은 점점 서로 다른 책임을 갖게 되었고, 결국 Reverse Proxy라는 계층이 자연스럽게 자리 잡게 되었다.

그래서 오늘날 Reverse Proxy는 선택적인 최적화가 아니라 대부분의 웹 서비스가 기본적으로 채택하는 아키텍처가 되었다. Nginx나 Caddy를 사용하는 이유도 단순히 "프록시 서버가 필요해서"가 아니라, HTTP와 관련된 공통 기능을 애플리케이션으로부터 분리하기 위해서다.

결국 Reverse Proxy는 새로운 기능을 추가한 기술이라기보다, 웹이 성장하면서 역할을 재배치한 결과라고 볼 수 있다. 이러한 기술들을 볼때 문제점이나 수요, 그리고 그에 대한 해결책을 찾아보는 것도 굉장히 재미있는 것 같다.