React์ useOptimistic ํ ์ผ๋ก ๋งค๋๋ฌ์ด ์ฌ์ฉ์ ๊ฒฝํ์ ๊ตฌํํ์ธ์. ๋๊ด์ UI ์ ๋ฐ์ดํธ ํจํด, ๋ชจ๋ฒ ์ฌ๋ก ๋ฐ ๊ตญ์ ๊ตฌํ ์ ๋ต์ ์ดํด๋ณด์ธ์.
React useOptimistic: ๊ธ๋ก๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ ๋๊ด์ UI ์ ๋ฐ์ดํธ ํจํด ๋ง์คํฐํ๊ธฐ
์ค๋๋ ๋น ๋ฅด๊ฒ ๋ณํํ๋ ๋์งํธ ์ธ๊ณ์์ ์ ์ฐํ๊ณ ๋ฐ์์ฑ์ด ๋ฐ์ด๋ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ๋ ๊ฒ์ ๋ฌด์๋ณด๋ค ์ค์ํ๋ฉฐ, ํนํ ๋ค์ํ ๋คํธ์ํฌ ํ๊ฒฝ๊ณผ ์ฌ์ฉ์ ๊ธฐ๋์น๋ฅผ ๊ฐ์ง ์ ์ธ๊ณ ์ฌ์ฉ์์๊ฒ ์๋น์ค๋ฅผ ์ ๊ณตํ๋ ๊ธ๋ก๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ์ฐ ๋์ฑ ๊ทธ๋ ์ต๋๋ค. ์ฌ์ฉ์๋ ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ์ํธ ์์ฉํ ๋ ์ฆ๊ฐ์ ์ธ ํผ๋๋ฐฑ์ ๊ธฐ๋ํฉ๋๋ค. ํญ๋ชฉ์ ์ฅ๋ฐ๊ตฌ๋์ ์ถ๊ฐํ๊ฑฐ๋, ๋ฉ์์ง๋ฅผ ๋ณด๋ด๊ฑฐ๋, ๊ฒ์๋ฌผ์ ์ข์์๋ฅผ ๋๋ฅด๋ ๋ฑ ์์ ์ ์์ํ๋ฉด UI๊ฐ ์ฆ์ ๋ณ๊ฒฝ ์ฌํญ์ ๋ฐ์ํ ๊ฒ์ด๋ผ๊ณ ์์ํฉ๋๋ค. ๊ทธ๋ฌ๋ ํนํ ์๋ฒ ํต์ ๊ณผ ๊ด๋ จ๋ ๋ง์ ์์ ์ ๋ณธ์ง์ ์ผ๋ก ๋น๋๊ธฐ์ ์ด๋ฉฐ ์๋ฃํ๋ ๋ฐ ์๊ฐ์ด ๊ฑธ๋ฆฝ๋๋ค. ์ด๋ฌํ ๋๊ธฐ ์๊ฐ์ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋๋ฆฌ๊ฒ ์๋ํ๋ ๊ฒ์ฒ๋ผ ๋๊ปด์ง๊ฒ ํ์ฌ ์ฌ์ฉ์๋ฅผ ์ข์ ์ํค๊ณ ์ ์ฌ์ ์ผ๋ก ์ดํ๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐ ๋๊ด์ UI ์ ๋ฐ์ดํธ๊ฐ ์ฌ์ฉ๋ฉ๋๋ค. ํต์ฌ ์์ด๋์ด๋ ๋น๋๊ธฐ ์์ ์ด ์ค์ ๋ก ์๋ฃ๋๊ธฐ ์ ์๋ *๋ง์น* ์ฑ๊ณตํ ๊ฒ์ฒ๋ผ ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ฅผ ์ฆ์ ์ ๋ฐ์ดํธํ๋ ๊ฒ์ ๋๋ค. ๋์ค์ ์์ ์ด ์คํจํ๋ฉด UI๋ฅผ ๋กค๋ฐฑํ ์ ์์ต๋๋ค. ์ด ์ ๊ทผ ๋ฐฉ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ธ์ง๋ ์ฑ๋ฅ๊ณผ ์๋ต์ฑ์ ํฌ๊ฒ ํฅ์์์ผ ํจ์ฌ ๋ ๋งค๋ ฅ์ ์ธ ์ฌ์ฉ์ ๊ฒฝํ์ ๋ง๋ญ๋๋ค.
๋๊ด์ UI ์ ๋ฐ์ดํธ ์ดํด
๋๊ด์ UI ์ ๋ฐ์ดํธ๋ ์์คํ ์ด ์ฌ์ฉ์ ์์ ์ด ์ฑ๊ณตํ ๊ฒ์ด๋ผ๊ณ ๊ฐ์ ํ๊ณ ํด๋น ์ฑ๊ณต์ ๋ฐ์ํ๋๋ก UI๋ฅผ ์ฆ์ ์ ๋ฐ์ดํธํ๋ ๋์์ธ ํจํด์ ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์ฌ์ฉ์์๊ฒ ์ฆ๊ฐ์ ์ธ ์๋ต์ฑ์ด๋ผ๋ ๋๋์ ์ค๋๋ค. ๊ธฐ๋ณธ ๋น๋๊ธฐ ์์ (์: API ํธ์ถ)์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๊ณ์ ์ํ๋ฉ๋๋ค. ์์ ์ด ๊ฒฐ๊ตญ ์ฑ๊ณตํ๋ฉด ์ถ๊ฐ UI ๋ณ๊ฒฝ์ด ํ์ํ์ง ์์ต๋๋ค. ์คํจํ๋ฉด UI๊ฐ ์ด์ ์ํ๋ก ๋๋์๊ฐ๊ณ ์ฌ์ฉ์์๊ฒ ์ ์ ํ ์ค๋ฅ ๋ฉ์์ง๊ฐ ํ์๋ฉ๋๋ค.
๋ค์ ์๋๋ฆฌ์ค๋ฅผ ๊ณ ๋ คํ์ญ์์ค.
- ์์ ๋ฏธ๋์ด ์ข์์: ์ฌ์ฉ์๊ฐ ๊ฒ์๋ฌผ์ ์ข์์๋ฅผ ๋๋ฅด๋ฉด ์ข์์ ์๊ฐ ์ฆ์ ์ฆ๊ฐํ๊ณ ์ข์์ ๋ฒํผ์ด ์๊ฐ์ ์ผ๋ก ๋ณ๊ฒฝ๋ฉ๋๋ค. ์ข์์๋ฅผ ๋ฑ๋กํ๋ ์ค์ API ํธ์ถ์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋ฐ์ํฉ๋๋ค.
- ์ ์ ์๊ฑฐ๋ ์ฅ๋ฐ๊ตฌ๋: ์ผํ ์นดํธ์ ํญ๋ชฉ์ ์ถ๊ฐํ๋ฉด ์ฅ๋ฐ๊ตฌ๋ ์๊ฐ ์ฆ์ ์ ๋ฐ์ดํธ๋๊ฑฐ๋ ํ์ธ ๋ฉ์์ง๊ฐ ํ์๋ฉ๋๋ค. ์๋ฒ ์ธก ์ ํจ์ฑ ๊ฒ์ฌ ๋ฐ ์ฃผ๋ฌธ ์ฒ๋ฆฌ๋ ๋์ค์ ๋ฐ์ํฉ๋๋ค.
- ๋ฉ์์ง ์ฑ: ๋ฉ์์ง๋ฅผ ๋ณด๋ด๋ฉด ์๋ฒ ํ์ธ ์ ์๋ ์ฑํ ์ฐฝ์ '๋ณด๋' ๋๋ '์ ๋ฌ๋จ'์ผ๋ก ์ฆ์ ํ์๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค.
๋๊ด์ UI์ ์ฅ์
- ์ธ์ง๋ ์ฑ๋ฅ ํฅ์: ๊ฐ์ฅ ํฐ ์ด์ ์ ์ฌ์ฉ์์๊ฒ ์ฆ๊ฐ์ ์ธ ํผ๋๋ฐฑ์ ์ ๊ณตํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ด ํจ์ฌ ๋ ๋น ๋ฅด๊ฒ ๋๊ปด์ง๋๋ก ๋ง๋๋ ๊ฒ์ ๋๋ค.
- ์ฌ์ฉ์ ์ฐธ์ฌ๋ ํฅ์: ๋ฐ์์ฑ์ด ๋ฐ์ด๋ ์ธํฐํ์ด์ค๋ ์ฌ์ฉ์์ ์ฐธ์ฌ๋ฅผ ์ ์งํ๊ณ ์ข์ ๊ฐ์ ์ค์ ๋๋ค.
- ๋ ๋์ ์ฌ์ฉ์ ๊ฒฝํ: ์ธ์ง๋ ์ง์ฐ์ ์ต์ํํจ์ผ๋ก์จ ๋๊ด์ UI๋ ๋ ๋ถ๋๋ฝ๊ณ ์ฆ๊ฑฐ์ด ์ํธ ์์ฉ์ ๊ธฐ์ฌํฉ๋๋ค.
๋๊ด์ UI์ ๊ณผ์
- ์ค๋ฅ ์ฒ๋ฆฌ ๋ฐ ๋กค๋ฐฑ: ์ค์ํ ๊ณผ์ ๋ ์คํจ๋ฅผ ์ ์์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๊ฒ์ ๋๋ค. ์์ ์ด ์คํจํ๋ฉด UI๊ฐ ์ด์ ์ํ๋ก ์ ํํ๊ฒ ๋๋์๊ฐ์ผ ํ๋ฉฐ, ์ด๋ ์ฌ๋ฐ๋ฅด๊ฒ ๊ตฌํํ๊ธฐ ์ด๋ ค์ธ ์ ์์ต๋๋ค.
- ๋ฐ์ดํฐ ์ผ๊ด์ฑ: ๋ฒ๊ทธ์ ์๋ชป๋ ์ํ๋ฅผ ๋ฐฉ์งํ๋ ค๋ฉด ๋๊ด์ ์ ๋ฐ์ดํธ์ ์ค์ ์๋ฒ ์๋ต ๊ฐ์ ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ๋ณด์ฅํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
- ๋ณต์ก์ฑ: ๋๊ด์ ์ ๋ฐ์ดํธ, ํนํ ๋ณต์กํ ์ํ ๊ด๋ฆฌ ๋ฐ ์ฌ๋ฌ ๋์ ์์ ์ ์ฌ์ฉํ๋ฉด ์ฝ๋๋ฒ ์ด์ค์ ์๋นํ ๋ณต์ก์ฑ์ ์ถ๊ฐํ ์ ์์ต๋๋ค.
React์ `useOptimistic` ํ ์๊ฐ
React 19์์๋ ๋๊ด์ UI ์ ๋ฐ์ดํธ์ ๊ตฌํ์ ๋จ์ํํ๋๋ก ์ค๊ณ๋ `useOptimistic` ํ ์ ์๊ฐํฉ๋๋ค. ์ด ํ ์ ์ฌ์ฉํ๋ฉด ๊ฐ๋ฐ์๊ฐ ๋๊ด์ ์ํ๋ฅผ ๊ตฌ์ฑ ์์ ๋ด์์ ์ง์ ๊ด๋ฆฌํ ์ ์์ผ๋ฏ๋ก ํจํด์ ๋ ์ ์ธ์ ์ด๊ณ ์ถ๋ก ํ๊ธฐ ์ฝ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค. ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฐ ์๋ฒ ์ธก ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ์๋ฃจ์ ๊ณผ ์๋ฒฝํ๊ฒ ์ด์ธ๋ฆฝ๋๋ค.
`useOptimistic` ํ ์ ๋ ๊ฐ์ง ์ธ์๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- `current` ์ํ: ์ค์ ์๋ฒ์ ์ปค๋ฐ๋ ์ํ์ ๋๋ค.
- `getOptimisticValue` ํจ์: ์ด์ ์ํ์ ์ ๋ฐ์ดํธ ์์ ์ ๋ฐ์ ๋๊ด์ ์ํ๋ฅผ ๋ฐํํ๋ ํจ์์ ๋๋ค.
๋๊ด์ ์ํ์ ํ์ฌ ๊ฐ์ ๋ฐํํฉ๋๋ค.
`useOptimistic`์ ๊ธฐ๋ณธ ์์
์ฆ๊ฐ์ํฌ ์ ์๋ ์นด์ดํฐ์ ๊ฐ๋จํ ์๋ก ์ค๋ช ํ๊ฒ ์ต๋๋ค. `setTimeout`์ ์ฌ์ฉํ์ฌ ๋น๋๊ธฐ ์์ ์ ์๋ฎฌ๋ ์ด์ ํฉ๋๋ค.
์๋ฒ์์ ๊ฐ์ ธ์จ ์นด์ดํธ๋ฅผ ๋ํ๋ด๋ ์ํ ์กฐ๊ฐ์ด ์๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค. ์ฌ์ฉ์๊ฐ ์ด ์นด์ดํธ๋ฅผ ๋๊ด์ ์ผ๋ก ์ฆ๊ฐ์ํฌ ์ ์๋๋ก ํ๋ ค๊ณ ํฉ๋๋ค.
import React, { useState, useOptimistic } from 'react';
function Counter({ initialCount }) {
const [count, setCount] = useState(initialCount);
// The useOptimistic hook
const [optimisticCount, addOptimistic] = useOptimistic(
count, // The current state (initially the server-fetched count)
(currentState, newValue) => currentState + newValue // The function to calculate the optimistic state
);
const increment = async (amount) => {
// Optimistically update the UI immediately
addOptimistic(amount);
// Simulate an asynchronous operation (e.g., API call)
await new Promise(resolve => setTimeout(resolve, 1000));
// In a real app, this would be your API call.
// If the API call fails, you'd need a way to reset the state.
// For simplicity here, we assume success and update the actual state.
setCount(prevCount => prevCount + amount);
};
return (
Server Count: {count}
Optimistic Count: {optimisticCount}
);
}
์ด ์์์:
- `count`๋ ์๋ฒ์์ ๊ฐ์ ธ์จ ์ค์ ์ํ๋ฅผ ๋ํ๋ ๋๋ค.
- `optimisticCount`๋ `addOptimistic`์ด ํธ์ถ๋ ๋ ์ฆ์ ์ ๋ฐ์ดํธ๋๋ ๊ฐ์ ๋๋ค.
- `increment`๊ฐ ํธ์ถ๋๋ฉด `addOptimistic(amount)`๊ฐ ํธ์ถ๋์ด ํ์ฌ `count`์ `amount`๋ฅผ ๋ํ์ฌ `optimisticCount`๋ฅผ ์ฆ์ ์ ๋ฐ์ดํธํฉ๋๋ค.
- ์ง์ฐ ํ(API ํธ์ถ ์๋ฎฌ๋ ์ด์ ) ์ค์ `count`๊ฐ ์ ๋ฐ์ดํธ๋ฉ๋๋ค. ๋น๋๊ธฐ ์์ ์ด ์คํจํ๋ฉด ์คํจํ ์์ ์ด์ ์ ์ด์ ๊ฐ์ผ๋ก `optimisticCount`๋ฅผ ๋๋๋ฆฌ๋ ๋ก์ง์ ๊ตฌํํด์ผ ํฉ๋๋ค.
`useOptimistic`์ ๊ณ ๊ธ ํจํด
`useOptimistic`์ ํ์ ๋ชฉ๋ก, ๋ฉ์์ง ๋๋ ๋๋ ทํ ์ฑ๊ณต ๋ฐ ์ค๋ฅ ์ํ๊ฐ ์๋ ์์ ๊ณผ ๊ฐ์ ๋ ๋ณต์กํ ์๋๋ฆฌ์ค๋ฅผ ์ฒ๋ฆฌํ ๋ ์ง์ ์ผ๋ก ๋น์ ๋ฐํฉ๋๋ค.
๋๊ด์ ๋ชฉ๋ก
ํญ๋ชฉ์ ๋๊ด์ ์ผ๋ก ์ถ๊ฐ, ์ ๊ฑฐ ๋๋ ์ ๋ฐ์ดํธํ ์ ์๋ ๋ชฉ๋ก์ ๊ด๋ฆฌํ๋ ๊ฒ์ ์ผ๋ฐ์ ์ธ ์๊ตฌ ์ฌํญ์ ๋๋ค. `useOptimistic`์ ์ฌ์ฉํ์ฌ ํญ๋ชฉ ๋ฐฐ์ด์ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
์ฌ์ฉ์๊ฐ ์ ์์ ์ ์ถ๊ฐํ ์ ์๋ ์์ ๋ชฉ๋ก์ ๊ณ ๋ คํ์ญ์์ค. ์ ์์ ์ ๋ชฉ๋ก์ ์ฆ์ ํ์๋์ด์ผ ํฉ๋๋ค.
import React, { useState, useOptimistic } from 'react';
function TaskList({ initialTasks }) {
const [tasks, setTasks] = useState(initialTasks);
const [optimisticTasks, addOptimisticTask] = useOptimistic(
tasks,
(currentTasks, newTaskData) => [
...currentTasks,
{ id: Date.now(), text: newTaskData.text, pending: true } // Mark as pending optimistically
]
);
const addTask = async (taskText) => {
addOptimisticTask({ text: taskText });
// Simulate API call to add the task
await new Promise(resolve => setTimeout(resolve, 1500));
// In a real app:
// const response = await api.addTask(taskText);
// if (response.success) {
// setTasks(prevTasks => [...prevTasks, { id: response.id, text: taskText, pending: false }]);
// } else {
// // Rollback: Remove the optimistic task
// setTasks(prevTasks => prevTasks.filter(task => !task.pending));
// console.error('Failed to add task');
// }
// For this simplified example, we assume success and update the actual state.
setTasks(prevTasks => prevTasks.map(task => task.pending ? { ...task, pending: false } : task));
};
return (
Tasks
{optimisticTasks.map(task => (
-
{task.text} {task.pending && '(Saving...)'}
))}
);
}
์ด ๋ชฉ๋ก ์์์:
- `addTask`๊ฐ ํธ์ถ๋๋ฉด `addOptimisticTask`๊ฐ ์ฌ์ฉ๋์ด `pending: true` ํ๋๊ทธ์ ํจ๊ป ์ ์์ ๊ฐ์ฒด๋ฅผ `optimisticTasks`์ ์ฆ์ ์ถ๊ฐํฉ๋๋ค.
- UI๋ ์ด ์ ์์ ์ ๋ถํฌ๋ช ๋๊ฐ ๊ฐ์๋ ์ํ๋ก ๋ ๋๋งํ์ฌ ์์ง ์ฒ๋ฆฌ ์ค์์ ์๋ฆฝ๋๋ค.
- ์๋ฎฌ๋ ์ด์ ๋ API ํธ์ถ์ด ๋ฐ์ํฉ๋๋ค. ์ค์ ์๋๋ฆฌ์ค์์๋ ์ฑ๊ณต์ ์ธ API ์๋ต ์ ์๋ฒ์์ ์ค์ `id`๋ก `tasks` ์ํ๋ฅผ ์ ๋ฐ์ดํธํ๊ณ `pending` ํ๋๊ทธ๋ฅผ ์ ๊ฑฐํฉ๋๋ค. API ํธ์ถ์ด ์คํจํ๋ฉด ๋ณด๋ฅ ์ค์ธ ์์ ์ `tasks` ์ํ์์ ํํฐ๋งํ์ฌ ๋๊ด์ ์ ๋ฐ์ดํธ๋ฅผ ๋๋๋ ค์ผ ํฉ๋๋ค.
๋กค๋ฐฑ ๋ฐ ์ค๋ฅ ์ฒ๋ฆฌ
๋๊ด์ UI์ ์ง์ ํ ๋ณต์ก์ฑ์ ๊ฐ๋ ฅํ ์ค๋ฅ ์ฒ๋ฆฌ ๋ฐ ๋กค๋ฐฑ์ ์์ต๋๋ค. `useOptimistic` ์์ฒด๋ ๋ง๋ฒ์ฒ๋ผ ์คํจ๋ฅผ ์ฒ๋ฆฌํ์ง ์์ต๋๋ค. ๋๊ด์ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํฉ๋๋ค. ์ค๋ฅ ์ ์ํ๋ฅผ ๋๋๋ฆฌ๋ ์ฑ ์์ ์ฌ์ ํ ๊ฐ๋ฐ์์๊ฒ ์์ต๋๋ค.
์ผ๋ฐ์ ์ธ ์ ๋ต์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ๋ณด๋ฅ ์ค์ธ ์ํ ํ์: ์ํ ๊ฐ์ฒด์ ํ๋๊ทธ(`isSaving`, `pending`, `optimistic` ๋ฑ)๋ฅผ ์ถ๊ฐํ์ฌ ์งํ ์ค์ธ ๋๊ด์ ์ ๋ฐ์ดํธ์ ์ผ๋ถ์์ ๋ํ๋ ๋๋ค.
- ์กฐ๊ฑด๋ถ ๋ ๋๋ง: ์ด๋ฌํ ํ๋๊ทธ๋ฅผ ์ฌ์ฉํ์ฌ ๋๊ด์ ํญ๋ชฉ์ ์๊ฐ์ ์ผ๋ก ๊ตฌ๋ณํฉ๋๋ค(์: ๋ค๋ฅธ ์คํ์ผ, ๋ก๋ฉ ํ์๊ธฐ).
- ์ค๋ฅ ์ฝ๋ฐฑ: ๋น๋๊ธฐ ์์ ์ด ์๋ฃ๋๋ฉด ์ค๋ฅ๋ฅผ ํ์ธํฉ๋๋ค. ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ์ค์ ์ํ์์ ๋๊ด์ ์ํ๋ฅผ ์ ๊ฑฐํ๊ฑฐ๋ ๋๋๋ฆฝ๋๋ค.
import React, { useState, useOptimistic } from 'react';
function CommentSection({ initialComments }) {
const [comments, setComments] = useState(initialComments);
const [optimisticComments, addOptimisticComment] = useOptimistic(
comments,
(currentComments, newCommentData) => [
...currentComments,
{ id: `optimistic-${Date.now()}`, text: newCommentData.text, author: newCommentData.author, status: 'pending' }
]
);
const addComment = async (author, text) => {
const optimisticComment = { id: `optimistic-${Date.now()}`, text, author, status: 'pending' };
addOptimisticComment({ text, author });
try {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 2000));
// Simulate a random failure for demonstration
if (Math.random() < 0.3) { // 30% chance of failure
throw new Error('Failed to post comment');
}
// Success: Update the actual comments state with a permanent ID and status
setComments(prevComments =>
prevComments.map(c => c.id.startsWith('optimistic-') ? { ...c, id: Date.now(), status: 'posted' } : c)
);
} catch (error) {
console.error('Error posting comment:', error);
// Rollback: Remove the pending comment from the actual state
setComments(prevComments =>
prevComments.filter(c => !c.id.startsWith('optimistic-'))
);
// Optionally, show an error message to the user
alert('Failed to post comment. Please try again.');
}
};
return (
Comments
{optimisticComments.map(comment => (
-
{comment.author}: {comment.text} {comment.status === 'pending' && '(Sending...)'}
))}
);
}
์ด ๊ฐ์ ๋ ์์์:
- ์ ๋๊ธ์ `status: 'pending'`์ผ๋ก ์ถ๊ฐ๋ฉ๋๋ค.
- ์๋ฎฌ๋ ์ด์ ๋ API ํธ์ถ์ ์ค๋ฅ๋ฅผ ๋ฐ์์ํฌ ๊ฐ๋ฅ์ฑ์ด ์์ต๋๋ค.
- ์ฑ๊ณต ์ ๋ณด๋ฅ ์ค์ธ ๋๊ธ์ ์ค์ ID ๋ฐ `status: 'posted'`๋ก ์ ๋ฐ์ดํธ๋ฉ๋๋ค.
- ์คํจ ์ ๋ณด๋ฅ ์ค์ธ ๋๊ธ์ `comments` ์ํ์์ ํํฐ๋ง๋์ด ๋๊ด์ ์ ๋ฐ์ดํธ๊ฐ ํจ๊ณผ์ ์ผ๋ก ๋๋๋ ค์ง๋๋ค. ์ฌ์ฉ์์๊ฒ ๊ฒฝ๊ณ ๊ฐ ํ์๋ฉ๋๋ค.
๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ `useOptimistic` ํตํฉ
์ต์ React ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ์ฐ React Query(TanStack Query) ๋๋ SWR๊ณผ ๊ฐ์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ข ์ข ์ฌ์ฉ๋ฉ๋๋ค. ์ด๋ฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์๋ฒ ์ํ์ ํจ๊ป ๋๊ด์ ์ ๋ฐ์ดํธ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํด `useOptimistic`๊ณผ ํตํฉ๋ ์ ์์ต๋๋ค.
์ผ๋ฐ์ ์ธ ํจํด์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ์ด๊ธฐ ์ํ: ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ด๊ธฐ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
- ๋๊ด์ ์ ๋ฐ์ดํธ: ๋ฎคํ ์ด์ ์ ์ํํ ๋(์: React Query์์ `mutateAsync`) `useOptimistic`์ ์ฌ์ฉํ์ฌ ๋๊ด์ ์ํ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- `onMutate` ์ฝ๋ฐฑ: React Query์ `onMutate`์์ ์ด์ ์ํ๋ฅผ ์บก์ฒํ๊ณ ๋๊ด์ ์ ๋ฐ์ดํธ๋ฅผ ์ ์ฉํ ์ ์์ต๋๋ค.
- `onError` ์ฝ๋ฐฑ: React Query์ `onError`์์ ์บก์ฒ๋ ์ด์ ์ํ๋ฅผ ์ฌ์ฉํ์ฌ ๋๊ด์ ์ ๋ฐ์ดํธ๋ฅผ ๋๋๋ฆด ์ ์์ต๋๋ค.
`useOptimistic`์ ๊ตฌ์ฑ ์์ ์์ค ์ํ ๊ด๋ฆฌ๋ฅผ ๋จ์ํํ์ง๋ง ์ด๋ฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ํตํฉ์๋ ํน์ ๋ฎคํ ์ด์ ์๋ช ์ฃผ๊ธฐ ์ฝ๋ฐฑ์ ๋ํ ์ดํด๊ฐ ํ์ํฉ๋๋ค.
React Query ์์ (๊ฐ๋ ์ )
`useOptimistic`์ React ํ ์ด๊ณ React Query๋ ์์ฒด ์บ์๋ฅผ ๊ด๋ฆฌํ์ง๋ง ํ์ํ ๊ฒฝ์ฐ UI ๊ด๋ จ ๋๊ด์ ์ํ์ ๋ํด `useOptimistic`์ ํ์ฉํ๊ฑฐ๋ ์ข ์ข ์ ์ฌํ๊ฒ ๋๊ปด์ง๋ React Query์ ๋ด์ฅ ๋๊ด์ ์ ๋ฐ์ดํธ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
React Query์ `useMutation` ํ ์๋ ๋๊ด์ ์ ๋ฐ์ดํธ์ ์ค์ํ `onMutate`, `onSuccess` ๋ฐ `onError` ์ฝ๋ฐฑ์ด ์์ต๋๋ค. ์ผ๋ฐ์ ์ผ๋ก `onMutate`์์ ์บ์๋ฅผ ์ง์ ์ ๋ฐ์ดํธํ๊ณ `onError`์์ ๋๋๋ฆฝ๋๋ค.
import React from 'react';
import { useQuery, useMutation, QueryClient } from '@tanstack/react-query';
const queryClient = new QueryClient();
// Mock API function
const fakeApi = {
getItems: async () => {
await new Promise(res => setTimeout(res, 500));
return [{ id: 1, name: 'Global Gadget' }];
},
addItem: async (newItem) => {
await new Promise(res => setTimeout(res, 1500));
if (Math.random() < 0.2) throw new Error('Network error');
return { ...newItem, id: Date.now() };
}
};
function ItemList() {
const { data: items, isLoading } = useQuery(['items'], fakeApi.getItems);
const mutation = useMutation({
mutationFn: fakeApi.addItem,
onMutate: async (newItem) => {
await queryClient.cancelQueries(['items']);
const previousItems = queryClient.getQueryData(['items']);
queryClient.setQueryData(['items'], (old) => [
...(old || []),
{ ...newItem, id: 'optimistic-id', isOptimistic: true } // Mark as optimistic
]);
return { previousItems };
},
onError: (err, newItem, context) => {
if (context?.previousItems) {
queryClient.setQueryData(['items'], context.previousItems);
}
console.error('Error adding item:', err);
},
onSuccess: (newItem) => {
queryClient.invalidateQueries(['items']);
}
});
const handleAddItem = () => {
mutation.mutate({ name: 'New Item' });
};
if (isLoading) return Loading items...;
return (
Items
{(items || []).map(item => (
-
{item.name} {item.isOptimistic && '(Saving...)'}
))}
);
}
// In your App component:
//
//
//
์ด React Query ์์ ์์:
- `onMutate`๋ ๋ฎคํ ์ด์ ์ด ์์๋๊ธฐ ์ ์ ๊ฐ๋ก์ฑ๋๋ค. ๊ฒฝํฉ ์กฐ๊ฑด์ ๋ฐฉ์งํ๊ธฐ ์ํด `items`์ ๋ํ ๋ณด๋ฅ ์ค์ธ ์ฟผ๋ฆฌ๋ฅผ ์ทจ์ํ ๋ค์ `isOptimistic: true`๋ก ํ์๋ ์ ํญ๋ชฉ์ ์ถ๊ฐํ์ฌ ์บ์๋ฅผ ๋๊ด์ ์ผ๋ก ์ ๋ฐ์ดํธํฉ๋๋ค.
- `onError`๋ `onMutate`์์ ๋ฐํ๋ `context`๋ฅผ ์ฌ์ฉํ์ฌ ์บ์๋ฅผ ์ด์ ์ํ๋ก ๋ณต์ํ์ฌ ๋๊ด์ ์ ๋ฐ์ดํธ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๋กค๋ฐฑํฉ๋๋ค.
- `onSuccess`๋ ์บ์๊ฐ ๋๊ธฐํ๋๋๋ก ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๊ฐ์ ธ์ `items` ์ฟผ๋ฆฌ๋ฅผ ๋ฌดํจํํฉ๋๋ค.
๋๊ด์ UI์ ๋ํ ๊ธ๋ก๋ฒ ๊ณ ๋ ค ์ฌํญ
์ ์ธ๊ณ ์ฌ์ฉ์๋ฅผ ์ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ ๋ ๋๊ด์ UI ํจํด์ ํน์ ๊ณ ๋ ค ์ฌํญ์ ๋์ ํฉ๋๋ค.
1. ๋คํธ์ํฌ ๊ฐ๋ณ์ฑ
์ง์ญ์ด ๋ค๋ฅธ ์ฌ์ฉ์๋ ๋คํธ์ํฌ ์๋์ ์์ ์ฑ์ด ํฌ๊ฒ ๋ค๋ฆ ๋๋ค. ๋น ๋ฅธ ์ฐ๊ฒฐ์์ ์ฆ๊ฐ์ ์ผ๋ก ๋๊ปด์ง๋ ๋๊ด์ ์ ๋ฐ์ดํธ๋ ๋๋ฆฌ๊ฑฐ๋ ๋ถ์์ ํ ์ฐ๊ฒฐ์์ ์กฐ๊ธฐ์ ๋๊ปด์ง๊ฑฐ๋ ๋ ๋์ ๋๋ ๋กค๋ฐฑ์ผ๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
- ์ ์ํ ์๊ฐ ์ด๊ณผ: ์ธก์ ๊ฐ๋ฅํ ๊ฒฝ์ฐ ๋คํธ์ํฌ ์ํ์ ๋ฐ๋ผ ๋๊ด์ ์ ๋ฐ์ดํธ์ ๋ํ ์ธ์ง๋ ์ง์ฐ์ ๋์ ์ผ๋ก ์กฐ์ ํ๋ ๊ฒ์ ๊ณ ๋ คํ์ญ์์ค.
- ๋ ๋ช ํํ ํผ๋๋ฐฑ: ๋๋ฆฐ ์ฐ๊ฒฐ์์๋ ๋๊ด์ ์ ๋ฐ์ดํธ๊ฐ ์๋๋ผ๋ ์์ ์ด ์งํ ์ค์์ ๋ํ๋ด๋ ๋ ๋ช ์์ ์ธ ์๊ฐ์ ์ ํธ(์: ๋ ๋์ ๋๋ ๋ก๋ฉ ์คํผ๋, ์งํ๋ฅ ํ์์ค)๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ์ผ๊ด ์ฒ๋ฆฌ: ์ฌ๋ฌ ์ ์ฌํ ์์ (์: ์ฌ๋ฌ ํญ๋ชฉ์ ์ฅ๋ฐ๊ตฌ๋์ ์ถ๊ฐ)์ ๊ฒฝ์ฐ ์๋ฒ์ ๋ณด๋ด๊ธฐ ์ ์ ํด๋ผ์ด์ธํธ์์ ์ผ๊ด ์ฒ๋ฆฌํ๋ฉด ๋คํธ์ํฌ ์์ฒญ์ ์ค์ด๊ณ ์ธ์ง๋ ์ฑ๋ฅ์ ํฅ์์ํฌ ์ ์์ง๋ง ์ ์คํ ๋๊ด์ ๊ด๋ฆฌ๊ฐ ํ์ํฉ๋๋ค.
2. ๊ตญ์ ํ(i18n) ๋ฐ ํ์งํ(l10n)
์ค๋ฅ ๋ฉ์์ง ๋ฐ ์ฌ์ฉ์ ํผ๋๋ฐฑ์ด ์ค์ํฉ๋๋ค. ์ด๋ฌํ ๋ฉ์์ง๋ ํ์งํ๋๊ณ ๋ฌธํ์ ์ผ๋ก ์ ์ ํด์ผ ํฉ๋๋ค.
- ํ์งํ๋ ์ค๋ฅ ๋ฉ์์ง: ์ฌ์ฉ์์๊ฒ ํ์๋๋ ๋กค๋ฐฑ ๋ฉ์์ง๊ฐ ๋ฒ์ญ๋๊ณ ์ฌ์ฉ์ ๋ก์ผ์ผ์ ์ปจํ ์คํธ์ ๋ง๋์ง ํ์ธํฉ๋๋ค. `useOptimistic` ์์ฒด๋ ํ์งํ๋ฅผ ์ฒ๋ฆฌํ์ง ์์ต๋๋ค. ์ด๋ ์ ์ฒด i18n ์ ๋ต์ ์ผ๋ถ์ ๋๋ค.
- ํผ๋๋ฐฑ์ ๋ฌธํ์ ๋์์ค: ์ฆ๊ฐ์ ์ธ ํผ๋๋ฐฑ์ ์ผ๋ฐ์ ์ผ๋ก ๊ธ์ ์ ์ด์ง๋ง ํผ๋๋ฐฑ์ *์ ํ*์ ๋ฌธํ์ ์กฐ์ ์ด ํ์ํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ์ง๋์น๊ฒ ๊ณต๊ฒฉ์ ์ธ ์ค๋ฅ ๋ฉ์์ง๋ ๋ฌธํ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ์ธ์๋ ์ ์์ต๋๋ค.
3. ์๊ฐ๋ ๋ฐ ๋ฐ์ดํฐ ๋๊ธฐํ
์ ์ธ๊ณ์ ์ฌ์ฉ์๊ฐ ๋ถ์ฐ๋์ด ์์ผ๋ฏ๋ก ์๊ฐ๋๊ฐ ๋ค๋ฅธ ์ฌ์ฉ์ ๊ฐ์ ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ด ์ค์ํฉ๋๋ค. ๋๊ด์ ์ ๋ฐ์ดํธ๋ ์๋ฒ ์ธก ํ์์คํฌํ ๋ฐ ์ถฉ๋ ํด๊ฒฐ ์ ๋ต์ผ๋ก ์ ์คํ๊ฒ ๊ด๋ฆฌํ์ง ์์ผ๋ฉด ๋ฌธ์ ๋ฅผ ์ ํ์ํฌ ์ ์์ต๋๋ค.
- ์๋ฒ ํ์์คํฌํ: ์๊ฐ๋ ์ฐจ์ด ๋๋ ์๊ณ ์๊ณก์ ์ํฅ์ ๋ฐ์ ์ ์๋ ํด๋ผ์ด์ธํธ ์ธก ํ์์คํฌํ๊ฐ ์๋ ์ค์ํ ๋ฐ์ดํฐ ์์ ์ง์ ๋ฐ ์ถฉ๋ ํด๊ฒฐ์๋ ํญ์ ์๋ฒ์์ ์์ฑ๋ ํ์์คํฌํ๋ฅผ ์ฌ์ฉํ์ญ์์ค.
- ์ถฉ๋ ํด๊ฒฐ: ๋ ๋ช ์ ์ฌ์ฉ์๊ฐ ๋์ผํ ๋ฐ์ดํฐ๋ฅผ ๋๊ด์ ์ผ๋ก ๋์์ ์ ๋ฐ์ดํธํ๋ ๊ฒฝ์ฐ ๋ฐ์ํ ์ ์๋ ์ถฉ๋ ์ฒ๋ฆฌ๋ฅผ ์ํ ๊ฐ๋ ฅํ ์ ๋ต์ ๊ตฌํํฉ๋๋ค. ์ฌ๊ธฐ์๋ ์ข ์ข Last-Write-Wins ์ ๊ทผ ๋ฐฉ์ ๋๋ ๋ ๋ณต์กํ ๋ณํฉ ๋ก์ง์ด ํฌํจ๋ฉ๋๋ค.
4. ์ ๊ทผ์ฑ(a11y)
์ฅ์ ๊ฐ ์๋ ์ฌ์ฉ์, ํนํ ํ๋ฉด ํ๋ ๊ธฐ๋ฅผ ์ฌ์ฉํ๋ ์ฌ์ฉ์๋ ์์ ์ ์์ ์ํ์ ๋ํ ๋ช ํํ๊ณ ์๊ธฐ์ ์ ํ ์ ๋ณด๊ฐ ํ์ํฉ๋๋ค.
- ARIA ๋ผ์ด๋ธ ์์ญ: ARIA ๋ผ์ด๋ธ ์์ญ์ ์ฌ์ฉํ์ฌ ๋๊ด์ ์ ๋ฐ์ดํธ์ ํ์ ์ฑ๊ณต ๋๋ ์คํจ ๋ฉ์์ง๋ฅผ ํ๋ฉด ํ๋ ๊ธฐ ์ฌ์ฉ์์๊ฒ ์๋ฆฝ๋๋ค. ์๋ฅผ ๋ค์ด `aria-live="polite"` ์์ญ์ "ํญ๋ชฉ์ด ์ฑ๊ณต์ ์ผ๋ก ์ถ๊ฐ๋์์ต๋๋ค" ๋๋ "ํญ๋ชฉ์ ์ถ๊ฐํ์ง ๋ชปํ์ต๋๋ค. ๋ค์ ์๋ํ์ญ์์ค."๋ผ๊ณ ์๋ฆด ์ ์์ต๋๋ค.
- ์ด์ ๊ด๋ฆฌ: ๋๊ด์ ์ ๋ฐ์ดํธ ๋๋ ๋กค๋ฐฑ ํ ์ด์ ์ด ์ ์ ํ๊ฒ ๊ด๋ฆฌ๋์ด ์ฌ์ฉ์๋ฅผ UI์ ๊ด๋ จ ๋ถ๋ถ์ผ๋ก ์๋ดํ๋์ง ํ์ธํฉ๋๋ค.
`useOptimistic` ์ฌ์ฉ์ ๋ํ ๋ชจ๋ฒ ์ฌ๋ก
`useOptimistic`์ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ๊ณ ๊ฐ๋ ฅํ๊ณ ์ฌ์ฉ์ ์นํ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ค๋ฉด:
- ๋๊ด์ ์ํ๋ฅผ ๋จ์ํ๊ฒ ์ ์ง: `useOptimistic`์์ ๊ด๋ฆฌํ๋ ์ํ๋ ์ด์์ ์ผ๋ก UI ์ํ ๋ณ๊ฒฝ์ ์ง์ ์ ์ผ๋ก ๋ํ๋ด์ผ ํฉ๋๋ค. ๋๋ฌด ๋ง์ ๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง์ ๋๊ด์ ์ํ ์์ฒด์ ํฌํจํ์ง ๋ง์ญ์์ค.
- ๋ช ํํ ์๊ฐ์ ์ ํธ: ๋๊ด์ ์ ๋ฐ์ดํธ๊ฐ ์งํ ์ค์์ ๋ํ๋ด๋ ๋ช ํํ ์๊ฐ์ ์งํ(์: ๋ฏธ๋ฌํ ๋ถํฌ๋ช ๋ ๋ณ๊ฒฝ, ๋ก๋ฉ ์คํผ๋, ๋นํ์ฑํ๋ ๋ฒํผ)๋ฅผ ํญ์ ์ ๊ณตํฉ๋๋ค.
- ๊ฐ๋ ฅํ ๋กค๋ฐฑ ๋ก์ง: ๋กค๋ฐฑ ๋ฉ์ปค๋์ฆ์ ์ฒ ์ ํ ํ ์คํธํฉ๋๋ค. ์ค๋ฅ ์ UI ์ํ๊ฐ ์ ํํ๊ณ ์์ธก ๊ฐ๋ฅํ๊ฒ ์ฌ์ค์ ๋๋์ง ํ์ธํฉ๋๋ค.
- ์์ง ์ผ์ด์ค ๊ณ ๋ ค: ์ฌ๋ฌ ๋ฒ์ ๋น ๋ฅธ ์ ๋ฐ์ดํธ, ๋์ ์์ ๋ฐ ์คํ๋ผ์ธ ์ํ์ ๊ฐ์ ์๋๋ฆฌ์ค์ ๋ํด ์๊ฐํด ๋ด ๋๋ค. ๋๊ด์ ์ ๋ฐ์ดํธ๋ ์ด๋ป๊ฒ ๋์ํฉ๋๊น?
- ์๋ฒ ์ํ ๊ด๋ฆฌ: ์ผ๊ด์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด ์ ํํ ์๋ฒ ์ํ ๊ด๋ฆฌ ์๋ฃจ์ (์: React Query, SWR ๋๋ ์์ฒด ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ๋ก์ง)๊ณผ `useOptimistic`์ ํตํฉํฉ๋๋ค.
- ์ฑ๋ฅ: ๋๊ด์ UI๋ *์ธ์ง๋* ์ฑ๋ฅ์ ํฅ์์ํค์ง๋ง ์ค์ ์ํ ์ ๋ฐ์ดํธ ์์ฒด๊ฐ ์ฑ๋ฅ ๋ณ๋ชฉ ํ์์ด ๋์ง ์๋๋ก ํฉ๋๋ค.
- ๋๊ด์ ํญ๋ชฉ์ ๊ณ ์ ์ฑ: ๋ชฉ๋ก์ ์ ํญ๋ชฉ์ ๋๊ด์ ์ผ๋ก ์ถ๊ฐํ ๋ ์์ ๊ณ ์ ์๋ณ์(์: `optimistic-`๋ก ์์)๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฒ์์ ์๊ตฌ ID๋ฅผ ๋ฐ๊ธฐ ์ ์ ๋กค๋ฐฑ ์ ์ฝ๊ฒ ๊ตฌ๋ณํ๊ณ ์ ๊ฑฐํ ์ ์๋๋ก ํฉ๋๋ค.
๊ฒฐ๋ก
`useOptimistic`์ React ์ํ๊ณ์ ๊ฐ๋ ฅํ๊ฒ ์ถ๊ฐ๋์ด ๋๊ด์ UI ์ ๋ฐ์ดํธ๋ฅผ ๊ตฌํํ๋ ์ ์ธ์ ์ด๊ณ ํตํฉ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. ์ฌ์ฉ์ ์์ ์ ์ธํฐํ์ด์ค์ ์ฆ์ ๋ฐ์ํจ์ผ๋ก์จ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ธ์ง๋ ์ฑ๋ฅ๊ณผ ์ฌ์ฉ์ ๋ง์กฑ๋๋ฅผ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ต๋๋ค.
๊ทธ๋ฌ๋ ๋๊ด์ UI์ ์ง์ ํ ๊ธฐ์ ์ ์ธ์ฌํ ์ค๋ฅ ์ฒ๋ฆฌ์ ์ํํ ๋กค๋ฐฑ์ ์์ต๋๋ค. ๊ธ๋ก๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ ๋ ์ด๋ฌํ ํจํด์ ๋คํธ์ํฌ ๊ฐ๋ณ์ฑ, ๊ตญ์ ํ, ์๊ฐ๋ ์ฐจ์ด ๋ฐ ์ ๊ทผ์ฑ ์๊ตฌ ์ฌํญ๊ณผ ํจ๊ป ๊ณ ๋ คํด์ผ ํฉ๋๋ค. ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ฐ๋ฅด๊ณ ์ํ ์ ํ์ ์ ์คํ๊ฒ ๊ด๋ฆฌํจ์ผ๋ก์จ `useOptimistic`์ ํ์ฉํ์ฌ ์ ์ธ๊ณ ์ฌ์ฉ์๋ฅผ ์ํ ์ง์ ์ผ๋ก ๋ฐ์ด๋ ๋ฐ์์ฑ์ด ๋ฐ์ด๋ ์ฌ์ฉ์ ๊ฒฝํ์ ๋ง๋ค ์ ์์ต๋๋ค.
์ด ํ ์ ํ๋ก์ ํธ์ ํตํฉํ ๋ ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํค๋ ๋๊ตฌ์ด๋ฉฐ ๋ชจ๋ ๊ฐ๋ ฅํ ๋๊ตฌ์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ ์ฌ๋ ฅ์ ์ต๋ํ ๋ฐํํ๋ ค๋ฉด ์ ์คํ ๊ตฌํ๊ณผ ์๊ฒฉํ ํ ์คํธ๊ฐ ํ์ํ๋ค๋ ์ ์ ๊ธฐ์ตํ์ญ์์ค.