본문 바로가기

TIL

console.error() 와 throw error 의 차이

이전부터 함수를 작성할 때 에러처리를 위해 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 함수에서 처리하고 컴포넌트별 특수한 처리는 컴포넌트에서 처리하여 유연성과 일관성을 모두 확보하자.