JavaScript

[JavaScript] RTK Query: Automated Re-fetching

hid1 2023. 1. 6. 18:43

 

 

Automated Re-fetching | Redux Toolkit

RTK Query > Usage > Automated Refetching: cache invalidation management

redux-toolkit.js.org

 

 

query 엔드포인트에 구독을 추가하게 되면, 캐시 데이터가 존재하지 않는 경우에만 요청이 전송되고 존재하는 경우 기존의 데이터가 제공된다.

 

RTK Query는 "cache tag" 시스템을 사용하여 mutation 엔드포인트에 영향을 받는 데이터가 있는 query 엔드포인트에 대해 re-fetching을 자동화한다.

각 엔드포인트 + 파라미터의 조합은 'queryCacheKey'를 제공한다. "cache tag"시스템을 사용하면 RTK Query에 특정 쿼리 캐시가 특정 태그를 제공했음을 알릴 수 있다. 쿼리 캐시가 주는 태그를 무효화하는 mutation이 발생하면 캐시된 데이터는 무효화된 것으로 간주되고 캐시된 데이터에 대한 활성화된 구독이 있으면 re-fetch 된다.

 

 

1. 정의

 

Tags

 

tag는 re-fetching을 목적으로 캐싱 및 무효화 동작을 제어하기 위해 특정 데이터 모음에 지정할 수 있는 이름이다.

mutation 이후 캐시된 데이터에 첨부된 '라벨'로 간주되어 데이터가 mutation의 영향을 받아야 하는지 여부를 결정할 수 있다.

 

Providing tags

query는 캐시된 데이터가 tag를 제공하도록 할 수 있다.

이렇게 하면 쿼리에서 반환하는 캐시된 데이터에 어떤 'tag'가 연결되는지 결정된다.

 

providesTags argument는 문자열 배열 (ex. ['Post'], [{type: 'Post', id: 1}]), 혹은 이러한 배열을 반환하는 콜백 함수일 수 있다. 그 함수는 결과를 첫 번째 인수로, 응답 오류를 두 번째 인수로, 인수를 세 번째 인수로 query 메서드에 전달합니다. 쿼리의 성공 여부에 따라 결과 또는 오류 인수가 정의되지 않을 수 있다.

 

Invalidating tags

mutation은 tag를 기반으로 캐시된 특정 데이터를 무효화할 수 있다.

따라서 캐시에서 다시 가져오거나 제거할 캐시된 데이터가 결정된다.

 

invalidatesTags argument는 문자열 배열 (ex. ['Post'], [{type: 'Post', id: 1}]), 혹은 이러한 배열을 반환하는 콜백 함수일 수 있다. 그 함수는 결과를 첫 번째 인수로, 응답 오류를 두 번째 인수로, 인수를 세 번째 인수로 query 메서드에 전달합니다. 쿼리의 성공 여부에 따라 결과 또는 오류 인수가 정의되지 않을 수 있다.

 

 

2. Cache tags

 

RTK Query는 'tag'라는 개념을 사용하여 한 엔드포인트에 대한 mutation이 다른 엔드포인트의 query에 의해 제공된 일부 데이터를 무효화하려는 의도인지 여부를 확인한다. 캐시 데이터가 비활성화되면 providing query를 다시 가져오거나(컴포넌트가 여전히 해당 데이터를 사용하는 경우) 캐시에서 데이터를 제거한다.

API slice를 정의할 때, createApi는 tagTypes 속성에 대한 태그 타입의 이름 배열을 받는다. 이는 API slice에 대한 쿼리가 제공할 수 있는 태그 이름 옵션의 목록이다.

 

예시로 엔드포인트가 캐시에 "Posts", "Users"를 제공할 수 있음을 선언한다.

 

const api = createApi({
  tagTypes: ['Post', 'User'],
})

 

이러한 태그를 캐시에 제공할 수 있는 것으로 선언함으로써, 개별 mutation 엔드포인트가 캐시에 특정 부분에 영향을 주는지 여부를 제어할 수 있다. 

 

Providing cache data

 

각 개별 query 엔드포인트는 캐시된 데이터가 특정 태그를 제공할 수 있다. 하나 이상의 query 엔드포인트에서 캐시된 데이터와 하나 이상의 mutation 엔드포인트 동작 간의 관계를 사용할 수 있다.

 

providing tag는 별도의 query 엔드포인트에서 고유한 관계가 없다. providing tag는 엔드포인트에서 반환된 캐시된 데이터를 비활성화하고 캐시에서 다시 가져오거나 제거해야 하는지 여부를 결정하는데 사용된다. 두 개의 개별 엔드포인트가 동일한 태그를 제공하는 경우에도 고유한 캐시된 데이터를 제공한다. 이 데이터는 나중에 mutation에서 선언된 단일 태그에 의해 모두 무효화될 수 있다.

 

아래는 getPosts 쿼리 엔드포인트 속성에 providesTags를 사용하여  'Post' 태그를 캐시에 제공함을 선언한다.  

const api = createApi({
  endpoints: (build) => ({
    getPosts: build.query({
      query: () => '/posts',
      providesTags: ['Post'],
    }),
})

보다 세밀하게 제어하기 위해 태그에 연결된 ID가 있을 수 있다. 이를 통해 '특정 태그 중 하나'와 '특정 태그의 특정 인스턴스'를 구별할 수 있다.

 

아래는 엔드포인트에서 반환된 결과에 따라 특정 ID와 연결되었음을 선언한다.

const api = createApi({
  endpoints: (build) => ({
    getPosts: build.query({
      query: () => '/posts',
      providesTags: (result, error, arg) =>
        result
          ? [...result.map(({ id }) => ({ type: 'Post', id })), 'Post']
          : ['Post'],
    }),
})

id는 성공적인 결과에 대해 사용된다. 오류가 발생하면 결과가 제공되지 않으며, 여전히 해당 태그의 특정 인스턴스가 아닌 일반적인 'Post' 태그를 제공한 것으로 간주된다.

 

 

Invalidating cache data

각 개별 mutation의 엔드포인트는 기존 캐시된 데이터에 대한 특정 태그를 무효화할 수 있다. 하나 이상의 query 엔드포인트에서 캐시된 데이터와 하나 이상의 mutation 엔드포인트 동작 간의 관계를 사용할 수 있다.

 

mutation 엔드포인트의 'invalidatesTags' 속성은 이 목적으로 사용된다.

 

아래는 addPost 및 editPost 엔드포인트에 대해 invalidatesTags 속성을 사용하여 'Post' 태그가 있는 캐시된 데이터를 무효화한다고 선언한다.

 

const api = createApi({
    addPost: build.mutation({
      query: (body) => ({
        url: 'post',
        method: 'POST',
        body,
      }),
      invalidatesTags: ['Post'],
    }),
    editPost: build.mutation({
      query: (body) => ({
        url: `post/${body.id}`,
        method: 'POST',
        body,
      }),
      invalidatesTags: ['Post'],
    })
})

 

위의 예를 들어, addPost 또는 editPost mutation이 호출되고 완료한 후 'Post' 태그와 함께 제공된 캐시 데이터가 더 이상 유효하지 않음을 RTK Query에 알려준다. 컴포넌트가 현재 'Post' 태그에 대해 캐시된 데이터가 없으면 서버에서 최신 데이터를 가져오기 위해 re-fetch 한다.

 

예시 시나리오

1. useGetPostsQuery() hook을 사용하여 해당 엔드포인트에 캐시된 데이터를 구독하는 컴포넌트가 렌더링된다.

2. /posts 요청이 발생되고, 서버는 ID가 1,2,3인 게시물로 응답한다.

3. getPosts 엔드포인트는 수신된 데이터를 캐시에 저장하고 다음 태그가 제공되었음을 내부적으로 등록한다.

[
  { type: 'Post', id: 1 },
  { type: 'Post', id: 2 },
  { type: 'Post', id: 3 },
]

4. eidtPost mutation는 특정 게시물을 변경하기 위해 발생한다.

5. RTK Query는 내부적으로 'Post' 태그가 비활성화 되었음을 등록하고 이전에 제공된 'Post' 태그를 캐시에서 제거한다.

6. getPosts 엔드포인트에서 잘못된 캐시 데이터가 있는 'Post' 태그를 제공했지만 컴포넌트가 여전히 데이터에 구독되어 있으므로 /posts 요청이 자동으로 다시 발생하여 새 데이터를 가져오고 업데이트된 캐시 데이터에 대한 새 태그를 등록한다.

 

무효화된 데이터를 보다 세밀하게 제어하기위해 무효화된 태그는 제공된 태그와 동일한 방식으로 연결된 ID를 가질 수 잇다. 이를 통해 '특정 태그 중 하나'와 '특정 태그의 특정 인스턴스'를 구별할 수 있다. 

 

아래는 editPost mutation이 mutation 함수를 호출할 때 전달된 ID를 사용하여 Post 태그의 인스턴스를 무효화한다고 선언한다.

 

const api = createApi({
    editPost: build.mutation({
      query: (body) => ({
        url: `post/${body.id}`,
        method: 'POST',
        body,
      }),
      invalidatesTags: (result, error, arg) => [{ type: 'Post', id: arg.id }],
    })
})

'Post' 태그를 무효화하는 대신 editPost mutation 함수를 호출하면 제공된 ID에 대한 태그만 무효화된다. 즉 엔드포인트에서 캐시된 데이터가 동일한 ID에 대한 'Post'를 제공하지 않는 경우, 해당 데이터는 '유효'로 유지되며 자동으로 다시 가져오기 위해 트리거되지 않는다.

 

 

 

 

 

반응형