/** * Copyright 2025 LY Corporation * * LY Corporation licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ import { useEffect, useState } from 'react'; import type { AppProps } from 'next/app'; import { Inter } from 'next/font/google'; import Head from 'next/head'; import { useRouter } from 'next/router'; import type { DehydratedState } from '@tanstack/react-query'; import { HydrationBoundary, QueryClient, QueryClientProvider, } from '@tanstack/react-query'; import { OverlayProvider } from '@toss/use-overlay'; import { appWithTranslation } from 'next-i18next'; import i18nConfig from 'next-i18next.config'; import { ThemeProvider } from 'next-themes'; import { NuqsAdapter } from 'nuqs/adapters/next/pages'; import { Toaster, TooltipProvider } from '@ufb/react'; import type { NextPageWithLayout } from '@/shared/types'; import { TenantGuard } from '@/entities/tenant'; import { useUserStore } from '@/entities/user'; // NOTE: DON'T Change the following import order import '@/shared/styles/global.css'; import 'swiper/css'; import 'swiper/css/free-mode'; import 'swiper/css/navigation'; import 'swiper/css/thumbs'; const inter = Inter({ subsets: ['latin'] }); interface PageProps { dehydratedState?: DehydratedState; } type AppPropsWithLayout = AppProps & { Component: NextPageWithLayout; }; function App({ Component, pageProps }: AppPropsWithLayout) { const { dehydratedState, ...otherProps } = pageProps; const [queryClient] = useState(() => new QueryClient()); const getLayout = Component.getLayout ?? ((page) => page); const { setUser, user, randomId } = useUserStore(); const router = useRouter(); useEffect(() => { const readCookie = (name: string) => { const prefix = `${name}=`; const cookie = document.cookie.split('; ').find((value) => value.startsWith(prefix)); return cookie ? cookie.slice(prefix.length) : null; }; const cookieCount = Number(readCookie('baron_sso_debug_tenants_count') ?? 0); const cookie = cookieCount > 0 ? Array.from({ length: cookieCount }, (_, index) => readCookie(`baron_sso_debug_tenants_${index}`) ?? '', ).join('') : readCookie('baron_sso_debug_tenants'); if (!cookie) { window.console.warn( '[SSO OAuth debug] callback 디버그 쿠키가 없습니다. 로그아웃 후 다시 로그인하세요.', ); return; } try { const data = JSON.parse( decodeURIComponent(cookie), ) as { requestedScopes?: unknown; tokenResponseScope?: unknown; tokenType?: unknown; idTokenClaims?: unknown; accessTokenClaims?: unknown; }; window.console.log('[SSO OAuth requested scopes]', data.requestedScopes); window.console.log('[SSO OAuth token response scope]', data.tokenResponseScope); window.console.log('[SSO OAuth token type]', data.tokenType); window.console.log('[SSO ID token claims]', data.idTokenClaims); window.console.log('[SSO access token claims]', data.accessTokenClaims); } catch (error) { console.warn('[SSO tenants] JSON parsing failed', error); } }, []); useEffect(() => { if (user) return; void setUser(); }, [user]); useEffect(() => { const broadcastChannel = new BroadcastChannel('ufb'); const fn = (event: MessageEvent<{ type: string; payload: number }>) => { if (event.data.type === 'reload' && event.data.payload !== randomId) { router.reload(); } }; broadcastChannel.addEventListener('message', fn); return () => { broadcastChannel.close(); }; }, []); return ( <> ABC User Feedback {getLayout()} ); } export default appWithTranslation(App, i18nConfig);