CORS 에러 vs 네트워크 오류, 브라우저는 왜 구분해주지 않을까
브라우저에서 API를 호출하다 "요청 실패"만 뜨고 정작 왜 실패했는지는 알 수 없었던 경험이 있을 겁니다. 서버 헤더 설정이 빠져서인지, 서버가 아예 죽어서인지 코드만 봐서는 구분이 안 됩니다. 이건 개발자 실수가 아니라 브라우저의 fetch() API가 의도적으로 정보를 숨기도록 설계됐기 때문입니다. 이 가이드는 왜 그렇게 설계됐는지, 그리고 실제로 원인을 구분하려면 무엇을 봐야 하는지 정리합니다.
1. TypeError 하나로 뭉뚱그려지는 두 가지 실패
fetch()로 요청을 보냈을 때 응답을 아예 받지 못하고 실패하는 경우는 크게 두 갈래입니다. 하나는 CORS 정책 위반으로 브라우저가 응답을 자바스크립트에 넘기지 않고 차단한 경우, 다른 하나는 서버가 다운됐거나 DNS 조회가 실패하는 등 연결 자체가 성립하지 않는 순수 네트워크 오류입니다. 원인은 완전히 다르지만 fetch()는 두 경우 모두 TypeError: Failed to fetch라는 동일한 예외로만 던집니다. catch 블록에서 error.message를 아무리 들여다봐도 어느 쪽인지 알려주는 단서는 없습니다.
2. 왜 브라우저는 일부러 이유를 숨기는가
이건 버그가 아니라 보안 설계입니다. 만약 fetch가 "CORS 헤더가 없어서 차단됨"과 "서버가 응답하지 않음"을 자바스크립트에 구분해서 알려준다면, 악의적인 스크립트가 이 정보를 이용해 사용자가 로그인한 내부 네트워크의 서버가 실제로 존재하는지(응답은 하는데 CORS로 막혔는지) 아니면 아예 존재하지 않는지(네트워크 오류인지)를 추론할 수 있게 됩니다. 이런 정보 유출을 막기 위해 스펙 자체가 두 실패를 동일한 불투명한 오류로 처리하도록 정해져 있습니다. 즉 개발자에게는 불편하지만, 공격자에게 정보를 안 주기 위한 의도적인 트레이드오프입니다.
3. 코드만으로는 답이 없다: 개발자도구에서 확인하는 법
fetch()가 던지는 예외 객체 자체에는 원인 구분 정보가 없으므로, 실제 원인을 알려면 브라우저 개발자도구를 직접 열어야 합니다. Network 탭에서 해당 요청을 보면 요청이 서버까지 도달했는지(응답 상태 코드가 찍히는지), 아니면 아예 대기 상태에서 실패했는지 구분됩니다. Console 탭에는 CORS 위반 시 브라우저가 별도의 경고 로그(예: "has been blocked by CORS policy")를 남기는데, 이 로그는 자바스크립트 코드로는 읽을 수 없고 사람이 눈으로 봐야만 확인할 수 있는 정보입니다.
| 확인 위치 | CORS 차단일 때 | 순수 네트워크 오류일 때 |
|---|---|---|
| Network 탭 상태 | 요청이 서버에 도달, 응답도 옴(상태 코드 보임) | 요청 자체가 실패(pending/failed로 표시) |
| Console 로그 | "blocked by CORS policy" 경고 표시 | CORS 관련 로그 없음 |
| JS catch(e)에서 본 정보 | TypeError: Failed to fetch | TypeError: Failed to fetch (동일) |
4. 모두의 툴 API 테스터는 이 문제를 어떻게 안내하는가
실제 API 테스터의 요청 처리 코드를 보면 다음과 같이 구현돼 있습니다.
catch(e){ if(e.name==='TypeError'){ /* CORS 차단 또는 네트워크 오류 안내 메시지 표시 */ } }즉 이 도구도
e.name만으로는 CORS인지 네트워크 오류인지 구분할 수 없다는 브라우저의 한계를 그대로 인정하고, "CORS 정책으로 차단되었거나, URL 오타·서버 다운·네트워크 연결 문제일 수 있습니다"라는 식으로 두 가능성을 함께 안내하는 메시지를 보여줍니다. 원인을 하나로 단정하지 않는 것 자체가 정확한 설계입니다.
5. 실전에서 원인을 좁히는 체크리스트
- 다른 도구로 같은 URL을 호출해본다:
curl이나 Postman처럼 브라우저의 CORS 정책이 적용되지 않는 환경에서 같은 요청이 성공한다면, 문제는 CORS일 가능성이 매우 높습니다. 거기서도 실패한다면 서버 자체 문제입니다. - Console에서 CORS 경고 문구를 찾는다: 브라우저가 남기는 경고 로그에 "Access-Control-Allow-Origin"이라는 단어가 보이면 CORS 문제로 확정할 수 있습니다.
- Network 탭에서 상태 코드 유무를 확인한다: 상태 코드(200, 404, 500 등)가 찍혔다면 서버까지는 도달한 것이므로 네트워크 문제가 아니라 CORS 헤더 부재입니다.
자주 묻는 질문
Q. TypeError 외에 다른 예외 이름이 뜨는 경우도 있나요?
네. URL 형식 자체가 잘못됐다면 fetch 호출 시점에 다른 오류가 발생할 수 있고, 요청이 서버에 도달해 응답 코드(404, 500 등)를 받은 경우는 예외가 아니라 정상적인 응답으로 처리됩니다. CORS·네트워크 오류가 TypeError로 뭉뚱그려지는 것은 요청 자체가 실패했을 때만 해당합니다.
Q. CORS 문제는 클라이언트에서 고칠 수 없나요?
고칠 수 없습니다. CORS 허용 여부는 응답 헤더(Access-Control-Allow-Origin)를 서버가 설정해야만 해결됩니다. 클라이언트 쪽에서 우회하려면 자체 서버를 프록시로 두고 서버 대 서버로 호출하는 방법뿐입니다.
Q. HTTPS 페이지에서 HTTP API를 호출해도 같은 오류가 뜨나요?
네. Mixed Content 정책 위반도 브라우저가 요청 자체를 차단하며, 이 역시 자바스크립트 입장에서는 CORS·네트워크 오류와 동일한 TypeError로 보입니다. Console 탭에 "Mixed Content" 경고가 뜨는지로 구분할 수 있습니다.
Q. 모두의 툴 API 테스터로 CORS 여부를 확정할 수 있나요?
아니요. 이 도구도 브라우저 fetch를 그대로 사용하므로 동일한 한계를 가집니다. 두 가능성을 함께 안내할 뿐, 최종 확정은 Network·Console 탭을 직접 확인하거나 curl 등 다른 환경에서 재현해봐야 합니다.