파일 전송으로 이해하는 Stateless vs. Stateful
들어가며
Disclaimer
-
Last Update: 2024/11/23
-
인증 과정에서 주로 대비되는 것으로만 여겨지는 Statefulness와 Statlessness를 제대로 들여다보기 위해, 클라이언트와 서버의 통신 및 트래픽 차원에서의 둘을 비교 정리해보자는 의미에서 작성된 글입니다.
Stateful vs. Stateless
State
상태(State)는 이전에 진행된 이벤트나 유저 상호작용을 기억하는 시스템에서 저장된 바로 그러한 정보를 의미합니다. 주로 통신 프로토콜, 방화벽, 그리고 암호화에서 중요한 개념이 됩니다.
사용자 인증 과정에서 세션을 이용한 로그인을 진행할 것인지, 토큰을 이용한 로그인을 이용할 것인지 고민해 본 경험이, 대표적으로 Stateful 혹은 Stateless를 마주하는 순간입니다.
이번 글에서는 어플리케이션 차원에서보다 통신 프로토콜 차원에서 Stateful과 Stateless가 어떻게 이용되는지 알아보겠습니다.
예시: 파일 전송
Stateful: FTP
FTP(File Transfer Protocol)는 1971년에 처음 등장한 파일 전송 프로토콜입니다. FTP는 클라이언트와 서버 간의 파일 업로드 및 다운로드를 지원합니다. 여기에서 명령 채널과 데이터 채널을 분리해 이용하며, 세션 동안 상태 정보를 유지합니다.
명령 채널에서는 클라이언트와 서버 간 명령어 및 응답이 교환되기에 연결이 항상 유지됩니다. 대신 데이터 채널에서는 파일 데이터나 디렉토리 목록이 전송되기에 필요 시 열리고 전송이 완료되면 닫히는 구조입니다.
FTP 연결은 TCP에 기반해, 사용자 이름과 비밀번호를 통한 인증 요청 후 성공 시 세션이 설정되는 방식으로 진행됩니다. 앞서 말한 명령 채널은 세션 정보를 기반으로 처리되죠. 데이터 채널 역시 명령 채널에서 협의된 작업 디렉터리 상태, 전송 모드, Active/Passive 모드, 인증 상태 등의 세션 정보에 기반해 열립니다.
즉, 어떠한 디렉터리에서 작업이 처리 될 것인지, 텍스트 파일을 전송하는지 바이너리 데이터를 전송하는지, 클라이언트가 서버에게 사용할 포트를 알리는지 반대인지, 인증이 성공했는지 실패했는지 등의 정보가 세션에 저장되고 이용되는 셈입니다.
Stateless: HTTP
HTTP(HyperText Transfer Protocol)은 1990년대 초에 웹 콘텐츠 전송을 목적으로 설계된 프로토콜이지만, 파일 전송에도 이용됩니다. HTTP를 통한 파일 전송은 클라이언트가 특정 파일이나 데이터를 요청하면, 서버가 이를 응답으로 반환하는 방식으로 이루어지는데요.
클라이언트에서는 GET 이나 POST, PUT 메서드를 이용하여 서버로부터 리소스를 요청하거나 데이터를 업로드합니다. 요청에는 URI, 헤더, 본문, 인증 정보 등이 포함되는데요. 여기에서는 각 요청마다 필요한 모든 정보가 포함되어야 합니다.
또한, HTTP는 요청마다 Content-Type
헤더를 이용해 모든 유형의 파일을 전송할 수 있도록 설계되었습니다. text/plain
, text/html
, image/png
, application/json
, application/octet-stream
, multipart/form-data
등의 MIME(Multipurpose Internet Mail Extensions) 유형을 통해 파일 형식을 지정할 수 있죠. 요청에서도 Accept
헤더를 통해 자신이 처리가능한 MIME 유형을 전송하기도 합니다.
즉, HTTP에서는 각 요청이 이전 요청과 무관해 독립적이므로 필요한 모든 정보가 요청에 포함되어야하는 셈입니다. 따라서, 대규모 파일 전송의 경우 Range
헤더를 통해 이어서 파일의 특정 부분을 요청하는 방식이 쓰이기도 합니다. 인증 상태를 유지하는 일은 클라이언트의 책임이 되기도 합니다.
HTTP vs. FTP (on State)
특징 | HTTP (Stateless) | FTP (Stateful) |
---|---|---|
상태 유지 여부 | 요청과 응답이 독립적이며, 서버는 각 요청 간 상태를 기억하지 않음. | 서버는 세션 상태를 기억하며, 디렉터리 위치, 전송 모드, 인증 상태 등을 유지. |
연결 방식 | 단일 TCP 연결에서 요청-응답 주기 처리. Keep-Alive 이용 시 단일 연결에서 여러 요청 처리. | 두 개의 TCP 연결 사용: 명령 채널(항상 열림)과 데이터 채널(필요 시 열림). |
상태 관리 | 클라이언트가 쿠키, 세션, JWT 등을 이용해 상태를 관리해야 함. | 상태가 명령 채널에 유지되므로 추가적인 상태 관리가 필요 없음. |
다중 작업 처리 | 요청마다 독립적으로 처리되므로, 다중 작업 시 별도의 요청을 전송해야 함. | 명령 채널을 유지하면서 연속적으로 여러 파일을 처리 가능. |
작업 디렉터리 | 각 요청이 파일의 경로를 명시적으로 포함해야 함. | 명령 채널에서 현재 작업 디렉터리(CWD)를 유지하며 연속 작업 가능. |
확장성 | Stateless 설계로 인해 서버 간 요청 분산(로드 밸런싱) 및 확장이 용이. | 상태를 유지해야 하므로, 많은 클라이언트를 처리할 때 서버 리소스가 소모됨. |
중단 작업 처리 | Range 요청을 통해 파일의 특정 범위만 요청 가능. 중단된 다운로드 재개도 가능. | 명령 채널에서 상태를 유지하므로, 중단된 파일 전송을 쉽게 이어받을 수 있음. |
사용 사례 | 웹 브라우징, REST API, 정적 파일 전송, 대규모 콘텐츠 배포. | 대용량 파일 전송, 서버 간 데이터 백업, 파일 시스템 관리. |
파일 업로드: 클라이언트 요청부터 서버 응답까지
이제 HTTPS 통신을 이용해 파일 업로드를 처리할 때, 클라이언트부터 서버 응답까지의 과정에서 Stateless 혹은 Stateful한 방식이 어떻게 개입하는지 살펴보겠습니다.
1. 파일 업로드 요청 생성
사용자는 웹 어플리케이션에서 파일 업로드 버튼을 클릭하고 파일을 선택합니다. 여기에서 업로드 요청은 POST 메서드로 전송되며, 파일을 HTTP 요청의 본문에 포함해 서버로 전송합니다.
Stateless vs. Stateful
Stateless
- HTTP 요청 자체는 이전 요청과 무관하고, 서버 역시 세션 상태를 저장하지 않으므로 상태 정보가 포함되지 않습니다.
Stateful
- 만약 사용자 인증 과정이 필요하다면, 클라이언트 브라우저는 세션 ID나 JWT 토큰을 요청 헤더에 포함합니다.
- 쿠키, 로컬 스토리지, 세션 스토리지 등을 활용해 클라이언트는 상태를 유지합니다.
2. DNS 조회
브라우저는 요청을 보내기 전에 도메인 이름을 IP 주소로 변환하는 과정에서 DNS 조회를 수행합니다. https://glenn-syj.github.io
로 접근하려고 한다면, DNS 요청을 통해 위 DNS의 IP 주소를 얻는데요.
브라우저에서 로컬 DNS 캐시를 확인하고, 캐시에 없다면 운영 체제의 DNS 클라이언트가 DNS 서버에 질의합니다.
Stateless vs. Stateful
Stateless
- 표준 DNS 질의는 53번 포트의 UDP를 이용해 이루어집니다. 기본적으로 512바이트의 데이터 크기 제한이 있으며, 초과 시 요청이 분할되거나 TCP로 전환됩니다.
- DoH(DNS over HTTPS)는 요청과 응답을 HTTPS로 암호화하여 전송하는데, TCP 443번 포트를 이용합니다.
- 주요 브라우저는 DoH를 기본 활성화하기도 하는데요. Firefox는 Cloudflare(1.1.1.1)을 기본 DoH 서버로 지정합니다.
Stateful
- TCP 전환(표준 DNS), TLS 연결(DoH)이 진행되는 과정에서는 Stateful합니다.
3. TCP 연결 설정
클라이언트는 파일을 전송하기 위해 서버와 TCP의 3 way 핸드셰이크를 수행하고 연결합니다. TCP는 신뢰성, 순서 보장, 연결 관리를 위해 각 단계에서 상태를 필수적으로 유지해야 합니다.
Stateless vs. Stateful
Stateful
- TCP에서는 SYN(클라이언트의 연결 요청), SYN-ACK(서버의 요청 수락 및 응답), ACK(클라이언트의 확인)을 거쳐 연결을 설정합니다. 이후 각 노드는 연결 상태와 초기 시퀀스 번호를 저장해 통신 동안 이용합니다.
- TCP에서는 시퀀스 번호(데이터 순서 추적), ACK 번호(수신된 데이터 확인 상태)를, 윈도우 크기(흐름 제어)를 상태로써 유지합니다.
Stateless
- UDP를 이용하면 상태를 유지하지 않지만, HTTPS는 TCP를 필수적으로 이용합니다.
4. TLS 핸드셰이크
TCP 연결이 설정되면 클라이언트와 서버는 TLS(Transport Layer Security) 핸드셰이크를 통해 암호화된 통신 채널을 설정합니다. TLS는 전송 계층인 TCP 위에서 보안을 제공하고, 응용 계층의 데이터와 상호작용하며 안전하게 이용되도록 합니다.
Stateful vs. Stateless
Stateful
- HTTPS에서는 암호화 프로토콜로서 TLS를 이용합니다.
- Client Hello를 통해서 클라이언트는 지원하는 암호화 알고리즘 목록, TLS 버전 등을 서버로 보냅니다.
- Server Hello를 통해서 서버는 클라이언트가 지원하는 암호화 알고리즘 중 하나를 선택하고, 자신의 인증서와 랜덤 값을 보냅니다.
- 클라이언트는 서버의 인증서를 검증하고 세션 키를 교환합니다.
- 교환이 완료된다면 이를 알리고, 이후 모든 데이터는 암호화되어 전송됩니다.
5. HTTP 파일 업로드 요청 전송
브라우저는 파일 데이터를 포함한 HTTPS 요청을 서버로 전송합니다. 헤더에는 URI 및 메서드 등 요청 정보와 파일 메타 데이터, 클라이언트에 저장되어 있는 인증 정보를 포함할 수 있는데요. 여기에서 요청의 바디는 multipart/form-data
형식으로 포함된다고 합시다.
Stateful vs. Stateless
Stateless
- HTTP 요청은 독립적으로 처리되며, 상태를 유지하지 않습니다.
- 만약 대규모 데이터라면,
Range
헤더를 활용해 이어서 전송하는 방식을 취해도 되겠습니다.
6. 클라이언트 측 네트워크 통과
요청 데이터는 클라이언트의 네트워크 인프라를 지나서 서버로 전달되어야 하는데요. 이 과정에서는 방화벽, 라우터 및 NAT를 이용해야 합니다.
클라이언트 측의 방화벽은 클라이언트에서 데이터가 나가는 것을 의미하는, 아웃바운드 트래픽을 검사합니다. 대부분의 로컬 컴퓨터에서는 아웃바운드 트래픽이 기본적으로 허용됩니다.
이러한 요청 데이터는 Wifi 네트워크를 통해 라우터로 전송된다고 가정해보겠습니다. 이후 라우터와 NAT(Network Address Translation) 요청이 라우터를 통과해, 사설 IP 주소가 공인 IP 주소로 변환됩니다.
Stateful vs. Stateless
Stateless
- IP(Internet Protocol)은 인터넷 계층에서 데이터 패킷을 전달하는 역할을 하는데요. 데이터는 출발지로서 출발 IP와 목적 IP를 기반으로 전송됩니다. 여기에서도 각 패킷은 독립적으로 처리되고, 관계가 저장되지 않습니다.
Stateful
- 운영체제의 방화벽은 연결 추적 테이블(Connection Tracking Table)을 유지하면서 출발지 IP 및 포트, 목적지 IP 및 포트, 프로토콜 및 연결 상태를 기록합니다.
- NAT는 포트 매핑 테이블을 유지하면서, NAT 테이블을 활용해 클라이언트의 사설 IP 및 포트, 라우터의 공인 IP 및 포트, 목적지 서버의 IP 및 포트를 추적합니다. 이 정보들은 이후 응답 트래픽을 원래 요청한 클라이언트로 전달하고, 비인가된 외부 트래픽을 차단하는 데 이용됩니다.
7. ISP 네트워크 경유
NAT를 통해 변환된 요청은 ISP의 네트워크를 통해 인터넷으로 전달됩니다. ISP 네트워크는 클라이언트-ISP 연결을 담당하는 액세스 네트워크, 패킷을 다른 ISP나 인터넷 백본으로 라우팅하는 중간라우터 및 스위치, DNS 캐싱 서버로 구성됩니다.
액세스 네트워크로 전달된 클라이언트의 요청은 라우터와 스위치로 구성된 ISP의 코어 네트워크로 이동합니다. 여기에서 BGP나 OSPF와 같은 라우팅 프로토콜을 이용해 최적 경로가 선택하고, 패킷을 인터넷 백본이나 다른 ISP로 전달합니다.
Stateful vs. Stateless
Stateless
- ISP 네트워크 장비인 라우터와 스위치는 패킷을 Stateless하게 전달합니다. 특히, 패킷의 목적지 IP, 출발지 IP, 포트만을 기반으로 독립적인 최적 경로로 전달한다고 말할 수 있겠습니다.
Stateful
- DDos 방어나 비인가된 트래픽 차단 등 보안서 서비스 ISP 방화벽이 이용된다면 이는 Stateful한 방식으로 동작하는데요. 테이블에 기록된 연결 상태를 추적하고, 요청 상태와 맞지 않는 트래픽은 차단됩니다.
- 방화벽은 SYN-ACK 응답에 대해서 ACK 응답이 없는 연결을 제한해 SYN Flood에 대응하거나, HTTP 세션의 지속성 및 요청 빈도를 분석하면서 HTTP Flood를 방어하기도 합니다.
8. 인터넷 백본 통과
ISP를 거쳐 인터넷 백본으로 들어온 패킷은 글로벌 인터넷 백본으로 연결됩니다. 인터넷 백본은 고속으로 데이터 전송을 처리하는 인프라로, 대규모의 고성능 라우터와 광케이블로 구성되어 있습니다.
인터넷 교환 지점(IXP)에서는 패킷이 적절한 네트워크로 전달되도록 중계하며, 해저 광케이블 등의 백본 라우팅을 통해 대규모 라우터가 패킷을 최적 경로로 전달합니다.
Stateless vs. Stateful
Stateless
- 백본 라우터는 패킷의 상태 정보를 유지하지 않고, 독립적으로 처리합니다. 라우팅은 패킷 헤더를 기반으로 수행됩니다.
- 초대규모 트래픽을 처리하므로, 스케일링과 고속 데이터 처리에서 Stateless한 설계는 필연적이라고도 볼 수 있겠습니다.
9. 서버 측 네트워크 도착
패킷은 서버가 위치한 데이터 센터 혹은 호스팅 네트워크로 도착합니다. 데이터 센터 내부 네트워크는 코어 계층, 분배 계층, 액세스 계층이라는 계층적 구조로 나뉩니다.
먼저, 엣지 라우터는 외부 네트워크와 네부 네트워크의 경계를 연결하면서, 트래픽 필터링 및 부하 분산을 처리합니다. 이후 내부 라우팅을 통해 내부 네트워크로 패킷이 전달되어, 최정 서버 위치가 확인되고 전달됩니다.
Stateless vs. Stateful
Stateless
- 데이터 센터 내부의 라우터와 스위치도 Stateless한 방식으로 동작합니다. 패킷 헤더를 기반으로 독립적인 패킷으로 전달하기에, 상태 정보 저장 없이 대규모 네트워크에서 효율적으로 동작할 수 있습니다.
10. 서버 측 방화벽 통과
서버의 네트워크 방화벽은 패킷을 검사하고, 헝요된 트래픽만 서버로 전달합니다. 방화벽은 패킷 필터링, 상태 기반 필터링, 침입 탐지 및 방지라는 기능을 수행합니다.
패킷 필터링을 통해서 특정 IP 대역, 포트번호, 프로토콜, 패킷 내용에서의 트래픽을 허용하거나 차단합니다. 상태 기반 필터링을 통해서 패킷의 연결 상태를 추적해 핸드셰이크 미완료된 연결이나 세션과 무관환 응답 패킷은 차단합니다.
Statelss vs. Stateful
Stateful
- 서버 측 네트워크의 방화벽도 Stateful하게 동작합니다.
11. 로드 밸런서 통과
만약 로드 밸런서가 있다면, 트래픽이 로드 밸런서를 통과해 여러 서버 중 하나로 분배됩니다. 이를 통해 트래픽 분산과 고가용성, Stateful하다면 세션 관리를 보장할 수 있습니다.
로드 밸런싱에는 L4 로드 밸런싱과 L7 로드 밸런싱이 있는데요. L4 로드 밸런싱은 TCP/UDP 수준에서 패킷 IP와 포트 정보에 기반해 트래픽을 분산합니다. 이는 패킷 레벨에서 처리하기에, 응용 계층의 데이터는 읽지 않습니다.
반면, L7 로드 밸런싱은 HTTP/HTTPS 수준에서 요청 URL, 쿠키, 헤더, 세션 정보를 분석해 적합한 서버에 전달합니다. SSL 종료가 일어나기도 합니다.
현재 예시에서는 HTTP 헤더를 통해 범위를 나누는 멀티파트 파일 업로드를 진행하고 있으므로, Stateful한 L7 로드밸런싱을 이용한다고 가정하겠습니다. 로드 밸런서는 계층과 방식에 따라 Statless하거나 Stateful하게 적절히 선택해야 합니다.
Stateless vs. Stateful
Stateful
- L7 Stateful 로드밸런싱에서는 클라이언트의 세션 ID, 쿠키, 또는 IP 해싱을 기반으로 요청을 고정하는데요. 모든 업로드 요청이 동일한 서버에서 처리되도록 보장되어야 하는 까닭입니다.
- HTTP 쿠키에서 세션 정볼르 읽어 동일한 서버로 요청하는 쿠키 기반 지속, 업로드 요청 URI에 세션 정보를 포함하는 URI 기반 지속, 클라이언트 IP를 기반으로 요청을 서버에 고정하는 IP 해싱 등을 이용할 수 있습니다.
- 게다가 TLS 세션을 로드 밸런서에서 처리하면 서버의 부하를 감소 시킬수도 있습니다.
- 그러나 상태를 유지하는 서버가 다운되면 업로드가 중단되므로, 세션 복제나 다른 서버로의 세션 이동, 혹은 재시도 처리에 대한 고려도 필요합니다.
12. 웹 애플리케이션 방화벽(WAF) 통과
WAF(Web Application Firewall)는 애플리케이션 계층에서 트래픽을 심층적으로 분석해, 악의적인 요청을 탐지하고 차단해 애플리케이션을 보호합니다. SQL 인젝션 방어, XSS 방지, DDoS 완화 등의 기능을 수행하는데요.
대표적인 WAF로는 AWS WAF, Cloudflare WAF, ModSecurity, NAXSI, F5 Advanced WAF 등이 있습니다.
Stateless vs. Stateful
Stateful
- WAF는 한 번의 요청만을 분석하는 것이 아니라, 다양한 공격 방식에서 여러 요청 간의 관계를 파악하기도 하므로 Staetful할 필요가 있습니다.
13. 웹 서버 도착
Nginx나 Apache 등의 웹서버는 클라이언트의 요청을 수신하고 처리합니다. 여기에서 정적 콘텐츠를 직접 반환하거나 동적 요청을 애플리케이션 서버로 전달합니다.
Stateless vs. Stateful
Stateless
- 기본적으로 웹 서버는 Stateless하게 작동합니다. HTTPS 통신으로 multipart 요청이 들어온다고 해도 바운더리를 기준으로 데이터를 분리하고 처리할 뿐 요청 간 상태는 추적하지 않습니다.
- 단순한 multipart 요청을 통한 업로드는 정적으로 처리될 수도 있습니다.
Stateful
- 세션 지속성이 요구되거나 대용량 파일 업로드 시 청크로 나뉘어 처리된다면 상태가 추적되어야 합니다. 이러한 경우에는 Stateful하도록 이용되어야 합니다. 주로 메모리나 Redis와 같은 캐시 및 어플리케이션에 서버에서 설정될 수도 있지만, NGINX Upload Module을 추가로 이용하는 경우도 있습니다.
14. 애플리케이션 서버 처리
웹 서버를 통해 애플리케이션 서버에 동적 요청이 전달됩니다. 여기에서 요청 데이터 파싱, 인증 및 권한 확인, 비즈니스 로직 실행, 응답 생성 등이 이루어지게 됩니다.
요청 데이터를 파싱하고, 폼 데이터 및 파일 데이터를 분리하며, 청크 정보를 추출하는 일이 일어납니다. 추가적으로 사용자 인증 및 권한 확인도 진행됩니다. 만약 Stateful한 방식을 이용한다면 상태 추적이 진행됩니다.
병합 신호까지 확인된다면 올바른 순서로 정렬하고 모든 청크를 결합하여 하나의 파일로 생성합니다. 이후 임시 청크 파일은 삭제합니다. 경로 존재 여부를 확인하거나 생성한 뒤에 업로드 파일을 저장합니다.
요청 처리 결과는 HTTP 응답 메시지로 생성해 클라이언트로 전송합니다. 응답에는 상태 코드, 헤더, 본문이 포함되며 TLS 암호화를 통한 전송이 이루어집니다.
나가며
이번 글에서는 상태(state)에 대해서, 파일 업로드를 기반으로 유상태(Stateful)과 무상태(Stateless)의 차이를 살펴보았습니다. 특히, FTP와 HTTP에 있어서의 차이를 살펴보며 기본적인 stateful 및 stateless의 차이점을 알 수 있게 되었습니다.
특히, 이번 글에서는 클라이언트의 HTTPS 통신 파일 업로드 요청이 백엔드 서버의 응답까지 진행되는 과정을 대략적으로 살펴보았는데요. 이를 실제로는 혼재되어 통신에서 중요한 역할을 차지하고 있다는 점을 알게 되었습니다. Stateless한 특성과 Stateful한 특성이 서로를 보완하기도 하고요.
네트워크와 네트워크의 경계면이 연결될 때 Stateful하게 이용된다는 점도 흥미로웠습니다. 방화벽이 대표적인 예시였습니다. 실제로는 Stateless한 방화벽도 있는데, 일반적으로는 Stateful한 방화벽이 이용된다고 알고 있어 이를 서술하지는 않았습니다.
마지막으로 이 글을 쓴 배경 중 하나는, 세션 기반 로그인 vs. 토큰 기반 로그인에서처럼 Stateful과 Stateless가 서로를 배척하는 것처럼 이해되고 있을지 모른다는 반성이었습니다. 네트워크 환경 이해 부족으로 아주 깊게 작성하지는 못했지만, 전체적인 과정에서 Stateful과 Stateless라는 특성을 한 층 더 깊게 이해할 수 있다면 좋겠습니다.
참고자료
https://www.redhat.com/en/topics/cloud-native-apps/stateful-vs-stateless
https://en.wikipedia.org/wiki/Stateless_protocol
https://support.mozilla.org/en-US/kb/firefox-dns-over-https
https://www.geeksforgeeks.org/stateless-vs-stateful-load-balancing/
https://www.fortinet.com/resources/cyberglossary/stateful-vs-stateless-firewall