Next.js ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ค๊ตญ์ด๋ฅผ localStorage ๊ธฐ๋ฐ์ผ๋ก ์ฒ๋ฆฌํ ๋ ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค.
- ์ด๊ธฐ ํ๋ฉด ๋ก๋ฉ ์ ๋ฒํผ ํ ์คํธ๊ฐ ๋น์ด ์์
- ์๋ก๊ณ ์นจ ํ์๋ ์ ์์ ์ผ๋ก ๋ค๊ตญ์ด ๋ฌธ๊ตฌ๊ฐ ํ์๋จ
์ ๋ฌธ์ ์ ์์ธ์ ๋ถ์ํ๊ณ ํด๋ผ์ด์ธํธ ํ๊ฒฝ์์ ๋ค๊ตญ์ด๋ฅผ ๋ถ๋ฌ์ค๋๋ก ์์ ํ๊ณ , ์ฑ๋ฅ ์ต์ ํ ์์ ์ ํด๋ณด์์ต๋๋ค.
๋ฌธ์ ์์ธ
const DUMMY_DATA = [
{
id: 0,
name: getLang("WD000266"),
...
},
...
];
getLang(code)๋ localStorage์์ ๋ค๊ตญ์ด ๋ฌธ์์ด์ ๋ถ๋ฌ์ค๋ ํจ์์ ๋๋ค.
export const getLang = (code) => {
if (typeof window !== "undefined") {
const langList = JSON.parse(localStorage.getItem("langList"));
return langList?.[code] || "";
}
return code;
};
์ฃผ์ ์์ธ
- SSR ํ๊ฒฝ์์ window, localStorage๋ ์กด์ฌํ์ง ์์
์๋ฒ ์ธก ๋ ๋๋ง ์ค getLang()์ด ์คํ๋๋ฉด ๋น ๋ฌธ์์ด ๋ฐํ - ์ปดํฌ๋ํธ ์ธ๋ถ ์ ์ญ ์ค์ฝํ์์ ์คํ๋จ
ํด๋ผ์ด์ธํธ์์ localStorage๊ฐ ์ค๋น๋๊ธฐ ์ ํธ์ถ๋๋ฏ๋ก ๋ค๊ตญ์ด ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค์ง ๋ชปํจ
ํด๊ฒฐ ๋ฐฉ๋ฒ
1. ํด๋ผ์ด์ธํธ ์ ์ฉ ํ ์์ฑ
// hooks/useLang.js
import { useEffect, useState } from "react";
import { loadLangList } from "/utils";
const useLang = (code) => {
const [lang, setLang] = useState("");
useEffect(() => {
const langList = loadLangList();
setLang(langList?.[code] || "");
}, [code]);
return lang;
};
export default useLang;
- ํด๋ผ์ด์ธํธ ๋ง์ดํธ ์ดํ์๋ง localStorage ์ ๊ทผ
- React lifecycle์ ์์ ํ๊ฒ ๋์
2. ์ปดํฌ๋ํธ ๋ด๋ถ์์ ๋ฐ์ดํฐ ๊ตฌ์ฑ
const Component = () => {
const nameDownload = useLang("WD000266");
const namePrint = useLang("WD000258");
const nameRefresh = useLang("WD000265");
const nameAction = useLang("WD000264");
const nameRemove = useLang("WD000227");
const buttonPanelItems = useMemo(
() => [
{ id: 0, name: nameDownload, key: "download" },
{ id: 1, name: namePrint, key: "print" },
{ id: 2, name: nameRefresh, key: "refresh" },
{ id: 3, name: nameAction, key: "action" },
{ id: 4, name: nameRemove, key: "remove" },
{ id: 5, name: "bar", key: "bar" },
],
[nameDownload, namePrint, nameRefresh, nameAction, nameRemove]
);
return buttonPanelItems.map((item) => (
<button key={item.key}>{item.name}</button>
));
};
- ์ ์ญ์ด ์๋ ์ปดํฌ๋ํธ ๋ด๋ถ์์ ๋ฐ์ดํฐ๋ฅผ ์ ์
- useMemo๋ฅผ ํตํด ์ฑ๋ฅ ์ต์ ํ
3. ์บ์ ์ ์ฉ์ผ๋ก ์ฑ๋ฅ ์ต์ ํ
// utils/utils.js
let cachedLangList = null;
export const loadLangList = () => {
if (cachedLangList !== null) return cachedLangList;
try {
const stored = localStorage.getItem("langList");
cachedLangList = stored ? JSON.parse(stored) : {};
} catch {
cachedLangList = {};
}
return cachedLangList;
};
- localStorage๋ ์ต์ด 1ํ๋ง ์ ๊ทผ
- ์ดํ ๋ฉ๋ชจ๋ฆฌ ์บ์๋ ๊ฐ์ฒด ์ฌ์ฉ
์ฑ๋ฅ ์ธก์
useEffect(() => {
console.time("Original");
for (let i = 0; i < 1000; i++) {
getLangOriginal("WD000258");
}
console.timeEnd("Original");
console.time("Optimized");
for (let i = 0; i < 1000; i++) {
getLang("WD000258");
}
console.timeEnd("Optimized");
}, []);
| ๊ธฐ์กด ๋ฐฉ์ | ์ฝ 103.9ms |
| ์ต์ ํ ๋ฐฉ์ | ์ฝ 0.059ms |
> ์ฝ 1,700๋ฐฐ ์ฑ๋ฅ ํฅ์ ํ์ธ
์บ์ ๋์ ์๋ฆฌ
- cachedLangList๋ ๋ชจ๋ ์ค์ฝํ์ ์ ์ฅ๋๋ฉฐ, JavaScript์ Heap ๋ฉ๋ชจ๋ฆฌ์ ์ ์ง๋จ
- SPA ํ๊ฒฝ์์๋ ํ์ด์ง ์ด๋ ์์๋ ์บ์ ์ ์ง
- ํ์ด์ง ์๋ก๊ณ ์นจ์ด๋ ๋ธ๋ผ์ฐ์ ์ข ๋ฃ ์ ์บ์ ์ด๊ธฐํ
์ฃผ์์ฌํญ
- ์ธ๋ถ์์ localStorage ๋ฐ์ดํฐ๊ฐ ์์ ๋๋ฉด ์บ์ ๋ฌดํจํ๊ฐ ํ์ํจ
- ๋์ฉ๋ ๋ฐ์ดํฐ์ผ ๊ฒฝ์ฐ ๋ฉ๋ชจ๋ฆฌ ์ ์ ๋ฅผ ๊ณ ๋ คํด์ผ ํจ
์ ๋ฆฌ
| ๊ฐ์ ์ | ๊ฐ์ ํ | |
| SSR ๋์ | window, localStorage ์ค๋ฅ ๋ฐ์ | ํด๋ผ์ด์ธํธ ์ฌ์ด๋์์ ์์ ํ๊ฒ ์ฒ๋ฆฌ |
| ์ฑ๋ฅ | ๋งค๋ฒ JSON ํ์ฑ ๋ฐ localStorage ์ ๊ทผ | ์บ์ ์ ์ฉ์ผ๋ก ํ ๋ฒ๋ง ์ ๊ทผ |
| ๊ตฌ์กฐ | ์ ์ญ ์ค์ฝํ์์ ์คํ | ์ปดํฌ๋ํธ ๋ด๋ถ์์ ๋ค๊ตญ์ด ๋ฐ์ดํฐ ๊ตฌ์ฑ |
| ๋ ๋๋ง ์์ ์ฑ | ์ด๊ธฐ ๋น ๋ฌธ์์ด ๋ ๋๋ง ๊ฐ๋ฅ์ฑ ์์ | ํด๋ผ์ด์ธํธ ๋ง์ดํธ ํ ์ ํํ๊ฒ ๋ ๋๋ง ๊ฐ๋ฅ |
(๋ก์ปฌ์คํ ๋ฆฌ์ง ๋์ ์ ์ JSON ํ์ผ์ ์ฌ์ฉํ๋ฉด ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง ์ ๋ฌธ์ ๊ฐ ์์๊ฒ ์ง๋ง,
๋ฐฑ์๋์์๋ ๋ค๊ตญ์ด ๋ฐ์ดํฐ๊ฐ ํ์ํ ์ ๊ณผ ์ ์ ํ์ผ ๋ฐฉ์์ ๋ฐฐํฌ ์์๋ง ๋ค๊ตญ์ด ์์ ์ด ๊ฐ๋ฅํ๋ค๋ ์ ์ฝ์ด ์์ด
์ ์ถฉ์(CSR)์ ํตํด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ต๋๋ค.)
'Front-end' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| Next.js ๋น๋ ์ต์ ํ : Babel โ SWC ์ ํ (1) | 2025.08.17 |
|---|---|
| Monorepo ๋์ ๊ธฐ: ํ๋ก ํธ์๋ ๋ง์ด๊ทธ๋ ์ด์ ์ค๊ณ ์ ๋ฆฌ (0) | 2025.08.17 |
| useState์ ๋น๋๊ธฐ์ฑ (0) | 2025.04.13 |
| React key prop๊ณผ ์ํ ์ด๊ธฐํ (0) | 2025.02.23 |
| Timer Interval ์ง์ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๋ฐฉ๋ฒ (2) | 2025.02.02 |