๐น 1. ๋ฌธ์ ์ํฉ ๋ฐ ๊ฐ์
๐ ๋ฌธ์ : ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋ UI๊ฐ ๊น๋นก์ด๋ ํ์ ๋ฐ์
- React์์ API ํธ์ถ์ ํตํด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋, ๋ฐ์ดํฐ๊ฐ ๋์ฐฉํ๊ธฐ ์ ๊น์ง UI๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ๋ ๋๋ง๋์ง ์์ ์ ์์
- ์๋ฅผ ๋ค์ด, goalAmount๊ฐ ์ด๊ธฐ์๋ null์ด์ง๋ง, ์๋ฒ ์๋ต ํ 100000์ผ๋ก ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ, ํ๋ฉด์ด ๊ฐ์๊ธฐ ๋ณํ๋ฉด์ ๊น๋นก์ด๋ ํ์์ด ๋ฐ์ํ ์ ์์
- ์ด๋ฌํ UI ๊น๋นก์(Flash of Unstyled Content, FOUC)์ ์ฌ์ฉ์ ๊ฒฝํ(UX)์ ์ ํ์ํฌ ์ ์์
์ฐธ๊ณ ๋ก
FOUC (๊น๋นก์)์ด ๋ฐ์ํ๋ ์กฐ๊ฑด
1. ๋น๋๊ธฐ ์์ฒญ์ ์๋ต์ด ์ง์ฐ๋์์๋ (await ์์ด ๋น๋๊ธฐ ์์ฒญ์ด ์คํ๋ฌ์๋)
2. ์ํ ๊ฐ์ด ๋น๋๊ธฐ ์๋ต ์ดํ ๋ณ๊ฒฝ๋ ๋
• useState()๋ก ๊ด๋ฆฌํ๋ ๊ฐ(goalAmount ๋ฑ)์ด ๋น๋๊ธฐ ์๋ต ์ดํ ์ ๋ฐ์ดํธ๋ ๊ฒฝ์ฐ, React๊ฐ ๋ค์ ๋ ๋๋ง์ ์ํ
• ๋ ๋๋ง์ด ๋ฐ๋ณต๋๋ฉด์ UI๊ฐ “์ด๊ธฐ ์ํ → ๋ฐ์ดํฐ๊ฐ ์ฑ์์ง ์ํ”๋ก ๋ณํํ๋ฉด์ ๊น๋นก์์ด ๋ฐ์ํจ.
3. ์กฐ๊ฑด๋ถ ๋ ๋๋ง ์, ์ํ๊ฐ ๋ค๋ฅธ UI๋ฅผ ๋ณด์ฌ์ค ๊ฒฝ์ฐ
์๋ฅผ๋ค๋ฉด,
{goalAmount ? <p>ํ์ฌ ์์ฐ: {goalAmount}์</p> : <p>์์ฐ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ์ค...</p>}
• ์์ ๊ฐ์ ์กฐ๊ฑด๋ถ ๋ ๋๋ง์ ์ฌ์ฉํ ๊ฒฝ์ฐ,
1. goalAmount๊ฐ null์ด๋ฉด "์์ฐ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ์ค..."์ด ํ์๋จ
2. ๋น๋๊ธฐ ์๋ต์ด ๋์ฐฉํ ํ goalAmount๊ฐ ์ ๋ฐ์ดํธ๋๋ฉด ์๋ก์ด UI(ํ์ฌ ์์ฐ: 100000์)๊ฐ ๋ ๋๋ง๋จ
3. UI๊ฐ ๊ฐ์๊ธฐ ๋ณ๊ฒฝ๋๋ฉด์ ๊น๋นก์์ด ๋ฐ์ํจ
4. setTimeout(), useEffect() ๋ฑ์ด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ ํ UI๋ฅผ ๋ณ๊ฒฝํ ๋
• useEffect()์์ fetchBudget()์ ์คํํ๊ณ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋ ๋ UI๊ฐ ๋ค์ ๋ ๋๋ง๋จ
• ์ด๊ธฐ๊ฐ(null)๋ก ๋ ๋๋ง๋ ํ, ๋ฐ์ดํฐ๊ฐ ๋ค์ด์จ ํ UI๊ฐ ๋ณ๊ฒฝ๋๋ฉด์ ๊น๋นก์ ๋ฐ์ ๊ฐ๋ฅ
๐ ์๊ฐํด๋ธ ์์ด๋์ด์ ๋ชฉํ
โ
๋น๋๊ธฐ ์์ฒญ์ด ๋๋ ๋๊น์ง await์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ด
โ
๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ ํ UI๊ฐ ๊ฐ์๊ธฐ ๋ณ๊ฒฝ๋์ง ์๋๋ก setTimeout()์ ํ์ฉํ์ฌ ์์ฐ์ค๋ฝ๊ฒ ์ ํ
โ
๋ก๋ฉ ์ค ์ํ๋ฅผ ์ถ๊ฐํ์ฌ ์ฌ์ฉ์์๊ฒ ํผ๋๋ฐฑ์ ์ ๊ณตํ๊ณ ๊น๋นก์์ ์ต์ํ
๐น 2. ๋น๋๊ธฐ ์์ฒญ(await)์ ํตํด ๋ฐ์ดํฐ๊ฐ ์ค๋น๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๊ธฐ
๐ await ์์ด ์คํํ๋ฉด ๋ฐ์ํ๋ ๋ฌธ์
const handleLogin = async (e) => {
e.preventDefault();
setLoading(true);
try {
const data = await login(email, password);
localStorage.setItem('accessToken', data.data.accessToken);
localStorage.setItem('refreshToken', data.data.refreshToken);
setFetchingBudget(true);
const budgetAmount = fetchBudget(); // โ `await` ์์ด ์คํ → ์๋ต์ด ๋์ฐฉํ๊ธฐ ์ ์ ๋ค์ ์ฝ๋ ์คํ๋จ
setFetchingBudget(false);
console.log('๐ฆ ๋ก๊ทธ์ธ ํ ๋ฐ์ ์์ฐ ๊ธ์ก:', budgetAmount);
// โ ๋ฐ์ดํฐ๊ฐ ์์ง ์์ ๋ navigate ์คํ → ์๋ชป๋ ํ์ด์ง ์ด๋ ๊ฐ๋ฅ์ฑ ์์
navigate(budgetAmount !== null && budgetAmount > 0 ? '/main' : '/initial');
} catch (error) {
console.error('๋ก๊ทธ์ธ ์คํจ:', error);
} finally {
setLoading(false);
}
};
โ ๏ธ ๋ฌธ์ ์
1๏ธโฃ fetchBudget()์ด ์คํ๋์์ง๋ง, ์๋ต์ด ๋์ฐฉํ๊ธฐ ์ ์ ๋ค์ ์ฝ๋(navigate())๊ฐ ์คํ๋จ
2๏ธโฃ goalAmount๊ฐ ์์ง null์ด๋ฏ๋ก, ์๋ชป๋ UI ์ํ์์ ํ์ด์ง๊ฐ ๋ณ๊ฒฝ๋ ๊ฐ๋ฅ์ฑ์ด ์์
3๏ธโฃ ๋น๋๊ธฐ ์๋ต์ด ๋์ฐฉํ ํ UI๊ฐ ๊ฐ์๊ธฐ ๋ณ๊ฒฝ๋๋ฉด์ ๊น๋นก์ด๋ ํ์ ๋ฐ์
๐น 3. setTimeout() + await ํ์ฉํ์ฌ UI๊ฐ ๋ถ๋๋ฝ๊ฒ ์ ํ๋๋๋ก ํ๊ธฐ
โ ์ฌ๋ฐ๋ฅธ ์ฝ๋ (await๊ณผ setTimeout์ ์ฌ์ฉํ์ฌ ๊น๋นก์ ๋ฐฉ์ง)
const handleLogin = async (e) => {
e.preventDefault();
setLoading(true);
try {
const data = await login(email, password);
localStorage.setItem('accessToken', data.data.accessToken);
localStorage.setItem('refreshToken', data.data.refreshToken);
setModalMessage({
type: 'success',
message: '๋ก๊ทธ์ธ ์ฑ๊ณต! ์์ฐ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๊ณ ์์ต๋๋ค!',
});
// โ
์๋ฒ ์๋ต์ ๊ธฐ๋ค๋ฆด ๋ `await` ์ฌ์ฉ
setFetchingBudget(true);
const budgetAmount = await fetchBudget();
setFetchingBudget(false);
console.log('๐ฆ ๋ก๊ทธ์ธ ํ ๋ฐ์ ์์ฐ ๊ธ์ก:', budgetAmount);
// โ
UI๊ฐ ๊ฐ์๊ธฐ ๋ณ๊ฒฝ๋์ง ์๋๋ก `setTimeout()`์ ์ฌ์ฉํ์ฌ ์์ฐ์ค๋ฝ๊ฒ ์ ํ
setTimeout(() => {
navigate(budgetAmount !== null && budgetAmount > 0 ? '/main' : '/initial');
}, 500);
} catch (error) {
console.error('๋ก๊ทธ์ธ ์คํจ:', error);
setModalMessage({
type: 'error',
message: '๋ก๊ทธ์ธ ์คํจ! ๋ค์ ์๋ํด์ฃผ์ธ์.',
});
} finally {
setLoading(false);
}
};
๐น 4. setTimeout()์ ์ฌ์ฉํ ์ด์
โ await์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ | - goalAmount ๊ฐ์ด ํ์คํ ์ ๋ฐ์ดํธ๋จ- null์ํ์์ UI๊ฐ ๋ ๋๋ง๋์ง ์์ | - ๋ฐ์ดํฐ ๋ก๋ฉ ์๊ฐ์ด ๊ธธ๋ฉด ์ฌ์ฉ์๊ฐ ์๋ต์ ๋๋ฆฌ๊ฒ ์ฒด๊ฐํ ์ ์์ |
โ setTimeout()์ผ๋ก ํ๋ฉด ์ ํ ๋๋ ์ด ์ถ๊ฐ | - goalAmount ๊ฐ์ด ๋ณ๊ฒฝ๋ ํ ๋ถ๋๋ฝ๊ฒ ํ๋ฉด ์ ํ๋จ- ๊น๋นก์ ๋ฐฉ์ง ํจ๊ณผ | - ๋๋ฌด ์งง์ผ๋ฉด ํจ๊ณผ๊ฐ ์๊ณ , ๋๋ฌด ๊ธธ๋ฉด UX ๋ถํธํจ (500ms~700ms๊ฐ ์ ์ ) |
๐ ์ต์ข ๊ฒฐ๋ก
๋น๋๊ธฐ ์์ฒญ(await fetchBudget())์ ์ฌ์ฉํ๋ฉด ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ค๋ฆด ์ ์์ง๋ง, React์ ์ํ ๋ณ๊ฒฝ ๋ฐฉ์ ๋๋ฌธ์ UI๊ฐ ๊น๋นก์ผ ์ ์๋ค.
์ด๋ฌํ ๊น๋นก์์ ๋ฐฉ์งํ๋ ค๋ฉด setTimeout(500ms)์ ์ถ๊ฐํ์ฌ ํ๋ฉด ์ ํ์ด ๋ถ๋๋ฝ๊ฒ ์ด๋ฃจ์ด์ง๋๋ก ํด์ผ ํ๋ค.
์ฌ์ฉ์ ๊ฒฝํ ์ฆ, UX๋ฅผ ๊ณ ๋ คํ๋ฉด loading ์ํ๋ฅผ ์ถ๊ฐํ์ฌ "๋ก๋ฉ ์ค..."์ ํ์ํ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ผ ๊ฒ ๊ฐ๊ธดํ๋ค๋ง, ๋ ๋ชจ๋ฌ ๋ฉ์์ง๋ก ์ด๋ฅผ ์ฒ๋ฆฌํ๋ค.
์ฆ, ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ ํ UI์ ๋ฐ์ํ ๋, await์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ , setTimeout()์ ํ์ฉํ์ฌ UI๊ฐ ์์ฐ์ค๋ฝ๊ฒ ์ ํ๋๋๋ก ํ๋ ๊ฒ์ด ์ฌ์ฉ์ ์ ์ฅ์์ ์ ์ผ