ERROR

Hee·2024년 2월 20일
0

새로운 프로젝트를 진행하는 중에
에러페이지 개발을 맡게 되었습니다🙂

에러페이지 기획

처음 전달받은 기획에는 nav bar가 있는 형태였다.

nav bar를 없애자고 제안한 이유는 많은 레퍼런스를 찾아 보기도 했고..
(nav bar가 존재하는 NotFound 페이지도 있기는 했다 (ex. JIRA))

nav bar에 다양한 기능이 존재하여 유저 데이터를 컨트롤 하기 힘들어 보였고..
예외 페이지에서의 사용자 행동을 제한? 유도하는 데에 좋다고 생각했다.

기획자분께 컨펌을 받고서 한가지 더 고민이 생겼다.

NotFound 페이지는 '홈으로 가기' 버튼, 응닶없음 페이지는 '다시 시도' 버튼이었다.

이로 인해 생기는 문제는 사용자가 데이터 응닶없음 페이지에서 벗어날 경로가 없다는 것이다.

그래서 다시 기획자분께 해당 사항에 대해 의견을 부탁드렸다.
내가 생각한 대안은

  1. 홈 기능만 하는 nav bar 사용
  2. 텍스트링크를 포함한 가이드 문구로 사용자 행동 유도

2번으로 결정되었고 드디어 시작🦁

개발 방향 설정

NotFound 페이지 처리

사용자가 존재하지 않는 페이지에 접근하려 할 때, 즉 예외 경로에 대한 접근을 시도할 경우 NotFound 페이지를 표시

응답없음 페이지 처리

서버에서 500번대 오류, 즉 서버 내부 오류가 발생했을 경우로 제한하여 이를 처리하기 위해 응답없음 페이지를 렌더링한다.

전역 상태 관리 및 에러 처리
본 프로젝트에서는 전역 상태 관리를 위해 zustand를 사용하고 있어서 이를 이용했다.

  • Axios Response Interceptor 활용: axios의 응답 인터셉터를 사용하여 서버로부터 500번대 오류 응답을 받을 경우, 해당 오류 정보를 전역 상태 관리 스토어에 저장합니다. 이를 통해 애플리케이션의 다양한 부분에서 오류 정보에 접근하고 반응할 수 있습니다.

  • 에러 상태 기반 렌더링: 스토어에 저장된 에러 상태를 읽어, 에러가 존재하는 경우 에러 페이지 컴포넌트를 렌더링합니다.

코드 구현

에러 상태 관리하기

create 함수를 사용하여 에러 상태 관리를 위한 useErrorStore를 구현한다.
에러 상태(error), 에러를 추가하는 setError 함수, 그리고 에러를 제거하는 clearError 함수를 정의한다.

const useErrorStore = create<ErrorState>((set) => ({
    error: null,
    setError: (error) => set(() => ({ error })),
    clearError: () => set(() => ({ error: null })),
}))

Axios Response Interceptor 설정하기

axios의 response interceptor를 설정하여 서버로부터 500번대 에러 응답을 받을 경우, 에러 상태를 업데이트한다.
이 로직은 이를 이용하는 모든 API 호출에 일괄적으로 적용되어, 에러 처리를 전역에서 관리할 수 있게 한다.

publicService.interceptors.response.use(
    (response) => {
        return response
    },
    async (error) => {
        if (error.response?.status === 401) {
            console.error(error.response)
        }
        if (error.response?.status >= 500) {
            useErrorStore.getState().setError(error)
        }
        return Promise.reject(error)
    }
)

에러 페이지 렌더링 로직

ErrorBoundaryRoute 컴포넌트는 useErrorStore를 사용하여 현재 에러 상태를 관찰하고, 에러가 있을 경우 NotResponding 컴포넌트를 렌더링한다. 에러가 없다면, Outlet 컴포넌트를 통해 자식 라우트를 렌더링한다.


const ErrorBoundaryRoute = () => {
    const error = useErrorStore((state) => state.error)

    if (error) {
        return <NotResponding />
    }

    return <Outlet />
}

export default ErrorBoundaryRoute

라우터 설정

ErrorBoundaryRoute를 상위 라우트 요소로 사용함으로써, 모든 자식 라우트에서 에러 처리 로직을 적용하도록 한다. 에러가 발생하면 에러 페이지가 렌더링되고, 그렇지 않을 경우 정상적인 페이지 구조가 유지된다.

const Router = () => {
    return (
        <BrowserRouter>
            <Routes>
                <Route element={<ErrorBoundaryRoute />}>
                    <Route path="/" element={<NavigationLayout />}>
               {nav bar 있는 경로}                  
                    </Route>
                    <Route path="*" element={<NotFound />} />
                    {nav bar 없는 경로}
                </Route>
            </Routes>
        </BrowserRouter>
    )
}
export default Router

이 구조를 통해, 에러가 발생했을 때는 NotResponding 컴포넌트가 렌더링되고

NavigationLayout 컴포넌트는 nav 바와 함께 중첩 라우트를 렌더링하는 레이아웃을 정의한다. 이 컴포넌트 내에서 은 자식 라우트의 컴포넌트가 렌더링될 위치를 지정한다.

export const NavigationLayout = () => (
    <>
        <Navigation />
        <DefaultContainer>
            <Outlet />
        </DefaultContainer>
    </>
)

에러가 없는 경우에는 NavigationLayout 자식들은 해당 컴포넌트에 정의된 레이아웃에 따라, 자식이 아니면 기존 위치에 사용자 인터페이스가 표시된다.

profile
*^^*

0개의 댓글