들어가며
리액트를 하나보면 필연적으로 흰화면에 loading...를 띄우는 경험을 해보셨을 것이라 생각합니다. 그리고 주로 데이터가 없는 경우 if 문으로 분기처리를 하게 됩니다.
위의 방식이 나쁜것은 아닌데 하나의 컴포넌트에 return 문이 여러개가 되는 상황이 발생합니다. 아니면 삼항연산자로 분기처리를 하던가요.
그래서 조금더 리액트스러운 방법을 생각했고 Suspense와 Error Boundary를 통해 개선했습니다. 이번 글에서는 이 경험을 다뤄보도록 할게요.
React-query는 왜 도입했는가?
뜬금없이 loading... 처리 얘기를 하다가 갑자기 React-query가 왜 나오냐? 하실수도 있습니다.
하지만 생각하보면 당연히 데이터패칭과 관계될수밖에 없는 것이 주로 loading을 보여줘야 할때는 데이터를 가져올때입니다.
그리고 데이터를 가져오는건 fetch 나 axios를 쓰죠. 그리고 React query는 이런 작업을 편리하게 도와주는 라이브러리입니다.
(자세한 사용법은 여기선 다루진 않을게요.)
다시 react query로 돌아가서, 제가 실제로 처음 사용한 라이브러리는 react-query가 아닌 swr입니다. 진행하고 있는 사이드 프로젝트가 Next를 사용하고 있고 그래서 자연스레 swr를 도입했습니다. (같은 회사 vercel에서 만들었거든요. ㅎㅎ)
라이브러리를 통해 UI와 데이터를 편하게 제어하고 캐싱하는 기능이 너무 맘에 들어 회사에도 도입하자고 세미나를 했습니다.
근데 세미나에서 한가지 욕심을 부리게 됩니다. swr도 써봤으니 대응되는 react query를 써보고 싶었던 겁니다. ㅎㅎㅎ
그래서 빠르게 브랜치 따서 쿵짝쿵짝 테스트도 해보고 적용도 해보고 했습니다.
팀 내 세미나에서 react-query를 적용한 예시는 차트 데이터 패칭 캐싱이었습니다.( 가장 극적인 요소일것같아서 )
해당 기능의 순서는 아래와 같았습니다.
- 차트 데이틀 클릭
- 해당 키값으로 서버에 데이터 요청
- 키값과 매칭된 데이터쿼리를 sql 쿼리로 바꾸고 데이터 생성하여 차트 데이터 반환
- 리액트에서 해당 데이터를 통해 차트를 랜더링
그리고 이 기능에 캐싱이 좋은점이, 대용량 또는 복잡한 쿼리의 경우 요청부터 랜더링까지 수십초까지 딜레이되어버리는 상황이 발생했습니다. 물론 해당 기능은 비동기로 처리되어 유저의 행동을 방해하지 않지만 통상적으로 허용하는 기다림은 몇초 이내인 것을 생각하면 매우 위험한 상황이라고 생각했습니다.
그래서 최소한으로 같은 요청을 반복하지 말자라는 생각으로 해당 기능에 캐싱을 도입하고 발표를 잘 마무리 했습니다.
React-query의 기본 코드
잠깐 react-query의 예시를 보겠습니다. 결국 실제 데이터 요청은 fetch함수가 진행하고 key와 option를 통해 여러 가지 값을 구조 분해로 받습니다.
제가 알기로는 로딩중, 요청 중, 에러, 데이터 이렇게 크게 4가지를 돌려주는 것으로 알고 있습니다.
return 문이 여러개에요...
이렇게 사용하고 있다가 문득 생각이 들었습니다.
Axios도 결국 async await 구조로 하게 되면 성공한 경우에만 동작하고 결국 error나 loading 같은 경우 try catch 혹은 별도의 loading 값을 처리해야 하는 구조가 됩니다.
UI입장에서는 이러한 상태에 맞게 매번 랜더링을 해야하는 컴포넌트가 달라지고 이는 결국 한 컴포넌트 내에서 return이 상태별로 존재해야만 하는 상황으로 귀결됩니다.
코드를 보면 loginStatus, lessonForStudent 에 따라 return으로 돌려주는 컴포넌트 달라지게 됩니다.
Suspense, Error Boundary의 등판
그래서 생각했습니다. loading과 Error에 대한 상황처리를 이미 React에서 해주려고 하는데 왜 별도 분기를 통해 해결하는가입니다.
그리고 도입했습니다. React Suspense와 Error Boundary에 대한 소개는 공식문서를 참고하시면 됩니다.
해서 이 둘을 이용하면 Error와 isLoading에 대한 컴포넌트를 React 스럽게 표현할 수 있지 않을까 생각이 들었습니다.
대략 간략화하자면 이런 구조입니다.
react-query에 suspense를 쓴다고 선언하고 실제 Suspense와 ErrorBoundary를 통해 컴포넌트를 사용하는 것입니다. 이렇게 되면 isLoading, error일 경우를 처리할 필요가 없어서 훨씬 직관적인 코드를 구현할 수 있게 되었습니다.
따라서 기존에 상황에 맞춰 여러개의 return 문을 통해 랜더링 했던 과거에서 현재 react 스러운 코드를 작성할 수 있게 되었습니다.
또한 각 서비스의 error나 loading 상태에 맞게 LoadingPage, ErrorPage에도 props를 받을 수 있게 코드를 수정하여 적용하니 깔끔해졌습니다.
다만 suspense 자체가 react18이라 강제 업데이트를 해야 적용할수 있다는 점 참고 바랍니다.
저는 이미 아래 글에서 Suspense를 쓰기 위해 강제로 18로 업데이트해서 바로 적용할 수 있었습니다.
사이드 프로젝트에 적용한 모습
중간에 < 실제 레슨을 확인해보세요/> 와 <라운드인 추천 프로/> 컴포넌트가 있습니다.
해당 컴포넌트는 각각 Suspense와 ErrorBoundary로 감싸져있습니다. 그래서 컴포넌트가 비동기로 서버에 요청을 보내고 데이터 요청이 완료되는 순간 각각의 컴포넌트가 랜더링되는 모습을 확인할수 있습니다.
본래라면 기존에 Suspense 가 없다면 각각의 컴포넌트마다 분기처리를 해야했지만 이젠 간편하게 다이나믹한 화면을 만들 수 있게 되었습니다.
그리고 특정 서비스가 에러가 생긴다고 하여도 다른 서비스가 올바르게 동작한다면 다른 컴포넌트는 제대로 랜더링되기에 유저에게 불쾌함을 주지 않을 수도 있습니다.
마무리
사내에 리드 개발자가 없고 다들 React가 처음이라 각자 다른 스타일, 다른 방법을 가져와서 공유하고 또 적용하곤 합니다.
근데 Errorboudary나 Suspense는 공식문서상에 소개되어 있음에도 이를 적용시키고 실제 로직에 녹이는건 또다른 역량인 듯 싶습니다.
그래도 요즘 짱짱 IT 회사분들이 알짜배기 컨퍼런스들을 많이들 열어주고 계셔서 감지덕지하면서 보고 있습니다. 나중엔 그런 컨퍼런스에 발표할만한 실력이 되었으면..
'개발 > 리액트' 카테고리의 다른 글
[리액트] React.memo를 이용해 성능 최적화를 해보자 (1) | 2022.01.03 |
---|---|
[리액트] RIW 프로젝트 - scroll animated number widget (0) | 2021.12.16 |
[리액트] 리액트 컴포넌트의 변천사, 역사에 대하여 (0) | 2021.07.29 |
[리액트] table에는 thead와 tbody가 필요해요(feat. react 형님들) (0) | 2021.07.19 |
[리액트] 프로젝트에 웹팩을 적용해보자 #3 (feat.타입스크립트) (0) | 2021.07.06 |
댓글