0718 프론트엔드 실무 심화 (1/N): 상태 관리와 서버 상태 관리
✅ 1. 프론트엔드 상태란 무엇인가?
- 상태(State)는 화면이 현재 어떤 값을 기준으로 보여지고 있는지를 결정하는 데이터입니다.
- 프론트엔드에서는 버튼이 열려 있는지, 모달이 보이는지, 검색어가 무엇인지, API로 받아온 상품 목록이 무엇인지, 로그인한 관리자가 누구인지 같은 값들이 모두 상태입니다.
- React에서는 상태가 바뀌면 화면이 다시 렌더링됩니다.
상태 변경
↓
React 렌더링
↓
화면 변경
➕ 1-1. 상태 예시
상담 신청 모달이 열려 있는가?
선택한 상품은 무엇인가?
검색어는 무엇인가?
현재 페이지 번호는 몇 번인가?
API 로딩 중인가?
에러가 발생했는가?
로그인한 관리자 정보는 무엇인가?
상품 목록 데이터는 무엇인가?
- 프론트엔드 개발에서 상태 관리는 화면 품질과 유지보수성에 직접 영향을 줍니다.
- 상태를 아무 데나 두면 화면이 복잡해지고 버그가 늘어납니다.
✅ 2. 상태 관리를 잘해야 하는 이유
- 상태 관리는 단순히
useState를 쓰는 문제가 아닙니다.
- 어떤 상태를 어디에 둘지, 서버 데이터와 로컬 UI 상태를 어떻게 분리할지 결정하는 설계 문제입니다.
➕ 2-1. 상태 관리가 안 좋을 때 생기는 문제
모달이 의도치 않게 닫힘
검색 조건이 초기화됨
페이지 이동 후 필터가 사라짐
API 데이터를 전역 상태에 넣고 계속 꼬임
로딩/에러 처리가 화면마다 다름
관리자 테이블 새로고침 후 상태가 이상함
같은 데이터를 여러 곳에서 따로 관리함
➕ 2-2. 좋은 상태 관리의 효과
화면 흐름이 예측 가능함
컴포넌트 역할이 명확함
API 데이터 갱신이 쉬움
필터/페이지네이션 유지가 쉬움
버그 추적이 쉬움
AI에게 작업을 맡겨도 결과가 안정적임
- 특히 관리자 페이지는 검색, 필터, 테이블, 모달, 권한, 엑셀 다운로드가 섞이기 때문에 상태 설계가 중요합니다.
✅ 3. 상태의 종류
- 프론트엔드 상태는 크게 5가지로 나눠서 보면 이해하기 쉽습니다.
| 상태 종류 | 의미 | 예시 |
|---|
| 로컬 UI 상태 | 한 컴포넌트 안에서만 쓰는 상태 | 모달 열림, 탭 선택 |
| 전역 클라이언트 상태 | 여러 컴포넌트가 공유하는 상태 | 로그인 사용자, 테마 |
| 서버 상태 | API로 받아온 데이터 | 상품 목록, 상담 목록 |
| URL 상태 | 주소에 남겨야 하는 상태 | 검색어, 페이지, 필터 |
| 폼 상태 | 사용자가 입력 중인 값 | 이름, 전화번호, 상품 선택 |
- 이 구분이 중요합니다.
- 모든 상태를 전역 상태로 넣으면 안 되고, 모든 상태를
useState로만 처리해도 안 됩니다.
✅ 4. 로컬 UI 상태
- 로컬 UI 상태는 특정 컴포넌트 안에서만 필요한 상태입니다.
- 다른 화면이나 다른 컴포넌트가 알 필요 없는 값입니다.
➕ 4-1. 예시
드롭다운 열림 여부
상담 신청 모달 열림 여부
현재 선택된 탭
토글 ON/OFF
hover 상태
간단한 confirm 모달 상태
➕ 4-2. useState 예시
function ProductDetailPage() {
const [isConsultModalOpen, setIsConsultModalOpen] = useState(false);
return (
<>
<button onClick={() => setIsConsultModalOpen(true)}>
상담 신청
</button>
{isConsultModalOpen && (
<ConsultModal onClose={() => setIsConsultModalOpen(false)} />
)}
</>
);
}
- 이런 상태는 굳이 전역 상태로 올릴 필요가 없습니다.
- 가까운 곳에 두는 것이 가장 좋습니다.
✅ 5. 전역 상태
- 전역 상태(Global State)는 여러 컴포넌트에서 공유해야 하는 클라이언트 상태입니다.
- 예를 들어 로그인한 관리자 정보, 권한, 테마, 사이드바 접힘 여부 같은 값입니다.
➕ 5-1. 전역 상태로 둘 만한 것
로그인한 관리자 정보
관리자 권한
테마 설정
사이드바 접힘 여부
전역 알림 토스트
전역 Confirm 모달
➕ 5-2. 전역 상태로 두면 안 좋은 것
상품 목록 API 결과
상담 목록 API 결과
검색 결과
페이지네이션 결과
서버에서 가져온 대부분의 목록 데이터
- 서버에서 가져온 데이터는 전역 상태보다 TanStack Query 같은 서버 상태 관리 도구로 관리하는 것이 좋습니다.
- 전역 상태는 “클라이언트 안에서 유지해야 하는 상태”에 집중해야 합니다.
✅ 6. 서버 상태란 무엇인가?
- 서버 상태(Server State)는 프론트엔드가 소유한 데이터가 아니라 서버에서 가져오는 데이터입니다.
- 상품 목록, 상담 목록, 주문 목록, 배너 목록, FAQ, 관리자 목록은 모두 서버 상태입니다.
프론트엔드:
데이터를 보여줌
백엔드:
데이터의 실제 원본을 가짐
즉, 프론트는 서버 데이터를 임시로 캐싱해서 보여주는 것
➕ 6-1. 서버 상태의 특징
비동기로 가져옴
로딩 상태가 있음
에러 상태가 있음
캐싱이 필요함
다른 사용자가 변경할 수 있음
시간이 지나면 stale해질 수 있음
수정 후 다시 갱신해야 함
- 서버 상태는 일반
useState로 관리하기 까다롭습니다.
- 그래서 TanStack Query 같은 도구가 필요합니다.
✅ 7. 서버 상태를 전역 상태에 넣으면 생기는 문제
- 예전에는 Redux 같은 전역 상태에 API 결과를 넣는 방식이 많았습니다.
- 하지만 서버 상태를 전역 상태로 직접 관리하면 코드가 복잡해집니다.
➕ 7-1. 문제 예시
API 호출 코드가 여러 곳에 흩어짐
로딩/에러 상태를 직접 관리해야 함
캐시 만료 기준이 없음
수정 후 목록 갱신을 직접 처리해야 함
같은 API를 여러 컴포넌트에서 중복 호출함
뒤로가기 시 데이터 유지가 어려움
➕ 7-2. 나쁜 예시
const [products, setProducts] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
setIsLoading(true);
fetch('/api/products')
.then((res) => res.json())
.then((data) => setProducts(data.items))
.catch((err) => setError(err))
.finally(() => setIsLoading(false));
}, []);
- 작은 화면에서는 괜찮아 보이지만, 목록/필터/페이지네이션/수정/삭제가 붙으면 금방 복잡해집니다.
✅ 8. TanStack Query란 무엇인가?
- TanStack Query는 React에서 서버 상태를 관리하기 위한 라이브러리입니다.
- API 호출, 캐싱, 로딩, 에러, 재요청, 데이터 무효화, 페이지네이션, optimistic update 같은 기능을 도와줍니다.
React Component
↓
useQuery / useMutation
↓
API 호출
↓
캐싱
↓
로딩/에러/성공 상태 제공
➕ 8-1. TanStack Query가 해결해주는 것
API 로딩 상태 관리
에러 상태 관리
응답 데이터 캐싱
같은 queryKey 중복 요청 방지
데이터 stale 관리
수정 후 목록 자동 갱신
페이지 이동 후 캐시 재사용
백그라운드 refetch
- 서버 상태는 TanStack Query에 맡기고, 로컬 UI 상태는 React state로 관리하면 구조가 깔끔해집니다.
✅ 9. queryKey
- queryKey는 서버 상태 캐시를 구분하는 고유한 키입니다.
- TanStack Query에서 가장 중요한 개념 중 하나입니다.
➕ 9-1. queryKey 예시
['products']
['product', productId]
['admin', 'consults', { page, status, source }]
['admin', 'orders', { page, keyword }]
- queryKey가 같으면 같은 캐시를 공유합니다.
- 검색 조건이나 페이지가 다르면 queryKey도 달라져야 합니다.
➕ 9-2. 관리자 상담 목록 queryKey
const queryKey = [
'admin',
'consults',
{
page,
limit,
status,
source,
keyword,
startDate,
endDate,
},
];
- 필터 조건이 queryKey에 들어가야 조건이 바뀔 때 새 데이터를 가져옵니다.
- queryKey 설계가 엉성하면 다른 조건의 데이터가 섞일 수 있습니다.
✅ 10. useQuery 기본 사용
➕ 10-1. 상품 목록 조회 예시
function ProductListPage() {
const { data, isLoading, isError, error } = useQuery({
queryKey: ['products'],
queryFn: () => productApi.getProducts(),
});
if (isLoading) {
return <ProductListSkeleton />;
}
if (isError) {
return <ErrorMessage message="상품 목록을 불러오지 못했습니다." />;
}
return <ProductList products={data.items} />;
}
➕ 10-2. 장점
isLoading, isError, data를 바로 사용할 수 있습니다.
- 같은 queryKey를 쓰는 다른 컴포넌트가 있으면 캐시를 재사용할 수 있습니다.
- API 호출 상태를 직접
useState로 관리하지 않아도 됩니다.
✅ 11. staleTime과 gcTime
- TanStack Query에서는 데이터가 언제 오래된 것으로 볼지, 캐시를 언제 지울지 정할 수 있습니다.
| 옵션 | 의미 |
|---|
staleTime | 데이터가 fresh로 간주되는 시간 |
gcTime | 사용되지 않는 캐시를 메모리에서 제거하기까지 시간 |
➕ 11-1. 예시
useQuery({
queryKey: ['products'],
queryFn: () => productApi.getProducts(),
staleTime: 1000 * 60,
gcTime: 1000 * 60 * 5,
});
staleTime: 1분이면 1분 동안은 같은 데이터를 fresh로 봅니다.
- 상품 카테고리, FAQ, 배너처럼 자주 안 바뀌는 데이터는 staleTime을 길게 줄 수 있습니다.
➕ 11-2. 기준 예시
상품 카테고리:
5분 ~ 30분
배너 목록:
1분 ~ 10분
관리자 상담 목록:
짧게 또는 조건 변경 시 refetch
관리자 통계:
1분 ~ 5분
로그인 사용자 정보:
상황에 따라 길게 유지
- 무조건 짧게 잡으면 API 호출이 많아지고, 너무 길게 잡으면 오래된 데이터가 보일 수 있습니다.
✅ 12. useMutation
- useMutation은 서버 데이터를 생성, 수정, 삭제할 때 사용합니다.
- 상담 신청, 상품 수정, 상태 변경, 배너 삭제, 엑셀 Export 요청 같은 작업에 적합합니다.
➕ 12-1. 상담 신청 mutation 예시
const createConsultMutation = useMutation({
mutationFn: (payload: CreateConsultPayload) => {
return consultApi.createConsult(payload);
},
onSuccess: () => {
alert('상담 신청이 완료되었습니다.');
closeModal();
},
onError: (error) => {
showToast('상담 신청 중 문제가 발생했습니다.');
},
});
➕ 12-2. 사용 예시
function handleSubmit(values: CreateConsultFormValues) {
createConsultMutation.mutate({
name: values.name,
phone: values.phone,
productId: values.productId,
});
}
isPending을 사용하면 버튼 중복 클릭을 막을 수 있습니다.
<button
type="submit"
disabled={createConsultMutation.isPending}
>
{createConsultMutation.isPending ? '신청 중...' : '상담 신청'}
</button>
✅ 13. invalidateQueries
- 데이터를 수정한 뒤에는 관련 목록을 다시 가져와야 합니다.
- TanStack Query에서는
invalidateQueries로 특정 queryKey를 무효화할 수 있습니다.
➕ 13-1. 상담 상태 변경 후 목록 갱신
const queryClient = useQueryClient();
const updateStatusMutation = useMutation({
mutationFn: adminConsultApi.updateStatus,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ['admin', 'consults'],
});
},
});
- 상담 상태를 변경한 뒤 관리자 상담 목록을 다시 가져오게 할 수 있습니다.
- queryKey prefix를 잘 설계하면 관련 목록을 한 번에 무효화할 수 있습니다.
✅ 14. URL 상태
- URL 상태는 주소에 남겨야 하는 상태입니다.
- 검색어, 필터, 페이지 번호, 정렬 조건처럼 새로고침하거나 링크를 공유해도 유지되어야 하는 값입니다.
➕ 14-1. URL 상태로 두면 좋은 것
검색어
페이지 번호
페이지 사이즈
상태 필터
유입경로 필터
날짜 범위
정렬 조건
탭 선택
➕ 14-2. 관리자 상담 목록 예시
/admin/consults?page=2&status=PENDING&source=NAVER&keyword=아이폰
- 이렇게 URL에 남기면 새로고침해도 같은 조건을 유지할 수 있습니다.
- 운영자가 특정 검색 결과를 공유하기도 쉽습니다.
✅ 15. URL 상태와 서버 상태 연결
- 관리자 목록 화면에서는 URL 상태와 TanStack Query를 같이 쓰는 구조가 좋습니다.
URL query string
↓
page/status/source/keyword 파싱
↓
queryKey에 포함
↓
API 요청
↓
테이블 렌더링
➕ 15-1. 예시 흐름
const searchParams = useSearchParams();
const page = Number(searchParams.get('page') ?? 1);
const status = searchParams.get('status') ?? undefined;
const keyword = searchParams.get('keyword') ?? undefined;
const consultsQuery = useQuery({
queryKey: ['admin', 'consults', { page, status, keyword }],
queryFn: () =>
adminConsultApi.getConsults({
page,
status,
keyword,
}),
});
- URL 상태가 바뀌면 queryKey가 바뀌고, queryKey가 바뀌면 새 API 요청이 나갑니다.
- 관리자 테이블에서는 이 패턴이 매우 중요합니다.
✅ 16. 폼 상태
- 폼 상태는 사용자가 입력 중인 값입니다.
- 상담 신청, 사전예약 신청, 관리자 상품 등록/수정, 배너 등록, 검색 폼에서 사용됩니다.
➕ 16-1. 폼 상태의 특징
입력 중 값이 계속 바뀜
유효성 검증이 필요함
에러 메시지가 필요함
submit 시 서버로 전송됨
초기값이 필요할 수 있음
수정 폼은 서버 데이터와 연결됨
- 간단한 폼은
useState로도 충분합니다.
- 복잡한 폼은
react-hook-form과 zod를 사용하는 것이 좋습니다.
➕ 16-2. 상담 신청 폼 예시
const form = useForm<CreateConsultFormValues>({
defaultValues: {
name: '',
phone: '',
productId: product.id,
},
});
- 폼 상태는 서버 상태와 분리해서 봐야 합니다.
- 서버에서 가져온 상품 정보는 서버 상태이고, 사용자가 입력 중인 이름/전화번호는 폼 상태입니다.
✅ 17. 상태를 어디에 둘지 결정하는 기준
➕ 17-1. 판단 질문
이 상태는 한 컴포넌트에서만 쓰는가?
여러 컴포넌트가 공유해야 하는가?
서버에서 가져온 데이터인가?
URL에 남아야 하는가?
사용자가 입력 중인 값인가?
새로고침 후에도 유지되어야 하는가?
다른 사용자가 변경할 수 있는 데이터인가?
➕ 17-2. 기준표
| 상태 | 추천 위치 |
|---|
| 모달 열림 | 로컬 useState |
| 드롭다운 열림 | 로컬 useState |
| 로그인 관리자 정보 | 전역 상태 또는 query |
| 상품 목록 | TanStack Query |
| 상담 목록 | TanStack Query |
| 검색어/페이지 | URL query |
| 상담 신청 입력값 | form state |
| 테마 | 전역 상태 |
| 토스트 메시지 | 전역 UI store |
- 상태의 종류를 구분하면 구조가 훨씬 깔끔해집니다.
✅ 18. Zustand 사용 기준
- Zustand는 가볍고 사용하기 쉬운 전역 상태 관리 라이브러리입니다.
- Redux보다 설정이 적고, 전역 UI 상태나 로그인 사용자 상태 관리에 적합합니다.
➕ 18-1. Zustand에 넣기 좋은 것
사이드바 접힘 여부
전역 토스트
전역 confirm modal
관리자 UI 설정
테마
간단한 클라이언트 상태
➕ 18-2. Zustand에 넣지 않는 것이 좋은 것
상품 목록 API 결과
상담 목록 API 결과
주문 목록 API 결과
검색 결과
서버에서 가져온 대부분의 데이터
➕ 18-3. 예시
type SidebarStore = {
isCollapsed: boolean;
toggle: () => void;
};
export const useSidebarStore = create<SidebarStore>((set) => ({
isCollapsed: false,
toggle: () =>
set((state) => ({
isCollapsed: !state.isCollapsed,
})),
}));
- Zustand는 클라이언트 상태에 집중시키고, 서버 데이터는 TanStack Query에 맡기는 조합이 좋습니다.
✅ 19. 관리자 페이지 상태 설계 예시
➕ 19-1. 상담 관리 화면
URL 상태:
page, limit, status, source, keyword, startDate, endDate
서버 상태:
상담 목록, 상담 상세, 상태 변경 이력
로컬 UI 상태:
상세 모달 열림, 상태 변경 모달 열림
폼 상태:
상태 변경 사유, 상담 메모 입력값
전역 상태:
로그인 관리자 정보, 권한, 토스트
➕ 19-2. 상품 관리 화면
URL 상태:
page, category, keyword, isVisible
서버 상태:
상품 목록, 상품 상세, 카테고리 목록
로컬 UI 상태:
상품 등록 모달, 이미지 미리보기
폼 상태:
상품명, 출고가, 지원금, 요금제, 노출 여부
전역 상태:
관리자 권한, 토스트, confirm modal
- 화면별로 상태를 구분해두면 컴포넌트 구조를 잡기 쉬워집니다.
✅ 20. 상태 관리에서 자주 하는 실수
➕ 20-1. 모든 것을 전역 상태로 올림
문제:
작은 모달 상태까지 전역으로 관리
결과:
상태 추적 어려움
불필요한 렌더링
컴포넌트 재사용 어려움
- 가까운 곳에서 끝나는 상태는 가까운 곳에 두는 것이 좋습니다.
➕ 20-2. 서버 상태를 useState로 직접 관리
문제:
API 데이터, loading, error, refetch를 직접 구현
결과:
화면마다 코드 반복
캐싱 없음
수정 후 갱신 누락
- 서버 상태는 TanStack Query로 관리하는 것이 좋습니다.
➕ 20-3. 검색 조건을 URL에 안 남김
문제:
관리자 상담 목록 필터를 local state로만 관리
결과:
새로고침하면 조건 사라짐
뒤로가기 동작 이상
운영자가 검색 결과 공유 불가
- 관리자 목록의 검색/필터/페이지는 URL 상태로 두는 것이 좋습니다.
➕ 20-4. queryKey에 필터 조건을 빼먹음
useQuery({
queryKey: ['admin', 'consults'],
queryFn: () => adminConsultApi.getConsults({ status }),
});
- status가 바뀌어도 queryKey가 같으면 캐시가 꼬일 수 있습니다.
좋은 방식:
useQuery({
queryKey: ['admin', 'consults', { status }],
queryFn: () => adminConsultApi.getConsults({ status }),
});
✅ 21. 실무 체크리스트
➕ 21-1. 상태 분리 체크리스트
- 로컬 UI 상태와 서버 상태를 구분했는가?
- 서버 데이터는 TanStack Query로 관리하는가?
- 검색/필터/페이지네이션은 URL 상태로 관리하는가?
- 폼 입력값은 form state로 관리하는가?
- 전역 상태에는 정말 공유가 필요한 값만 넣는가?
- 모달 열림 같은 단순 상태를 과하게 전역화하지 않았는가?
- 로그인 관리자 정보와 권한은 일관된 위치에서 관리되는가?
- 같은 서버 데이터를 여러 곳에서 중복 관리하지 않는가?
➕ 21-2. TanStack Query 체크리스트
- queryKey에 모든 검색 조건이 포함되어 있는가?
- queryFn이 API 호출만 담당하는가?
- staleTime 기준이 데이터 성격에 맞는가?
- mutation 성공 후 필요한 query를 invalidate하는가?
- 로딩/에러/빈 상태 UI가 있는가?
- 같은 API를 중복 호출하지 않는가?
- 수정/삭제 후 목록 갱신이 누락되지 않는가?
- 관리자 목록에서 페이지/필터 변경이 정상 동작하는가?
➕ 21-3. 관리자 페이지 체크리스트
- 필터 조건이 새로고침 후에도 유지되는가?
- 뒤로가기/앞으로가기 동작이 자연스러운가?
- 특정 검색 결과 URL을 공유할 수 있는가?
- 상태 변경 후 목록이 갱신되는가?
- 상세 모달과 목록 데이터가 충돌하지 않는가?
- 권한 없는 버튼은 숨기되, API 권한도 백엔드에서 막는가?
- 에러 발생 시 운영자가 이해할 수 있는 메시지가 나오는가?
- 로딩 중 중복 클릭이 막히는가?
✅ 22. AI를 활용해 상태 관리를 설계할 때 질문법
- AI에게 프론트 상태 관리를 물어볼 때는 화면의 역할, API 데이터, 필터, 모달, 폼, 권한 조건을 같이 알려줘야 합니다.
- 단순히 “상태 관리 어떻게 해?”라고 하면 너무 일반적인 답변이 나옵니다.
➕ 22-1. 좋은 질문 예시
React + TanStack Query + Zustand로 관리자 상담 관리 화면의 상태 구조를 설계하고 싶어.
상황:
1. 상담 목록은 API로 조회함
2. 검색 조건은 page, limit, status, source, keyword, startDate, endDate가 있음
3. 검색 조건은 새로고침해도 유지되어야 함
4. 상담 상세는 모달로 보여줌
5. 상담 상태 변경도 모달에서 처리함
6. 상태 변경 성공 후 상담 목록과 상세 데이터가 갱신되어야 함
7. 로그인한 관리자 권한에 따라 버튼 노출이 달라짐
8. VIEWER는 상태 변경 버튼을 볼 수 없음
9. 로딩/에러/빈 상태 UI도 필요함
요청:
- 로컬 상태, URL 상태, 서버 상태, 전역 상태 분리
- queryKey 설계
- useQuery/useMutation 구조
- invalidateQueries 기준
- Zustand에 넣을 상태와 넣지 말아야 할 상태
- 컴포넌트 구조
- 자주 생기는 버그와 방지법
을 실무 기준으로 정리해줘.
➕ 22-2. AI 답변 검증 기준
- 서버 상태를 Zustand/Redux에 모두 넣으라고 하지 않는가?
- 검색 조건을 URL 상태로 관리하라고 하는가?
- queryKey에 필터 조건을 포함하는가?
- mutation 후 invalidateQueries를 설명하는가?
- 로컬 UI 상태와 폼 상태를 구분하는가?
- 권한 버튼 숨김과 백엔드 권한 검사를 구분하는가?
- 로딩/에러/빈 상태 UI를 포함하는가?
- 현재 서비스 규모에 비해 과하게 복잡하지 않은가?
📌 요약
- 상태(State)는 화면이 현재 어떤 값을 기준으로 보여지는지를 결정하는 데이터입니다.
- 프론트엔드 상태는 로컬 UI 상태, 전역 클라이언트 상태, 서버 상태, URL 상태, 폼 상태로 나눠서 생각하면 구조가 명확해집니다.
- 모달 열림, 드롭다운 열림 같은 단순 UI 상태는 가까운 컴포넌트 안에서
useState로 관리하는 것이 좋습니다.
- 로그인 관리자 정보, 전역 토스트, 사이드바 접힘 여부 같은 값은 Zustand 같은 전역 상태로 관리할 수 있습니다.
- 상품 목록, 상담 목록, 주문 목록, 배너 목록처럼 서버에서 가져오는 데이터는 TanStack Query로 관리하는 것이 좋습니다.
- 검색어, 페이지, 상태 필터, 날짜 범위처럼 새로고침 후에도 유지되어야 하는 값은 URL query string으로 관리하는 것이 좋습니다.
- TanStack Query의 queryKey에는 API 결과를 구분하는 모든 조건이 포함되어야 합니다.
- 데이터를 생성/수정/삭제하는 작업은 useMutation으로 처리하고, 성공 후 관련 query를 invalidate해야 화면 데이터가 갱신됩니다.
- 관리자 페이지는 URL 상태, 서버 상태, 로컬 모달 상태, 폼 상태, 전역 권한 상태를 명확히 나누면 유지보수가 쉬워집니다.
- 상태를 무조건 전역화하거나, 서버 상태를 직접 useState로 관리하거나, queryKey에 필터 조건을 빼먹는 실수를 조심해야 합니다.