이전부터 함수를 작성할 때 에러처리를 위해 console.error() 와 throw error 중 한가지를 써왔었다.
이번에 댓글의 답글 기능을 구현하는 중 에러가 발생했을 때 어떤 메세지가 조회되는지 확인하려고 추가하다가 아무생각없이 써왔던 두가지의 차이가 무엇인지 궁금해지면서 찾아보게되었다.
// 댓글 추가 (+답글)
export const useAddCommentMutation = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (data: {
id: string;
commentItem: string;
parentId?: string; // 답글일 경우 부모 댓글 ID
}) => {
try {
await addPostComment(data);
} catch (error) {
console.error("댓글 추가 실패:", error);
throw error;
}
},
onSuccess: (_, { id: postId }) => {
queryClient.invalidateQueries({ queryKey: ["comment", postId] });
},
});
};
console.error 와 throw error 의 차이가 무엇일까?
console.error()
목적 : 개발자가 디버깅할 때 에러 정보를 확인하기 위한 로깅
동작 : 브라우저 콘솔에 에러 메세지를 출력
영향 : 앱 실행에는 영향 X
throw error
목적 : 에러를 상위 컴포넌트나 에러 핸들러에게 전달
동작 : 실제로 에러를 발생시켜 Tanstack-query의 에러 처리 로직을 실행시킴
영향 : onError 콜백 or 에러 상태 처리가 가능
사용
1. 두가지 다 사용했을 때
개발 시 에러 정보를 확인할 수 있고 적절한 에러 처리 로직을 실행할 수 있으며 사용자에게 에러 메세지를 보여줄 수 있다!
// 1. 둘 다 사용
export const useAddCommentMutation = () => {
return useMutation({
mutationFn: async (data) => {
try {
await addPostComment(data);
} catch (error) {
console.error("댓글 추가 실패:", error); // 개발자를 위한 로깅
throw error; // Tanstack Query의 에러 처리를 위한 전파
}
},
onError: (error) => {
// 사용자에게 에러 메시지 표시
alert("댓글 작성에 실패했습니다.");
}
});
};
2. console.error 만 사용했을 때
에러 처리 로직 실행을 위한 전파가 되지 않는다.
// 2. console.error 만 사용
export const useAddCommentMutation = () => {
return useMutation({
mutationFn: async (data) => {
try {
await addPostComment(data);
} catch (error) {
console.error("댓글 추가 실패:", error);
// 에러가 전파되지 않으므로 onError 실행 X
return null;
}
}
});
};
3. throw error 만 사용
에러 처리 로직 실행을 위한 전파는 되지만 에러 로그를 조회할 수 없어 디버깅이 어렵다.
// 3. throw error 만 사용
export const useAddCommentMutation = () => {
return useMutation({
mutationFn: async (data) => {
try {
await addPostComment(data);
} catch (error) {
throw error; // 에러 로그 조회 X
}
}
});
};
그러면 어떻게 사용하는게 좋을까?
1. 에러 로깅 추가
(*에러 로깅 : 프로그램 or 앱에서 발생하는 오류나 예외 상황을 체계적으로 기록하는 과정 → 원인 분석 및 해결책을 찾는 데 도움)
catch (error) {
console.error("Error in addPostComment:", error);
throw error;
}
2. 커스텀 에러 객체 사용
catch (error) {
throw new Error(`Failed to add comment: ${error.message}`);
}
이와 같은 방법을 통해 에러를 더 효과적으로 관리하고 처리할 수 있게 되면서, 동시에 에러의 원인을 찾고 해결하는 시간을 단축할 수 있다.
그렇다면 한가지 더 궁금한 점이 생겼다.
"onError 처리는 mutation 함수 내부에서 사용 해야하는가?
아니면 컴포넌트에서 사용 해야 하는가?"
1. Mutation 함수 내부에서 사용
// 1. Mutation 함수 내부에서 사용
export const useAddCommentMutation = () => {
return useMutation({
mutationFn: async (data) => {
try {
await addPostComment(data);
} catch (error) {
console.error("댓글 추가 실패:", error);
throw error;
}
},
// 여기서 사용
onError: (error) => {
alert("댓글 작성에 실패했습니다."); // 공통적인 에러 처리
},
onSuccess: () => {
console.log("댓글이 작성되었습니다."); // 공통적인 성공 처리
}
});
};
장점 | 공통적인 에러 처리 로직을 한 곳에서 관리하여 코드 중복을 방지할 수 있다. |
단점 | 컴포넌트별 특수한 에러 처리가 어려우므로 유연성이 떨어진다. |
2. 컴포넌트 에서 사용
// 2. 컴포넌트 에서 사용
function CommentComponent() {
const { mutate: addComment } = useAddCommentMutation();
return (
<button
onClick={() => {
addComment(
{ id, commentItem },
{
onSuccess: () => {
console.log("댓글 작성 성공!");
// 컴포넌트 특정 동작 (ex.입력창 초기화)
setCommentItem("");
},
onError: (error) => {
// 컴포넌트 특정 에러 처리
setError("댓글 작성에 실패했습니다.");
}
}
);
}}
>
댓글 작성
</button>
);
}
장점 | 컴포넌트별로 상황에 따라 유연하게 에러 처리가 가능하고 UI 상태와 직접 연동이 가능하다. |
단점 | 코드가 중복될 가능성이 있으며 일관성 유지가 어려울 수 있다. |
그러면 어떻게 사용해야 할까?
→ 둘다 상황에 맞게 적절하게 활용하자.
공통된 로직은 mutation 함수에서 처리하고 컴포넌트별 특수한 처리는 컴포넌트에서 처리하여 유연성과 일관성을 모두 확보하자.
'TIL' 카테고리의 다른 글
Next.js 스크롤 방식의 Time Picker 구현하기(feat. Tailwind CSS) (0) | 2024.10.29 |
---|---|
월간 달력 출력하기 (FullCalendar / Shadcn) (0) | 2024.10.28 |
Tanstack Query - querykey (0) | 2024.10.25 |
상세페이지 - 각 댓글별 수정 모드 (0) | 2024.10.24 |
Tanstack Query - useMutation 오류 (0) | 2024.10.23 |