이번에 진행 중인 프로젝트에서 로그인 여부에 따라서 다르게 렌더링 해야 하는 컴포넌트가 늘어갔다.

로그인 여부를 전달하기 위해서 내가 선택한 방법은 최상단 컴포넌트에서 토큰을 확인해서 변수에 담아두는 방식이었다. 그리고 이 값이 필요한 자식 컴포넌트의 props 값으로 로그인 여부를 전달했다.

이러한 경우에는 A 컴포넌트는 로그인 여부 값이 필요하지 않지만 A 컴포넌트의 자식인 B 컴포넌트에는 그 값이 필요하기 때문에 A 컴포넌트도 로그인 여부 값을 가지고 있어야 한다. 컴포넌트 depth가 깊어 지거나 하면 관리가 복잡해지는 구조를 가지게 될 것이다.

그러다 React의 Context API를 알게되었다. context를 사용하면 데이터를 전역으로 관리하고 사용할 수 있다.

const App = () => {
  const [isLoggedIn, setIsLoggedIn] = useState(false)

  useEffect(() => {
    const cookie = new Cookies()
    setIsLoggedIn(cookie.get('auth_token') !== undefined)
  }, [])

...
}

기존에는 위와 같이 App에서 쿠키를 가져와서 토큰이 있는지를 검사하고 그 boolean 값을 가지고 있는다.

(프론트단에서 로그인 여부를 쿠키 값으로 확인하는 게 올바른 방식인지 확인이 필요하다..)

그다음에는 아래 코드와 같이 해당 값이 필요한 자식 컴포넌트로 보내준다.

<Routes>
  <Route path='/' element={<Home isLoggedIn={isLoggedIn} />}/>

  ...
</Routes>

Context API 적용하기

import React, { useState } from 'react'
import { useContext } from 'react'
import { Cookies } from 'react-cookie'

const token = new Cookies().get('auth_token')

export const LoginContext = React.createContext(null)

export const LoginProvider = ({ children }) => {
  const [isLoggedIn, setIsLoggedIn] = useState(token !== undefined)
  const value = {isLoggedIn, setIsLoggedIn}

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

먼저 context를 사용하기 위해서 createContext를 사용해 LoginContext를 만든다. 기본값은 null로 지정했다. 로그인 여부를 판단하면서 새로운 value가 세팅이 되는데 이때 담기는 값들은 isLoggedIn와 setIsLoggedIn 변수이다.

export const useLoginContext = () => useContext(LoginContext)

다음은 로그인 상태를 업데이트하고 구독할 수 있는 커스텀 훅을 만들었다.

...
import { LoginProvider } from './shared/LoginContext.jsx'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <LoginProvider>
      <App />
    </LoginProvider>
  </React.StrictMode>,
)

그리고 최상단 컴포넌트에서 LoginProvider로 하위 컴포넌트를 감싸준다.

const App = () => {
  const {isLoggedIn, setIsLoggedIn} = useLoginContext()

  const handleLogout = () => {
    setIsLoggedIn(logout())

    window.location.reload();
  }

이제 위와 같이 useLoginContext를 불러오고 그 안에 들어있는 value를 담아준다. 바로 아래에 handleLogout 함수가 있는데 header부분을 컴포넌트로 나눠주지 않아서 이렇게 처리하게 만들어 놨다. 당연히 이 부분도 컴포넌트화를 진행해야 한다.

<Routes>
  <Route path='/' element={<Home />}/>

...
</Routes>

이제는 Home 컴포넌트에게 로그인 여부 값을 전달하지 않아도 된다. Home에서 useLoginContext를 사용하고 그 안에서 값을 구독하면 된다.

Context API로 데이터를 전역에서 관리하는 방법을 배우고 프로젝트에 적용해 보았다. 아직 리팩터링이 필요한 부분이 많다는 걸 다시 알게 되었다. 그리고 로그인, 로그아웃, 회원가입 로직도 만들어둔 LoginContext를 이용하는 방식으로 다시 한번 리팩터링을 진행해야겠다.


Uploaded by N2T