처리중입니다. 잠시만 기다려주세요.
TTJ 코딩클래스
정규반 단과 자료실 테크 뉴스 코딩 퀴즈
테크 뉴스
Hacker News 2026.05.18 70

가끔 터지는 ECONNRESET, 그 짜증나는 에러의 진짜 정체

Hacker News 원문 보기

"분명 어제까진 잘 됐는데"의 범인

서버 개발하다 보면 한 번쯤 만나는 에러가 있어요. 로그에 갑자기 찍히는 ECONNRESET. 한국말로 하면 "커넥션이 강제로 끊겼다"는 뜻인데, 문제는 이게 재현이 잘 안 된다는 거예요. 하루에 한두 번, 혹은 일주일에 한 번씩 어쩌다 한 번 터지고, 그때마다 누군가는 슬랙에서 "이거 뭐죠?"라고 묻죠. movq.de 블로그에 올라온 글이 바로 이 "가끔 터지는 ECONNRESET"의 정체를 끈질기게 추적한 이야기예요. 읽다 보면 "아, 내가 겪었던 그 버그가 이거였구나" 싶을 거예요.

ECONNRESET이 뭐냐면, TCP의 "갑작스러운 작별 인사"

먼저 용어부터 풀어볼게요. TCP 연결을 정상적으로 끊을 때는 FIN 패킷을 주고받으면서 "이제 끝낼게요" 하고 인사를 나눠요. 양쪽이 서로 끝내겠다고 합의하는 4-way handshake 과정이죠. 그런데 가끔 한쪽이 RST(리셋) 패킷을 갑자기 보내요. 이건 "인사고 뭐고 없이 연결 강제 종료"라는 신호예요. 이걸 받은 쪽 애플리케이션이 보는 게 바로 ECONNRESET 에러예요.

이 RST가 날아오는 이유는 크게 몇 가지가 있어요. 상대가 이미 닫은 소켓에 우리가 데이터를 보냈을 때, 방화벽이나 NAT 장비가 오래된 연결을 끊었을 때, OS의 TCP 백로그가 가득 차서 새 연결을 못 받을 때, 혹은 상대 프로세스가 비정상 종료됐을 때 등이에요. 문제는 애플리케이션 로그만 보면 "왜" 그런 건지 알 수가 없다는 거예요. 그냥 "끊겼다"만 알 수 있어요.

블로그 글이 찾아낸 진짜 원인

원본 글의 핵심은 이거예요. 저자는 자기 서비스에서 ECONNRESET이 가끔 발생하는 걸 발견하고, tcpdump로 패킷을 직접 캡처해서 분석했어요. tcpdump가 뭐냐면, 네트워크 인터페이스를 지나가는 모든 패킷을 그대로 떠다가 보여주는 도구예요. 애플리케이션 위가 아니라 OS 아래쪽에서 진짜 무슨 일이 일어나는지 볼 수 있는 거죠.

캡처한 패킷을 따라가 보니, HTTP Keep-Alive로 재사용하던 연결을 서버 쪽에서 먼저 닫는 타이밍클라이언트가 그 연결로 새 요청을 보내는 타이밍이 절묘하게 겹치는 게 원인이었어요. 서버는 "이 연결 너무 오래 쉬었으니 닫자" 하고 FIN을 보냈는데, 그 FIN이 클라이언트에 도착하기 직전에 클라이언트가 "이 연결 살아있겠지" 하고 요청을 끼워 넣은 거예요. 그러면 서버는 "이미 닫은 연결로 데이터가 왔네?" 하면서 RST를 쏴버리고, 클라이언트는 ECONNRESET을 만나게 돼요.

이게 무서운 건, 누구의 잘못도 아니라는 점이에요. 서버는 정해진 정책대로 idle 연결을 정리한 거고, 클라이언트는 풀에서 살아 있다고 표시된 연결을 가져다 쓴 거예요. 그저 네트워크 왕복 지연 때문에 둘의 "세계관"이 잠깐 어긋났을 뿐이죠. 이걸 영어권에서는 race condition, 우리말로는 경쟁 조건이라고 불러요.

왜 이게 점점 자주 보일까

예전에는 ECONNRESET이 그렇게 큰 문제가 아니었어요. 요청-응답 한 번 하고 끊는 단순한 패턴이 많았거든요. 그런데 요즘 백엔드는 HTTP Keep-Alive, Connection Pool, gRPC long-lived stream, WebSocket 같이 "연결을 오래 유지하고 재사용"하는 방식이 표준이에요. 연결 하나 맺는 비용(TLS 핸드셰이크 포함)이 비싸니까 최대한 아껴 쓰는 거죠.

이런 패턴에서는 "이미 죽은 연결을 살아있다고 착각하는 순간"이 자주 생겨요. 특히 클라우드 환경에서는 그 사이에 로드밸런서(L4/L7), NAT 게이트웨이, 서비스 메시 사이드카 같은 중간 장비가 잔뜩 끼어 있고, 각자 자기만의 idle timeout을 가지고 있어요. AWS NLB는 기본 350초, ALB는 60초, 일부 NAT 게이트웨이는 5분처럼 다 달라요. 그래서 가장 짧은 timeout을 가진 장비가 먼저 연결을 끊으면 양쪽 끝단은 모르고 있다가 ECONNRESET을 만나게 되는 거예요.

비슷한 케이스와 해결 패턴

이런 종류의 문제는 언어와 프레임워크를 가리지 않고 나와요. Node.js의 http.Agent 기본 keep-alive, Python의 requests.Session, Java의 Apache HttpClient, Go의 http.Transport, 다 비슷한 이슈를 안고 있어요. 그래서 라이브러리들도 점점 대응을 추가해 왔어요.

첫 번째 패턴은 클라이언트 측 idle timeout을 서버보다 짧게 설정하는 거예요. 서버가 60초에 닫는다면 클라이언트는 50초쯤에 먼저 닫아서, RST가 날아올 여지를 없애는 거죠. 두 번째는 요청 실패 시 자동 재시도(idempotent한 경우에만)예요. Go의 http.Transport는 ECONNRESET이 나면 한 번은 자동으로 재시도해줘요. 세 번째는 HTTP/2나 HTTP/3로 옮기는 것인데, 이들은 연결 생명주기를 명시적으로 관리하는 GOAWAY 같은 메커니즘이 있어서 좀 더 우아하게 끊을 수 있어요.

한국 개발자에게 주는 시사점

실무에서 가장 와닿는 부분은 두 가지예요. 첫째, "가끔 터지는 ECONNRESET은 무시해도 된다"는 생각을 다시 한번 점검해 보세요. 대부분은 race condition이 맞지만, 만약 그 비율이 점점 늘고 있다면 인프라 어딘가의 timeout 설정이 어긋났다는 신호일 수 있어요. 모니터링에 ECONNRESET 발생률 그래프를 추가해두면 좋아요.

둘째, 장비 간 timeout 정렬을 문서화하세요. 우리 서비스에서 클라이언트 → CDN → ALB → 애플리케이션 → DB로 이어지는 경로가 있다면, 각 단계의 idle timeout이 어떻게 설정돼 있는지 한눈에 보이는 표를 만들어 두는 거예요. 신규 입사자가 와서 "왜 가끔 504가 떠요?" 물을 때 즉답할 수 있어요.

마무리

ECONNRESET은 "버그"라기보단 "분산 시스템의 자연 현상"에 가까워요. 완전히 없앨 수는 없고, 잘 다루는 게 답이에요. 핵심은 재시도 정책 + timeout 정렬 + 모니터링, 이 세 가지를 묶어서 설계하는 거예요.

질문 하나. 여러분 서비스에서 가장 짜증나는 "가끔 터지는" 에러는 뭔가요? 그리고 그게 진짜 버그였는지, 아니면 분산 시스템의 자연 현상이었는지 한번 돌이켜 보면 재밌을 것 같아요.


🔗 출처: Hacker News

이 뉴스가 유용했나요?

이 기술을 직접 배워보세요

파이썬으로 자동화를 시작해보세요

파이썬 기초부터 자동화까지 실전 강의.

파이썬 강의 보기

"비전공 직장인인데 반년 만에 수익 파이프라인을 여러 개 만들었습니다"

실제 수강생 후기
  • 비전공자도 6개월이면 첫 수익
  • 20년 경력 개발자 직강
  • 자동화 프로그램 + 소스코드 제공

매일 AI·개발 뉴스를 받아보세요

주요 테크 뉴스를 매일 아침 이메일로 전해드립니다.

스팸 없이, 언제든 구독 취소 가능합니다.