From 3a06b477d9104626310f3c03f135cdc0f35db236 Mon Sep 17 00:00:00 2001 From: simorimi <141118352+simorimi@users.noreply.github.com> Date: Thu, 28 Mar 2024 17:39:50 +0900 Subject: [PATCH 1/4] Update README.md From 60adc2f9d952f28b41192e663468e72034cc1d84 Mon Sep 17 00:00:00 2001 From: simhorim Date: Wed, 14 Aug 2024 23:01:21 +0900 Subject: [PATCH 2/4] =?UTF-8?q?docs(README):=20level.3=20=EC=B4=88?= =?UTF-8?q?=EA=B3=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 249cefe0..24e82024 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,15 @@ -## 지금 실천할 작은 목표 하나만 세우고 실천한 후 다음 목표 세우기 +### 마감 효과와 빠른 피드백으로 효율적으로 일하기 -‘우아한테크코스’를 시작한 지 어느덧 4달이 흘렀다. 입소 전 리액트를 다루어 본 적 없던 내가 지금은 리액트로 페이지를 만들고 있다. 그렇게 미션에 몰두하며 앞만 보고 달려오던 중 글쓰기를 이유로 레벨 1 유연성 강화 목표를 회고하였다. `부족함보다는 성취감에 집중하기.` 당시 더 이상 작아짐을 허락하지 않기 위하여 세웠던 목표다. 앞서 말했듯 나는 프론트엔드에 대해서 제대로 공부한 적이 없었다. 개발과 거리가 먼 전공이었기에 교양 수준 정도의 프로그래밍을 다루어본 것이 전부였다. 그러던 중 교육용 프로그램을 만들면 좋겠다는 생각 하나로 무작정 이 공부를 시작하였다. 막연하게 공부하던 중 운이 닿아 소중한 기회를 잡을 수 있었고 그렇게 난 이곳에 들어오게 되었다. 희망을 품었던 것과는 달리 이곳에서 내가 느낀 첫 감정은 좌절감이었다. 개발을 배우러 오는 곳이라고 생각하던 것과는 다르게 이미 개발을 잘하는 크루들이 대다수였기에 스스로가 한없이 작아 보이기 시작하였다. 그래도 유연성 강화 목표를 꾸준히 실천한 덕분에 이제는 더 이상 타인과 비교하지 않는다. 타인과의 비교에서 오는 부족함보다는 온전히 나를 바라보며 성취에 집중할 수 있게 되었다. +우아한테크코스도 레벨 1, 2를 지나 어느덧 레벨 3 끝을 바라보고 있다. 이렇게 레벨별 글쓰기를 할 때 돼서야 과거를 회상해 보곤 한다. 정신없이 눈앞에 있는 것들을 해치우다 뒤를 돌아보면 감회가 새롭다. 실력이 늘었느냐고 스스로에게 질문을 던져보면 늘 그렇듯 늘었다는 대답이 돌아온다. 리액트를 모르던 내가 UI를 그리고 있는 것을 보면 그러하다. 그러나 다시 현재를 바라보면 부족한 점들이 여기저기 흩 뿌려져 있다. 수확할 것들이 끝없이 내 주위를 감싸고 있다는 느낌은 지울 수 없다. 막막하지만서도 가까이 둘러싸고 있는 씨앗을 하나씩 수확할 수밖에… -그러나 개발할 때도 하나를 해결하면 또 다른 곳에서 문제가 생기지 않던가? 이 과정에서 문제가 발생하였다. 보완할 것들이 끝없이 나타났고, 그렇게 작성한 계획은 하나가 열, 열이 어느덧 한 페이지를 넘어갈 정도로 늘어났다. '하...' 계획들을 바라보니 한숨부터 나왔다. 결국 그 계획들에 압도당하여 실천하지 못하였다. 계획을 실천하지 못하니 그에 따른 또 다른 계획이 파생되고... 이런 식으로 계획을 위한 계획만 늘어가 자연스레 성취감과도 거리가 멀어졌다. 이 악순환이 싫었다. 악순환을 끊어 계획이 실천으로, 더 나아가 실천을 통해 성취감을 이루고 싶었다. 결국 핵심은 실천에 있었다. '어떻게 하면 실천을 더 쉽게 할 수 있을까?' 고민의 해답은 오래전에 봤던 한 책으로부터 찾을 수 있었다. '아주 작은 습관의 힘’. 실천력을 올리기 위해서 지금 당장 실천할 작은 목표가 필요했다. 그렇게 레벨 2 유연성 강화 목표는 `지금 실천할 작은 목표 하나만 세우고 실천한 후 다음 목표 세우기.`가 되었다. +레벨 1, 2 때 나의 유연성 강화 목표는 주위 사람들과 비교하지 않고 나만의 속도로 성취하는 것과 해야 할 것들이 많을 때 눈앞에 목표에 대해서만 집중하여 압도당하지 않는 것이었다. 처음에는 주위 사람들과 비교와 많은 할 일들로 인하여 마냥 두렵고 힘들었지만, 적절한 유연성 강화 목표 덕분에 나의 속도로 하나씩 학습할 수 있었고 해야 할 일이 많아도 하나씩 집중하여 무사히 마침표를 찍을 수 있었다. 특히 레벨 2 마지막 미션에 들어서는 리뷰어분께 구현 완성도가 높다는 칭찬을 듣고 나니 바닥을 찍었던 자신감이 어느새 차올라 보다 단단해지고 있음을 느꼈다. -목표를 설정하고 얼마 지나지 않아 이를 실천해 볼 좋은 기회가 주어졌다. 예비군. 해당 일정으로 미션 시작이 늦어졌다. 그래서 다른 크루들은 화요일부터 시작하는 미션을 나는 금요일이 되어서야 시작할 수 있었다. 금토일월… 짧은 시간이었기에 마음이 급했지만, 많은 할 일과 짧은 시간이라는 압박이 오히려 목표를 실천해 보기 좋은 환경이라 생각했다. 그래서 '월요일까지 끝내자!'라는 조급한 마음이 아닌 '지금 당장 할 작은 목표를 세우고 성취하자'는 생각으로 접근했다. 이 접근법은 효과적이었다. 전과 달리, 작은 목표만을 바라보니 실천하는 데 부담이 없었고, 덕분에 미션을 훨씬 생산적으로 수행할 수 있었다. +레벨 1, 2 때 쌓아온 자신감을 바탕으로 레벨 3을 마주하였다. 레벨 3 주제는 프로젝트. 프로젝트가 처음이었기에 프로젝트는 설렘 보다는 두려움으로 다가왔다. ‘잘할 수 있을까? 아니 잘하는 건 둘째 치고 내 몫을 할 수 있을까?’ 평소 책임감이 강했던 터라 팀원들에게 피해를 끼치고 싶지 않았다. 그러한 마음가짐으로 시작한 첫 번째 스프린트는 우리 팀 프로젝트의 방향을 정하는 시기였다. 지금까지 중 가장 코드 구현과는 거리가 멀었고 매시간 회의의 연속이었지만, 경험이 많았던 크루들이 주도로 힘들지 않게 보낼 수 있었다. -이렇게 좋은 경험들만 가득하면 좋겠지만 사람은 망각과 습관의 동물. 페어 활동 등으로 정신없이 시간을 보내고 나면 유연성 강화 목표를 잊은 채 기존의 방식으로 돌아온다. 그러다 유연성 강화 스터디 하는 날이 되어서야 비로소 해당 목표를 상기할 뿐. 변화가 필요했다. 어떻게 하면 나의 목표를 끊임없이 상기할 수 있을까? `기록.` 사람이 잊지 않기 위하여 자연스레 하는 행동. 그 행동이 나에게 필요했다. 그래서 작은 목표 하나하나에 대해서 기록하기 시작했다. 기록하고 실천한 것에 대해서 체크하는 방식으로 실천한 것들을 채워나갔다. 이 사소한 변화는 생각보다 큰 효과를 가져다주었다. 할 일 하나만을 기록해 두니 해당 목표에 대해서 행동하기 쉬웠고 성취한 목표들이 시각적으로 쌓이니 이는 생각보다 큰 성취감을 가져다주었다. 더불어 유연성 강화 목표에 대해서도 자연스레 상기하게 되었다. +문제는 두 번째 스프린트였다. 방학부터 첫 번째 스프린트까지 코드 구현과는 멀어져 있던 터라 처음에는 천천히 진행하다가 손에 익고서 속도를 올렸으면 했다. 바람과는 다르게 두 번째 스프린트 때 욕심 내보자는 팀 분위기 속에 많은 작업이 필요했다. 그렇게 삐걱거리는 손으로 작업하였다. 이전 미션에서는 한 줄 한 줄을 충분히 고민하며 개발하였기에 개발 속도가 빠르지 않았으나 프로젝트를 진행하면서는 그럴 수 없었다. 고민이 길어질수록 원하는 목표치를 달성하지 못하고 내 몫을 결국 다른 팀원들이 나눠서 더 작업하는 모습을 볼 수 있었다. 그런 모습을 볼수록 미안함이 커졌고 미안함은 좌절감으로 이어져 더는 버틸 수 없었다. -이제 기록은 습관이 되었다. 방황하지 않는다. 그저 작은 목표 하나를 기록하고 성취하기를 반복할 뿐이다. 물론 때로는 목표를 망각하기도 하고, 이전의 습관으로 돌아기도 할 것이다. 그럴 때마다 자책이 아닌 격려로 나를 이끌기를 바란다. `부족함보다는 성취감, 큰 목표보다는 지금 실천할 작은 목표에 집중하리라.` +변화가 필요했다. 보다 효율적으로 작업을 해야 했다. 효율적으로 작업할 수 있는 방법을 찾기 위하여 나를 분석하였다. 그 결과 몇 가지 문제점을 찾을 수 있었다. 고민의 시간이 너무 길다는 점, 집중력 있게 작업하지 못한 점 그리고 피드백이 오래 걸린다는 점 이렇게 3가지를 꼽을 수 있었다. 이에 대한 해결 방법 또한 나에 대한 분석을 통해 찾을 수 있었다. 고민의 시간을 줄이면서 동시에 집중력을 높일 방법은 `마감 효과`를 이용하는 것이었다. 마감 시간을 설정하였을 때 집중력이 높아지는 것을 과거의 경험을 통해 알고 있었다. 이를 적용하기 위하여 15분 타이머를 맞추고 그 시간만큼만 고민하니 놀라울 만큼 집중할 수 있었고 의식하지 못한 사이 고민이 길어지는 것을 막을 수 있었다. 또한 이 시간이 넘어가는 것은 모르는 것으로 정의하고 고민보다는 피드백을 받는 방향으로 수정하였다. + +다음으로 빠르게 피드백을 받는 방법에 대해 고민하였다. 기존의 경우 공식 문서를 먼저 찾아보고 어려우면 블로그들을 참고한 다음 다시 공식 문서로 이해하는 식으로 부족한 부분에 대해 피드백 받았다. 해당 방법은 피드백을 받는 데 지나치게 오래 걸려 효율적인 작업과는 거리가 멀었다. 이를 해결하려는 방법을 고민한 끝에 생성형 AI 클로드를 활용하기로 했다. 과외를 받듯이 고민한 부분 중 애매하거나 잘 모르는 부분에 대해서 하나하나 질문하여 `빠르게 피드백`을 받았다. 또한 15분이 넘어가는 고민에 대해서도 과감하게 질문하며 빠르게 피드백 받는 방식을 취하였다. 그 결과 이전보다 훨씬 효율적으로 작업할 수 있었고 팀원들과 작업 속도를 맞출 수 있었다. + +이번 과정을 거치면서 협력을 위해서 작업의 속도를 맞추는 것이 꽤 중요하다고 느꼈다. 이를 위하여 효율적인 작업을 고민하였고 다행히 현재는 팀원들과 속도를 맞추며 작업하고 있다. 지금은 괜찮지만, 또다시 시련이 찾아올 것으로 생각한다. 그러나 시련 속에서도 지금까지 극복했던 힘으로 또다시 극복할 것으로 기대한다. 눈 앞에 씨앗들을 하나씩 수확해 가며 머지않아 다가올 가을의 황금빛 물결을 맞을 수 있기를. From 3940303c912ecd0fae73b3057e69bb471f18cd5b Mon Sep 17 00:00:00 2001 From: simhorim Date: Mon, 19 Aug 2024 22:23:33 +0900 Subject: [PATCH 3/4] =?UTF-8?q?refactor(README):=201=EC=B0=A8=20=ED=87=B4?= =?UTF-8?q?=EA=B3=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 24e82024..4b9e2463 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ ### 마감 효과와 빠른 피드백으로 효율적으로 일하기 -우아한테크코스도 레벨 1, 2를 지나 어느덧 레벨 3 끝을 바라보고 있다. 이렇게 레벨별 글쓰기를 할 때 돼서야 과거를 회상해 보곤 한다. 정신없이 눈앞에 있는 것들을 해치우다 뒤를 돌아보면 감회가 새롭다. 실력이 늘었느냐고 스스로에게 질문을 던져보면 늘 그렇듯 늘었다는 대답이 돌아온다. 리액트를 모르던 내가 UI를 그리고 있는 것을 보면 그러하다. 그러나 다시 현재를 바라보면 부족한 점들이 여기저기 흩 뿌려져 있다. 수확할 것들이 끝없이 내 주위를 감싸고 있다는 느낌은 지울 수 없다. 막막하지만서도 가까이 둘러싸고 있는 씨앗을 하나씩 수확할 수밖에… +우아한테크코스도 레벨 1, 2를 지나 어느덧 레벨 3 끝을 바라보고 있다. 이렇게 레벨별 글쓰기를 할 때가 되어서야 과거를 회상해 보곤 한다. 정신없이 눈앞에 있는 것들을 해치우다 뒤를 돌아보면 감회가 새롭다. 실력이 늘었느냐고 스스로에게 질문을 던져보면 늘 그렇듯 늘었다는 대답이 돌아온다. 리액트를 모르던 내가 UI를 그리고 있는 것을 보면 그러하다. 그러나 다시 현재를 바라보면 부족한 점들이 여기저기 흩뿌려져 있다. 수확할 것들이 끝없이 내 주위를 감싸고 있다는 느낌은 지울 수 없다. 막막하지만서도 가까이 둘러싸고 있는 씨앗을 하나씩 수확할 수밖에… -레벨 1, 2 때 나의 유연성 강화 목표는 주위 사람들과 비교하지 않고 나만의 속도로 성취하는 것과 해야 할 것들이 많을 때 눈앞에 목표에 대해서만 집중하여 압도당하지 않는 것이었다. 처음에는 주위 사람들과 비교와 많은 할 일들로 인하여 마냥 두렵고 힘들었지만, 적절한 유연성 강화 목표 덕분에 나의 속도로 하나씩 학습할 수 있었고 해야 할 일이 많아도 하나씩 집중하여 무사히 마침표를 찍을 수 있었다. 특히 레벨 2 마지막 미션에 들어서는 리뷰어분께 구현 완성도가 높다는 칭찬을 듣고 나니 바닥을 찍었던 자신감이 어느새 차올라 보다 단단해지고 있음을 느꼈다. +레벨 1, 2 때 나의 유연성 강화 목표는 주위 사람들과 비교하지 않고 나만의 속도로 성취하는 것과 해야 할 것들이 많을 때 눈앞에 목표에 대해서만 집중하여 압도당하지 않는 것이었다. 처음에는 주위 사람들과의 비교와 많은 할 일들로 인하여 마냥 두렵고 힘들었지만, 적절한 유연성 강화 목표 덕분에 나의 속도로 하나씩 학습할 수 있었고 해야 할 일이 많아도 하나씩 집중하여 무사히 마침표를 찍을 수 있었다. 특히 레벨 2 마지막 미션에 들어서는 리뷰어분께 구현 완성도가 높다는 칭찬을 듣고 나니 바닥을 찍었던 자신감이 어느새 차올라 보다 단단해지고 있음을 느꼈다. -레벨 1, 2 때 쌓아온 자신감을 바탕으로 레벨 3을 마주하였다. 레벨 3 주제는 프로젝트. 프로젝트가 처음이었기에 프로젝트는 설렘 보다는 두려움으로 다가왔다. ‘잘할 수 있을까? 아니 잘하는 건 둘째 치고 내 몫을 할 수 있을까?’ 평소 책임감이 강했던 터라 팀원들에게 피해를 끼치고 싶지 않았다. 그러한 마음가짐으로 시작한 첫 번째 스프린트는 우리 팀 프로젝트의 방향을 정하는 시기였다. 지금까지 중 가장 코드 구현과는 거리가 멀었고 매시간 회의의 연속이었지만, 경험이 많았던 크루들이 주도로 힘들지 않게 보낼 수 있었다. +레벨 1, 2 때 쌓아온 자신감을 바탕으로 레벨 3을 마주하였다. 레벨 3 주제는 프로젝트. 프로젝트가 처음이었기에 프로젝트는 설렘 보다는 두려움으로 다가왔다. ‘잘할 수 있을까? 아니 잘하는 건 둘째 치고 내 몫을 할 수 있을까?’ 평소 책임감이 강했던 터라 팀원들에게 피해를 끼치고 싶지 않았다. 그러한 마음가짐으로 시작한 첫 번째 스프린트는 우리 팀 프로젝트의 방향을 정하는 시기였다. 매시간 익숙지 않은 회의의 연속이었지만, 경험이 많았던 크루들의 주도로 힘들지 않게 보낼 수 있었다. -문제는 두 번째 스프린트였다. 방학부터 첫 번째 스프린트까지 코드 구현과는 멀어져 있던 터라 처음에는 천천히 진행하다가 손에 익고서 속도를 올렸으면 했다. 바람과는 다르게 두 번째 스프린트 때 욕심 내보자는 팀 분위기 속에 많은 작업이 필요했다. 그렇게 삐걱거리는 손으로 작업하였다. 이전 미션에서는 한 줄 한 줄을 충분히 고민하며 개발하였기에 개발 속도가 빠르지 않았으나 프로젝트를 진행하면서는 그럴 수 없었다. 고민이 길어질수록 원하는 목표치를 달성하지 못하고 내 몫을 결국 다른 팀원들이 나눠서 더 작업하는 모습을 볼 수 있었다. 그런 모습을 볼수록 미안함이 커졌고 미안함은 좌절감으로 이어져 더는 버틸 수 없었다. +문제는 두 번째 스프린트였다. 방학부터 첫 번째 스프린트까지 코드 구현과는 멀어져 있던 터라 처음에는 천천히 진행하다가 손에 익고서 속도를 올렸으면 했다. 바람과는 다르게 두 번째 스프린트 때 욕심 내보자는 팀 분위기 속에 많은 작업이 필요했다. 그렇게 삐걱거리는 손으로 작업하였다. 이전 미션에서는 한 줄 한 줄을 충분히 고민하며 개발하였으나 프로젝트를 진행하면서는 그럴 수 없었다. 고민이 길어질수록 원하는 목표치를 달성하지 못하고 내 몫을 결국 다른 팀원들이 나눠서 더 작업하는 모습을 볼 수 있었다. 그런 모습을 볼수록 미안함이 커졌고 미안함은 좌절감으로 이어져 더는 버틸 수 없었다. 변화가 필요했다. 보다 효율적으로 작업을 해야 했다. 효율적으로 작업할 수 있는 방법을 찾기 위하여 나를 분석하였다. 그 결과 몇 가지 문제점을 찾을 수 있었다. 고민의 시간이 너무 길다는 점, 집중력 있게 작업하지 못한 점 그리고 피드백이 오래 걸린다는 점 이렇게 3가지를 꼽을 수 있었다. 이에 대한 해결 방법 또한 나에 대한 분석을 통해 찾을 수 있었다. 고민의 시간을 줄이면서 동시에 집중력을 높일 방법은 `마감 효과`를 이용하는 것이었다. 마감 시간을 설정하였을 때 집중력이 높아지는 것을 과거의 경험을 통해 알고 있었다. 이를 적용하기 위하여 15분 타이머를 맞추고 그 시간만큼만 고민하니 놀라울 만큼 집중할 수 있었고 의식하지 못한 사이 고민이 길어지는 것을 막을 수 있었다. 또한 이 시간이 넘어가는 것은 모르는 것으로 정의하고 고민보다는 피드백을 받는 방향으로 수정하였다. 다음으로 빠르게 피드백을 받는 방법에 대해 고민하였다. 기존의 경우 공식 문서를 먼저 찾아보고 어려우면 블로그들을 참고한 다음 다시 공식 문서로 이해하는 식으로 부족한 부분에 대해 피드백 받았다. 해당 방법은 피드백을 받는 데 지나치게 오래 걸려 효율적인 작업과는 거리가 멀었다. 이를 해결하려는 방법을 고민한 끝에 생성형 AI 클로드를 활용하기로 했다. 과외를 받듯이 고민한 부분 중 애매하거나 잘 모르는 부분에 대해서 하나하나 질문하여 `빠르게 피드백`을 받았다. 또한 15분이 넘어가는 고민에 대해서도 과감하게 질문하며 빠르게 피드백 받는 방식을 취하였다. 그 결과 이전보다 훨씬 효율적으로 작업할 수 있었고 팀원들과 작업 속도를 맞출 수 있었다. -이번 과정을 거치면서 협력을 위해서 작업의 속도를 맞추는 것이 꽤 중요하다고 느꼈다. 이를 위하여 효율적인 작업을 고민하였고 다행히 현재는 팀원들과 속도를 맞추며 작업하고 있다. 지금은 괜찮지만, 또다시 시련이 찾아올 것으로 생각한다. 그러나 시련 속에서도 지금까지 극복했던 힘으로 또다시 극복할 것으로 기대한다. 눈 앞에 씨앗들을 하나씩 수확해 가며 머지않아 다가올 가을의 황금빛 물결을 맞을 수 있기를. +이번 과정을 거치면서 협력을 위해서 작업의 속도를 맞추는 것이 꽤 중요하다고 느꼈다. 이를 위하여 효율적인 작업을 고민하였고 다행히 현재는 팀원들과 속도를 맞추며 작업하고 있다. 지금은 괜찮지만, 또다시 시련이 찾아올 것이라 생각한다. 그러나 다가올 시련 속에서도 지금까지 극복했던 힘으로 또다시 극복할 것이라 기대한다. 눈 앞에 씨앗들을 하나씩 수확해 가며 머지않아 다가올 가을, 황금빛 물결을 맞을 수 있기를. From 9f49421de9d65320229c1a77184f2a555e22211b Mon Sep 17 00:00:00 2001 From: simhorim Date: Tue, 1 Oct 2024 23:44:11 +0900 Subject: [PATCH 4/4] =?UTF-8?q?docs(TechnicalWriting):=201=EC=B0=A8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TechnicalWriting.md | 632 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 632 insertions(+) create mode 100644 TechnicalWriting.md diff --git a/TechnicalWriting.md b/TechnicalWriting.md new file mode 100644 index 00000000..bae09362 --- /dev/null +++ b/TechnicalWriting.md @@ -0,0 +1,632 @@ +# TanStack Query + +# **TanStack Query란?** + +--- + +## 소개 + +> `TanStack Query (FKA React Query)` is often described as the missing data-fetching library for web applications, but in more technical terms, it makes **`fetching, caching, synchronizing and updating server state`** in your web applications a breeze. + +공식 문서에서 TanStack Query에 대해서 서버 상태를 보다 쉽고 효율적으로 가져오고, 캐싱하며, 동기화 및 업데이트한다고 설명합니다. + +> 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**.` + +반면에, TanStack Query 외 대다수 상태 관리 라이브러리는 클라이언트 상태 관리에는 효과적이지만, 비동기 혹은 서버 상태 관리의 경우는 그다지 효과적이지 않다고 설명하며 이러한 이유로 TanStack Query가 등장하게 된 배경을 설명하고 있습니다. + +위 설명을 살펴보면 `서버 상태`라는 용어가 반복적으로 등장하는데 해당 용어가 생소할 수 있습니다. 그렇기에 본 TanStack Query에 대한 설명에 앞서 서버 상태에 대해서 알아보겠습니다. + +## **서버 상태란?** + +> Is persisted remotely in a location you may not control or own +> Requires asynchronous APIs for fetching and updating +> Implies shared ownership and can be changed by other people without your knowledge +> Can potentially become "out of date" in your applications if you're not careful + +공식 문서에서 서버 상태란 다음 4가지 특징을 가지는 상태로 설명합니다. + +- 직접 관리하거나 소유하지 않은 원격 저장소에 보관됨. +- fetching 과 updating을 위해서는 비동기 API 요청이 필요. +- 여러 사용자가 공유하는 데이터로, 내가 모르는 사이에 다른 사람에 의해 변경 가능. +- 주의하지 않으면 앱에서 사용 중인 데이터가 최신 상태를 유지하지 않을 수 있음. + +해당 설명에 대해서 이해를 돕기 위하여 구체적인 예시와 클라이언트 상태와의 비교를 통해서 설명하겠습니다. + +### 클라이언트 상태 + +: 클라이언트 측(브라우저)에서 로컬로 관리하며 해당 클라이언트에만 존재하는 상태를 의미합니다. + +예시: UI 상태(모달 열림/닫힘), 폼 입력값, 페이지 스크롤 위치 등 + +```tsx +const Modal = (isOpen: boolean) => { + return isOpen ?
모달
: null; +}; + +const Page = () => { + const [isOpen, setIsOpen] = useState(false); + + const handleToggleModal = () => { + setIsOpen((prev) => !prev); + }; + + return ( + <> + + + + ); +}; +``` + +### 서버 상태 + +: 서버(데이터베이스)에 저장되고 관리되는 데이터로, 클라이언트가 필요할 때 요청하여 받아오는 데이터를 의미합니다. + +예시: API에서 가져온 사용자 프로필, 상품 목록, 뉴스 피드 등 + +```tsx +export interface Product { + id: number; + name: string; + price: number; + category: string; +} + +async function fetchProducts(): Promise { + const response = await fetch("http://simorimi.com/products"); + return await response.json(); +} +``` + +둘의 차이 중 핵심은 `관리 주체`입니다. 해당 상태에 대해서 관리 주체가 서버라면 서버 상태가, 클라이언트라면 클라이언트 상태가 됩니다. + +## **서버 상태 관리 시 발생하는 어려움들** + +> Caching... (possibly the hardest thing to do in programming) +> Deduping multiple requests for the same data into a single request +> Updating "out of date" data in the background +> Knowing when data is "out of date" +> Reflecting updates to data as quickly as possible +> Performance optimizations like pagination and lazy loading data +> Managing memory and garbage collection of server state +> Memoizing query results with structural sharing + +공식 문서에서 서버 상태 관리 시 어려움으로 다음과 같이 언급합니다. + +- 캐싱 + : 여기서 캐싱이란 파일 복사본을 캐시 또는 임시 저장 위치에 저장하여 보다 빠르게 액세스할 수 있도록 하는 프로세스를 의미합니다. +- 같은 데이터에 대한 중복 요청을 단일 요청 통합 +- 백그라운드에서 오래된 데이터 업데이트 +- 데이터가 얼마나 오래되었는지를 파악 +- 데이터 업데이트를 가능한 빠르게 반영 +- 페이지 네이션 및 데이터 지연 로드와 같은 성능을 최적화 +- 서버 상태의 메모리 및 가비지 수집 관리 +- 구조 공유를 사용하여 쿼리 결과를 메모화 + : 여기서 구조 공유란 데이터 구조의 변경되지 않은 부분은 그대로 유지하며 변경된 부분만 새로 생성하는 것을 말합니다. + +또 다른 어려움으로 서버 상태를 관리하다 보면 보일러플레이트 코드를 발생시킵니다. 여기서 보일러플레이트 코드란 여러 가지 상황에서 거의 또는 전혀 변경하지 않고 재사용할 수 있는 컴퓨터 언어 텍스트를 의미합니다. 아래의 예시와 같이 서버 상태를 관리하기 위해서 데이터, 로딩 그리고 에러를 관리하기 위하여 useState와 useEffect가 반복해서 등장하게 됩니다. + +```tsx +import React, { useState, useEffect } from "react"; + +function UserComponent({ userId }) { + const [user, setUser] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchUser = async () => { + setIsLoading(true); + setError(null); + try { + const response = await fetch(`https://api.example.com/users/${userId}`); + if (!response.ok) { + throw new Error("Failed to fetch user"); + } + const data = await response.json(); + setUser(data); + } catch (err) { + setError(err.message); + } finally { + setIsLoading(false); + } + }; + + fetchUser(); + }, [userId]); + + if (isLoading) return
Loading...
; + if (error) return
Error: {error}
; + if (!user) return null; + + return
{user.name}
; +} +``` + +이처럼 하나의 상태를 관리하는 길고 복잡한 보일러플레이트 코드가 각각의 서버 상태마다 존재하게 됩니다. + +## **서버 상태 관리 시 발생하는 어려움들을 해결하는 TanStack query** + +서버 관리 시 발생하는 어려움을 효율적으로 해결하기 위하여 등장한 것이 TanStack Query 입니다. TanStack Query는 앞서 살펴본 어려움들을 모두 기능적으로 지원합니다. + +- 캐싱 + : TanStack Query는 쿼리 키를 기반으로 메모리 내 캐싱을 수행합니다. 각 쿼리 결과는 고유한 쿼리 키와 연결되어 저장됩니다. +- 같은 데이터에 대한 중복 요청을 단일 요청 통합 + : 동일한 쿼리 키로 여러 요청이 동시에 발생하면, TanStack Query는 이를 단일 네트워크 요청으로 통합합니다. +- 백그라운드에서 오래된 데이터 업데이트 + : `refetchInterval` 옵션을 사용하여 주기적으로 백그라운드에서 데이터를 자동으로 업데이트할 수 있습니다. +- 데이터가 얼마나 오래되었는지를 파악 + : `staleTime` 옵션을 통해 데이터가 "오래된(stale)" 상태가 되는 시점을 설정할 수 있습니다. 이를 통해 데이터가 얼마나 오래되었는지를 파악합니다. +- 데이터 업데이트를 가능한 빠르게 반영 + : React의 상태 관리와 통합되어 있어, 데이터가 업데이트되면 자동으로 컴포넌트가 리렌더링 됩니다. +- 페이지네이션 및 데이터 지연 로드와 같은 성능을 최적화 + - 페이지네이션: `useInfiniteQuery` 훅을 제공하여 무한 스크롤이나 "더 보기" 기능을 쉽게 구현할 수 있습니다. + - 데이터 지연 로드: `enabled` 옵션을 사용하여 조건부로 쿼리를 실행할 수 있습니다. +- 서버 상태의 메모리 및 가비지 수집 관리 + : `gcTime` 옵션을 통해 비활성 쿼리가 가비지 수집 관리의 대상이 되어 메모리를 관리합니다. +- 구조 공유를 사용하여 쿼리 결과를 메모화 + : 내부적으로 구조 공유(structural sharing)를 사용하여 불필요한 리렌더링을 방지하고 메모리 사용을 최적화합니다. + +> Help you remove many lines of complicated and misunderstood code from your application and replace with just a handful of lines of React Query logic. + +또한, TanStack Query로 서버 상태를 관리하게 되면 서버 상태 관리할 때 복잡해질 수 있는 코드들을 단 몇 줄로 줄일 수 있습니다. 아래에 예시는 어려움에서 소개했던 예시 코드를 TanStack Query로 마이그레이션 한 코드입니다. 두 코드를 비교하면 반복적인 useState와 useEffect가 사라졌으며 길고 복잡했던 코드가 단 몇 줄로 줄었음을 알 수 있습니다. + +```tsx +import React from 'react'; +import { useQuery } from 'react-query'; + +const fetchUser = async (userId) => { + const response = await fetch(`https://api.example.com/users/${userId}`); + if (!response.ok) { + throw new Error('Failed to fetch user'); + } + return response.json(); +}; + +function UserComponent({ userId }) { + const { data: user, isLoading, error } = useQuery( + ['user', userId], + () => fetchUser(userId), + ); + + if (isLoading) return
Loading...
; + if (error) return
Error: {error.message}
; + if (!user) return null; + + return
{user.name}
; +``` + +> Powerful asynchronous state management for TS/JS, React, Solid, Vue, Svelte and Angular + +이러한 특징으로 인하여 TanStack Query가 서버 상태를 관리하는데 강력한 도구임을 알 수 있습니다. + +# 기본 설정 + +--- + +## **QueryClient** + +> The QueryClient can be used to interact with a cache + +QueryClient를 사용하면 캐시와 상호작용을 할 수 있습니다. 또한 defaultOptions를 이용하면 모든 Query 또는 Mutation에 기본 옵션을 추가할 수 있습니다. 대표적인 예로는 staleTime 혹은 gcTime 설정이 있습니다. 물론 defaultOptions가 없어도 괜찮습니다. + +QueryClient는 다양한 메서드를 제공합니다. 이 중 대표적인 것으로 쿼리를 무효화하고 최신화를 진행하는 queryClient.invalidateQueries 메서드가 있습니다. 이에 대해서는 핵심 개념 설명에서 자세히 다루겠습니다. + +이외 다른 메서드들은 [공식 문서](https://tanstack.com/query/v5/docs/reference/QueryClient#queryclient) 를 참고하면 됩니다. + +```tsx +import { QueryClient } from "@tanstack/react-query"; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: Infinity, + }, + }, +}); +// const queryClient = new QueryClient(); + +queryClient.invalidateQueries({ queryKey: ["todos"] }); +``` + +## Query Client Provider + +> Use the QueryClientProvider component to connect and provide a QueryClient to your application + +TanStack Query를 사용하기 위해서 `QueryClientProvider`를 애플리케이션 최상단에 감싸주고 `QueryClient` 인스턴스를 client props로 넣어주면 됩니다. 이를 통해 QueryClient를 애플리케이션에 연결하고 제공합니다. + +```tsx +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; + +const queryClient = new QueryClient(); + +function App() { + return ...; +} +``` + +# 캐싱 라이프 사이클 + +--- + +## staleTime 과 gcTime + +본격적인 캐싱 라이플 사이클을 이해하기에 앞서 필요한 개념인 staleTime과 gcTime에 대해서 설명하겠습니다. + +### staleTime + +> Stale queries are refetched automatically in the background when: +> +> - New instances of the query mount +> - The window is refocused +> - The network is reconnected +> - The query is optionally configured with a refetch interval + +`staleTime`에서 stale이라는 단어는 fresh의 반대말로 썩은, 오래된 이라는 의미로 staleTime은 데이터가 fresh에서 stale 상태로 변경되는데 걸리는 시간을 의미합니다. fresh 상태일 때는 쿼리 인스턴스가 새롭게 mount 즉, 스크린에서 사용되어도 네트워크 요청(fetch)이 일어나지 않는 데 반해 stale인 경우는 새롭게 mount 될 때 네트워크 요청이 일어납니다. 기본값은 0입니다. + +### gcTime + +> Query results that have no more active instances of useQuery, useInfiniteQuery or query observers are labeled as "inactive" and remain in the cache in case they are used again at a later time. +> +> - By default, "inactive" queries are garbage collected after **5 minutes**. + +gcTime은 Garbage Collection Time의 줄임말로 쿼리 인스턴스가 unmount 되면 즉, 스크린에서 더이상 사용되지 않으면 데이터는 inactive 상태로 변경됩니다. inactive 상태가 된 캐시는 `gcTime`만큼 유지되다 가비지컬렉터의 수집 대상이 됩니다. 기본값은 5분입니다. + +## 캐싱 라이프 사이클 + +> At its core, TanStack Query manages query caching for you based on query keys. + +TanStack Query는 쿼리 키를 바탕으로 쿼리 캐싱을 진행합니다. + +> This caching example illustrates the story and lifecycle of: +> +> - Query Instances with and without cache data +> - Background Refetching +> - Inactive Queries +> - Garbage Collection + +이제 캐싱 라이프 사이클의 이해를 돕기위하여 예를 들어 설명하겠습니다. staleTime(0)과 gcTime(5분)은 기본값입니다. + +> A new instance of useQuery({ queryKey: ['todos'], queryFn: fetchTodos }) mounts. + +[’todos’] 쿼리 키를 최초로 사용합니다. 이때, fetchTodos 쿼리 함수로 데이터를 요청하여 가져옵니다. 가져온 데이터는 해당 쿼리 키 아래에 캐싱됩니다. staleTime이 0이기에 이 데이터는 즉시 stale 상태가 됩니다. + +> A second instance of useQuery({ queryKey: ['todos'], queryFn: fetchTodos }) mounts elsewhere. + +['todos'] 쿼리 키는 이미 사용했기에 해당 쿼리 키 아래에 데이터를 즉시 가져옵니다. 그러나 해당 데이터는 stale 상태이기에 fetchTodos 쿼리 함수로 새롭게 데이터를 요청하여 가져옵니다. 가져온 데이터로 해당 쿼리 키 아래 캐싱된 데이터를 업데이트 합니다. 이로 인하여 해당 쿼리 키를 사용하는 모든 인스턴스는 새로운 데이터로 업데이트 됩니다. + +> Both instances of the useQuery({ queryKey: ['todos'], queryFn: fetchTodos }) query are unmounted and no longer in use. + +['todos'] 쿼리 키를 더 이상 사용하지 않으면 inactive 상태가 됩니다. inactive 상태로 설정한 gcTime이 지나게 되면 해당 쿼리 키 아래에 데이터는 삭제되고 가비지 컬렉터 대상이 됩니다. + +> Before the cache timeout has completed, another instance of useQuery({ queryKey: ['todos'], queryFn: fetchTodos }) mounts. The query immediately returns the available cached data while the fetchTodos function is being run in the background. When it completes successfully, it will populate the cache with fresh data. + +gcTime이 지나기 전에 해당 쿼리 키를 사용하는 인스턴스가 다시 mount 된다면 fetchTodos 쿼리 함수를 통해 새롭게 데이터를 요청하고 가져옵니다. 가져온 데이터로 쿼리 키 아래 캐싱된 데이터를 업데이트 합니다. 해당 경우 다시 active 상태가 되어 gcTime은 영향을 미치지 않습니다. + +# **핵심 개념 3가지** + +--- + +## **Queries(useQuery)** + +> A query is a declarative dependency on an asynchronous source of data that is tied to a **`unique key`**. A query can be used with any Promise based method (including GET and POST methods) to fetch data from a server. If your method modifies data on the server, we recommend using [**Mutations**](https://tanstack.com/query/latest/docs/framework/react/guides/mutations) instead. + +쿼리는 고유한 키와 연결된 비동기 데이터 소스에 대한 선언적 의존성입니다. 이 말을 쉽게 풀어서 얘기하면 `queryKey`를 기반으로 `쿼리 캐싱`을 관리한다는 것을 의미합니다. 또한 쿼리는 서버로부터 데이터를 가져오기 위해 Promise 기반의 모든 메서드(GET과 POST 메서드 포함)와 함께 사용될 수 있습니다. 만약 서버의 데이터를 수정하고자 한다면, 쿼리 대신 뮤테이션 사용을 권합니다. + +### 옵션 + +queries에 해당하는 useQuery 훅을 사용하기 위해서는 queryKey와 queryFn이 필수입니다. + +```tsx +const result = useQuery({ + queryKey: ["todos", 5, { preview: true }], + queryFn: fetchTodos, +}); +``` + +queryKey + +: queryKey는 `배열`로 지정해 줘야 하며 이는 단일 문자열만 포함된 배열이 될 수도 있고, 여러 문자열과 중첩된 객체로 구성된 복잡한 형태일 수도 있습니다. + +queryFn + +: queryFn는 데이터를 성공적으로 가져오면 데이터를 resolved 하는, 실패하면 에러를 throw 하는 `Promise`를 반환하는 함수여야 합니다. + +이 외에는 선택입니다. 선택 옵션으로는 gcTime, staleTime 등 수많은 옵션이 존재합니다. 다른 옵션들은 [공식 문서](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery)를 참고하시길 바랍니다. + +```tsx +const result = useQuery({ + queryKey: ["todos"], + queryFn: fetchTodos, + staleTime: 0, + gcTime: 5 * 60 * 1000, + // ... options +}); +``` + +### 주요 반환 값 + +`data` + +> Defaults to undefined. +> +> The last successfully resolved data for the query. + +기본값은 undefined이며 가장 최근에 쿼리 요청 성공했을 때의 resolved된 data를 가집니다. + +`error` + +> Defaults to null +> +> The error object for the query, if an error was thrown. + +기본값은 null이며 쿼리 요청이 실패했을 때 throw된 error 객체를 가집니다. + +`status` + +> The result object contains a few very important states you'll need to be aware of to be productive. A query can only be in one of the following states at any given moment: +> +> - isPending or status === 'pending' - The query has no data yet +> - isError or status === 'error' - The query encountered an error +> - isSuccess or status === 'success' - The query was successful and data is available + +쿼리는 status 값으로 다음 세 가지 상태 중 하나만 가질 수 있습니다. + +- status === ‘pending’(isPending) + +: 캐싱 된 데이터가 없고, 쿼리 시도가 아직 완료되지 않은 상태를 의미합니다. + +- status === 'error'(isError) + +: 쿼리 요청 중 에러가 발생한 상태를 의미합니다. + +- status === 'success'(isSuccess) + +: 쿼리 요청이 성공했고 데이터를 이용할 수 있는 상태를 의미합니다. + +```tsx +function Todos() { + const { isPending, isError, data, error, ...} = useQuery({ + queryKey: ['todos'], + queryFn: fetchTodos, + }) + + if (isPending) { + return Loading... + } + + if (isError) { + return Error: {error.message} + } + + return ( +
    + {data.map((todo) => ( +
  • {todo.title}
  • + ))} +
+ ) +} +``` + +이 외에도 다양한 반환 값들이 있습니다. 그중 자주 사용하는 몇 가지에 대해서 소개하겠습니다. + +`fetchStatus` + +> fetchStatus === 'fetching' - The query is currently fetching. +> +> fetchStatus === 'paused' - The query wanted to fetch, but it is paused. Read more about this in the [**Network Mode**](https://tanstack.com/query/latest/docs/framework/react/guides/network-mode) guide. +> +> fetchStatus === 'idle' - The query is not doing anything at the moment. + +- fetchStatus === 'fetching'(isFetching) + +: fetching 중인 상태를 의미합니다. 캐싱 된 데이터가 있더라도 fetching 여부에 따라 true/false를 반환합니다. + +- fetchStatus === 'paused'(isPaused) + +: 쿼리는 fetch를 원하나 멈춘 상태를 의미합니다. 해당 값은 네트워크 연결이 끊어졌을 때 예외 처리 시 유용합니다. + +- fetchStatus === 'idle'(isIdle) + +: 쿼리가 fetch를 하지 않는 상태를 의미합니다. + +status가 존재하는데 왜 fetchStatus가 필요할까 하는 의문이 들 수 있습니다. 그 이유는 같은 status 값이라도 다른 fetchStatus 조합이 나올 수 있기 때문입니다. + +> a query in success status will usually be in idle fetchStatus, but it could also be in fetching if a background refetch is happening. +> +> a query that mounts and has no data will usually be in pending status and fetching fetchStatus, but it could also be paused if there is no network connection. + +예를 들어, status가 success인 경우 일반적으로는 fetchStatus가 idle 값을 가지겠지만, 이미 데이터를 가진 상태에서 refetch를 하는 경우에는 fetchStatus가 fetching 값을 가질 수 있습니다. 또한, status가 pending인 경우 fetching을 진행하여 fetchStatus 값이 fetching일 수 있지만, 네트워크가 끊겼다면 fetchStatus는 paused 값을 가질 수 있습니다. 이러한 이유로 status와 fetchStatus 각각을 따로 두어 관리하는 것입니다. + +이 외 isPending과 비슷한 isLoading도 있습니다. + +- isLoading(isFetching && isPending) + +: 캐싱 된 데이터가 없을 때, 쿼리 함수가 실행되면 로딩 여부에 따라 true/false를 반환합니다. + +isLoading과 isPending, isFetching과 isPending이 헷갈릴 수 있습니다. 해당 의미의 명확한 구분을 위하여 각 값을 비교해 보겠습니다. + +- isLoading vs isPending + - isLoading은 캐싱 된 데이터가 없을 때, fetching의 경우에만 true 값을 가질 수 있습니다. + - isPending은 캐싱 된 데이터가 없을 때는 fetching 전이나 중일 때 언제든 true 값을 가질 수 있습니다. +- isFetching vs isPending + - isFetching은 캐싱 된 데이터가 있어도 fetching 여부에 따라 true 값을 가질 수 있습니다. + - isPending은 캐싱 된 데이터가 있으면 항상 false 값만 가집니다. + +이 외 다양한 반환 값들이 있습니다. 다른 반환 값들은 [공식 문서](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery)를 참고하시길 바랍니다. + +## **Mutations(useMutation)** + +> Unlike queries, mutations are typically used to create/update/delete data or perform server side-effects. For this purpose, TanStack Query exports a useMutation hook. + +mutations의 경우 queries와 달리 데이터를 create/update/delete 하거나 서버에 사이드 이펙트를 일으키는 경우 사용합니다. HTTP 메서드로는 post, patch, delete 요청 시 주로 mutations를 사용합니다. + +### 의문 + +여기까지 학습하면 왜 GET의 경우에는 queries를 써야 하고 이 외의 경우는 mutations를 사용해야하는지 궁금할 것입니다. 해당 의문의 답은 `캐싱의 차이`입니다. queries의 경우 데이터를 받아오면 옵션에서 설정한 쿼리 키를 바탕으로 해당 쿼리 키 아래에 캐싱을 진행합니다. 반면, mutations의 경우 옵션으로 쿼리 키를 가지지 않습니다. 즉, 쿼리 키를 바탕으로 캐싱을 진행하지 않습니다. 이렇기에 데이터를 가져오는 작업은 queries를 서버에 사이드 이펙트를 일으키는 작업에는 mutations를 사용합니다. mutations의 경우 뮤테이션 키를 가지지만 해당 키는 캐싱을 위한 키가 아닙니다. 해당 키는 mutation의 defaults를 설정하기 위하여 사용되는 값이기에 캐싱과는 무관합니다. + +### 옵션 + +옵션의 경우 promise를 반환하는 함수인 mutationFn이 필수입니다. + +```tsx +const mutation = useMutation({ + mutationFn: createTodo, + onMutate() { + /* ... */ + }, + onSuccess(data) { + console.log(data); + }, + onError(err) { + console.log(err); + }, + onSettled() { + /* ... */ + }, + // ... options +}); +``` + +이 외에는 선택입니다. 그중 자주 사용하는 것 몇 가지를 소개하겠습니다. + +- onMutate + +: mutationFn이 실행되기 전에 실행되는 함수로 mutation 함수가 받을 동일한 변수를 받습니다. 이는 주로 낙관적 업데이트를 구현할 때 많이 사용됩니다. + +- onSuccess + +: mutationFn이 성공했을 때 실행되는 함수로 mutation의 결과를 받습니다. mutation 결과의 한 예로 post의 경우는 새로 생성된 데이터를 의미합니다. + +- onError + +: mutationFn이 실패했을 때 실행되는 함수로 error 객체를 받습니다. + +- onSettled + +: try…catch…finally 구문의 finally처럼 요청의 성공 여부와 관계없이 실행되는 함수를 의미합니다. + +이 외 다양한 옵션이 있습니다. 다른 옵션들은 [공식 문서](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation)를 참고하시길 바랍니다. + +### 주요 반환 값 + +`data` + +> Defaults to undefined. +> +> The last successfully resolved data for the mutation. + +기본값은 undefined이며 가장 최근에 뮤테이션 요청 성공했을 때의 resolved된 data를 가집니다. + +`error` + +> Defaults to null +> +> The error object for the query, if an error was encountered. + +기본값은 null이며 뮤테이션 요청이 실패했을 때 error 객체를 가집니다. + +`status` + +> A mutation can only be in one of the following states at any given moment: +> +> - isIdle or status === 'idle' - The mutation is currently idle or in a fresh/reset state +> - isPending or status === 'pending' - The mutation is currently running +> - isError or status === 'error' - The mutation encountered an error +> - isSuccess or status === 'success' - The mutation was successful and mutation data is available + +뮤테이션의 경우 fetchStatus는 따로 없고 status만 가집니다. + +- status === 'idle'(isIdle) + +: mutation이 아직 실행되지 않았거나, 이전 실행 후 리셋된 상태를 의미합니다. + +- status === 'pending'(isPending) + +: mutation이 진행 중인 상태를 의미합니다. + +- status === 'error'(isError) + +: mutation 중 에러가 발생한 상태를 의미합니다. + +- status === 'success'(isSuccess) + +: mutation이 성공하고 데이터가 이용할 수 있는 상태를 의미합니다. + +```tsx +const {data, error, isIdle, isPending, isError, isSuccess ...} = useMutation({ + mutationFn: createTodo, +}); + + if (isPending) { + return Loading... + } + + if (isError) { + return Error: {error.message} + } + + return ( +
    + {data.map((todo) => ( +
  • {todo.title}
  • + ))} +
+ ) +``` + +`isPaused` + +> will be true if the mutation has been paused +> see [**Network Mode**](https://tanstack.com/query/latest/docs/framework/react/guides/network-mode) for more information. + +: 뮤테이션을 원하나 멈춘 상태를 의미합니다. 해당 값은 네트워크 연결이 끊어졌을 때 예외 처리 시 유용합니다. + +이 외 다양한 반환 값들이 있습니다. 다른 반환 값들은 [공식 문서](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation)를 참고하시길 바랍니다. + +## **QueryInvalidation(invalidateQueries)** + +> The QueryClient has an invalidateQueries method that lets you intelligently mark queries as stale and potentially refetch them too! + +> When a query is invalidated with invalidateQueries, two things happen: +> +> - It is marked as stale. This stale state overrides any staleTime configurations being used in useQuery or related hooks +> - If the query is currently being rendered via useQuery or related hooks, it will also be refetched in the background + +invalidateQueries는 queryClient의 메서드로 useQueryClient를 통해 호출합니다. invalidateQueries는 쿼리를 무효화 및 최신화합니다. 이를 이용하여 쿼리를 무효화 하게 되면 어떠한 staleTime 설정보다 우선시되어 해당 쿼리를 stale 상태로 만듭니다. 그리고 해당 쿼리가 이미 렌더링 중이라면 백그라운드에서 자동으로 새로운 데이터를 가져옵니다. 이를 통해 화면을 최신상태로 유지합니다. invalidateQueries에 옵션이 없는 경우에는 캐시 안에 있는 모든 쿼리를 무효화 및 최산화합니다. 옵션으로 쿼리 키를 넣어주면 해당 쿼리 키를 가진 모든 쿼리를 무효화 및 최신화합니다. + +```tsx +import { useQuery, useQueryClient } from "@tanstack/react-query"; + +const queryClient = useQueryClient(); + +queryClient.invalidateQueries(); +queryClient.invalidateQueries({ queryKey: ["todos"] }); +``` + +### 참고자료 + +--- + +https://tanstack.com/query/latest/docs/framework/react/overview + +https://tanstack.com/query/v5/docs/reference/QueryClient#queryclient + +https://tanstack.com/query/v5/docs/framework/react/reference/QueryClientProvider +https://tanstack.com/query/v5/docs/framework/react/devtools + +https://tanstack.com/query/v5/docs/framework/react/guides/important-defaults + +https://tanstack.com/query/v5/docs/framework/react/guides/caching + +https://tanstack.com/query/latest/docs/framework/react/guides/queries + +https://tanstack.com/query/latest/docs/framework/react/reference/useQuery + +https://tanstack.com/query/latest/docs/framework/react/guides/mutations + +https://tanstack.com/query/latest/docs/framework/react/reference/useMutation + +https://tanstack.com/query/latest/docs/framework/react/guides/query-invalidation