-
마카오 기프트에서는 되던 게 왜 프로젝트에서는 안돼? - useEffect, Guard ClauseToday I Learned 2022. 11. 9. 15:48
지난 주에 UI 컴포넌트에서 fetch해온 상태가 왜 정상적으로 값을 갖고 있지 않고 undefined 상태를 가졌었는지 오전에 있었던 이슈를 통해 알 수 있었다.
이슈를 살펴보기 전에 먼저. React에서는 데이터 가져오기, 구독 설정하기, 수동으로 React 컴포넌트의 DOM을 수정하는 등의 부수 효과들을 Effect Hook 내부에서 수행하도록 하게 할 수 있다. 이때 Effect Hook은 컴포넌트들이 먼저 렌더링된 이후에야 수행된다.
즉, 컴포넌트가 최초로 렌더링된 뒤, 부수 효과를 실행해 상태를 가져오게 하기까지 컴포넌트에 전달되는 상태가 비어 있는 순간이 있다. 이때 UI 컴포넌트에서 렌더링을 수행하기 전에 보호절로 상태를 검사해서, 상태가 비어 있을 때에는 대체 요소를 출력하도록 해서 비어 있는 상태에 접근해 예외를 발생시키는 것을 방지할 수 있다.
// UI 컴포넌트 예시 코드 export default function Posts({ posts }) { if (!posts) { return ( <p>Now Loading...</p> ); } return ( <ul> {posts.map((post) => ( <li key={post.id}> // ... </li> ))} </ul> ) }
일단 지금 진행하고 있는 프로젝트에서는 왜 Effect Hook에서 상태를 가져오도록 했는데도 자꾸 상태가 없다고 하는지를 알 수 있었다. 렌더링과 부수 효과의 실행 순서를 정확히 구분하지 못하고 있었던 데에서 발생한 문제였다.
이 내용은 사실 지금까지 프로젝트를 진행하면서 참고했던 이전 프로젝트에서, 페이지 컴포넌트에 보호절이 없는데도 왜 정상적으로 상태를 가져오는지 이해가 가지 않아 곳곳에 console.log를 찍어보면서 실행 순서를 찾아보던 도중 알게 된 내용이었다.
export default function ProductsPage() { const productStore = useProductStore(); useEffect(() => { console.log('useEffect 진입'); productStore.fetchProducts(1); productStore.setCurrentPage(1); }, []); console.log('store에서 값 가져옴'); const { products, pagesCount, currentPage } = productStore; console.log('products', products); const navigateToProduct = (productId) => { navigate(`/products/${productId}`, { state: { productId, }, }); }; const switchPage = (page) => { productStore.fetchProducts(page); productStore.setCurrentPage(page); }; return ( <Products products={products} pagesCount={pagesCount} currentPage={currentPage} onClickProduct={navigateToProduct} onClickPage={switchPage} /> ); }
'생각해보니 UI 컴포넌트를 확인하지 않았었다!'는 생각이 오전에 머리를 스쳐 지나갔다.
export default function Products({ products, pagesCount, currentPage, onClickProduct, onClickPage, }) { // onClick Functions return ( <Container> <HeroSection /> <ListSection> {!products.length ? ( <ListTitle hasContent={products.length} > 상품이 존재하지 않습니다. </ListTitle> ) : ( <> <ListTitle hasContent={products.length} > 인기선물을 한 자리에 모았어요 </ListTitle> //... </> )} </ListSection> </Container> ); }
product의 상태를 확인하는 부분이 존재했다.
단순히 요구사항 중의 하나라고 생각하고 구현했던 '상품이 존재하지 않습니다' 부분이 사실은 상태를 받아오기 전까지는 상태를 이용해 렌더링을 하지 않게 하는 보호절의 역할을 겸하고 있었다.
실제로 보호절에 들어가기 전에 props로 받아오는 상태 객체들의 요소에 접근을 시도해 보니 undefined임이 확인되었다. 공식 문서를 처음 읽을 때에는 무슨 소리인지 이해가 가지 않았던 '부수 효과'에 대한 내용이 조금은 이해가 되는 순간이었다.
'Today I Learned' 카테고리의 다른 글
핵심 가치에 집중하니 크기는 줄어도 내용은 견고해졌다 (0) 2022.11.11 최소한의 기능으로 다시 시작하기 (0) 2022.11.10 돌돌설 (돌고 돌아 설계 문서 작성부터 다시) (0) 2022.11.08 프로젝트 설계 문서 공개 리팩터링 (1) 2022.11.07 하나의 메서드를 두 개 이상의 기능이 공유하면 발생할 수 있는 문제 (0) 2022.11.06