프로덕션 배포를 진행하며 함께 프론트를 진행하던 팀원이 미참여하게 되었다. 그래서 혼자 데모버전의 프로젝트를 리팩토링하고 프로덕션에 맞게 고치기로 했다.
그 과정의 첫 번째로 로그인 로직에 대한 점검 겸 리팩토링을 진행하였는데 아래와 같은 상황이 발생했다.
<Header />, <Navbar /> 등 만 정적으로 프리렌더링해서 SSR을 통해 사용자에게 html 먼저 보내주고, 뮤직룸 등 실시간 데이터들을 불러오는 컴포넌트들은 CSR(클라이언트 컴포넌트)로 처리Application error: a client-side exception has occurred (see the browser console for more information).
즉, 클라이언트에서 로그인 유무 확인 로직이 불분명하다.
이를 해결하기 위해 일단 클라이언트에서의 로그인 유무 분기 처리를 확실히 하도록 고치기로 했다.
그럼 로그인 유무 확인은 어디서 해야할까?
일반 리액트라면 app 컴포넌트에서 useEffect를 이용해 로그인 확인을 통해 접근 분기를 다르게 할 수 있었을 것 같다.
그렇다면 Next.js의 SSR에서는 어떻게 확인해야할까?
여러 방법이 있겠지만 우선 Next.js의 미들웨어를 이용해 페이지마다 사용자의 로그인 유무를 확인하기로 했다.
시스템의 일부가 데이터를 통신하고 관리할 수 있도록 하는 모든 소프트웨어(서비스)
페이지를 렌더링하기 전 서버 측에서 실행되는 함수
export const config = {
matcher: '/about/:path*',
}
토큰의 유효성은 쿠키와 axios interceptors를 통해 관리된다.
accessToken과refreshToken을 사용한다.
1. 로그인 시 둘 다 발급 -> 쿠키에 각각 저장
2. 네트워크 요청 시 헤더에 담아서 보냄
- 토큰이 유효하지 않은 경우 401 발생 -> axios interceptor에서 재발급 로직 작동
- 정상적으로 발급 완료 -> 새 토큰 쿠키에 저장
- 토큰 재발급 오류 시 -> removeCookie 후 로그인 페이지로 리다이렉트
// src/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const accessToken = request.cookies.get('accessToken')?.value;
const pathUrl = request.nextUrl.pathname;
// 로그인 된 사용자가 로그인 페이지 요청 시 / 페이지로 강제 리다이렉트
if (accessToken && pathUrl.startsWith('/login')) {
return NextResponse.redirect(new URL('/', request.url));
}
// 로그인 미완료된 사용자가 일반 페이지 요청 시 로그인 페이지로 강제 리다이렉트
if (!accessToken && !pathUrl.startsWith('/login')) {
return NextResponse.redirect(new URL('/login', request.url));
}
}
export const config = {
matcher: [
/*
* 다음 목록들로 시작되는 경로를 미들웨어 체크에서 제외하기:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
* - server/ (네트워크 요청 경로)
*/
{
source: '/((?!api|_next/static|_next/image|favicon.ico|server).*)',
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
},
],
};
middleware.ts 파일을 app 폴더와 같은 레벨에 생성한다.middleware 함수는 request 객체를 매개변수로 갖는데 request 객체에서 쿠키의 토큰값을 가져올 수 있다.request 객체의 nextUrl의 pathname으로 현재 요청의 경로를 가져온다.NextResponse.redirect(경로)로 분기 처리를 해준다.accessToken이 존재하고, pathUrl이 '/login'로 시작한다면, 로그인된 사용자가 로그인 페이지를 요청한 경우 -> 메인 페이지로 이동accessToken이 존재하지 않고, pathUrl이 '/login'으로 시작하지 않는다면, 로그인이 완료되지 않은 사용자가 일반 페이지를 요청한 경우 -> 로그인 페이지로 이동/api로 시작하는 경로), 정적 파일(/_next/static로 시작하는 경로), 이미지 최적화 파일(/_next/image로 시작하는 경로), 파비콘 파일(favicon.ico 경로), 프록시 설정한 네트워크 요청 URL(server/로 시작하는 경로)를 제외시켜준다.
Next.js 미들웨어를 처음으로 프로젝트에 사용해보며 사용법을 익히고 원리를 이해할 수 있어서 좋은 경험이었다.
코드 리팩토링과 분석 과정에서 원래의 코드와 로직을 해석하고 문제를 찾는 것이 예상보다 어려웠지만, 이러한 과정을 통해 많은 경험을 쌓을 수 있었던것 같다. 또한, 타인이 작성한 코드를 분석하면서 주석을 잘 달고 유지보수가 쉽게 코드를 작성하는 것의 중요성을 다시 한 번 깨닫게 되었다.