기본 콘텐츠로 건너뛰기

Cursor ECONNRESET 10분 해결법

대표 썸네일

새벽 두 시, Cursor 터미널에 claude 명령어를 치고 리팩토링을 맡겼는데 갑자기 화면이 멈췄다. 몇 초 뒤 콘솔에 뜨는 메시지는 API Error: Unable to connect to API (ECONNRESET). 재시도 카운터가 1부터 11까지 올라가더니 결국 세션이 끊긴다. 코드는 절반만 작성된 채로.

이 에러, 검색해보면 GitHub Issues와 Reddit, Cursor 포럼에 비슷한 하소연이 줄줄이 달려 있습니다. 인증 문제도 아니고 쿼터 초과도 아닌데 왜 갑자기 연결이 끊기는 걸까요. 결론부터 말하면 이건 API 문제가 아니라 소켓(Socket) 문제입니다. 원인을 하나씩 짚고, 실제로 터미널에 쳐서 바로 적용할 수 있는 해결책을 순서대로 정리했습니다.

ECONNRESET, 대체 왜 뜨는 걸까

ECONNRESET은 "상대방이 연결을 강제로 끊었다"는 뜻의 TCP 소켓 에러입니다. 택배로 비유하면 이해가 빠릅니다. 도로(네트워크)는 멀쩡히 뚫려 있는데, 중간 검수소(로컬 프록시나 DNS 서버)가 주소를 잘못 확인해서 상자를 트럭째로 강제 회수해버리는 상황입니다. 배송 기사(API 서버)는 잘못한 게 없는데 중간 관문에서 문제가 터지는 것입니다.

이게 왜 하필 Cursor 내장 터미널에서 자주 발생하냐면, Cursor가 Electron 기반 앱이라 자체적으로 네트워크 트래픽을 가로채는 레이어를 갖고 있기 때문입니다. 여기에 Claude Code CLI는 Bun/Node.js 런타임으로 동작하면서 HTTP/2 스트리밍으로 긴 응답을 계속 받아오는데, 이 스트리밍이 길어질수록 중간 어딘가(로컬 프록시, TCP Keepalive 타임아웃, DNS 조회 지연)에서 소켓이 끊길 확률이 높아집니다.

여기서 헷갈리기 쉬운 부분이 있습니다. 401이나 429 에러와 ECONNRESET은 원인이 완전히 다릅니다.

에러 코드 실제 원인 체감 증상 해결 방향
401 Unauthorized API 키 오류/만료 요청 즉시 거부됨 API 키 재발급
429 Too Many Requests 요청 한도 초과 "rate limit" 문구와 함께 명확히 거부 재시도 대기 or 플랜 업그레이드
ECONNRESET 로컬 프록시/DNS/소켓 타임아웃 잘 되다가 갑자기 뚝 끊김, 재시도만 반복 네트워크 계층 설정 수정

401, 429는 Anthropic 서버가 "네 요청을 봤고, 거절한다"고 명확히 답하는 것입니다. 반면 ECONNRESET은 서버가 응답조차 하기 전에 중간에서 연결 자체가 끊어지는 상황이라, 아무리 API 키를 재발급해도 소용없습니다. 이 차이를 모르고 키만 계속 바꾸다가 시간을 날리는 경우를 실제로 많이 봤습니다.

전체 흐름을 그림으로 보면 이렇습니다.

[Cursor 터미널: claude 명령 실행]
       │
       ├───► (1) DNS 조회 ──► [IPv6 응답 지연] ──► [TCP Handshake 타임아웃] ──┐
       │                                                                       │
       ├───► (2) 로컬 프록시 잔재? ──► [127.0.0.1 루프백 우회 후 차단] ────────┼──► ⚠️ ECONNRESET 발생
       │                                                                       │
       └───► (3) 정상 연결 (IPv4 우선 / 프록시 없음) ──► Anthropic API 정상 응답 ┘
    

원인이 세 갈래(DNS, 프록시 잔재, 소켓 타임아웃)로 나뉘니, 아래 3단계를 순서대로 밟으면서 하나씩 소거해나가는 게 가장 빠릅니다.

1단계: 터미널 네트워크 상태부터 확인하자

가장 먼저 해야 할 일은 "진짜로 네트워크가 문제인지" 확인하는 것입니다. Claude Code를 다시 실행하기 전에, Cursor 터미널에 아래 명령어부터 쳐보세요.

# Anthropic API 엔드포인트에 TLS 핸드셰이크가 정상적으로 이루어지는지 확인
# 정상이라면 401 또는 405 응답 코드가 뜬다 (이건 정상! 키 인증 전 단계라 거절되는 게 맞음)
# 만약 여기서부터 응답이 없거나 timeout이 뜬다면 네트워크 계층 문제가 확실하다
curl -I --max-time 10 https://api.anthropic.com

# 응답 예시 (정상):
HTTP/2 401
content-type: application/json

여기서 응답이 아예 없거나 curl: (28) Operation timed out이 뜬다면, Cursor가 아직도 이전에 설정했던 로컬 프록시를 물고 있을 가능성이 크습니다. Cursor 포럼에 보고된 사례 중 상당수가 이전에 VPN이나 사내 프록시(127.0.0.1:10809 같은 포트)를 설정했다가 해제했는데도, Electron 캐시에 그 설정이 남아있는 경우였습니다.

이럴 땐 프록시를 강제로 무시하고 Cursor를 재실행해보세요.

# macOS/Linux: 프록시 없이 Cursor 실행
/Applications/Cursor.app/Contents/MacOS/Cursor --no-proxy-server

# Windows (PowerShell): 프록시 우회 실행
& "C:\Users\$env:USERNAME\AppData\Local\Programs\cursor\Cursor.exe" --no-proxy-server

이 명령으로 Cursor를 켠 다음 다시 터미널에서 claude 명령을 실행해보면, 프록시 잔재가 원인이었던 경우 바로 해결되는 걸 확인할 수 있습니다.

2단계: Node/Bun의 DNS 우선순위를 IPv4로 강제하기

curl 테스트는 정상인데도 여전히 claude 명령에서만 ECONNRESET이 뜬다면, 이번엔 DNS 조회 순서를 의심해볼 차례입니다. 요즘 대부분의 노트북과 공유기는 IPv4와 IPv6을 동시에 지원하는 "듀얼 스택" 환경인데, 일부 네트워크 구간에서 IPv6 응답이 유독 느리면 Node/Bun 런타임이 IPv6로 먼저 접속을 시도하다가 타임아웃 후 소켓을 강제 종료해버립니다.

전화 통화에 비유하면, 상대방 번호가 두 개(집 전화, 휴대폰)인데 하필 안 받는 번호로 먼저 걸었다가 연결이 안 되니 그냥 끊어버리는 셈입니다. 아예 잘 받는 번호(IPv4)로만 걸도록 강제하면 이 문제는 대부분 사라집니다.

셸 설정 파일에 아래 한 줄을 추가해보세요. zsh를 쓴다면 ~/.zshrc, bash라면 ~/.bashrc에 넣으면 됩니다.

# ~/.zshrc 또는 ~/.bashrc 맨 아래에 추가

# Node.js DNS 조회 시 IPv6보다 IPv4 결과를 우선적으로 사용하도록 강제
# dual-stack 환경에서 IPv6 응답 지연으로 인한 TCP handshake 타임아웃을 예방
export NODE_OPTIONS="--dns-result-order=ipv4first"

# 설정 반영 (Cursor 터미널을 새로 열거나 아래 명령 실행)
source ~/.zshrc

# 잘 적용됐는지 확인
echo $NODE_OPTIONS
# 출력: --dns-result-order=ipv4first
💡 주의사항: Cursor 내장 터미널은 새 세션마다 셸 설정 파일을 다시 읽기 때문에 반드시 Cursor를 완전히 재시작해야 환경변수가 실제로 적용됩니다. 단순히 새 터미널 탭만 여는 걸로는 부족할 때가 많았습니다.

3단계: HTTP/1.1 호환 모드와 MCP 세션 정리

1, 2단계로도 해결되지 않는다면 마지막으로 확인할 부분은 HTTP/2 스트리밍 자체의 호환성 문제입니다. 회사 방화벽이나 일부 보안 소프트웨어는 HTTP/2의 멀티플렉싱 스트림을 제대로 처리하지 못해서, 오래 유지되는 스트리밍 연결을 의심스러운 트래픽으로 판단하고 중간에 끊어버리기도 합니다.

Cursor 설정(SettingsNetwork)에서 HTTP 호환 모드를 1.1로 낮춰보세요.

// Cursor settings.json에 추가 (Cmd/Ctrl + Shift + P → "Preferences: Open Settings (JSON)")
{
  "cursor.network.httpCompatibilityMode": "http1.1",
  "cursor.terminal.keepAliveTimeout": 60000
}

또한 Claude Code가 MCP(Model Context Protocol, 여러 도구와 컨텍스트를 주고받는 표준 규격이라고 생각하면 됩니다) 세션을 여러 개 물고 있는 상태에서 터미널을 새로 열면, 이전 세션이 좀비 프로세스로 남아 포트나 소켓 자원을 붙잡고 있는 경우가 있습니다. 아래 명령으로 정리하고 시작하면 훨씬 안정적입니다.

# 남아있는 claude 관련 프로세스를 모두 찾아서 종료
# (혹시 다른 중요 프로세스가 없는지 이름을 꼭 확인하고 실행할 것)
ps aux | grep claude

# PID를 확인한 뒤 강제 종료
kill -9 <PID>

# 이후 새 터미널 세션에서 다시 실행
claude

여기까지 진행했는데도 증상이 반복된다면, 사내망이나 VPN 환경 자체가 스트리밍 연결을 차단하는 정책을 갖고 있을 가능성이 높습니다. 이 경우엔 네트워크 관리자에게 api.anthropic.com에 대한 아웃바운드 스트리밍 허용을 요청하는 것이 정공법입니다.

💡 요약 및 체크리스트

순서는 간단합니다. curl로 네트워크 자체를 먼저 검증하고, 프록시 잔재를 --no-proxy-server로 걷어낸 뒤, NODE_OPTIONS로 IPv4를 강제하고, 마지막으로 HTTP 호환 모드와 좀비 프로세스를 정리하세요. 대부분의 ECONNRESET은 1~2단계 선에서 해결됩니다.

Cursor와 Claude Code 조합 외에 다른 AI 코딩 도구의 설정법이나 자동화 팁이 궁금하다면 LLM & 코드 가이드 카테고리에서 관련 글들을 모아뒀으니 참고하면 좋습니다.

이 방법으로 에러를 해결했다면, 어떤 단계에서 풀렸는지 댓글로 알려주세요. 프록시 문제였는지, DNS 문제였는지 공유해주시면 비슷한 상황에 있는 다른 개발자들에게도 큰 도움이 됩니다.

DEV
기술 에디터

LLM API 최적화 및 개발 환경 트러블슈팅 전문가. 실무에서 직접 검증한 해결책을 전달합니다.

댓글

이 블로그의 인기 게시물

Gemini Many-Shot Prompting: Why 500 Examples Beat Fine-Tuning

No More Git Conflicts: Automate PR Reviews with Cline

기밀 유출 없는 DeepSeek R1 무료 로컬 실행법