“비동기 프로그래밍”: 순서가 보장되지 않는 상황
function getBazFromX(x) {
if (x === undefined) {
return undefined;
}
if (x.foo === undefined) {
return undefined;
}
if (x.foo.bar === undefined) {
return undefined;
}
return x.foo.bar.baz;
}
x.foo.bar.baz 프로퍼티에 안전하게 접근하려고 하는 코드
→ 함수가 하는 일은 굉장히 단순하나, 코드가 복잡. 각 프로퍼티에 접근하는 핵심 기능이 코드로 드러나지 않음(명령어에 노이즈가 많아서 명확하게 드러나지 않음).
function getBazFromX(x) {
return x?.foo?.bar?.baz;
}
Optional Chaining 문법을 활용한 동일 함수
→ 코드가 간결. ‘성공한 경우’를 생각하는 x.foo.bar.baz와 문법적 차이가 크지 않음. 함수의 역할을 한눈에 파악할 수 있음.
function fetchAccounts(callback){
fetchUserEntity((err, user) => {
if(err != null) {
callback(err, null);
return;
}
fetchUserAccounts(user.no, (err, accounts) => {
if(err != null) {
callback(err, null);
return;
}
callback(null, accounts);
});
});
}
Promise 없던 시절 Callback 비동기 처리 코드
→ ‘성공하는 경우’와 ‘실패하는 경우’가 섞여서 처리됨. 코드를 작성하는 입장에서 매번 에러 유무를 확인해야 함.
async function fetchAccounts() {
const user = await fetchUserEntity();
const accounts = await fetchUserAccounts(user.no);
return accounts;
}
→ ‘성공하는 경우’만 다루고, ‘실패하는 경우’는 catch 절에서 분리해 처리함. ‘실패하는 경우’에 대한 처리를 외부에 위임할 수 있음.
1. 성공, 실패의 경우를 분리해 처리할 수 있다.
2. 비즈니스 로직을 한눈에 파악할 수 있다.
→ 이에 2개의 비동기 리소스를 가져오게 되면 3의 제곱, 즉 9개의 상태를 가지게됨.
→ 문제 해결 방법: React Suspense for Data fetching
아직 리액트 자체에서는 실험 단계로 테스트 중이나, 라이브러리를 활용해 이를 구현할 수 있음.
Recoil에서는 비동기 셀렉터로 Suspence를 일으켜, 비동기가 필요한 컴포넌트를 Suspence로 적절히 감싸주기만 하면 됨.
이를 통해 데이터가 준비되는 대로 하나씩 자연스럽게 불러올 수 있음.
→ 실제 상태관리, 메모이제이션 등의 작업은 컴포넌트를 감싸는 리액트 프레임워크가 수행
→ 실제 로딩 상태, 에러 상태 처리는 컴포넌트를 감싸는 부모 컴포넌트가 수행
실패할 수 있는 함수는 에러를 throw 문으로 발생시킴. 실제 에러 처리는 컴포넌트를 감싸는 부모 함수가 수행
오늘의 궁금한 점