React에서 데이터를 가져오는 방법에 대해 얘기하면 반드시 등장하는 두 개념이 있다. Waterfall과 Parallel이다. 이 두 개념은 단순히 "빠르고 느린 것"의 차이가 아니라, React의 렌더링 구조 자체에서 비롯된 문제이다.
이 글은 카페에서 음료를 주문하는 상황을 실로 꿰어서, Waterfall이 왜 발생하고 Parallel로 바꾸면 어떤 구조적 변화가 일어나는지, 그리고 실제로 어떤 방법으로 해결할 수 있는지를 설명하자.
카페에서 음료를 주문하는 상황을 떠올려보자. 카운터 직원이 손님에게 "아메리카노 주문받아드립니다"라고 하고, 아메리카노를 만들어서 건네준 후에야 "수정과도 주문하실 건가요?"라고 물어보는 것이다.
두 음료는 완전히 독립적으로 만들 수 있는 주문이지만, 직원이 하나씩 순차적으로 처리하는 구조 때문에 손님의 총 대기 시간은 아메리카노 제작 시간 + 수정과 제작 시간이 된다. 아메리카노가 완료되지 않을 때까지는 수정과의 존재조차 알지 못한다.
이것이 React에서 발생하는 Waterfall과 정확히 같은 구조이다.
React는 컴포넌트 트리를 위에서 아래로 순차적으로 렌더한다. 부모가 완료되어야 자식이 mount된다. 그리고 각 컴포넌트가 자신의 데이터를 useEffect로 가져오는 구조라면, 렌더 순서 자체가 fetch 순서가 된다.
컴포넌트 트리:
App
└── Layout
└── UserProfile ← fetchUser
└── UserPosts ← fetchPosts
└── PostComments ← fetchComments
카페로 치면 이렇다. UserProfile은 아메리카노, UserPosts는 수정과, PostComments는 주스이다. 직원이 아메리카노를 만들고 건네준 후에야 수정과를 만들고, 수정과를 건네준 후에야 주스를 만드는 것이다.
시간축으로 그으면:
시간축 (Waterfall):
UserProfile mount → fetchUser 시작
..................... fetchUser 완료
UserPosts mount → fetchPosts 시작
................. fetchPosts 완료
PostComments mount → fetchComments 시작
.................. fetchComments 완료
총 로딩 시간 = fetchUser + fetchPosts + fetchComments
Network 탭에서도 이렇게 보인다.