Published 2023. 1. 21. 22:36

hoc를 사용한 이유

프로젝트 구조는 매우 간단해서 하나의 페이지로만 구성되어 있었다. 이때 로그인 여부에 따라 컴포넌트 렌더링 여부를 나누려고 했다.

 

구조를 생각해봤을 때 전체적인 로그인 상태를 관리하는 context api가 있고 여기서 로그인 상태를 가져와 나누었는데, 이때 로그인 여부 확인 - 로그인 컴포넌트 조건문 로직이 많이 중복되었다. 따라서 이 부분을 관리하기 위해 hoc를 통해 더 단순화 시킬 수 있을거 같아 여러 문서들을 참고했다.

 

구현

react-router 공식 example을 참고해서 구현했다.

 

provider를 정의해서 로그인 로직을 구현했는데 NextJS라서 cookie를 가져오는 부분이 렌더링 문제가 발생했다.

아래와 같은 오류가 발생했는데 일단 구현을 목표로 정리해야할 부분으로 메모한 뒤 useEffect로 클라이언트 렌더링 해버리는 것으로 막아버렸다.

 

리팩토링은 내일해보는 것으로...

 

React 18: Hydration failed because the initial UI does not match what was rendered on the server

I'm trying to get SSR working in my app but I get the error: Hydration failed because the initial UI does not match what was rendered on the server. Live demo code is here Live demo of problem is...

stackoverflow.com

import React from 'react'
import type { AuthContextType } from '@/types/auth'
import { findAccessToken, findRefreshToken } from '@/utils/token'
import useUpdateToken from './queries/useUpdateToken'

export const AuthContext = React.createContext<AuthContextType>(null!)

export function useAuth() {
  return React.useContext(AuthContext)
}

const AuthProvider = ({ children }: { children: React.ReactNode }) => {
  const [isToken, setIsToken] = React.useState(false)
  const [isRefreshToken, setIsRefreshToken] = React.useState(false)
  const { mutate: updateToken } = useUpdateToken()
  
  ...
  
  const value = {
    isToken,
    isRefreshToken,
  }

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}

export default AuthProvider

 

이후 withAuth hoc를 정의하여 간단하게 관리할 수 있도록 했다.

import React from 'react'
import { NextPage } from 'next'
import { useAuth } from './AuthProvider'

const withAuth = (Component: NextPage | React.FC, LoginComponent: React.FC) => {
  const Auth = () => {
    const auth = useAuth()

    return auth.isToken ? <Component /> : <LoginComponent />
  }

  return Auth
}

export default withAuth

 

onst HomeLayout = () => {
  return (
    <main className="min-h-screen max-w-7xl mx-auto pb-10">
      <ClipListTab />
      <CardList />
    </main>
  )
}

export default withAuth(HomeLayout, LoginLayout)

이후 withAuth로 감싸면 된다. 전체적으론 중복되는 부분을 많이 줄일 수 있었다.

 

에러가 발생했던 부분들

토큰 만료에 대해서

기존에는 axios 라이브러리를 활용해서 401 오류가 발생하는 것을 감지한 인터셉터를 둬서 만료되면 재발급받아 사용하는 것으로 구현해뒀는데, withAuth로 막아두니 401 오류를 받을 수 없어 refresh token이 있더라도 새로운 토큰을 발급받지 못했다.

 

따라서 AuthProvider에 useEffect를 걸어놨는데 이게 인위적으로 access token을 지우면 원하는 동작을 하는데 일반적인 흐름에서 만료되면 갱신되는지를 확인하지 못했다.

 

NextJS라서 발생되는 문제들

server에서도 동작하다보니 예측하지 못한 오류들이 많았다. 위처럼 hydrate 오류가 많이 났는데 이 부분에서 원하는 부분으로 구현하지 못하고 돌려돌려 구현하게 되었다.. 다시 테스트 해보면서 잘 되는 방향으로 구현해보려고 한다.

 

 

회고

로그인 구현은 전체적인 서비스에서 중요한 부분을 차지한다고 생각해서 전체적으로 만족할만큼 자연스럽게 동작할 수 있도록 하고싶었다. 생각보다 어색한 부분이 많아서 조금 더 시간을 들여 해볼 예정이다.

 

복사했습니다!