NestJS WsAdapter maxPayload 기본값 변경: 100MB에서 1MB로

NestJS 12.5 WsAdapter maxPayload 기본값 변경은 하위 호환을 깨는 변경이다. v12.5.0부터 @nestjs/platform-ws의 WsAdapter는 클라이언트가 보낼 수 있는 메시지 크기를 기본 1 MB로 제한한다. v12.4.x 이하에서 이 값은 100 MB였다. 한도를 넘는 메시지를 받으면 서버가 연결을 close code 1009로 닫는다.

이 변경은 ws 라이브러리 위에서 동작하는 WsAdapter와, 이 어댑터로 띄운 게이트웨이에 해당한다. 기준 버전은 v12.5.0, 근거 커밋은 109e52cd9b다. 1 MB를 넘는 메시지를 주고받는 서비스는 업그레이드 전에 한도를 명시해야 한다. 같은 릴리스에는 게이트웨이 접속 시점에 관한 동작 변경도 함께 들어갔다. 이 내용도 아래에서 다룬다.

목차

NestJS 12.5 WsAdapter maxPayload 기본값 변경 내용

maxpayload-default-shrink

NestJS v12.5.0 릴리스 노트의 ‘Notable changes > New options’ 항목 원문은 다음과 같다.

"WsAdapter takes maxPayload, the largest message a client may send (1 MB by default, was 100 MB; a gateway’s own maxPayload takes precedence, 0 lifts the limit); a larger message closes the connection with code 1009"

이 한 문장에 규칙 네 가지가 들어 있다. 표로 나누면 다음과 같다.

항목 v12.5.0 기준 동작
maxPayload의 의미 클라이언트가 보낼 수 있는 가장 큰 메시지 크기
기본값 1 MB (v12.4.x 이하: 100 MB)
게이트웨이 자체 maxPayload 어댑터 설정보다 우선
0 지정 한도 해제
한도 초과 시 연결을 code 1009로 종료

설정을 바꾸지 않고 업그레이드한 애플리케이션은 기본값 1 MB를 그대로 적용받는다. 이전 기본값에 기대어 큰 메시지를 보내던 클라이언트는 v12.5.0에서 연결이 끊긴다. 메시지 단위로 거부되는 것이 아니라 연결 자체가 닫힌다는 점이 이 변경의 영향 범위를 정한다.

릴리스 노트는 이 변경을 ‘Bug fixes > platform-ws’ 항목에도 fix(ws): limit the size of the messages a client may send(109e52cd9b)로 올렸다. 새 기능 추가가 아니라 수정으로 분류됐다는 의미다.

maxPayload 동작 원리: ws 라이브러리의 한도

ws-message-size-gate

NestJS 어댑터 공식 문서에 따르면 WsAdapter는 @nestjs/platform-ws에서 import하며 ws 라이브러리를 사용한다. 그래서 maxPayload의 단위와 경계값은 ws 쪽 정의를 함께 봐야 정확히 읽힌다.

ws 라이브러리 API 문서는 WebSocketServer와 WebSocket의 maxPayload 옵션을 이렇게 설명한다.

"The maximum allowed message size in bytes. The value is coerced to a 32-bit signed integer. Defaults to 104857600 (100 MiB). Set to 0 to disable the limit."

정리하면 ws의 maxPayload는 바이트 단위 값이다. 입력값은 32비트 부호 있는 정수로 강제 변환된다. ws 자체 기본값은 104857600(100 MiB)이고, 0이 한도 해제를 뜻한다. NestJS 릴리스 노트의 "0 lifts the limit"과 같은 규칙이다.

한도를 넘는 메시지와 연결된 오류 코드는 WS_ERR_UNSUPPORTED_MESSAGE_LENGTH다. ws 문서의 설명 원문은 "A message was received with a length longer than the maximum supported length, as configured by the maxPayload option."이다. 서버 로그나 클라이언트 쪽 오류 처리에서 이 코드와 close code 1009를 함께 확인하면 원인을 좁히기 쉽다.

flowchart LR
  A[클라이언트 메시지 수신] --> B{메시지 크기 > maxPayload?}
  B -->|아니오| C[게이트웨이 핸들러로 전달]
  B -->|예| D[연결 종료, code 1009]

위 흐름에서 판정 기준이 되는 값이 v12.5.0부터 1 MB로 내려갔다. maxPayload를 0으로 두면 판정 단계 자체가 없어진다.

설정 우선순위와 0·NaN 처리

maxPayload가 정해지는 경로는 둘이다. 어댑터 수준 값과 게이트웨이 자체 값이 있고, 둘 다 있으면 게이트웨이 쪽이 이긴다. 어느 쪽에도 값이 없을 때 기본값 1 MB가 쓰인다.

WsAdapter 옵션 객체

어댑터 공식 문서는 WsAdapter 생성자의 두 번째 인자로 messageParser, unclaimedUpgradeTimeout 같은 옵션을 담은 객체를 받는다고 설명한다. 문서의 예제는 다음과 같다.

const wsAdapter = new WsAdapter(app, {
  messageParser: (data) => {
    const [event, payload] = JSON.parse(data.toString());
    return { event, data: payload };
  },
});

릴리스 노트는 "WsAdapter takes maxPayload"라고만 적었다. 2026-11-13 조회 기준으로 어댑터 문서에는 maxPayload 언급이 없으므로, 이 옵션 객체에 maxPayload를 넣는 형태는 공식 예제로 확인된 사항이 아니다. 실제 설정 위치는 설치한 @nestjs/platform-ws 패키지의 타입 정의로 확인하는 편이 안전하다.

게이트웨이 자체 maxPayload

NestJS 게이트웨이 공식 문서는 @WebSocketGateway() 데코레이터의 두 번째 인자를 다음과 같이 설명한다.

"The second argument of the @WebSocketGateway() decorator passes any supported server option to the socket server constructor. If you don’t need a custom port, pass the options object as the only argument."

문서의 데코레이터 예제를 클래스에 붙이면 아래 형태다.

@WebSocketGateway(81, { transports: ['websocket'] })
export class EventsGateway {}

두 번째 인자가 소켓 서버 생성자로 전달된다는 설명과, ws의 WebSocketServer가 maxPayload 옵션을 받는다는 문서를 함께 보면 게이트웨이 단위 지정 경로를 짐작할 수 있다. 다만 gateways 문서에도 maxPayload 언급은 없다. 릴리스 노트의 "a gateway’s own maxPayload"가 정확히 이 옵션 객체를 가리키는지는 공식 예제로 확인되지 않았다.

NaN이 들어왔을 때

같은 릴리스에 fix(ws,socket.io): use the default for adapter limits given as nan(f956177a0e)이 포함됐다. v12.5.0부터 WsAdapter와 IoAdapter의 한도 옵션에 NaN이 주어지면 기본값이 적용된다.

한도 값을 Number(process.env.WS_MAX_PAYLOAD)처럼 환경변수에서 변환하는 코드라면 이 수정의 영향을 받는다. 환경변수가 비어 있으면 결과가 NaN이고, 이 경우 한도는 1 MB 기본값으로 동작한다. 의도적으로 한도를 풀려면 NaN이 아니라 0을 명시해야 한다.

한도 해제 값 사용 주의
v12.5.0의 한도 도입은 ‘fix(ws): limit the size of the messages a client may send’라는 수정으로 들어갔다. `maxPayload`를 `0`으로 두면 클라이언트 메시지 크기 제한이 다시 사라진다. 큰 메시지가 필요한 경우에도 해제보다 실제 필요한 크기를 바이트 단위로 지정하고, 그 값이 필요한 게이트웨이에만 적용하는 쪽이 이 수정의 취지에 맞다.
## 같은 릴리스의 게이트웨이 동작 변경

v12.5.0 릴리스 노트의 ‘New defaults and behaviour changes’ 항목에는 maxPayload 외에도 게이트웨이 동작을 바꾸는 항목이 두 개 있다. 둘 다 하위 호환을 깨는 변경이다.

게이트웨이 접속 시점 변경

첫째는 접속 시점이다. 원문은 "Gateways on their own port drop clients that connect before onApplicationBootstrap() has finished; socket.io clients reconnect"(7d705cc835)다. 자체 포트로 뜨는 게이트웨이는 onApplicationBootstrap()이 끝나기 전에 접속한 클라이언트를 끊는다. socket.io 클라이언트는 재접속한다고 명시돼 있고, ws 기반 클라이언트에 대한 별도 언급은 없다.

standalone 마이크로서비스 포트 변경

둘째는 standalone 마이크로서비스다. 원문은 "In a standalone microservice, a gateway without a port no longer opens a server on a random port; it gets a server that does not listen"(7359d3b06a)이다. v12.4.x 이하에서 포트 없는 게이트웨이가 임의 포트로 서버를 열던 구성은 v12.5.0에서 listen하지 않는 서버를 받는다.

WsAdapter를 쓰면서 자체 포트를 지정한 게이트웨이(위 예제의 81 같은 경우)는 maxPayload 기본값 변경과 접속 시점 변경을 동시에 받는다. 업그레이드 직후 연결 끊김이 보이면 두 원인을 구분해서 봐야 한다. 메시지 전송 중 1009로 닫히면 크기 한도, 부트스트랩 직후 접속이 끊기면 접속 시점 변경 쪽을 먼저 의심할 만하다.

WsAdapter maxPayload 외에 함께 생긴 한도

maxPayload는 NestJS 12.x에서 이어진 전송 계층 한도 정비의 한 부분이다. NestJS v12.2.0 릴리스 노트에는 다음 문장이 있다.

"IoAdapter and WsAdapter take maxConcurrentMessages, the number of a client’s messages handled at once (1000 by default; Infinity lifts it)"

같은 v12.2.0에 fix(ws): stop reading from a client that does not read its replies, fix(ws): drop messages from a client the server is closing, fix(websockets): bound how many messages of a client are handled at once 수정도 들어갔다. v12.2.0의 maxConcurrentMessages가 동시 처리 개수를 묶었다면, v12.5.0의 maxPayload는 메시지 한 건의 크기를 묶는다.

v12.5.0에서 다른 전송 계층에 함께 도입되거나 줄어든 기본 한도는 아래와 같다.

대상 옵션 기본값 해제 값
WsAdapter maxPayload 1 MB (이전 100 MB) 0
WsAdapter, IoAdapter (v12.2.0) maxConcurrentMessages 1000 Infinity
Socket.IO 어댑터 maxWaitingMessages 10,000 Infinity
TCP maxConcurrentMessages 연결당 1000 0
TCP maxBufferSize 16 Mi characters (이전 128 Mi) —
RabbitMQ maxMessageSize 16 MiB —
해제 값 옵션별 차이 주의
해제 값이 옵션마다 다르다는 점에 주의해야 한다. `maxPayload`와 TCP `maxConcurrentMessages`는 `0`, 어댑터의 `maxConcurrentMessages`와 `maxWaitingMessages`는 `Infinity`가 한도를 푼다. TCP `maxBufferSize`와 RabbitMQ `maxMessageSize`의 해제 방법은 릴리스 노트 발췌에 나오지 않아 칸을 비웠다.
Socket.IO의 `maxWaitingMessages`는 커밋 c98b5f782e(`fix(socket.io): cap how many messages of a client wait for their turn`)로 추가됐다. 차례를 기다리는 클라이언트 메시지 수를 제한하는 옵션이다.

업그레이드 전 점검 항목

v12.5.0으로 올리기 전에 확인할 항목을 순서대로 정리하면 다음과 같다.

  1. 1 MB를 넘는 메시지가 있는가. 파일 조각, 대용량 JSON, base64 인코딩 데이터를 WebSocket으로 보내는 클라이언트가 대상이다. 해당되면 v12.5.0에서 그 연결은 1009로 닫힌다.
  2. 클라이언트가 1009 종료를 다루는가. 연결 종료 코드를 기록하거나 재시도 정책을 두지 않았다면, 한도 초과가 원인 불명의 끊김으로 보인다.
  3. 한도를 어디에 둘 것인가. 어댑터 수준 값은 전체 게이트웨이에, 게이트웨이 자체 값은 그 게이트웨이에만 적용된다. 큰 메시지가 필요한 게이트웨이가 하나뿐이라면 그 게이트웨이에만 값을 주는 구성이 범위를 가장 좁힌다.
  4. 한도 값이 NaN이 될 수 있는가. 환경변수 변환 결과가 NaN이면 v12.5.0부터 기본값 1 MB가 적용된다. 배포 환경마다 변수가 실제로 설정됐는지 확인해야 한다.
  5. 자체 포트 게이트웨이의 접속 시점. onApplicationBootstrap() 완료 전 접속은 끊긴다. 헬스체크나 클라이언트가 서버 기동 직후 바로 붙는 구조라면 재접속 처리를 점검한다.
  6. standalone 마이크로서비스의 포트 없는 게이트웨이. 임의 포트에 의존하던 구성은 더 이상 listen하지 않는 서버를 받는다.
maxPayload 단위 설정 팁
크기 단위도 함께 맞춰야 한다. `ws`의 `maxPayload`는 바이트 단위이므로 값을 직접 지정할 때는 바이트로 계산한 정수를 넣는다. 32비트 부호 있는 정수로 강제 변환된다는 ws 문서의 조건도 상한을 정할 때 고려 대상이다.
## 확인되지 않은 부분과 적용 한계

이 글의 근거는 v12.5.0·v12.2.0 릴리스 노트, NestJS 어댑터·게이트웨이 문서, ws API 문서다(2026-11-13 조회). 이 범위로 확인되지 않은 지점은 다음과 같다.

1 MB 바이트 환산 불명확
릴리스 노트는 새 기본값을 ‘1 MB’로만 적었다. 1,000,000바이트인지 1,048,576바이트인지는 원문에 없고, 커밋 109e52cd9b의 소스는 이번 조사에서 읽지 않았다. 경계값 근처의 메시지를 다룬다면 기본값에 기대지 말고 바이트 값을 명시하는 쪽이 정확하다.
어댑터 문서와 게이트웨이 문서에는 조회 시점 기준으로 `maxPayload` 설명이나 설정 예제가 없다. 게이트웨이 자체 값을 지정하는 구체적 위치는 “두 번째 인자 옵션이 서버 생성자로 전달된다”는 일반 설명에서 추론한 것이며, 공식 예제로 검증된 내용이 아니다.

close code 1009의 표준 정의는 이번 근거 자료로 확인하지 않았다. 이 글에서는 릴리스 노트가 밝힌 "한도를 넘으면 1009로 닫힌다"는 동작만 다룬다. 또한 이 글은 v12.5.0을 기준으로 하며, 다른 릴리스 브랜치의 적용 여부는 범위에 넣지 않았다.

WsAdapter 한도 설정 이후 살펴볼 주제

maxPayload를 정했다면 같은 계열의 한도인 WsAdapter maxConcurrentMessages의 기본값 1000이 서비스 트래픽에 맞는지가 다음 검토 대상이다. Socket.IO를 쓰는 게이트웨이라면 NestJS IoAdapter maxWaitingMessages의 기본값 10,000과 해제 규칙도 같은 기준으로 점검할 만하다. 클라이언트 쪽에서는 WebSocket close code 1009 수신 시 재시도할지, 메시지를 나눠 보낼지를 정하는 종료 처리 정책이 이어지는 과제다. 전체 변경 목록은 NestJS 12.5.0 릴리스 노트와 NestJS 12 WebSocket 변경사항을 함께 대조하면 누락 없이 확인된다.

관련 글

이 글 공유하기