개발하는 일상

vue query 실무에 도입하기 본문

개발 기록

vue query 실무에 도입하기

롯데빙빙바 2024. 1. 21. 23:58

이 글은 vue query를 왜 실무에 도입했으며, 어떤 과정과 배움이 있었는지를 기록한 글이다. 비슷한 과정에 있는 분들이 도움을 받을 수 있을까하여 남겨본다. 이 글을 읽는 분은 기본적인 vue query에 대한 개념을 알고 있는 분이라고 생각하고 글을 썼다.

 

Vue Query를 도입하게 된 이유

먼저 요약하자면, 쉽게 이해할 수 있는 코드를 작성하기 위해 vue query를 도입하게 되었다. 기존 코드의 어떤 점이 가독성을 떨어뜨리게 되었는지 얘기해보겠다.

 

우리 팀은 vue를 클라이언트 개발에 사용하고 pinia를 상태관리 라이브러리로 사용했다. 서버에서 받아와야 하는 데이터(이하 서버 state)를 pinia store에서 같이 관리했는데, 아래 그림1 과 같은 코드를 작성했다.

그림1. 서버데이터를 함께 관리하는 pinia store 코드 예시

여기서 발생한 문제

  1. 서버 state와 클라이언트 state가 쉽게 구분되지 않는다. 그림1에서는 todos가 서버 state인지 알기 위해서 store 코드를 따라 살펴봐야한다.
  2. 여러 컴포넌트에서 같이 사용하는 서버 state(그림1 에서는 todos)가 fetch가 된 것인지 아닌지 알기 어렵다. 그러다보니 컴포넌트를 개발할 때 너도나도 fetch를 하게 되었다. 구현하고 보니 한 페이지에서 같은 api호출을 3번 이상하는 경우도 발생했다.
  3. 2의 불필요한 api 호출을 막기 위해 cache를 구현하면서 store의 구조가 더욱 복잡해졌다. 이 때부터 다른 사람들이 어떻게 해결했는지 살펴보기 시작했고, vue query를 선택하게 되었다.

Vue Query의 철학

서버 state와 클라이언트 state의 분리

서버 state를 분리하는 것이 어떤 이점을 가져다 줄까? tanstack query docs에 overview를 보면 이런 말이 나온다.

While most traditional state management libraries are great for working with client state, they are 
not so great at working with async or server state. This is because server state is totally different.

 

서버 state는 클라이언트 state와 완전히 다르기 때문에, 기존 클라이언트 state를 매니징하는 라이브러리로 서버 state를 관리하는 것은 좋지 않다는 내용이다. 아래에 이유도 나오는데, 요약하면 서버 state는 내가 온전히 관리할 수 없는 state라는 얘기이다. 예상할 수 없는 loading, error, out of date 같은 상태가 발생하므로 분리하여 관리하는 것이 더 나은 선택이라고 얘기한다.

실제로 서버 state가 분리되면서 코드 리뷰를 할 때 이 부분은 서버 state인데 왜 loading 처리를 하지 않았지? error를 처리하진 않아도 괜찮은가? 같은 문제가 이전보다 훨씬 잘 보이게 되었다. 서버 데이터를 조회하거나 수정하는 구조가 그에 알맞게 통일되면서 가독성도 높아졌다.

 

조회(query)와 수정(mutation)의 분리

vue query에서 데이터 조회는 선언적인 형태로 작성한다. useQuery로 데이터를 가져올 때, 결과 state인 data, isLoading, isError만 줄 뿐이다. fetch가 정확히 언제 실행되는지는 모른다. 조회가 side effect를 발생시키지 않으므로 fetch는 라이브러리가 알아서 실행하고 개발자는 발생한 결과에 맞춰서 코드를 작성할 뿐이다. 조회가 선언적으로 작성되면서 개발자는 결과 상태에만 집중하면 되었다. 코드에 나타나진 않지만 useQuery는 이런일을 추가로 해준다.

  • 여러 컴포넌트에서 동시에 호출하더라도 한 번의 api호출만 일어나도록 한다.
  • 조회가 실패할 경우 몇번의 재시도를 한다.
  • window가 다시 focus 되는 등의 특정 이벤트에서 다시 fetch해 데이터를 최신데이터로 유지해준다.

수정은 다르다. 데이터가 수정되면 side effect가 발생하기 쉽다. 예를 들어 유저가 탈퇴하면 유저 정보만 삭제 되고 마는 것이 아니라 유저의 게시글도 삭제될 수 있다. 이런 부분은 사용자가 정확히 원하는 시점에 실행되어야 하므로 vue query는 수정(mutation)을 명령적으로 실행하도록 설계했다. useMutation은 mutate 함수를 반환한다. useQuery는 fetch 함수를 반환하지 않는다는 점을 짚고 넘어가겠다.

 

선언적 코딩과 명령형 코딩을 때에 따라 적절히 활용하도록 유도한 vue query의 철학 덕분에 코드를 쉽게 작성하고 이해할 수 있었다.

 

Vue Query를 적용하며 만났던 문제

React Query의 예시를 Vue Query에 적용할 때

vue보다는 react를 많이 쓰다보니 예시 또한 vue query보다 react query가 많다. 글또 9기 활동을 하며 지원받은 Udemy 강의 React Query 도 react query 강의였다(vue query강의는 없었다). 그래서 react query의 예시를 참고해서 그대로 vue query에 쓰면 원하는대로 동작하지 않는 경우가 있다. 게시물을 보여주는 react 컴포넌트인 아래 그림2를 보자.

그림2. react query에서 pagination 예시

setPage를 통해 page 번호가 달라지면 해당 페이지의 posts가 fetch된다. 여기서 page는 단순한 숫자이다. 이 예시를 보고 vue query에 그대로 적용하면 아래 그림3과 같은 형태가 된다.

그림3. vue query를 활용한 pagination 잘못된 예시

그림 3의 경우 page가 변경되더라도 fetch가 일어나지 않는다. fetch가 일어나려면 아래 그림 4와 같이 ref 자체를 넘겨줘야한다.

그림4. vue query를 활용한 pagination 올바른 예시

enable 옵션 또한 query가 reactive하길 바란다면 ref타입을 넘겨줘야한다.

 

이것은 react의 경우 렌더링 함수 안에서 useQuery가 실행되고, vue의 경우 setup 함수 안에서 실행되기 때문에 달라지는 문제이다. vue quey를 렌더링 함수 안에서 호출하는 경우 경고가 뜨면서 동작하지 않으니 반응성을 원한다면 ref 타입 변수를 활용해야한다는 것을 잘 기억하면 된다.

잘 모르면서 규칙부터 만들려고 할 때

처음에는 pinia store를 어떻게든 활용하여 view와 서비스 로직을 어떻게든 떼어보려고 했었다. 어설프게 구조를 잡고 코딩해보았는데 오히려 코딩에 방해만 되었다. 이 과정에서 시간낭비를 3일 정도는 한 것 같다. 결국 가장 기본적인 형태로 돌아가서 컴포넌트의 setup에 클라이언트 state와 서버 state를 마구 때려넣으며 개발했다. 이렇게 개발하며 여러 경우의 수를 만나다 보니 자연스럽게 최적의 형태가 머리속에 떠올랐다. 어떤 새로운 기술을 도입할 때, 섣불리 규칙을 정하기보다는 가장 쉽고 기본적인 방법으로 다양한 경우의 수를 직접 구현해본 후에 규칙을 정하는 것이 오히려 빠른 길이라는 것을 느꼈다머릿속으로만 설계하면 직접 구현할 때 얻어맞는 경우가 많은 것 같다.

 

어쨌거나 우리팀은 이런 규칙을 세웠다.

  1. query나 mutation은 composable(react의 custom hook)로 따로 분리한다. 특히 query는 한 컴포넌트에서만 사용되는 경우가 드문 것 같아 쉽게 재사용 될 수 있도록 처음부터 분리해서 작성한다.
  2. pinia는 전역 state가 꼭 필요할 때만 사용한다. 기본적으로 컴포넌트의 setup에서 state를 선언하고 관리하도록 한다.
  3. 역할보다는 뷰나 서비스 로직상 가까운 코드를 가까이 둔다. 역할은 client state, server state, getter, action 등을 말한다. 예를 들면 회원정보 view, 글 정보 view 등 서비스 맥락에 필요한 state, getter, action을 가까이 둔다.
  4. 3번을 위해 너무 큰 컴포넌트를 작성하지 않고, view나 서비스 로직에 따라 적당한 크기로 컴포넌트를 나눈다. 재사용성을 위한 컴포넌트가 아니라도 가독성을 위해 컴포넌트를 나눌 수 있다.

마치며

vue query를 만든 tanstack query 팀의 문서와 글또 9기 활동을 하며 지원받은 Udemy의 React Query강의로 많은 도움을 받았다. 서버 데이터를 관리하는 문제를 먼저 깊이 고민한 분들이 있어서 그 분들의 철학을 만나고 생각의 범위를 넓힐 수 있었다. 예를 들어, 이전에 로그인 라우터 가드를 만들 때는 가드에서 api에 요청을 보내 회원인지 확인하고 아니라면 로그인 페이지로 보냈다. 내가 이해한 tanstack query의 철학은 api 요청을 보내서 응답이 올 때까지 기다리는 코드를 작성하지 않고 회원이라면 보여줄 화면, 로딩중에 보여줄 화면, 회원이 아니라면 보여줄 화면을 작성하는 방식으로 코드를 작성한다.

 

한 팀으로 오래 일하다보면 서로의 코드 스타일이나 철학도 비슷해질 때가 많은데, 좋은 라이브러리의 도입이 새로운 시야를 제공할 수 있다는 것을 알았다. 더 주니어일 때는 어떤 라이브러리가 원하는 기능을 제공하지 않을 때 "이런 기능은 없네, 불편하다" 라는 생각만 했는데, 그들의 철학에 맞지 않는 기능이어서 그랬을 수도 있다는 생각이 든다.

 

 

 

 

* 이 글을 작성하며 Udemy에서 강의 쿠폰을 지원받았습니다.

 

 
Comments