1. Libreria
  2. HTTP와 웹
  3. HTTP 상태 코드

Aggiornato 1 mese fa

링크를 클릭했습니다. 어딘가에 도착할 거라고 예상했습니다. 그런데 아무 곳에도 도착하지 못했습니다.

그게 바로 404입니다. 인터넷이 "찾아봤는데, 여기엔 아무것도 없어요"라고 말하는 방식입니다. 치명적인 의미의 오류는 아닙니다—문을 두드렸더니 집이 비어 있는 것과 비슷합니다. 건물은 존재합니다. 주소도 맞습니다. 하지만 만나러 온 사람이 집에 없습니다. 이사를 갔을 수도 있고, 번호를 잘못 알았을 수도 있습니다. 404는 알 수 없습니다.

서버가 실제로 말하는 것

서버가 404 Not Found를 반환할 때, 서버는 구체적인 사실을 주장하고 있습니다:

  • 요청을 이해했습니다
  • 요청하신 것을 찾아봤습니다
  • 이 주소에는 존재하지 않습니다
  • 존재했던 적이 있는지, 앞으로 존재할지는 모릅니다
GET /blog/that-post-you-bookmarked HTTP/1.1

HTTP/1.1 404 Not Found

마지막 항목이 중요합니다. 404는 본질적으로 불확실합니다. 서버는 "삭제되었다"거나 "존재한 적이 없다"고 말하는 게 아닙니다—단지 "지금 여기에 없다"고 말할 뿐입니다.

콘텐츠가 사라지는 이유

오타가 발생합니다. 누군가 /user/profile 대신 /usr/profile을 입력합니다. 글자 하나 차이로 완전히 다른 목적지가 됩니다.

콘텐츠가 이동합니다. 그 블로그 게시물은 3년 동안 존재했다가 새로운 URL 구조로 재편성되었습니다. 예전 링크는 여전히 돌아다닙니다.

삭제됩니다. 중단된 제품, 닫힌 계정, 삭제된 게시물. URL은 유령이 됩니다.

링크가 끊어집니다. 누군가 5년 전에 여러분의 사이트에 링크를 걸었습니다. 그 이후 두 번 재설계했습니다. 그들의 링크는 더 이상 존재하지 않는 구조를 가리킵니다.

ID가 존재하지 않습니다. GET /api/users/99999—하지만 99999번 사용자는 없습니다. 처음부터 없었습니다.

404 vs. 410: 불확실성 vs. 확실성

이 차이는 보기보다 더 중요합니다.

404 Not Found는 "이것이 어떻게 됐는지 모르겠다"는 뜻입니다:

GET /blog/some-post

HTTP/1.1 404 Not Found

검색 엔진은 이걸 보고 생각합니다: 다시 생길 수도 있겠다. 나중에 다시 확인해보자.

410 Gone은 "이것은 존재했지만 의도적으로 제거되었다"는 뜻입니다:

GET /blog/retracted-article

HTTP/1.1 410 Gone

검색 엔진은 이걸 보고 생각합니다: 색인에서 삭제하자. 다시 돌아오지 않을 테니.

알고 있을 때 410을 사용하세요. 모를 때는 404를 사용하세요.

404 vs. 403: 없음 vs. 금지됨

또 다른 중요한 차이입니다:

404는 리소스가 존재하지 않음을 의미합니다. 403 Forbidden은 존재하지만 접근할 수 없음을 의미합니다.

GET /api/users/123/private-data

HTTP/1.1 403 Forbidden

때로는 의도적으로 403 대신 404를 사용하기도 합니다—권한 없는 사용자에게 리소스의 존재 여부를 숨기기 위해서입니다. 공격자가 "존재하지 않음"과 "존재하지만 금지됨"의 차이를 알 수 없다면, 그것은 보안상의 이점입니다.

소프트 404 문제

이것은 실제 피해를 주는 흔한 실수입니다:

GET /deleted-page

HTTP/1.1 200 OK

<html>
  <body>죄송합니다, 이 페이지는 존재하지 않습니다!</body>
</html>

페이지는 "찾을 수 없음"이라고 말하지만 상태 코드는 "성공"이라고 말합니다. 이것은 검색 엔진을 혼란스럽게 합니다. 검색 엔진은 "페이지를 찾을 수 없음" 메시지를 실제 콘텐츠인 것처럼 색인화합니다. SEO가 나빠집니다. 사용자가 검색 결과에서 오류 페이지를 찾게 됩니다.

누락된 콘텐츠에 대해서는 항상 실제 404 상태 코드를 반환하세요. 본문은 친절하게 만들 수 있습니다. 상태 코드는 반드시 정직해야 합니다.

막다른 길을 안내로 바꾸기

404 페이지는 마찰의 순간입니다. 사용자는 무언가를 원했지만 얻지 못했습니다. 그 다음에 무엇을 하느냐에 따라 사용자가 실망하며 떠날지, 아니면 길을 찾을지가 결정됩니다.

최소한: 무슨 일이 일어났는지 알려주고 돌아갈 방법을 제공하세요.

<h1>페이지를 찾을 수 없습니다</h1>
<p>찾고 계신 페이지를 찾을 수 없습니다.</p>
<a href="/">홈페이지로 이동</a>

더 나은 방법: 선택지를 제공하세요.

<h1>해당 페이지가 존재하지 않습니다</h1>
<p>이 중 하나가 도움이 될 수 있습니다:</p>
<ul>
    <li><a href="/">홈페이지</a></li>
    <li><a href="/products">제품</a></li>
    <li><a href="/support">지원</a></li>
</ul>
<form action="/search">
    <input type="text" name="q" placeholder="검색...">
    <button type="submit">검색</button>
</form>

목표: "길을 잃었군요"를 "앞으로 나아갈 몇 가지 경로가 있습니다"로 바꾸는 것입니다.

API 응답

API는 무엇이 발견되지 않았는지 구체적으로 명시해야 합니다:

GET /api/users/99999

HTTP/1.1 404 Not Found
Content-Type: application/json

{
    "error": "User not found",
    "code": "USER_NOT_FOUND",
    "userId": 99999
}

클라이언트는 이를 의미 있게 처리할 수 있습니다:

async function getUser(userId) {
    const response = await fetch(`/api/users/${userId}`);

    if(response.status === 404) {
        return null;
    }

    if(!response.ok) {
        throw new Error(`Request failed: ${response.status}`);
    }

    return response.json();
}

리다이렉트를 사용해야 할 때

콘텐츠가 사라진 것이 아니라 이동한 경우, 404 대신 리다이렉트를 사용하세요:

GET /blog/old-url

HTTP/1.1 301 Moved Permanently
Location: /blog/new-url

이것은 사용자를 원하는 목적지로 이어주고 검색 엔진에 콘텐츠가 어디로 갔는지 알려줍니다. 해당 주소에 진짜 아무것도 없고 리다이렉트할 곳도 없을 때만 404를 사용하세요.

누락 경로 모니터링하기

모든 404는 정보입니다. 누군가 그곳에 무언가가 있을 것이라고 예상했습니다—잘못 입력했거나, 무언가가 끊어졌거나, 리다이렉트 없이 이동했기 때문입니다.

404를 기록하세요:

app.use(function(request, response, next) {
    response.on('finish', function() {
        if(response.statusCode === 404) {
            logger.warn({
                url: request.url,
                referrer: request.headers.referer,
                timestamp: new Date()
            });
        }
    });
    next();
});

패턴을 찾아보세요. 같은 URL이 반복적으로 나타난다면, 무언가가 그 URL에 링크하고 있는 것입니다. 참조 URL이 여러분의 사이트라면, 내부 링크가 끊어진 것입니다. 외부에서 온 것이라면, 해당 콘텐츠가 이동한 곳으로 리다이렉트를 추가하는 것을 고려하세요.

인간적인 현실

404는 지켜지지 않은 약속입니다. 누군가 어딘가에 도착할 것이라고 클릭했습니다. 해야 할 일이 두 가지 있습니다: 목적지가 존재하지 않는다고 솔직하게 알리고, 실제로 가고 싶은 곳을 찾도록 도와주는 것입니다.

기술적인 구현은 간단합니다. 인간적인 배려가 막다른 길과 도움이 되는 안내를 구분짓습니다.

404 Not Found에 관해 자주 묻는 질문

삭제된 콘텐츠에 404와 410 중 어느 것을 사용해야 할까요?

무언가를 의도적으로 제거했고 검색 엔진이 더 이상 확인하지 않도록 하고 싶다면 410 Gone을 사용하세요. 확실하지 않은 경우—삭제됐을 수도 있고, 처음부터 없었을 수도 있고, 어디로 이동했는지 모를 수도 있다면—404를 사용하세요. 410은 확실성의 표명이고, 404는 불확실성의 인정입니다.

일부 사이트가 금지된 콘텐츠에 403 대신 404를 반환하는 이유는 무엇인가요?

은닉에 의한 보안(Security through obscurity)입니다. 공격자가 "이 사용자는 존재하지 않습니다"와 "이 사용자는 존재하지만 접근할 수 없습니다"를 구분할 수 있다면, 유효한 사용자를 열거할 수 있습니다. 두 경우 모두 404를 반환하면 해당 정보를 숨길 수 있습니다. 이 트레이드오프가 디버깅 명확성 감소의 가치가 있는지는 보안 요구 사항에 따라 다릅니다.

404 오류가 얼마나 많으면 너무 많은 건가요?

가끔 발생하는 404는 정상입니다—사람들은 URL을 잘못 입력하기도 하고, 외부 사이트가 오래된 콘텐츠에 링크를 걸기도 합니다. 하지만 404가 급증하거나 트래픽의 상당 부분을 차지한다면, 무언가가 고장난 것입니다. 절대적인 수치보다는 시간에 따른 비율을 모니터링하세요.

404와 소프트 404의 차이점은 무엇인가요?

실제 404는 HTTP 상태 코드 404를 반환합니다. 소프트 404는 상태 코드 200(성공)을 반환하지만 "페이지를 찾을 수 없음" 콘텐츠를 표시합니다. 소프트 404는 검색 엔진을 혼란스럽게 하므로 항상 수정해야 합니다—현실에 맞는 실제 상태 코드를 반환하세요.

Questa pagina è stata utile?

😔
🤨
😃