-
Notifications
You must be signed in to change notification settings - Fork 9
[4주차/눈송이] 워크북 제출합니다. #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
register, | ||
handleSubmit, | ||
formState: { errors, isValid }, | ||
} = useForm<{ password: string; confirm: string }>({ |
There was a problem hiding this comment.
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) }); | ||
}; |
There was a problem hiding this comment.
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: "데이터를 불러오는 중 오류가 발생했습니다.", | ||
}); | ||
} |
There was a problem hiding this comment.
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를 통해 요청 설정과 실패 원인까지 추적할 수 있어 네트워크 문제 디버깅에 매우 유용합니다.
There was a problem hiding this comment.
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(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
빠른 클릭 등으로 URL이 짧은 시간에 여러 번 변경될 때, 이전 요청의 응답이 늦게 도착해 최신 상태를 덮어쓰는 race condition이 발생할 수 있습니다. 이를 방지하기 위해 AbortController를 사용해 이전 요청을 취소하는 로직을 만들어 보는걸 추천합니다!
There was a problem hiding this comment.
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 }
}
피드백 수용했습니다!
눈송이, 4주차도 수고하셨습니다! 머지해주세요~ |
✅ 워크북 체크리스트
✅ 컨벤션 체크리스트
📌 주안점