summaryrefslogtreecommitdiff
path: root/middleware.ts
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-04-08 03:08:19 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-04-08 03:08:19 +0000
commit9ceed79cf32c896f8a998399bf1b296506b2cd4a (patch)
treef84750fa6cac954d5e31221fc47a54c655fc06a9 /middleware.ts
parent230ce796836c25df26c130dbcd616ef97d12b2ec (diff)
로그인 및 미들웨어 처리. 구조 변경
Diffstat (limited to 'middleware.ts')
-rw-r--r--middleware.ts118
1 files changed, 102 insertions, 16 deletions
diff --git a/middleware.ts b/middleware.ts
index 4237bfb4..84ee2313 100644
--- a/middleware.ts
+++ b/middleware.ts
@@ -4,12 +4,61 @@ export const runtime = 'nodejs';
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import acceptLanguage from 'accept-language';
+import { getToken } from 'next-auth/jwt';
import { fallbackLng, languages, cookieName } from '@/i18n/settings';
acceptLanguage.languages(languages);
-export function middleware(request: NextRequest) {
+// 로그인이 필요 없는 공개 경로
+const publicPaths = [
+ '/evcp',
+ '/partners',
+ '/api/auth',
+];
+
+// 경로가 공개 경로인지 확인하는 함수
+function isPublicPath(path: string, lng: string) {
+ // 1. 정확한 로그인 페이지 매칭 (/ko/evcp, /en/partners 등)
+ if (publicPaths.some(publicPath => path === `/${lng}${publicPath}`)) {
+ return true;
+ }
+
+ // 2. auth API는 별도 처리
+ if (path.startsWith('/api/auth')) {
+ return true;
+ }
+
+ return false;
+}
+
+// 도메인-URL 일치 여부 확인 및 올바른 리다이렉트 경로 반환
+function getDomainRedirectPath(path: string, domain: string, lng: string) {
+ // 도메인이 없는 경우 리다이렉트 없음
+ if (!domain) return null;
+
+ // URL에 partners가 있는지 확인
+ const hasPartnersInPath = path.includes('/partners');
+ // URL에 evcp가 있는지 확인
+ const hasEvcpInPath = path.includes('/evcp');
+
+ // 1. 도메인이 'partners'인데 URL에 '/evcp/'가 있으면
+ if (domain === 'partners' && hasEvcpInPath) {
+ // URL에서 '/evcp/'를 '/partners/'로 교체
+ return path.replace('/evcp/', '/partners/');
+ }
+
+ // 2. 도메인이 'evcp'인데 URL에 '/partners/'가 있으면
+ if (domain === 'evcp' && hasPartnersInPath) {
+ // URL에서 '/partners/'를 '/evcp/'로 교체
+ return path.replace('/partners/', '/evcp/');
+ }
+
+ // 불일치가 없으면 null 반환 (리다이렉트 필요 없음)
+ return null;
+}
+
+export async function middleware(request: NextRequest) {
/**
* 1. 쿠키에서 언어 가져오기
*/
@@ -26,22 +75,16 @@ export function middleware(request: NextRequest) {
const { pathname, searchParams, origin } = request.nextUrl;
/**
- * 3. "/"" 경로로 들어온 경우 -> "/{lng}"로 리다이렉트
- * (예) / -> /en, / -> /ko ...
+ * 3. "/" 경로로 들어온 경우 -> "/{lng}"로 리다이렉트
*/
if (pathname === '/') {
const redirectUrl = new URL(`/${lng}`, origin);
- // 쿼리 파라미터가 있을 경우도 붙여주기
redirectUrl.search = searchParams.toString();
return NextResponse.redirect(redirectUrl);
}
/**
* 4. 현재 pathname이 언어 경로를 포함하고 있는지 확인
- * - /en
- * - /en/... (다른 하위 경로들)
- * - /ko
- * - /ko/...
*/
const hasValidLngInPath = languages.some(
(language) => pathname === `/${language}` || pathname.startsWith(`/${language}/`),
@@ -49,7 +92,6 @@ export function middleware(request: NextRequest) {
/**
* 5. 언어 경로가 누락된 경우 -> "/{lng}" + 기존 pathname 으로 리다이렉트
- * 예) /dashboard -> /en/dashboard
*/
if (!hasValidLngInPath) {
const redirectUrl = new URL(`/${lng}${pathname}`, origin);
@@ -57,28 +99,72 @@ export function middleware(request: NextRequest) {
return NextResponse.redirect(redirectUrl);
}
+ // 언어 코드 추출
+ const pathnameParts = pathname.split('/');
+ const detectedLng = pathnameParts[1]; // 예: /ko/partners -> ko
+
+ // 토큰 가져오기 (인증 상태 확인 및 도메인 검증에 사용)
+ const token = await getToken({ req: request });
+
+ /**
+ * 6. 인증된 사용자의 도메인-URL 일치 확인 및 리다이렉션
+ */
+ if (token && token.domain) {
+ // 사용자의 domain과 URL 경로가 일치하는지 확인
+ const redirectPath = getDomainRedirectPath(pathname, token.domain as string, detectedLng);
+
+ // 도메인과 URL이 일치하지 않으면 리다이렉트
+ if (redirectPath) {
+ const redirectUrl = new URL(redirectPath, origin);
+ redirectUrl.search = searchParams.toString();
+ return NextResponse.redirect(redirectUrl);
+ }
+ }
+
+ /**
+ * 7. 인증 확인: 공개 경로가 아닌 경우 로그인 체크 및 리다이렉트
+ */
+ if (!isPublicPath(pathname, detectedLng)) {
+ if (!token) {
+ // 어떤 로그인 페이지로 리다이렉트할지 결정
+ let loginPath;
+
+ // 경로에 따라 적절한 로그인 페이지 선택
+ if (pathname.includes('/partners') || pathname.startsWith(`/${detectedLng}/vendor`)) {
+ loginPath = `/${detectedLng}/partners`;
+ } else {
+ loginPath = `/${detectedLng}/evcp`;
+ }
+
+ // 로그인 후 원래 페이지로 리다이렉트하기 위해 callbackUrl 추가
+ const redirectUrl = new URL(loginPath, origin);
+ redirectUrl.searchParams.set('callbackUrl', request.url);
+
+ return NextResponse.redirect(redirectUrl);
+ }
+ }
+
/**
- * 6. 위 조건에 걸리지 않았다면 그대로 Next.js로 넘긴다.
+ * 8. 위 조건에 걸리지 않았다면 그대로 Next.js로 넘긴다.
*/
const response = NextResponse.next();
/**
- * 7. 쿠키에 저장된 언어와 현재 lng가 다르면 업데이트
+ * 9. 쿠키에 저장된 언어와 현재 lng가 다르면 업데이트
*/
const currentCookie = request.cookies.get(cookieName)?.value;
- if (lng && lng !== currentCookie) {
- response.cookies.set(cookieName, lng, { path: '/' });
+ if (detectedLng && detectedLng !== currentCookie) {
+ response.cookies.set(cookieName, detectedLng, { path: '/' });
}
return response;
}
/**
- * 8. (선택) 매칭할 경로 설정
- * - _next, 정적파일(.*), API 등의 경로는 제외시키는 예시
+ * 10. 매칭할 경로 설정
*/
export const config = {
matcher: [
- '/((?!_next|.*\\..*|api).*)',
+ '/((?!_next|.*\\..*|api/(?!auth)).*)',
],
}; \ No newline at end of file