<404 Not Found와 그에 대한 고민들. (feat. 예고편)>

강민수·2024년 2월 11일
0

아하 모먼트

목록 보기
2/4

1. 사건의 발단.

회사에서 최근에 경험한 논의 사항과 더불어, 이를 어떻게 어떤 관점에서 해석하고 해결했는 지, 추가로 react-query에서 아직 미흡했던 부분까지 다시 회고해 보기 위해 간만에 글을 끄적여 본다.

민수님. 이거 404로 주는 게 맞지 않을까요?
어떻게 생각하시나요?

갑자기, 내게 훅 들어온 질문.
그것은 백엔드 동료 개발자분이 api 개발 과정에서 내게 던진 질문이었다.

처음에는 이게 뭔 소리인가. 싶었다. 하지만, 이내 그 내용은 내게 현실이 되고 말았다.
어느날, 갑자기 api 요청을 했는데, 에러를 내뿜는 것이 아닌가... 분명 다른 api들은 특정 값이 없어도 그걸 404로 주지는 않았는데...
그럼 이것도 404를 주면 안 되는 거 아닌가? 최소한 값이 없다면 특정 필드의 경우 null을 주는 게 맞지 않나? 이런 온갖 생각들이 머리를 스쳤다.

결국 작업을 잠시 스톱하고 백엔드에서 질문한 논리를 다시 살펴봤다.

다시 돌이켜 보니, 백엔드 측에서 얘기한 것은 특정 요청의 리스폰스를 줄 때, 그게 없다면 당연히 404 not-found를 주는 것이 맞지 않은 가에 대한 논리였다.

다만, 프론트 입장에서는 그게 과연 맞는 것인지 다시 생각해 볼 필요가 있었다.

왜냐하면, 단순히 백엔드의 리소스 정보 없음의 '404'가 리소스 정보 없음만을 의미할 때 쓰는 것인지 혼동이 오기 시작했다.
프론트에서는 보통 위의 그림처럼, 사실상 페이지의 url이 없을 때도 분명 저렇게 보여주기 때문이었다.

404에 대한 명확한 의미를 더 찾아봤다.


-> rfc의 명세 내용.

404(찾을 수 없음) 상태 코드는 원본 서버가 대상 리소스에 대한 현재 표현을 찾지 못했거나 존재한다는 사실을 공개하지 않으려는 것을 나타냅니다. 404 상태 코드는 이러한 표현 부족이 일시적인지 영구적인지를 나타내지 않으며, 원본 서버가 구성 가능한 방법을 통해 해당 상태가 영구적일 가능성이 있음을 알고 있는 경우 404보다 410(사라짐) 상태 코드가 더 선호됩니다.404 응답은 메서드 정의 또는 명시적 캐시 제어에서 달리 명시하지 않는 한 휴리스틱적으로 캐싱할 수 있습니다([캐싱] 섹션 4.2.2 참조).

추가 서칭 내용.

서버 오류라고 생각하시는 분들도 계시겠지만,
해당 오류는 서버 자체는 존재하지만 서버에서 요청한 것을 찾을 수 없을 때 나타납니다.
보통 HTTP에서 사용자가 요청하는 페이지나 파일을 찾을 수 없을 때 가장 많이 발생하는데요.
유저의 인터넷 환경 문제가 아닌 사이트 제공자의 서버에 요청한 페이지가 없거나,
데이터가 없을 경우 나타나며, 가장 자주 발생하는 원인은 페이지가 이동되거나 삭제된 경우입니다.

결국, 정리해 보면, 404는 두 가지 의미로 해석될 수 있는 여지가 있는 status code라는 것을 다시 생각해 볼 수 있었다.
즉, 필자의 의견대로,

  1. 프론트 입장의 페이지 404 = 애초에 프론트 서버에 배포된 주소에 domain에 해당하는 리소스 자원등(html파일 등)이 없음을 의미하는 404.
  2. 백엔드 서버 입장에서 404 = 특정 요청에 대한 서버 단에서의 공급할 자원이 없음을 의미하는 404.

사실 그렇다 보니, 이게 양쪽 다 맞는 의미였다.

2. 외교 담판.

그렇다면, 우리는 원만한 해결을 위해 논의를 거쳐야 할 필요성이 있겠다고 생각이 들었다.
마치 서희의 외교담판 처럼... ㅎㅎㅎ

각자의 확고한 의견이 있을 땐, 어떤 쪽이 더 맞는 지 의견을 조율할 필요성. 그게 진정한 실리 외교라고 생각한다. 그래서 우리는 백엔드 팀과 논의를 거쳤다.

그래서 결론적으로 나온 의견은 다음과 같았다.

  1. 서버의 리소스에서 404를 주는 경우는 어떤 경우인가?
    -> 서버 측에서 애초에 기본 값을 저장할 필요도 없고, 정말로 리소스가 없는 경우에 404를 준다.
  2. 그렇다면, 이번 api 버전부터는 기존과는 다르게 더 명확하게 이를 404로 명시할 생각인가?
    -> 그렇다. 404를 명시적으로 할 것이고, 이에 따라 프론트 팀에서도 분명 에러 처리를 할 필요성이 있다고 생각한다.
  3. 그 기준이 틀릴 경우는 있는가? 추가로, 404가 있는 경우, api 명세에 기재 바란다.
    -> 현재로선 위의 기준대로 원칙을 세웠기 때문에 틀리지는 않을 것이다. 기재 필수적으로 하겠다.

이 의견을 토대로, 결국 백엔드 측과의 원만한 합의를 거쳤고, 우리 팀 역시 그에 따라 코드를 수정하기로 했다.

3. 본격적인 수정에 대한 고뇌.

이제 결국 받아들일 부분은 받아들여야 하는 상황이었다.

그렇다면, 우리가 당장 처리해야 할 부분은 react-query에서 error 처리를 어떤 식으로 하는 게 좋을까였다.

물론, 필자역시 error-boundary나 등등의 처리를 하면 좋겠다고 생각했지만, 그게 사실 현재 프로젝트에서는 적용하기 매우 힘든 구조였다.

  1. 왜냐하면, 우리는 기존의 private한 핸들러들을 우리의 서비스의 타입에 맞게 커스텀 훅으로 따로 빼서 사용중이었다.(헤더 셋팅등의 이유로).
  2. 그러다 보니, 이 핸들러를 사용하지 않고, 순수 tanstack-query의 훅스들을 사용하기에는 다소 무리가 있었고, 그에 따라 개발의 공수가 추가로 너무 발생했다.
  3. 모두가 연관된 코드가 하나 둘이 아니다 보니, 결국 이 api하나만을 위해서 전체 코드의 구조를 흔들 수는 없었다.(사실 이게 너무 강결합되어 있는 듯 하여, 좋지는 못한 구조라고 생각은 함. 다만, 과거 레거시 코드도 있다보니...)
  4. 현재 기획과 디자인의 요구 사항에서는 정말 데이터가 없는 경우와 특정 항목에 대한 데이터가 없는 화면이 일치해야만 했다. ㅜㅜ
  5. 그래서 필자는 이걸 어찌할 지 고민하다가, 결국 그냥 실제로는 에러가 아닌것 처럼 처리하는 게 나을듯해서. 그렇게 처리하기로 했다.

그게 무슨 말이냐 싶지만, 코드를 보면서 이건 설명해 보겠다.

  const { data } = useCustomQuery({
    queryKey: [QUERY_KEY],
    queryFn: async ({ headers }) => {
      try {
        return await getData(headers);
      } catch (error) {
        if (!isAxiosError(error)) {
          return undefined;
        }
        if (error.response?.status === 404) return '404';
        return undefined;
      }
    },
    placeholderData: keepPreviousData,
  });

위의 코드는 완벽한 구현코드는 아니지만, 살짝 대략적인 흐름만 파악해 주시기 바란다.

결국, 이게 좋은 방향일 지는 모르겠지만, 현재로서는 최선의 선택이라고 생각해서 고민한 결과다.
즉, queryFn 콜백 함수 처리 부분에서 try catch로 애초에 묶어서 값 자체를 지정해 주는 것이다.
다른 에러가 아닌, 404 에러일 경우에만 404스트링으로 return해 줘서 마치 마스킹하듯이 처리해 주는 것이다.

왜 굳이 이렇게 처리하는 가라는 의문을 가지실 수 있도 있지만, 앞선 이유들로 인해 404일 경우, 데이터가 없다는 ui를 그려줘야 했기 때문이다. 즉, 사용자 입장에서는 에러가 아닌, "어? 이거 데이터가 없는 거구나." 라는 느낌만 받고 다시 다른 조건으로 조회(여기서는 다른 데이터가 있는 날짜)했을 경우, 다시 data에 따라 분기된 화면을 바로 즉시 볼 수 있어야만 한다.

코드로는 몇 줄 없는 코드이지만, 굉장히 많은 고초를 겪은 코드라서 이렇게 회고해 보게 되었다.

4. 예고편 placeHolderData

추가로, 이번 구현을 토대로, 약간 하나 더 알게 된 확장판 같은 개념이 있었다.
그건 바로, 앞선 코드에서도 살짝 노출된 placeHolderData라는 쿼리의 옵션이다.
이에 대해서는 사실 아래 링크에서 보다 자세한 논의가 이뤄져 왔다.

궁금하신 분들은 아래 링크를 참조해 주시면 좋겠다.

https://github.com/TanStack/query/discussions/4426

필자의 아마 다음 글은 이 placeholderData라는 기능에 대한 이해와 상황을 바탕으로 한 글이 될 예정이다.

곧 다시 돌아오겠다.

profile
개발도 예능처럼 재미지게~

0개의 댓글