Middleware를 활용한 Secure Routes 설정 - Nextjs 1

rainlee·2024년 3월 2일

nextjs

목록 보기
1/1
post-thumbnail

백오피스 도메인에서 route protect를 리팩토링 하면서 Nextjs에서 제공하는 Middleware를 사용하였다. 기존에는 어떤 문제가 있었고 이를 해결하기 위해 왜 Middleware를 선택했는지 얘기해보도록 하겠습니다!

문제점

  1. 유저플로우가 올바르게 진행되지 않는다.
  • 로그인 페이지를 각 로그인 화면을 가지고 있던 상태에서, 사내 통합로그인 페이지가 생기면서 로그인 페이지로가면 로그인하러가기 버튼을 눌러 통합로그인 주소로 이동한다. 즉 쓸모없는 로그인 페이지가 존재한다.
  • 로그인한 유저는 로그인 페이지로 진입할 수 없어야 하는데 접근이 가능하며, 다시 로그인 플로우를 진행해야만 로그인이 가능하다.
  1. 라우팅 이동이 최대 16초가 걸려 UX를 해친다.
  • 30~1시간 정도 탭을 켜놓고 다시 인터렉션을 하면 라우팅 이동 시 최대 16초의 시간이 걸려 사용성에 지장이 생긴다.
  1. 데드코드 및 응집도가 낮은 코드가 많다.
    • 레거시 로그인 로직이 존재하여 다양하게 존재한다. 유지보수를 진행하면서 업데이트 되어야 했을 로그인 관련 코드가 방치되어 최초 로그인로직, 수정한 로그인 로직이 얽혀 느려지는 주요 원인이 되었다. 😇
  2. UI가 잠시 나타났다 사라지는 플리커링 현상
    • client 에서 route protecting을 하다보니 DOM이 랜더된 후 로직이 실행되어 1초정도 필요하지 않는 UI가 랜더된 후 라우트가 이동한다.

Secure Routes의 종류

Middleware에서 구현하기로 결정하기까지 Secure Routes에 대한 다양한 방식을 고민했습니다. 크게는 client 방식과 server 방식으로 나뉘어지고, 총 4가지의 방법이 있습니다.

  1. custom-hooks를 통한 Client-Side 방식 (AS-IS 방식)
  2. Higher Order Component(이하 HOC)를 통한 Client-Side 방식
  3. Server-Side를 활용한 방식
  4. Middleware를 활용한 방식

4가지를 방식을 고려하면서 최종적으로 Middleware를 활용하는게 다른 방식보다 효과적이고 좋다고 느꼈습니다. Client-Side의 hooks의 방식은 기존 코드 자체를 분석하고 고치는 비용이 더 많다고 생각했으며, HOC, Server-Side로 구현할 경우도 화이트/블랙리스트를 설정하는 방식이 Middleware방식보다 장점이 없다고 느꼈습니다.

미들웨어란?

Nextjs 공식문서에서는 아래와 같이 서술하였다. 한줄로 설명하자면 nextjs에서 페이지를 렌더링하기 전에 서버 측에서 실행되는 함수이다.

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.

어떨때 사용할까?

  • 페이지 렌더링 전에 인증을 확인하거나 요청을 확인한다.
  • 요청 데이터를 사전에 처리하거나 특정 API요청을 수행하거나 캐시를 관리한다.
  • 요청에 대한 응답을 변환하거나 에러를 처리할 수 있다.

리팩토링을 진행해보자..!

route protect의 구분하는 매개는 결국 로그인유무이다. 해당부분도 같이 리팩토링을 진행했지만, 주제가 섞여 쓸데없는 내용이 길어지므로 기회가 되면 다른글로 다루도록 하겠다. AS-IS 버전에서는 client에서 유효성을 진행하였지만, Middleware에서 로그인 유무를 체크하기 위해서 로그인 시 쿠키에 isAuthenticated key에 로그인 유무 boolean을 저장하고, Middleware에서 이 값을 보고 판단하여 로그인 유무를 체크하였다.

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

export function middleware(req: NextRequest) {
  const response = NextResponse.next();

  const isAuthenticated: boolean = JSON.parse(req.cookies.get('isAuthenticated')?.value ?? 'false');

  if (req.nextUrl.pathname.includes(SIGN_IN_PATH) && isAuthenticated) {
    const homeURL = new URL(HOME_PATH, req.nextUrl.origin);
    return NextResponse.redirect(homeURL.toString());
  }

  if (!whiteListRoutes.includes(req.nextUrl.pathname) && !isAuthenticated) {
    const signinURL = new URL(SIGN_IN_PATH, req.nextUrl.origin);
    return NextResponse.redirect(signinURL.toString());
  }


  return response;
}

const SIGN_IN_PATH = '/signin';
const HOME_PATH = '/editor';
const AUTH_PATH = '/oauth';

const whiteListRoutes = [AUTH_PATH, SIGN_IN_PATH];

리팩토링 후 개선점

그렇다면 리팩토링 후 맨 위에 얘기했던 4가지 개선점이 다 해결되었을까?

  1. 유저플로우가 올바르게 진행되지 않는다.
    • 해결완료
  2. 라우팅 이동이 최대 16초가 걸려 UX를 해친다.
    • 평균 2.4초로 85% 감소
  3. 데드코드 및 응집도가 낮은 코드가 많다.
    • useAuthenticatedUser, useAuthenticatedUserEffect 등 2가지의 custom-hooks로 관리하던 로직을 한곳에서 관리하여 응집도를 높였다.
  4. UI가 잠시 나타났다 사라지는 플리커링 현상
    • Middleware에서 redirect하는 경우는 DOM이 랜더링 되지 않으므로 해당 플리커 현상이 나타나지 않는다!

느낀점

이번 리팩토링을 진행하면서 다양하게 Secure Routes를 설정할 수 있는걸 알게되어 좋았던것 같다. 또한 설정을 할 때 auth에 관해서도 연계되기 때문에 auth도 같이 리팩토링을 진행하여 좀 더 견고한 코드가 된 것 같다. 이부분은 다른 포스팅에서 다루도록하겠습니다. 😄

참조링크

0개의 댓글