이전 글에서는 worker_processes와 worker_connections를 통해 Nginx가 처리할 수 있는 최대 동시 연결 수를 결정하는 방법을 살펴봤다.
이번 글에서는 그보다 한 단계 더 나아가, 실제 데이터를 얼마나 효율적으로 전송하는지에 영향을 주는 성능 최적화 설정들을 다룬다. 그리고 이 설정들을 모두 담은 기본 nginx.conf 템플릿을 작성하여 살펴보자.
sendfile - 파일 전송 방식 최적화
정적 파일(이미지, CSS, JS 등)을 클라이언트에게 전송할 때, Nginx가 내부적으로 어떤 방식으로 파일을 읽고 보내는지가 성능에 큰 영향을 미친다.
sendfile 설정 방식에 따른 파일 전달 방식

기본 방식인 sendfile off 파일 데이터가 커널 버퍼 → 사용자 공간(Nginx) → 커널 버퍼 로 두 번 복사된 후 전송된다.
복사가 일어날 때마다 CPU를 사용하고 메모리 대역폭을 소모한다.
반면, sendfile on 설정은 OS의 sendfile() 시스템 콜을 사용하여 커널 버퍼에서 네트워크로 직접 파일을 전송한다.
즉, 사용자 공간(Nginx)을 거치지 않으므로 불필요한 데이터 복사가 사라지기에 CPU 사용량과 메모리 복사 비용이 줄어들어 정적 파일 서빙에 성능적인 관점에서 효과가 크다.
tcp_nopush - 패킷 전송 최적화
해당 옵션은 기본적으로 sendfile_on 옵션과 함께 사용된다.
tcp_nopush 설정이 하는 기능을 간단하게 표현하자면, 응답 헤더와 파일 내용을 함께 패킷으로 묶어서 보낼지를 제어한다.
이는 아래의 이미지와 같은 차이를 지닌다.

tcp_nopush off의 경우 헤더와 파일 내용이 별도의 패킷으로 나눠져서 전송된다.
반면, tcp_nopush on의 경우에는 헤더와 파일 내용을 하나의 패킷으로 묶어서 전송하고, 남은 파일 내용을 패킷으로 보낸다.
따라서 on 설정의 경우, 패킷 수가 줄어들어 네트워크 관점에서 오버헤드가 감소하여 성능적인 이점을 지니게 되는 것이다.
단, 해당 설정은 sendfile 설정에 의존적으로 sendfile on으로 설정되지 않았다면, tcp_nopush 설정이 유효하게 동작하지 않는다.
tcp_nodelay - 지연 없는 즉시 전송
TCP에는 Nagle 알고리즘이라는 것이 있다.
작은 패킷을 바로 보내지 않고, 어느 정도 모아서 한꺼번에 보내는 최적화 기법이다.
네트워크 효율을 높이는 목적이지만, 실시간 응답이 중요한 상황에서는 오히려 지연을 유발한다.

Nagle 알고리즘은 일종의 Batch 처리와 같은 것으로, 예를 들어 cron job과 같은 스케쥴링을 통해 쌓인 데이터를 일정 주기로 처리하는 방식을 고려하면 이해하기 쉽다.
TCP_Nopush vs TCP_Nodelay 설정 충돌
위에서 다룬 두 설정의 목적과 동작을 정리하자면 아래와 같다.
| 설정 | 목적 | 동작 |
| tcp_nopush on | 패킷을 모아서 한 번에 전송 | 버퍼가 찰 때까지 기다렸다가 전송 |
| tcp_nodelay on | 즉시 전송 | 기다리지 않고 바로 전송 |
두 설정은 "모아서 보내기" vs "즉시 보내기"라는 정반대의 목적을 가지고 있는 것으로 보이기에 두 설정을 모두 on으로 켜놓아도 문제가 생기지 않는지 고민이 된다.
이에 대한, 답은 충돌이 없다 는 것이다. 그 이유는 tcp_cork에 있다.
TCP_CORK 란?
TCP_CORK 는 linux 소켓 옵션에 해당한다. 코르크라는 이름처럼, 소켓에 코르크(마개)를 꽂아서 데이터가 나가지 못하도록 막아두는 것이다.
TCP_CORK ON → "코르크 꽂음" → 데이터 쌓이는 중, 전송 안 함
TCP_CORK OFF → "코르크 뽑음" → 쌓인 데이터 한꺼번에 전송
그리고 Nginx는 sendfile 설정을 쓸 때 내부적으로 이 직접 TCP_CORK를 제어한다.
즉, 파일 전송에 있어서는 no_push on 동작을 통해 최대한 효율적인 패킷의 단위로 나눠서 데이터를 전송하여 오버헤드를 줄이고
같은 파일의 마지막 데이터 조각을 보내는 경우에는 no_delay로 동작하여 더 빠른 응답과 반응을 가능하게 만드는 것이다.
TCP_NOPUSH + TCP_NODELAY 동작 순서
두 설정을 킨 경우, 내부적으로 다음과 같은 동작을 수행한다.
① 파일 전송 시작
▼
② TCP_CORK ON (코르크 꽂음)
→ tcp_nopush 역할
→ HTTP 헤더 + 파일 내용을 버퍼에 쌓음
→ 꽉 찬 패킷(MSS) 단위로만 전송, 쪼개진 작은 패킷 안 보냄
▼
③ 파일 전송 완료 (마지막 남은 데이터)
▼
④ TCP_CORK OFF (코르크 뽑음)
→ tcp_nodelay 역할
→ 버퍼에 남아있던 마지막 조각 즉시 전송(최대 200ms 지연 없이 바로 flush)
tcp_nopush만 켜두면 마지막 조각이 Nagle 대기 시간(최대 200ms)만큼 지연될 수 있다. tcp_nodelay가 함께 있어야 마지막 조각도 즉시 나간다. 따라서 둘을 함께 쓰는 것이 Nginx의 공식 권장 설정이다.
파일 전송이 아닌 경우 (동적 응답)
sendfile은 디스크의 정적 파일을 전송할 때만 동작한다. API 응답이나 리버스 프록시처럼 백엔드에서 동적으로 생성된 응답은 sendfile을 타지 않는다.
이 경우 tcp_nopush는 사실상 아무 역할도 하지 못하고, tcp_nodelay만 단독으로 동작한다.
정적 파일 요청 → sendfile → tcp_nopush(본문) + tcp_nodelay(마지막 조각)
동적 응답 → sendfile 안 탐 → tcp_nopush 무효 → tcp_nodelay만 동작
동적 응답 상황에서 tcp_nodelay가 없으면, Nagle 알고리즘이 백엔드에서 온 응답 조각을 최대 200ms 붙잡아둔다. API 응답이 이유 없이 느려지는 원인이 될 수 있는 것이다.
상황별 동작 정리
| 상황 | tcp_nopush | tcp_nodelay |
| 정적 파일 전송 (본문) | ✅ TCP_CORK ON — 큰 덩어리로 묶어서 전송 | 대기 |
| 정적 파일 전송 (마지막 조각) | TCP_CORK OFF | ✅ 즉시 flush |
| 동적 응답 (API, 프록시) | ❌ 무효 (sendfile 안 탐) | ✅ Nagle 없이 즉시 전송 |
결국 tcp_nodelay는 정적 파일의 마지막 조각과 동적 응답 전체를 모두 커버한다.
keepalive_timeout
HTTP 요청 하나가 끝날 때마다 TCP 연결을 끊고 다시 맺으면, 매번 3-way handshake 비용이 발생한다. 특히 CSS, JS, 이미지 등 여러 리소스를 요청하는 웹 페이지에서는 이 오버헤드가 상당하다.
요청마다 연결을 새로 맺으므로, 요청이 많을수록 handshake 비용이 누적되는 것이다.

따라서 해당 설정을 on으로 켜두면 하나의 TCP 연결로 여러 요청을 처리한다.
keepalive_timeout은 마지막 요청 이후 연결을 얼마나 유지할지를 초 단위로 설정한다.
값이 너무 크면 불필요한 연결이 오래 점유되어 worker_connections를 낭비하게 되고, 너무 작으면 Keep-Alive의 이점이 줄어든다. 일반적으로 65초가 무난한 기본값으로 권유된다.
기본 nginx.conf 템플릿
지금까지 살펴본 성능 최적화 설정들을 모두 담은 기본 템플릿이다. 새로운 서버를 구성할 때 이 템플릿을 시작점으로 활용하면 좋을 것으로 판단된다.
http {
# ------------------------------
# 파일 전송 최적화
# ------------------------------
# zero-copy 방식으로 정적 파일 전송
sendfile on;
# sendfile on일 때 헤더+파일 내용을 하나의 패킷으로 묶어서 전송
tcp_nopush on;
# Nagle 알고리즘 비활성화 — 데이터 즉시 전송
tcp_nodelay on;
# 마지막 요청 후 65초 동안 TCP 연결 유지
keepalive_timeout 65;
}'Nginx' 카테고리의 다른 글
| 정적 파일 서빙 Path Matching 기법 - root와 alias (0) | 2026.05.28 |
|---|---|
| 네트워크 전송 관점에서 최적화를 위한 압축 설정 템플릿 (0) | 2026.05.21 |
| Nginx Process Model - 처리량 관점의 설정 (0) | 2026.05.14 |
| Nginx include 디렉티브 - 설정 파일 분리와 구조화 (0) | 2026.05.12 |
| Nginx의 계층 구조와 상속 및 재정의 방식 (0) | 2026.05.10 |
댓글