STUDY/Next js

[Next13] 인증 상태에 따른 페이지 리다이렉트 / 로그인, cookie, middleware

ez1n 2023. 10. 29. 22:06

 

[Next13_middleware]

 


<STUDY>

 

인증 여부에 따른 페이지 리다이렉트를 구현하고싶다@!

 

로그인이 되어있지 않은 유저가 접속할 때 로그인 페이지로 이동하는 과정에서

새로고침을 하는 경우에 메인 페이지가 보였다가 로그인 페이지로 이동하는 깜빡임 현상이 발생했다.

 

이 문제를 해결하기 위해 많은 고민을 했는데,

결론부터 이야기 하자면 cookie + Next.js의 middleware를 사용하여 해결하였다.

 

그래서 그동안 시도했던 방법들을 소개하려고 한다.

 

 

👉 cookie + middleware 바로 보기👈


 

1️⃣ localStorage

 

로그인 했을 때 localStorage에 로그인 정보를 저장하여 로그인 여부를 판별하는 방법이다.

 

import {useRoute} from 'next/navigation';

export default function App() {
  const router = userRoute();
  
  const auth = localStorage.getItem('auth');
  
  if (!auth) {
    return router.replace('/login');
  }
  
  return(
    <Component />
  );
}

 

이 방법은 Next.js의 CSR과 SSR의 차이로 인해 ReferenceError: localStorage is not defined 오류가 발생하는 문제가 있었다.

 

📌 ReferenceError: localStorage is not defined

Next.js는 Client-Side Rendering 이전에 Server-Side Rendering을 수행하는데,
SSR 중에는 Client-Side의 window 전역 객체를 사용할 수 없다!

그래서 SSR을 수행하는 동안 'auth == undefined'였다가 CSR에서 생성되어
값의 변화(차이)가 발생하기 때문에 저런 오류가 발생하는 것이었다.

 

그렇다고 화면 렌더링이 안되거나 로그인 여부를 판별하지 못하는 것은 아니었으나

공식문서에서는 Next.js에서는 window 객체를 사용하는 다른 방법을 추천해주고 있었다..

 

그래서 window 객체가 존재하는 경우에만 auth 값을 할당해보았다.

const auth = typeof window !== 'undefined' && localStorage.getItem('auth');

 

이렇게 변경하면 ReferenceError는 발생하지 않았지만..

역시나 SSR과 CSR 사이에서 값의 차이 때문에 깜빡임 현상이 지속되었다..

 

그래서 결국 실패ㅠㅠ

 

 

2️⃣ useEffect

 

우선 auth라는 state를 null로 초기화한 다음 useEffect를 이용하여 localStorage가 생성되면 값을 할당하는 방법이다.

 

import {useState, useEffect} from 'react';
import {useRoute} from 'next/navigation';

export default function App() {
  const router = useRoute();
  
  const [auth, setAuth] = useState(null);
  
  useEffect(() => {
    if (localStorage.getItem('auth')) {
      setAuth(localStorage.getItem('auth');
    }
  }, [localStorage.getItem('auth')]);
  
  if (!auth) {
    return router.replace('/login');
  }
  
  return (
    <Component />
  )  
}

 

이 방법은 공식문서에서 추천하고 있는 방법이지만,

auth의 처음 값이 null이고 이후 변경되기 때문에 깜빡임 현상이 해결되지는 않았다..

 

🔗 Next13 공식문서 바로가기 - Browser APIs

 

 

Deploying: Static Exports | Next.js

Using App Router Features available in /app

nextjs.org

 

 

3️⃣ jotai_atomWithStorage

 

전역 상태 관리 도구로 jotai를 사용하고 있었는데

storage에 상태를 저장하여 새로고침을 해도 값이 초기화되지 않는 atomWithStorage라는 util api를 사용해보았다.

 

  • store에서 authAtom을 atomWithStorage로 초기화한다.
// store/auth.js

import { atomWithStorage } from 'jotai/utils';

export const authAtom = atomWithStorage('auth', null);

 

  • 로그인하는 시점에 상태를 업데이트한다.
// app/login/page.js

import {useSetAtom} from 'jotai';
import {authAtom} from '@/store/auth';

export default function LoginPage() {
  const setAuth = useSetAtom(authAtom);
  
  const onLogin = async (e) => {
    e.preventDefault();
    
    const user = await login(); // 로그인 함수
    setAuth(user);
  }
  
  return (
  <form onSubmit={onLogin}>
    // ...
    <button type='submit'>Login</button>
  </form>
  )
}

 

  • 페이지에서 auth 상태를 불러와서 로그인 여부를 판별한다!
// app/page.js

import {useRoute} from 'next/navigation';
import {useAtomValue} from 'jotai';
import {authAtom} from '@/store/auth';

export default function App() {
  const router = useRoute();
  const auth = useAtomValue(authAtom); 
  
  if (!auth) {
    return router.replace('/login');
  }
  
  return (
    <Component/>
  )
}

 

전역상태로 저장하고 새로고침을 하는 경우에도 값이 유지되어 문제가 해결될 것이라 생각했는데

storage를 사용하기 때문인지 초기 값이 undefined인 것을 확인할 수 있었다.

 

그래서 이 방법도 실패...!

 

🔗 Jotai 공식문서 바로가기 - atomWithStorage

 

 

Storage — Jotai, primitive and flexible state management for React

Jotai takes a bottom-up approach to global React state management with an atomic model inspired by Recoil. One can build state by combining atoms and renders are optimized based on atom dependency. This solves the extra re-render issue of React context and

jotai.org

 

 

4️⃣ cookie + middleware

 

마지막으로 생각해낸 방법이 바로 cookie에 정보를 저장한 뒤 Server-Side에서 로그인 여부를 판별하는 것이었다.

 

SSR 단계에서 모든 작업이 끝나면 CSR이 실행되기 전에 페이지를 이동시킬 수 있기 때문에

깜빡임 현상을 해결할 수 있을 것이라는 생각에서 시작된 발상(?)이었다.

 

middleware.js 파일은 프로젝트의 루트 디렉토리에서 사용한다.

공식문서에서는 pages, app, src 폴더 중 하나에 위치하면 된다고 한다.

 

 

  • 로그인 시점에서 쿠키에 정보를 저장한다.
// app/login/page.js

export default function LoginPage() {
  const onLogin = async (e) => {
    e.preventDefault();
    
    const user = await login(); // 로그인 함수
    document.cookie = `auth=${user}`; // 쿠키에 정보 저장
  }
  
  return (
  <form onSubmit={onLogin}>
    // ...
    <button type='submit'>Login</button>
  </form>
  )
}

 

  • 쿠키에 auth라는 key를 가진 값이 존재하면 메인 페이지로,
  • 존재하지 않으면 로그인 페이지로 이동시킨다.
// src/middleware.js

import { NextResponse } from "next/server";

export function middleware(request) {
  const auth = request.cookies.has("auth");
  
  // 로그인 + 로그인 페이지
  if (auth && request.nextUrl.pathname.startsWith("/login")) {
    return NextResponse.redirect(new URL("/", request.url));
  }
  
  // 로그인 X + 로그인 페이지 X
  if (!auth && !request.nextUrl.pathname.startsWith("/login")) {
    return NextResponse.redirect(new URL("/login", request.url));
  }
}

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

 

📌 matcher

미들웨어가 실행될 특정 경로를 의미한다. (미들웨어 필터링)
※ 정규식 사용이 가능함

 

이 방법을 사용하면 깜빡임 없이 바로 페이지 리다이렉션이 가능하다.

 

 

🔗 Next13 공식문서 바로가기 - middleware

 

 

Routing: Middleware | Next.js

Middleware allows you to run code before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly. Middleware runs before cached co

nextjs.org

 

 


 

리액트를 사용했을 때는 react-router-dom에서 제공하는 Navigate 컴포넌트와 localStorage를 이용하여

페이지 리다이렉션 기능을 간단하게 구현할 수 있었다.

 

그런데 Next.js를 사용하면서 생각보다 제약사항이 많았고, Next.js의 기능을 모르고 있던 부분들도 상당히 많았기 때문에

더 시간이 오래 걸렸던 것 같다.

 

이번 기회에 Next.js에 대해 더 많이 공부하는 계기가 되었고

앞으로도 제공되는 기능들을 효율적으로 사용할 수 있도록 공부해야겠다는 생각이 들었다.

 

 


 

🔆내가 보려고 정리하는 Next.js🔆

 

👉ez1n github 구경하기👈 

 

 

ez1n - Overview

Front-End Developer. ez1n has 15 repositories available. Follow their code on GitHub.

github.com