Skip to content

Conversation

wooojin401
Copy link

✅ 워크북 체크리스트

  • 모든 핵심 키워드 정리를 마쳤나요?
  • 핵심 키워드에 대해 완벽히 이해하셨나요?
  • 이론 학습 이후 직접 실습을 해보는 시간을 가졌나요?
  • 미션을 수행하셨나요?
  • 미션을 기록하셨나요?

✅ 컨벤션 체크리스트

  • 디렉토리 구조 컨벤션을 잘 지켰나요?
  • pr 제목을 컨벤션에 맞게 작성하였나요?
  • pr에 해당되는 이슈를 연결하였나요?(중요)
  • 적절한 라벨을 설정하였나요?
  • 파트장에게 code review를 요청하기 위해 reviewer를 등록하였나요?
  • 닉네임/main 브랜치의 최신 상태를 반영하고 있는지 확인했나요?(매우 중요!)

📌 주안점

register,
handleSubmit,
formState: { errors, isValid },
} = useForm<{ password: string; confirm: string }>({

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 스키마와 연결하여 입력값을 자동으로 검사하고, 결과를 errors 객체에 담도록 잘 구현한 것 같습니다

const { name, value } = e.target;
setValues({ ...values, [name]: value });
setErrors({ ...errors, [name]: validate(name, value) });
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

입력할 때마다 해당 input의 name에 맞는 value를 갱신하고 동시에 validate로 검사한 결과를 errors에 반영해 실시간 유효성 검사가 가능한 점이 좋은 것 같습니다

loading: false,
error: "데이터를 불러오는 중 오류가 발생했습니다.",
});
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error 처리 시에는 AxiosError 사용을 추천합니다.
AxiosError는 Error를 확장한 타입으로, Axios 내부에서 발생하는 모든 실패 케이스(요청 취소, 타임아웃, 서버 오류, 네트워크 단절 등)를 구조화해 제공합니다.예를 들어 err.response에는 서버의 상태 코드와 응답 본문이 담겨 있어 세부 원인을 쉽게 파악할 수 있고, err.code나 err.config를 통해 요청 설정과 실패 원인까지 추적할 수 있어 네트워크 문제 디버깅에 매우 유용합니다.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// src/hooks/useCustomFetch.ts
import { useEffect, useState } from "react";
import axios, { AxiosError } from "axios";

interface FetchState {
data: T | null;
loading: boolean;
error: string | null;
}

export function useCustomFetch(url: string) {
const [state, setState] = useState<FetchState>({
data: null,
loading: true,
error: null,
});

useEffect(() => {
if (!url) return;

const fetchData = async () => {
  try {
    setState({ data: null, loading: true, error: null });

    const res = await axios.get<T>(url, {
      headers: {
        Authorization: `Bearer ${import.meta.env.VITE_TMDB_KEY}`,
        Accept: "application/json",
      },
    });

    setState({ data: res.data, loading: false, error: null });
  } catch (err) {
    // ✅ AxiosError 여부 확인
    if (axios.isAxiosError(err)) {
      const axiosError = err as AxiosError;

      // 서버 응답이 있는 경우 (ex. 404, 500)
      if (axiosError.response) {
        setState({
          data: null,
          loading: false,
          error: `서버 오류 (${axiosError.response.status}): ${
            (axiosError.response.data as any)?.message ??
            "요청을 처리할 수 없습니다."
          }`,
        });
      }
      // 요청은 갔지만 응답이 없는 경우 (네트워크 오류 등)
      else if (axiosError.request) {
        setState({
          data: null,
          loading: false,
          error: "서버로부터 응답이 없습니다. 네트워크 상태를 확인하세요.",
        });
      }
      // 요청 자체가 실패한 경우
      else {
        setState({
          data: null,
          loading: false,
          error: `요청 실패: ${axiosError.message}`,
        });
      }
    } else {
      // AxiosError가 아닌 일반적인 오류 처리
      setState({
        data: null,
        loading: false,
        error: "알 수 없는 오류가 발생했습니다.",
      });
    }
  }
};

fetchData();

}, [url]);

return state; // { data, loading, error }
}
알맞게 수정하였습니다!

error: null,
});

useEffect(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

빠른 클릭 등으로 URL이 짧은 시간에 여러 번 변경될 때, 이전 요청의 응답이 늦게 도착해 최신 상태를 덮어쓰는 race condition이 발생할 수 있습니다. 이를 방지하기 위해 AbortController를 사용해 이전 요청을 취소하는 로직을 만들어 보는걸 추천합니다!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// src/hooks/useCustomFetch.ts
import { useEffect, useState } from "react";
import axios, { AxiosError } from "axios";

interface FetchState {
data: T | null;
loading: boolean;
error: string | null;
}

export function useCustomFetch(url: string) {
const [state, setState] = useState<FetchState>({
data: null,
loading: true,
error: null,
});

useEffect(() => {
if (!url) return;

//  AbortController 생성
const controller = new AbortController();
const { signal } = controller;

const fetchData = async () => {
  try {
    setState({ data: null, loading: true, error: null });

    const res = await axios.get<T>(url, {
      headers: {
        Authorization: `Bearer ${import.meta.env.VITE_TMDB_KEY}`,
        Accept: "application/json",
      },
      signal,
    });

    setState({ data: res.data, loading: false, error: null });
  } catch (err) {
    if (axios.isCancel(err) || (err as AxiosError).code === "ERR_CANCELED") {
      console.warn("요청이 취소되었습니다:", url);
      return;
    }

    if (axios.isAxiosError(err)) {
      const axiosError = err as AxiosError;
      if (axiosError.response) {
        setState({
          data: null,
          loading: false,
          error: `서버 오류 (${axiosError.response.status}): ${
            (axiosError.response.data as any)?.message ??
            "요청을 처리할 수 없습니다."
          }`,
        });
      } else if (axiosError.request) {
        setState({
          data: null,
          loading: false,
          error: "서버 응답이 없습니다. 네트워크 상태를 확인하세요.",
        });
      } else {
        setState({
          data: null,
          loading: false,
          error: `요청 실패: ${axiosError.message}`,
        });
      }
    } else {
      setState({
        data: null,
        loading: false,
        error: "알 수 없는 오류가 발생했습니다.",
      });
    }
  }
};

fetchData();

return () => {
  controller.abort();
};

}, [url]);

return state; // { data, loading, error }
}
피드백 수용했습니다!

@sunnyinha
Copy link
Collaborator

눈송이, 4주차도 수고하셨습니다! 머지해주세요~

@wooojin401 wooojin401 merged commit 80ab640 into UMC-Inha:눈송이/main Oct 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Chapter04_사용자 인증부터 폼 유효성 검사까지, 서비스의 기본기 다지기

4 participants