25. 실습: RTK Query로 블로그 앱 만들기
Phase 5의 마지막 편입니다. 게시글 목록·상세·댓글·좋아요 기능을 가진 블로그 앱을 24편의 RTK Query로 구현하며, 21~24편에서 배운 미들웨어·캐시 태그 개념을 실전 규모로 적용합니다.
학습 목표
- 여러 리소스(게시글, 댓글)가 얽힌 API를 세밀한 캐시 태그로 설계할 수 있다.
- 좋아요처럼 즉각적인 반응이 필요한 뮤테이션에 낙관적 업데이트를 적용할 수 있다.
- 목록 조회, 상세 조회, 생성, 수정, 삭제가 서로의 캐시에 미치는 영향을 태그로 정확히 표현할 수 있다.
API 설계: postsApi
| |
세밀한 태그 설계가 필요한 이유
24편에서는 providesTags: ["Todo"]처럼 단순한 태그를 썼습니다. 게시글처럼 목록 화면과 상세 화면이 분리된 리소스는 더 세밀한 태그가 필요합니다.
{ type: "Post", id: "LIST" }: “게시글 목록"이라는 개념 자체에 대한 태그. 게시글이 추가/삭제되어 목록의 구성이 바뀔 때 무효화한다.{ type: "Post", id: postId }: 특정 게시글 하나에 대한 태그. 그 게시글의 내용이 수정될 때만 무효화한다.
updatePost가 id가 일치하는 태그만 무효화하도록 설계했기 때문에, 게시글 A를 수정해도 게시글 B의 상세 화면이나 목록 전체가 불필요하게 재요청되지 않습니다. 태그를 뭉뚱그려 "Post" 하나로만 쓰면, 게시글 하나만 바뀌어도 목록 전체와 다른 모든 상세 화면이 재요청되는 비효율이 생깁니다.
댓글 API: 게시글과의 관계 표현
| |
댓글이 게시글에 종속된 리소스이므로, 태그의 id로 게시글 ID를 사용합니다. 이렇게 하면 게시글 A에 댓글을 달아도 게시글 B의 댓글 목록에는 영향을 주지 않습니다.
좋아요 기능: 낙관적 업데이트(Optimistic Update)
좋아요 버튼은 사용자가 클릭했을 때 서버 응답을 기다리지 않고 즉시 UI에 반영되는 것이 자연스러운 사용자 경험입니다. RTK Query는 onQueryStarted로 이를 지원합니다.
| |
onQueryStarted는 뮤테이션이 시작되는 시점에 실행되는 콜백입니다. updateQueryData로 캐시된 getPostById 결과를 서버 응답 전에 미리 수정해두고, 실제 요청이 실패하면 patchResult.undo()로 원상 복구합니다. 사용자는 클릭 즉시 좋아요 수가 올라가는 것을 보고, 네트워크 실패 시에만(드문 경우) 잠깐 뒤 원래 값으로 되돌아가는 것을 보게 됩니다.
컴포넌트 조합
| |
likePost(postId)를 클릭하면 좋아요 수가 즉시 반영되고, addComment가 성공하면 getCommentsByPostId 캐시가 자동으로 무효화되어 댓글 목록이 최신 상태로 갱신됩니다. 컴포넌트 코드 어디에도 로딩 상태를 수동으로 관리하는 코드나, 댓글 추가 후 목록을 수동으로 재요청하는 코드가 없습니다.
Phase 5 전체 정리: 21~25편이 어떻게 연결되는가
flowchart TD mw["21편: 미들웨어 기초"] --> thunk["22편: Thunk"] mw --> saga["23편: Saga"] mw --> rtkq["24편: RTK Query"] thunk --> practice["25편: 블로그 앱 실습"] saga --> practice rtkq --> practice
21편에서 배운 미들웨어 구조가 22~24편 세 도구의 공통 기반이며, 이 편은 그중 실무에서 가장 자주 쓰이는 RTK Query를 중심으로 실제 규모의 기능(목록, 상세, 댓글, 좋아요)을 조립했습니다. 프로젝트에 따라 22편(단순 로직)이나 23편(복잡한 순서 제어)이 더 적합한 부분이 섞여 있을 수 있다는 것도 기억해두면 좋습니다.
실무 체크리스트
- 목록과 상세로 분리된 리소스에
{ type, id: "LIST" }와{ type, id }를 구분해 태그를 설계했는가? - 즉각적인 사용자 반응이 중요한 뮤테이션(좋아요, 토글)에 낙관적 업데이트를 적용했는가?
- 낙관적 업데이트가 실패했을 때
patchResult.undo()로 원상 복구되는 경로를 테스트했는가?
연습 과제
기초(★☆☆)
deletePost뮤테이션을 호출한 뒤, 게시글 목록에서 해당 항목이 자동으로 사라지는지 확인해보세요.
중급(★★☆)
- 댓글 삭제 기능(
deleteComment)을 추가하고, 해당 게시글의 댓글 목록에만 영향을 주도록 태그를 설계해보세요.
고급(★★★)
likePost의onQueryStarted에서 서버 요청이 의도적으로 실패하도록 만들어(네트워크 탭에서 요청 차단 등), 낙관적으로 올라갔던 좋아요 수가 정말로 되돌아가는지 확인해보세요.
요약
- 목록/상세가 분리된 리소스는
{ type, id: "LIST" }와{ type, id }두 종류의 태그로 세밀하게 캐시를 제어한다. - 낙관적 업데이트는
onQueryStarted+updateQueryData로 구현하며, 실패 시patchResult.undo()로 복구한다. - Phase 5의 21~24편은 모두 같은 미들웨어 기반 위에서 서로 다른 트레이드오프를 제공하는 도구들이다.
참고 문헌 및 출처(추천)
- Redux Toolkit 공식 문서, “RTK Query, Optimistic Updates”
- Redux 공식 문서, “Redux Essentials, Part 8: RTK Query Advanced Patterns”
![Featured image of post [Redux] 25. 실습: RTK Query로 블로그 앱 만들기](/post/redux/practice-blog-app/wordcloud_hu_ff433920287539e4.webp)
![[Redux] 23. Redux Saga - 강력한 사이드 이펙트 관리](/post/redux/redux-saga/wordcloud_hu_61e6fe9d8596c675.webp)
![[Redux] 24. RTK Query - 데이터 페칭의 혁명](/post/redux/rtk-query/wordcloud_hu_1807a958ecd17a19.webp)
![[Redux] 25. 실습: RTK Query로 블로그 앱 만들기](/post/redux/practice-blog-app/wordcloud_hu_1c4aa9e1677f5dbb.webp)
![[Redux] 26. Redux 프로젝트 구조 - 확장 가능한 설계](/post/redux/redux-project-structure/wordcloud_hu_379134b467bc38d9.webp)
![[Redux] 27. 정규화(Normalization) - 복잡한 데이터 관리](/post/redux/normalization/wordcloud_hu_f70eb8fa31f8f97c.webp)
![[Redux] 15. 실습: Counter와 Todo 앱 만들기](/post/redux/practice-counter-todo/wordcloud_hu_5c499868ea4452c8.webp)
![[Redux] 20. 실습: RTK로 앱 리팩터링](/post/redux/practice-rtk-app/wordcloud_hu_97bfaa8516f2360e.webp)
![[SEO] Open Graph 가이드 - OG 이미지·메타태그 실무 정리](/post/2025-09-01-opengraph-examples-introduction-og-guide/wordcloud_hu_ad65a50beaf94f37.webp)
![[Tutorial] Learn Prompting - 프롬프트 엔지니어링 무료 가이드 정리](/post/2022-12-30-learn-prompting/wordcloud_hu_6a9d105de4834753.webp)
![[Hardware] LattePanda Alpha에 Ubuntu 16.04 LTS 설치 가이드](/post/2018-12-06-install-ubuntu-16.04-on-lattepanda/wordcloud_hu_fc536f8de2cbd4bf.webp)