오랜만에 쭈꾸미 집을 들려 안부 인사를 묻고 웹사이트는 문제 없냐 물어봤다. 그런데 직원 핸드폰에서는 접속이 잘되는데 가끔 외국인 핸드폰에서는 404 에러가 뜬다고 한다. 아니 이런.. 그게 무슨 말이야?! 왜지 왤까.. 집에가면서 계속 생각을 해봤고 아무래도 vercel 에서 해외 IP 차단을 한 것 일 수도 있다고 생각이 들었다.
부랴부랴 집에 와서 vercel 에서의 내 프로젝트를 확인해 오류 로그를 보려고 하니 아뿔싸 분석기는 따로 import 해서 layout.tsx 에 넣어야 작동을 한다는 것!?
사실 다국어 언어 메뉴를 만들면서 초기구상은 nexti18n 을 사용하기로 했지만, 배움의 부담에 못이겨 하드코딩으로 진행했다. 그러다보니 유지보수는 물론,, 가독성이 완전 제로제로제로였다. 그래서 이를 잘 관리하기 위해 기존 기획했던 nexti18n 을 제대로 적용해보려고 한다.
처음부터 잘못됐던
나는 초반에 nexti18n 라이브러리를 적용하기 위해 설치를 했으나 구글링을 통해 제대로 확인해보니 Next13/14 App 기반 환경은 기존 라이브러리를 지원하지 못한다고 한다!?!
대충 app 에서는 지원하지 않는다는 의미
대신 i18next, react-i18next, i18next-resources-to-backend 라이브러리를 사용해 구성하라고 한다.
import {notFound} from 'next/navigation';
import {getRequestConfig} from 'next-intl/server';
// Can be imported from a shared config
const locales = ['en', 'ko'];
export default getRequestConfig(async ({locale}) => {
// Validate that the incoming `locale` parameter is valid
if (!locales.includes(locale as any)) notFound();
return {
messages: (await import(`../messages/${locale}.json`)).default
};
});
next-intl은 요청 범위의 구성 객체를 생성하여 사용자의 언어에 따라 메시지 및 기타 옵션을 제공할 수 있으며, 이를 서버 컴포넌트에서 사용할 수 있습니다.
4) middleware.ts
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
// A list of all locales that are supported
locales: ['en', 'ko'],
// Used when no locale matches
defaultLocale: 'en'
});
export const config = {
// Match only internationalized pathnames
matcher: ['/', '/(ko|en)/:path*']
};
middleware 에서는 요청에 따른 언어가 매칭되는지 확인하고 리다이렉트하거나 리라이트 하는 역할을 한다.
5) app/[locale]/layout.tsx
import {NextIntlClientProvider} from 'next-intl';
import {getMessages} from 'next-intl/server';
export default async function LocaleLayout({
children,
params: {locale}
}: {
children: React.ReactNode;
params: {locale: string};
}) {
// Providing all messages to the client
// side is the easiest way to get started
const messages = await getMessages();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
6) app/[locale]/page.tsx
import {useTranslations} from 'next-intl';
export default function Index() {
const t = useTranslations('Index');
return <h1>{t('title')}</h1>;
}
[locale] 이라는 파일 명을 통해 미들웨어에서 매칭된 언어인 경우 주소값을 가질 수 있다.
결과
테스트 성공!
기존 웹사이트에 적용하기
이제 next-intl 을 적용되었으니 기존 사이트에 추가해야한다. locale (언어 변수) 를 전역으로 관리해야한다는 아이디어가 생겨 상태를 관리할 수 있는 LanguageContext.tsx 를 만들었다.
여기서 저장된 selectedLanguage 는 다음 함수를 호출함으로 외부에서 사용할 수 있다.
3. useLanguage
export const useLanguage = (): LanguageContextProps => {
const context = useContext(LanguageContext);
if (context === undefined) {
throw new Error('useLanguage must be used within a LanguageProvider');
}
return context;
};
이미지에 사이즈가 명확하게 설정이 되어있지 않아 이미지 원본파일의 raw data가 그대로 적용되어 이미지 로딩이 커진것이었다..! 그래서 이미지에 명확한 사이즈를 선언하기 위해 고민을 해보았다.
우선은 Image 에 width , height 요소를 지정해주면 되지만 나는 이미지가 부모의 width, height 에 맞게 조절되어야 하기 때문에 이미지 크기를 지정하는건 올바르지 않다.
어떻게하면 부모크기만큼만 이미지가 가득 채워질지..?
fill ={true}
Next/Image 에는 여러가지 속성이 있고 속성 중 하나인 fill 이 있다. fill 을 사용하게 된다면 자동적으로 postion 이 "absolute" 가 지정된다. 따라서, 부모가 fixed 혹은 relative, absolute 여야 자식의 width, height 값이 조정된다고한다.