서버가 보내온 구조 신호, 502·504·500
서버 오류 코드는 무엇이 고장났는지가 아니라 어디까지 갔다가 실패했는지를 알려줍니다. 502에서 앱 로그를 뒤지면 아무것도 못 찾는 이유와, 확인할 곳을 절반으로 줄이는 명령 한 줄.

목차
502는 프록시가 상위 서버에서 유효한 응답을 받지 못했다는 뜻입니다.
서버 안에는 두 사람이 있습니다
요청이 서버에 도착하면 보통 두 단계를 거칩니다. 문 앞의 안내데스크(Nginx·Caddy 같은 웹서버)가 먼저 받고, 실제 일은 안쪽 담당자(여러분이 만든 앱)가 합니다.
이 구조를 알면 세 숫자가 완전히 다른 말을 하고 있다는 게 보입니다.
- 502 — 안내데스크가 담당자에게서 유효한 답을 받지 못했습니다
- 504 — 안내데스크가 정해진 시간 안에 답을 받지 못했습니다
- 500 — 답을 만든 서버가 처리 중 예상하지 못한 문제를 만났습니다
코드 하나만으로 원인을 확정할 수는 없습니다. 대신 어느 계층이 오류 응답을 만들었는지부터 좁히는 출발점으로 쓸 수 있습니다.
그래서 열어야 할 로그가 달라집니다
여기서 실전 차이가 생깁니다.
| 코드 | 가능한 경계 | 먼저 확인할 것 |
|---|---|---|
| 502 | 앱에 연결 실패 또는 앱의 불완전·잘못된 응답 | 프록시의 upstream 오류 → 앱 기동·종료 로그 |
| 504 | 앱·DB·외부 API 중 어딘가의 지연 또는 연결 시간 초과 | 프록시의 제한시간 → 앱·백엔드 처리 시간 |
| 500 | 500을 실제로 만든 서버 내부 | 응답을 만든 계층의 오류 로그 |
502에서 앱 로그가 비어 있을 수는 있습니다. 프록시가 앱에 연결조차 못 했다면 앱은 요청을 보지 못했기 때문입니다. 하지만 앱이 연결을 받은 뒤 헤더를 보내기 전에 종료됐거나 잘못된 응답을 보낸 경우에도 502가 생깁니다. 그래서 프록시 로그의 upstream 오류 문구를 먼저 읽고, 그 문구에 따라 앱 로그를 이어서 보는 순서가 안전합니다.
500도 스택 트레이스가 반드시 남는다고 단정할 수 없습니다. 오류 처리기가 내용을 숨겼거나 로그가 다른 출력으로 향할 수 있으므로, 응답을 만든 계층과 요청 식별자를 먼저 확인합니다.
확인할 곳을 절반으로 줄이는 명령 한 줄
코드를 구분했다면 다음은 범위를 좁히는 일입니다. 가장 효율이 좋은 건 프록시를 건너뛰고 앱에 직접 물어보는 것입니다.
curl -i --max-time 10 http://127.0.0.1:3000/
서버에 접속해서 이 한 줄을 칩니다. 127.0.0.1은 "이 컴퓨터 자신"이라는 뜻이라,
프록시를 거치지 않고 앱에게 곧장 갑니다. 결과는 세 가지 중 하나입니다.
- HTTP 응답이 돌아온다 → 적어도 그 주소·그 경로에는 앱이 응답합니다. 프록시의 대상 주소, Host/TLS 설정과 실제 실패한 경로를 비교합니다.
- Connection refused → 연결이 능동적으로 거절됐습니다. 흔한 원인은 수신 프로세스가 없거나 다른 주소·포트에 바인딩된 경우이며, 방화벽의 REJECT 규칙도 가능성에서 제외하지 않습니다.
- 아무 반응 없이 멈춘다 → 연결 또는 응답 어느 단계에서 기다리는지 더 나눠야 합니다. 앱의 느린 쿼리·외부 API뿐 아니라 경로의 패킷 드롭도 후보입니다.
한 번의 명령으로 후보가 3분의 1로 줄었습니다. 로그를 읽기 전에 이걸 먼저 하면 읽어야 할 로그의 양 자체가 달라집니다.
거부와 침묵은 다릅니다
앱끼리 통신할 때 나오는 두 오류도 같은 방식으로 갈립니다. 이름이 비슷해서 헷갈리지만 증상이 정반대입니다.
- ECONNREFUSED — 상대 운영체제나 중간 장비가 연결을 능동적으로 거절했습니다. 먼저 수신 프로세스와 바인딩 주소를 확인합니다.
- ETIMEDOUT — 제한시간까지 연결 또는 응답이 끝나지 않았습니다. 패킷 드롭, 잘못된 경로, 응답하지 않는 상대와 과부하를 함께 확인합니다.
기다림이 있었는지는 좋은 단서지만 원인을 확정하지는 않습니다. curl의
연결 시간과 첫 바이트 시간을 나눠 재면 네트워크 연결 단계인지, 연결 후 응답
단계인지 더 정확히 구분할 수 있습니다. 이 두 오류 코드의 의미는 Node.js 공식 문서의 시스템 오류
목록에 정의돼 있습니다.
시간도 단서입니다
504에서 특히 그렇습니다. 오류가 뜨기까지 걸린 시간이 거의 정확히 60초라면,
Nginx의 응답 대기 시간 기본값이 60초라는 점을 떠올려 볼 만합니다(공식 문서의
proxy_read_timeout
기본값).
앱이나 그 뒤의 백엔드가 아직 처리 중인데 프록시가 먼저 포기한 상황일 수 있습니다.
다만 이건 단서지 결론이 아닙니다. 실제 설정은 각자 다르고, 앱이나 클라우드 로드밸런서가 먼저 끊었을 수도 있습니다. 확인은 설정 파일에서 직접 해야 합니다.
그리고 여기서 흔한 함정이 하나 있습니다. 제한시간을 늘리면 504는 사라집니다. 하지만 느린 요청이 사라진 건 아닙니다. 사용자는 이제 오류 대신 3분을 기다립니다. 시간을 늘리는 건 응급처치이고, 왜 그렇게 오래 걸리는지 찾는 게 본치료입니다.
자주 틀리는 진단 세 가지
502에서 앱 로그를 무조건 건너뛰기. 프록시 로그가 connection refused라면
앱 기동과 포트를 보고, upstream prematurely closed나 잘못된 헤더를 가리키면
앱의 종료·응답 로그를 봐야 합니다. 같은 502라도 다음 확인 지점이 다릅니다.
"몰릴 때만 난다"를 무시하기. 이건 그냥 부가 정보가 아니라 강력한 근거입니다. 평소에는 통과하고 부하 때만 실패한다면 용량·대기열·느린 백엔드 가능성이 커집니다. 다만 특정 서버 한 대의 설정만 틀린 경우처럼 간헐적인 설정 문제도 있어 로그와 배포 구성을 함께 확인해야 합니다.
ECONNREFUSED에서 곧바로 방화벽을 열기. 먼저 해당 주소·포트에 수신
프로세스가 있는지 확인합니다. 방화벽이 DROP이 아니라 REJECT로 설정된 경우도
거절을 만들 수 있으므로, 프로세스와 바인딩이 정상일 때 규칙을 이어서 봅니다.
도커에서는 앱이 127.0.0.1에만 열려 다른 컨테이너가 못 닿는 경우가 흔합니다.
진단소에서 직접 좁혀 보기
지금까지의 판단 과정을 그대로 도구로 만들었습니다.
증상을 고르면 한 번에 한 가지씩 묻습니다. 답할 때마다 원인 후보가 줄어들고, 줄어드는 과정이 화면에 그대로 보입니다. 각 후보에는 확인할 명령 한 줄과 왜 그 후보를 그 순위로 봤는지가 함께 붙습니다.
502·503·504·500과 ECONNREFUSED·ETIMEDOUT·EADDRINUSE·ENOTFOUND를 다룹니다. 오류 메시지를 그대로 붙여넣어도 어떤 증상인지 찾아냅니다.
이 도구가 못 하는 것
진단소는 여러분의 서버에 접속하지 않습니다. 답한 내용만 가지고 확인할 순서를 정해주는 오프라인 추론 도구입니다. 그래서 원인을 확률로 계산하지 않고, "무엇을 먼저 확인할지"만 정합니다. 순위는 관측이 아니라 근거의 개수와 방향으로 매겨집니다.
실제 로그와 설정 파일을 대신하지도 않습니다. 진단소가 하는 일은 로그를 열기 전에 어느 로그를 열지 정해주는 것까지입니다. 그다음부터는 여러분의 서버가 답을 갖고 있습니다.

