feat(WEB): Phase 2-3 V2 마이그레이션 완료 및 ServerErrorPage 적용
Phase 2 완료 (4개): - 노무관리, 단가관리(건설), 입금, 출금 Phase 3 라우팅 구조 변경 완료 (22개): - 거래처(영업), 팝업관리, 공정관리, 게시판관리, 대손추심, Q&A - 현장관리, 실행내역, 견적관리, 견적(테스트) - 입찰관리, 이슈관리, 현장설명회, 견적서(건설) - 협력업체, 시공관리, 기성관리, 품목관리(건설) - 회계 도메인: 거래처, 매출, 세금계산서, 매입 신규 컴포넌트: - ErrorCard: 에러 페이지 UI 통일 - ServerErrorPage: V2 페이지 에러 처리 필수 - V2 Client 컴포넌트 및 Config 파일들 총 47개 상세 페이지 중 28개 완료, 19개 제외/불필요 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -489,11 +489,33 @@ export async function middleware(request: NextRequest) {
|
||||
const refreshResult = await refreshTokenInMiddleware(refreshToken);
|
||||
|
||||
if (refreshResult.success && refreshResult.accessToken) {
|
||||
// 새 토큰을 쿠키에 설정하여 응답과 함께 전송
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
// 🆕 request headers에 새 토큰 설정 (같은 요청 내 서버 컴포넌트가 읽을 수 있도록)
|
||||
// Set-Cookie는 응답 헤더에만 설정되어 같은 요청 내 cookies()로 읽을 수 없음
|
||||
// 따라서 request headers로 새 토큰을 전달하여 serverFetch에서 사용하도록 함
|
||||
const requestHeaders = new Headers(request.headers);
|
||||
requestHeaders.set('x-refreshed-access-token', refreshResult.accessToken);
|
||||
requestHeaders.set('x-refreshed-refresh-token', refreshResult.refreshToken || '');
|
||||
|
||||
// intlMiddleware 효과를 먼저 가져옴
|
||||
const intlResponse = intlMiddleware(request);
|
||||
|
||||
// 새 access_token 쿠키 설정
|
||||
// 새 response 생성: request headers 전달 + intlResponse 헤더 복사
|
||||
const response = NextResponse.next({
|
||||
request: {
|
||||
headers: requestHeaders,
|
||||
},
|
||||
});
|
||||
|
||||
// intlResponse의 헤더를 복사 (locale 관련 헤더 등)
|
||||
intlResponse.headers.forEach((value, key) => {
|
||||
if (key.toLowerCase() !== 'set-cookie') {
|
||||
response.headers.set(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
// 새 access_token 쿠키 설정 (클라이언트의 다음 요청을 위해)
|
||||
const accessTokenCookie = [
|
||||
`access_token=${refreshResult.accessToken}`,
|
||||
'HttpOnly',
|
||||
@@ -531,19 +553,19 @@ export async function middleware(request: NextRequest) {
|
||||
`Max-Age=${refreshResult.expiresIn || 7200}`,
|
||||
].join('; ');
|
||||
|
||||
intlResponse.headers.append('Set-Cookie', accessTokenCookie);
|
||||
intlResponse.headers.append('Set-Cookie', refreshTokenCookie);
|
||||
intlResponse.headers.append('Set-Cookie', tokenRefreshedCookie);
|
||||
intlResponse.headers.append('Set-Cookie', isAuthenticatedCookie);
|
||||
response.headers.append('Set-Cookie', accessTokenCookie);
|
||||
response.headers.append('Set-Cookie', refreshTokenCookie);
|
||||
response.headers.append('Set-Cookie', tokenRefreshedCookie);
|
||||
response.headers.append('Set-Cookie', isAuthenticatedCookie);
|
||||
|
||||
// 보안 헤더 추가
|
||||
intlResponse.headers.set('X-Robots-Tag', 'noindex, nofollow, noarchive, nosnippet');
|
||||
intlResponse.headers.set('X-Content-Type-Options', 'nosniff');
|
||||
intlResponse.headers.set('X-Frame-Options', 'DENY');
|
||||
intlResponse.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||
response.headers.set('X-Robots-Tag', 'noindex, nofollow, noarchive, nosnippet');
|
||||
response.headers.set('X-Content-Type-Options', 'nosniff');
|
||||
response.headers.set('X-Frame-Options', 'DENY');
|
||||
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||
|
||||
console.log(`✅ [Middleware] Pre-refresh complete, new tokens set in cookies`);
|
||||
return intlResponse;
|
||||
console.log(`✅ [Middleware] Pre-refresh complete, new tokens set in cookies and request headers`);
|
||||
return response;
|
||||
} else {
|
||||
// 갱신 실패 시 쿠키 삭제 후 로그인 페이지로
|
||||
// 🔴 CRITICAL: 쿠키를 삭제하지 않으면 무한 리다이렉트 루프 발생
|
||||
|
||||
Reference in New Issue
Block a user