에러헨들링
이전에도 loading쪽이랑 간단하게 정리를 했던 것 같은데 조금 디테일하게? 실습해 본 내용을 정리해보았다.
화면을 디자인했다거나 콘솔을 자주 확인한다거나 하기 보다는 코드 위주로 내용을 간단하게 정리해보면서 이런 메소드가 있고 이렇게 쓰는구나! 를 알아보자!
"use client";
import { useEffect } from "react";
// 오류라는건 서버,클라이언트 모든 환경에서 발생하기 때문에 다 대응할 수 있도록 만들기 위해 "use client" 명시
export default function Error({error}: {
error: Error; //자바스크립트의 Error 타입을 props로 내려서 적용
}) {;
useEffect(() => {
console.error(error.message);
}, [error]);
return (
<div>
<h3>오류가 발생했습니다.</h3>
</div>
);
}
간단하게 search 폴더의 index page와 동일한 위치에 error.tsx
로 파일 하나를 생성해주고 코드를 작성했다.
“use client”
를 맨 위에 명시오류라는건 서버, 클라이언트 모든 환경에서 발생하는 것이기 때문에 어떤 환경이든 다 대응해서 보여줄 수 있도록 만들고자 “use client”
를 적어준 것이다.
error
를 prop
으로 전달받고 자바스크립트의 Error
를 타입으로 명시해주었다. 그 다음 useEffect
훅을 사용해서 에러가 발생할 때마다 error
의 메소드인 message
를 콘솔에 출력되게 작성했다.
💡
error.tsx도 지금까지의 `layout`이나 `loading` 페이지처럼 `error` 아래에 있는 **모든 페이지에 영향**을 미치게 된다.
그러므로 이 `error` 페이지는 `search` 폴더 내의 모든 페이지에서 에러가 발생할 때 이 페이지의 화면이 보여질 것이다.
✅ 브라우저 화면 확인
(캡처 화면은 error.message
를 적용하지 않고 단순 error
만 작성했을 때의 화면이다.)
이렇게 에러가 발생하면 어떤 부분에서 발생한 문제인지 확인할 수 있다.
"use client";
import { useEffect } from "react";
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void; //어떠한 값도 받지 않는 void 타입
}) {
useEffect(() => {
console.error(error.message);
}, [error]);
return (
<div>
<h3>오류가 발생했습니다.</h3>
<button onClick={() => reset()}>다시 시도</button>
</div>
);
}
error
는 또 다른 매개변수로 reset()
메소드를 받게 되는데 이 함수는 어떠한 값도 받지 않는 void
타입으로 정리해주었다.
reset()
: 실행될 때마다 새로 렌더링 되는 메소드reset
메소드가 적용된 버튼을 눌러도 화면이 다시 그려지지 않게 된다. 그 이유는 이 메소드는 브라우저 측에 저장된 화면을 다시 렌더링해주는 것이기 때문이다. ( → 데이터 패칭이 다시 수행되지 않는다는 뜻!)
따라서 서버컴포넌트를 재가동 시켜도 여전히 정상적으로 화면에 데이터가 불러와지지 않는다.
즉, 클라이언트 내부에서 발생한 오류만 해결해주는 메소드라서 추가 작업이 필요하다.
"use client";
import { useRouter } from "next/navigation";
import { startTransition, useEffect } from "react";
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
const router = useRouter();
useEffect(() => {
console.error(error.message);
}, [error]);
return (
<div>
<h3>오류가 발생했습니다.</h3>
<button
onClick={() => {
startTransition(() => {
router.refresh();
//-> RecoBooks나 AllBooks 의 서버 컴포넌트들을 다시 실행시켜서 데이터를 RSC Payload에 저장해서 불러와라! 하는것
reset();
});
}}
>
다시 시도
</button>
</div>
);
}
refresh()
: 현재 페이지에 필요한 서버컴포넌트들을 다시 불러온다.reset()
: 에러 상태를 초기화하고 컴포넌트들을 다시 렌더링 한다.useRouter
의 refesh
는 서버컴포넌트들을 다시 실행 시켜서 그 데이터를 RSC Payload
에 저장 후 다시 보여줘! 하는 것이다.
🧐 startTransition
를 사용하지 않고 아래처럼 코드를 작성한다면 어떻게 될까?
<button
onClick={() => {
router.refresh();
reset();
};
}
>
다시 시도
</button>
refresh
는 비동기적으로 실행되기 때문에 이렇게 작성할 경우 refresh
의 작업이 다 끝나기도 전에 reset
메소드가 실행되어서 우리가 원하는 기능을 완성시킬 수 없다.
그렇다고 async
/ await
도 위 환경에서는 적용시킬 수 없다. 때문에 startTransition
이라는 리액트 기능을 사용해서 refresh
와 reset
이 동시에 작업이 실행되도록 구현하여 우리가 원하는 모습으로 만들어 준 것이다.