π» Promise μλ¬ λ¬΄μνκΈ°: μ‘°μ©ν μ€ν¨μ νΌλμ€λ¬μ΄ UI
Summary
λΉλκΈ° μμ
(Promise)μμ λ°μνλ μλ¬λ₯Ό λͺ
μμ μΌλ‘ μ²λ¦¬νμ§ μμΌλ©΄, μ ν리μΌμ΄μ
μ΄ μ‘°μ©ν μ€ν¨νκ³ μ¬μ©μμκ² νΌλμ€λ¬μ΄ UIλ μλͺ»λ μνλ₯Ό 보μ¬μ€ μ μμ΅λλ€. μ΄λ λλ²κΉ
μ μ΄λ ΅κ² νκ³ μ¬μ©μ κ²½νμ ν¬κ² μ ν΄ν©λλ€. λͺ¨λ Promiseμλ .catch()
λ₯Ό λΆμ΄κ±°λ async/await
ꡬ문μμ try...catch
λ₯Ό μ¬μ©νμ¬ μλ¬λ₯Ό μ‘κ³ , μ¬μ©μμκ² μ μ ν νΌλλ°±μ μ 곡ν΄μΌ ν©λλ€.
Why Wrong?
μ μ΄ ν¨ν΄μ΄ λ¬Έμ μΈκ°?
νλ‘ νΈμλ μ ν리μΌμ΄μ
μμ λ°±μλ API νΈμΆ, νμΌ μ½κΈ°, μ λλ©μ΄μ
μ§μ° λ± λΉλκΈ° μμ
μ νμμ μ
λλ€. μλ°μ€ν¬λ¦½νΈλ μ΄λ¬ν λΉλκΈ° μμ
μ κ²°κ³Όλ₯Ό Promise
κ°μ²΄λ₯Ό ν΅ν΄ λ€λ£Ήλλ€. Promise
λ μ±κ³΅(resolve
) λλ μ€ν¨(reject
) μνλ₯Ό λνλ΄λλ°, κ°λ°μκ° reject
μνλ₯Ό μ μ ν μ²λ¦¬(catch
)νμ§ μμΌλ©΄ λ¬Έμ κ° λ°μν©λλ€.
1. μ‘°μ©ν μ€ν¨ (Silent Failure)
Promise
κ° reject
λμμμλ λΆκ΅¬νκ³ .catch()
λ©μλκ° μ°κ²°λμ΄ μμ§ μμΌλ©΄, ν΄λΉ μλ¬λ Unhandled Promise RejectionμΌλ‘ κ°μ£Όλ©λλ€. λλΆλΆμ λͺ¨λ λΈλΌμ°μ λ Node.js νκ²½μμλ μ΄λ¬ν μλ¬λ₯Ό μ½μμ κ²½κ³ λ‘ μΆλ ₯νμ§λ§, μ ν리μΌμ΄μ
μ μ€ν νλ¦μ λ©μΆμ§ μκ³ κ³μλ©λλ€. κ°λ°μ λꡬλ₯Ό μ΄μ΄λ³΄μ§ μμΌλ©΄ μ΄λ¬ν μ€λ₯κ° λ°μνλμ§ μκΈ° μ΄λ ΅μ΅λλ€.
2. μμΈ‘ λΆκ°λ₯ν UI λ° μν
λΉλκΈ° μμ
μ΄ μ€ν¨νλλ° μλ¬ μ²λ¦¬κ° μλ€λ©΄, μ ν리μΌμ΄μ
μ μν(state)λ μλν λλ‘ μ
λ°μ΄νΈλμ§ μμ΅λλ€. μλ₯Ό λ€μ΄, μ¬μ©μ μ 보λ₯Ό λΆλ¬μ€λ API νΈμΆμ΄ μ€ν¨νμ§λ§ μλ¬λ₯Ό μ‘μ§ μμΌλ©΄, isLoading
μνκ° false
λ‘ λ°λλλΌλ user
λ°μ΄ν°λ μ¬μ ν null
μ΄κ±°λ μ΄μ κ° κ·Έλλ‘ λ¨μμμ μ μμ΅λλ€. μ΄λ‘ μΈν΄ μ¬μ©μμκ² λΉ νλ©΄, μ€λλ λ°μ΄ν°, λλ κΉ¨μ§ UIκ° νμλ μ μμΌλ©°, μ΄λ μ¬κ°ν μ¬μ©μ κ²½ν μ νλ‘ μ΄μ΄μ§λλ€.
3. λλ²κΉ μ μ΄λ €μ
μλ¬κ° λ°μν΄λ λͺ νν λ©μμ§λ μ²λ¦¬ λ‘μ§μ΄ μμΌλ©΄, λ¬Έμ μ μμΈμ νμ νκΈ° λ§€μ° μ΄λ ΅μ΅λλ€. μ΄λ λΆλΆμμ, μ μλ¬κ° λ°μνλμ§ μΆμ νκΈ° μν΄ λ λ§μ μκ°κ³Ό λ Έλ ₯μ΄ νμνκ² λ©λλ€.
4. μμ λμ λ° μ€μλ κ°λ₯μ±
μΌλΆ λΉλκΈ° μμ μ μ±κ³΅ μ νΉμ μμμ ν΄μ νκ±°λ λ€μ λ¨κ³λ₯Ό μ§νν΄μΌ ν©λλ€. μλ¬ μ²λ¦¬ μμ΄ μ€ν¨νλ κ²½μ°, μ΄λ¬ν μμ ν΄μ λ‘μ§μ΄ μ€νλμ§ μμ μμ λμκ° λ°μνκ±°λ, μμμΉ λͺ»ν μν©μμ μ ν리μΌμ΄μ μ΄ μ€μλν μ μμ΅λλ€.
How to Fix?
μ΄λ»κ² μμ ν΄μΌ νλκ°?
λΉλκΈ° μμ
μ μλ¬λ₯Ό ν¨κ³Όμ μΌλ‘ μ²λ¦¬νλ κ°μ₯ κΈ°λ³Έμ μΈ λ°©λ²μ Promise.prototype.catch()
λ©μλλ₯Ό μ¬μ©νκ±°λ async/await
ꡬ문 λ΄μμ try...catch
λΈλ‘μ μ¬μ©νλ κ²μ
λλ€. μ΄λ€ λ°©λ²μ μ¬μ©νλ , μλ¬ λ°μ μ μ¬μ©μμκ² νΌλλ°±μ μ 곡νκ³ , μ ν리μΌμ΄μ
μ μνλ₯Ό μμ μ μΌλ‘ κ΄λ¦¬νλ κ²μ΄ μ€μν©λλ€.
1. Promise.prototype.catch()
μ¬μ©νκΈ°
λͺ¨λ Promise
체μΈμ λ§μ§λ§μ .catch()
λ©μλλ₯Ό λΆμ¬ μλ¬λ₯Ό μ²λ¦¬ν©λλ€. μ΄λ Promise κΈ°λ° API (Fetch API λ±)λ₯Ό μ§μ μ¬μ©ν λ μ μ©ν©λλ€.
fetch('/api/data')
.then(response => response.json())
.then(data => {
// μ±κ³΅μ μΌλ‘ λ°μ΄ν° μ²λ¦¬
})
.catch(error => {
// μλ¬ λ°μ μ μ²λ¦¬
console.error('λ°μ΄ν°λ₯Ό κ°μ Έμ€λ μ€ μ€λ₯ λ°μ:', error);
// μ¬μ©μμκ² μ€λ₯ λ©μμ§ νμ λ±
});
2. async/await
μ try...catch
μ¬μ©νκΈ°
κ°λ
μ± λμ λΉλκΈ° μ½λλ₯Ό μν΄ async/await
λ₯Ό μ¬μ©νλ κ²½μ°, λκΈ° μ½λμ²λΌ try...catch
λΈλ‘μ μ¬μ©νμ¬ μλ¬λ₯Ό μ²λ¦¬ν μ μμ΅λλ€. μ΄λ λ μ§κ΄μ μ΄κ³ 체κ³μ μΈ μλ¬ κ΄λ¦¬λ₯Ό κ°λ₯νκ² ν©λλ€.
async function fetchData() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
// HTTP μλ¬ μν(4xx, 5xx)λ μλ¬λ‘ κ°μ£Όνμ¬ throw
throw new Error(`HTTP μλ¬! μν: ${response.status}`);
}
const data = await response.json();
// μ±κ³΅μ μΌλ‘ λ°μ΄ν° μ²λ¦¬
} catch (error) {
// μλ¬ λ°μ μ μ²λ¦¬
console.error('λ°μ΄ν°λ₯Ό κ°μ Έμ€λ μ€ μ€λ₯ λ°μ:', error);
// μ¬μ©μμκ² μ€λ₯ λ©μμ§ νμ λ±
}
}
3. μλ¬ μν λ° UI νΌλλ°± κ΄λ¦¬
μλ¬κ° λ°μνλ©΄ λ¨μν μ½μμ κΈ°λ‘νλ κ²μ λμ΄, μ¬μ©μμκ² μκ°μ μΈ νΌλλ°±μ μ 곡ν΄μΌ ν©λλ€. React μ»΄ν¬λνΈμμλ useState
ν
μ μ¬μ©νμ¬ μλ¬ μνλ₯Ό κ΄λ¦¬νκ³ , μ΄ μνμ λ°λΌ UIλ₯Ό λ€λ₯΄κ² λ λλ§ν μ μμ΅λλ€. μλ₯Ό λ€μ΄, μλ¬ λ©μμ§λ₯Ό νμνκ±°λ μ¬μλ λ²νΌμ μ 곡νλ λ±μ UX κ°μ μ κ³ λ €ν΄μΌ ν©λλ€.
// μμ: React μ»΄ν¬λνΈμμ μλ¬ μν κ΄λ¦¬
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
setError(null); // μλ‘μ΄ μμ² μμ μ μλ¬ μ΄κΈ°ν
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error('λ°μ΄ν° λ‘λ μ€ν¨!');
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
console.error('λ°μ΄ν° λ‘λ© μ€ μλ¬:', err);
} finally {
setIsLoading(false);
}
};
fetchData();
}, []);
if (isLoading) return <div>λ‘λ© μ€...</div>;
if (error) return <div style={{ color: 'red' }}>μλ¬ λ°μ: {error.message}</div>;
return <div>λ°μ΄ν°: {JSON.stringify(data)}</div>;
}
4. μ μ μλ¬ νΈλ€λ§ (μ ν μ¬ν)
μ ν리μΌμ΄μ
μ λ°μ κ±Έμ³ μ²λ¦¬λμ§ μμ Promise
μλ¬λ₯Ό ν¬μ°©νκΈ° μν΄ window.addEventListener('unhandledrejection', ...)
λ₯Ό μ¬μ©ν μλ μμ΅λλ€. μ΄λ μ΅μ’
λ°©μ΄μ μν μ νμ§λ§, κ°λ³ λΉλκΈ° μμ
μλ μ¬μ ν κ°λ³μ μΈ .catch()
λ try...catch
λΈλ‘μ μ μ©νλ κ²μ΄ μ’μ΅λλ€.
Before Code (Bad)
// React μ»΄ν¬λνΈ μμ
import React, { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
// μλ¬ μ²λ¦¬ μμ΄ μ¬μ©μ λ°μ΄ν°λ₯Ό κ°μ Έμ΄
fetch(`/api/users/${userId}`)
.then(response => response.json())
.then(data => {
setUser(data);
setIsLoading(false);
}); // π¨ .catch() λλ½!
// λ§μ½ fetchκ° μ€ν¨νλ©΄? λ€νΈμν¬ λ¬Έμ , μλ² μλ¬ λ±
// `user` μνλ null, `isLoading`μ falseκ° λμ΄
// UIλ λΉ νλ©΄μ΄κ±°λ λ‘λ© μ€νΌλκ° μ¬λΌμ§ μ±λ‘ μ΄μν μνλ₯Ό 보μ¬μ€.
// μ½μμλ Unhandled Promise Rejection κ²½κ³ λ§ λ¨μ.
}, [userId]);
if (isLoading) return <div>λ‘λ© μ€...</div>;
if (!user) return <div>μ¬μ©μ μ 보λ₯Ό λΆλ¬μ¬ μ μμ΅λλ€.</div>; // μ΄ λ©μμ§μ‘°μ°¨ μ λμ¬ μ μμ
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
After Code (Good)
// React μ»΄ν¬λνΈ μμ
import React, { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null); // μλ¬ μν μΆκ°
useEffect(() => {
const fetchUser = async () => {
setIsLoading(true);
setError(null); // μλ‘μ΄ μμ² μ μλ¬ μ΄κΈ°ν
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
// HTTP μλ¬ μν (4xx, 5xx) μ²λ¦¬
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (err) {
console.error("λ°μ΄ν° λ‘λ© μ€ μλ¬ λ°μ:", err);
setError(err); // μλ¬ μν μ
λ°μ΄νΈ
} finally {
setIsLoading(false); // λ‘λ© μνλ νμ ν΄μ
}
};
fetchUser();
}, [userId]);
if (isLoading) return <div>λ‘λ© μ€...</div>;
if (error) return <div style={{ color: 'red' }}>λ°μ΄ν°λ₯Ό λΆλ¬μ€λ λ° μ€ν¨νμ΅λλ€: {error.message}</div>;
if (!user) return <div>μ¬μ©μ μ 보λ₯Ό μ°Ύμ μ μμ΅λλ€.</div>; // μλ¬κ° μλ κ²½μ°μ μ¬μ©μ μμ μ²λ¦¬
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}