[Next.js] Auth와 Middleware

Middleware

next.js 서버로 요청이 전달되기 전에 코드를 실행할 수 있는 기능을 제공

들어오는 요청을 기반으로 응답을 재작성, 리다이렉트, 요청 또는 응답 헤더 수정, 혹은 직접 응답할 수 있다

 

생성

src 폴더에 middleware.ts 파일 생성

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
  console.log(request);
}

export const config = {
  matcher: '/',
};

- middleware(): 실제로 기능을 실행시켜줄 함수

- config: 어떤 페이지에서 해당 기능을 사용할 것인지 matcher를 설정할 수 있음

- 위 예시는 '/' 경로에 접속할 때마다 requst를 출력한다

 

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
  return NextResponse.redirect(new URL('/home', request.url));
}

export const config = {
  matcher: '/',
};

'/'에 접속시 '/home'으로 redirect 한다 (url까지 '/home'으로 변경)

rewrite 옵션도 있는데, 이 경우 url 자체는 '/'로 유지되지만 실제로는 '/home'의 컴포넌트가 출력된다

 

matcher의 옵션들

1) *

'/about/:path*' 와 같이 사용하는 경우 '/about/:path' 아래 모든 경로들에 적용된다

 

2) [ ]

다수의 matcher를 지정하고 싶은 경우 배열을 사용할 수 있다

matcher: ['/', 'dashboard/:path*']

 

3) 정규식 사용

matcher: "/((?!api|_next/static|_next/image|favicon.ico).*)"

와 같이 경로에 대해 원하는 조건을 정규식으로 표현할 수도 있다

 

middleware()에서 검증하기

export async function middleware(request: NextRequest) {
const isLogin = false;

  if(!isLogin && request.nextUrl.pathname.includes('/cart') {
    // 원하는 처리
  }
}

위와 같이 middleware 함수에서도 matcher에 맞아 들어온 요청을 조건문 등으로 검증할 수도 있다

 

 

Authentication

json-sever-auth 설치

json-server와 유사한데, auth 기능이 있는 라이브러리다

yarn add -D json-server-auth

 

// package.json
"scripts": {
  "auth": "json-server-auth --watch auth-db.json --port 8000"
}

 

auth-db.json 생성

루트 디렉토리에 파일을 생성한다

{
  "users": [
    {
      "email": "user@example.com",
      "password": "$2a$10$eHUzzsY1tbOmyGVqPwVEsuYRQb4LP2Hw/dMXgxp5p8eYE4UgcZMA.", 
      "id": 1
    }
  ]
}

 

api 검증

// app/api/sign-in/route.ts
import { NextResponse } from 'next/server';

export async funtcion POST(request: Request){
  const payload = await response.json();  // request body에 넣어둔 payload를 받을 수 있다
  return NextResponse.json({
    data: true,
  });
}

 

로그인 api 요청을 하는 페이지에서 바로 api url이 아니라 위에 만들어둔 내부 서버에 요청하도록 수정한다

// src/.../SignInForm.tsx

const onSubmit = async(value: FieldValues) => {
  const res = await fetch('/api/sign-in', {
    method: 'POST',
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      ...value,
    }),
  }
}

 

원래 localhost:8000 (json-server-auth 서버 주소)로 보내려던 걸 '/api/sign-in' 으로 보내줬으니,

이제 '/api/sign-in'에서 json-server-auth 서버로 보내도록 설정해줘야한다!

 

// app/api/sign-in/route.ts
import { NextResponse } from 'next/server';

export async funtcion POST(request: Request){
  const payload = await response.json();  // request body에 넣어둔 payload를 받을 수 있다
  
  const res = await fetch("http://localhost:8000/sign-in", {
    method: "POST",
    headers: {
      "content-Type": "application/json",
    },
    cache: "no-store",
    body: JSON.stringify({
       ...payload,
    })
  });
  
  const data = await res.json();
  const cookieStore = cookies();
  
  cookieStore.set("accessToken", data.accessToken, {
    httpOnly: true,  // 브라우저 쿠키에서는 accessToken에 접근할 수 없고, 서버 환경에서만 접근 가능해짐
  });
  
  return NextResponse.json({
    data: true,
  });
}

 

그럼 인증된 사용자인지 확인할 때 이 cookie에 저장된 값을 사용할 수 있을 것이다! (middleware)

export async function middleware(request: NextRequest) {
  const isLogin = !!request.cookies.get("accessToken")?.value;
  
  if(!isLogin && request.nextUrl.pathname.includes('/cart')) {
    return NextResponse.redirect(new URL('/sign-in', request.url));
  }
}

 

'Next.js' 카테고리의 다른 글

[Next.js/Tanstack-Query] Next에서 prefetch하기  (0) 2024.10.01
[Next.js] Parallel Routes & Intercepting Routes  (1) 2024.09.29
[Next.js] Caching  (1) 2024.09.28
[Next.js] Router Handler & Server Action  (0) 2024.09.27
[Next.js] Asset 최적화  (1) 2024.09.27