들어가며#
Next.js 15와 React 19 환경에서 데이터를 업데이트하는 방법은 서버 액션(Server Actions), useOptimistic, TanStack Query 등 정말 다양한데요.
이번 글에서는 각 유스케이스별로 가장 최적화된 데이터 업데이트 구현 방법을 자세히 알아볼 것입니다.
기본 방침#
Next.js 15와 React 19에서 데이터를 업데이트할 때 따라야 할 다섯 가지 핵심 원칙이 있는데요.
이 원칙들을 기억하면 대부분의 상황에서 올바른 방향으로 구현할 수 있습니다.
1. Next.js/React 표준 기능 우선 사용#
데이터 캐시, 서버 액션, revalidateTag, useOptimistic처럼 Next.js와 React의 표준 기능을 사용해 최대한 단순하게 구현하는 것이 최우선인데요.
표준 기능을 활용하면 프레임워크의 발전에 자연스럽게 따라갈 수 있고, 유지보수성도 크게 향상됩니다.
2. 복잡한 요구사항은 TanStack Query에 위임#
클라이언트에서 useQuery로 가져온 데이터를 업데이트하거나, 요구사항이 복잡할 경우에는 TanStack Query를 사용하는 것이 좋거든요.
TanStack Query는 낙관적 업데이트(Optimistic Updates), 세밀한 캐시 제어, 여러 쿼리 연동 등 고도의 데이터 업데이트 요구사항에 효과적으로 대응할 수 있습니다.
3. 업데이트는 서버 액션을 통해#
데이터 등록 및 수정 요청은 반드시 서버 액션을 거쳐 외부 API를 호출해야 하는데요.
서버 액션을 통하면 API 키나 토큰 같은 민감한 정보를 클라이언트에 노출하지 않고 안전하게 외부 API를 호출할 수 있기 때문입니다.
4. 서버 액션은 하나로 통합#
하나의 로직 흐름 안에서는 여러 개의 서버 액션을 사용하지 않고, 가급적 하나의 서버 액션으로 처리 과정을 통합해야 하는데요.
서버 액션은 순차적으로 실행되는 제약이 있어서, 여러 개를 호출하면 대기 시간이 길어져 사용자 경험을 해칠 수 있습니다.
5. 최종 데이터 정합성은 DB를 기준#
낙관적 업데이트나 캐시 조작은 어디까지나 사용자 경험(UX)을 위한 임시적인 표현으로 봐야 하거든요.
최종적인 데이터의 정합성은 항상 데이터베이스(DB)를 기준으로 삼는 것이 원칙입니다.
낙관적 업데이트로 UI를 즉시 바꾸더라도, 결국엔 서버에서 온 최신 데이터로 덮어씌워 데이터의 일관성을 보장해야 합니다.
유스케이스와 구현 방법#
데이터 업데이트의 주요 유스케이스와 각 상황에 맞는 최적의 구현 방법을 정리해봤는데요.
하나씩 자세히 살펴보겠습니다.
| 유스케이스 | 요약 | 구현 방법 |
|---|---|---|
| 1 | 일반적인 업데이트 | |
| 단순한 CRUD 작업 | 클라이언트 컴포넌트 + 서버 액션 + revalidateTag | |
| 2 | 낙관적 업데이트 (서버 로딩 + 단일 화면) | |
| UI 즉시 반영 후 DB 업데이트, 실패 시 롤백 | 클라이언트 컴포넌트 + startTransition + useOptimistic + 서버 액션 + revalidateTag | |
| 3 | 낙관적 업데이트 (서버 로딩 + 여러 화면) | |
| 여러 화면에 걸친 데이터 즉시 반영 | 클라이언트 컴포넌트 + startTransition + useOptimistic + 서버 액션 + revalidateTag | |
| 4 | 낙관적 업데이트 (클라이언트 로딩) | |
| 클라이언트에서 로딩한 데이터의 즉시 UI 반영 | 클라이언트 컴포넌트 + useMutation + onMutate + mutationFn + 서버 액션 + invalidateQueries | |
| 5 | 낙관적 업데이트 (복잡한 요구사항) | |
| 재요청 시점이나 캐시 관리가 복잡할 때 | 클라이언트 컴포넌트 + useQuery + useMutation + onMutate + mutationFn + 서버 액션 + onSettled 등 | |
| 6 | 디바운스 업데이트 (자동 저장) | |
| 입력 중 일정 시간 경과 후 업데이트 | 클라이언트 컴포넌트 + useState + useMutation + mutationFn + 서버 액션 | |
| 7 | 스트리밍 렌더링 (AI 채팅/진행률 표시) | |
| 실시간 데이터 렌더링 후 최종 반영 | 추후 추가 예정 |
그럼, 각 유스케이스에 대해 더 자세히 알아보겠습니다.
1. 일반적인 업데이트#
등록, 수정, 삭제 같은 표준적인 동기 방식의 CRUD 작업을 처리하는 가장 기본적인 패턴인데요.
서버 액션으로 데이터 업데이트를 실행하고, 성공하면 revalidateTag로 관련된 데이터 캐시를 갱신해서 화면과 데이터베이스 상태를 자동으로 일치시키는 방식입니다.
프로필 수정, 할 일 생성/삭제, 설정 저장 등 사용자가 명시적으로 저장 버튼을 눌러 실행하는 대부분의 업데이트 작업에 이 패턴을 사용하거든요.
Next.js 15에서는 이 방식이 데이터 업데이트의 기본 전략이라고 할 수 있습니다.
참고로, 폼(form)을 구현할 때는 useActionState를 사용하면 로딩 상태(pending)나 실행 결과를 한 번에 관리할 수 있어 편리합니다.
2. 낙관적 업데이트 (서버 로딩 + 단일 화면)#
데이터베이스 업데이트 완료를 기다리지 않고, 사용자 행동 직후에 바로 UI 변경을 반영하는 낙관적 업데이트 패턴인데요.
React 19의 useOptimistic 훅으로 UI 상태를 먼저 업데이트하고, 백그라운드에서 서버 액션으로 실제 DB 업데이트를 실행하는 것입니다.
좋아요 버튼, 읽음 표시, 토글 스위치처럼 사용자에게 즉각적인 시각적 피드백이 필요할 때 아주 유용하거든요.
Next.js 15와 React 19에서는 useOptimistic, 서버 액션, revalidateTag를 조합하면 낙관적 업데이트를 정말 간단하게 구현할 수 있습니다.
다만, 업데이트 실패 시 UI가 갑자기 원래대로 돌아가면 사용자가 혼란스러울 수 있으니, 롤백 전에 알림이나 토스트 메시지로 실패 사실을 알려주는 것이 좋습니다.
3. 낙관적 업데이트 (서버 로딩 + 여러 위치/화면)#
동일한 데이터가 여러 화면이나 여러 위치에 표시될 때 사용하는 낙관적 업데이트 패턴인데요.
사용자가 조작한 곳은 useOptimistic으로 즉시 UI에 반영하고, 다른 곳은 revalidateTag를 통해 자동으로 최신 데이터가 반영되도록 하는 방식입니다.
예를 들어, 상세 모달에서 할 일 상태를 바꾸면 모달 내부는 즉시 바뀌고, 뒤쪽의 목록 화면은 몇 초 뒤에 자동으로 업데이트되는 식이거든요.
이렇게 하면 사용자가 조작하는 곳에는 즉각적인 피드백을 주면서도, 다른 곳의 데이터 정합성까지 유지하며 UX를 높일 수 있습니다.
4. 낙관적 업데이트 (클라이언트 로딩)#
TanStack Query의 useQuery로 클라이언트에서 가져온 데이터를 낙관적으로 업데이트하는 패턴인데요.
useMutation의 onMutate 콜백에서 캐시를 즉시 수정하여 UI에 먼저 반영하고, mutationFn에서 서버 액션을 실행해 DB를 업데이트하는 것입니다.
모달을 열거나 탭을 이동할 때처럼 클라이언트에서 가져온 데이터를 즉시 UI에 반영하고 싶을 때 효과적이거든요.
성공하면 invalidateQueries로 최신 데이터를 다시 가져오고, 실패하면 onError에서 미리 저장해 둔 스냅샷으로 롤백하여 데이터 일관성을 유지합니다.
5. 낙관적 업데이트 (복잡한 요구사항)#
데이터 업데이트 후 데이터를 다시 가져오는 타이밍을 세밀하게 제어하거나, 여러 queryKey에 걸친 복잡한 캐시 관리가 필요할 때 사용하는 패턴인데요.
TanStack Query의 다양한 옵션들을 조합하여, 즉시 데이터를 다시 가져올지, 아니면 사용자의 다음 행동까지 기다릴지 등을 유연하게 제어할 수 있습니다.
예를 들어, 검색 결과 화면에서 필터 상태는 그대로 유지하면서 특정 항목만 낙관적으로 업데이트하고 싶을 때 이 패턴이 아주 유용하거든요.
TanStack Query의 고급 옵션들을 활용하면 성능과 UX를 최적화하면서 데이터 정합성까지 완벽하게 지킬 수 있습니다.
6. 디바운스 업데이트 (자동 저장)#
사용자가 입력할 때마다 저장하는 대신, 입력이 잠시 멈췄을 때만 저장 요청을 보내는 디바운스(debounce) 패턴인데요.
텍스트 에디터의 메모 기능이나 프로필 자기소개처럼 타이핑이 잦은 입력 필드의 자동 저장 기능에 주로 사용됩니다.
기본 구조는 4번. 클라이언트 로딩 낙관적 업데이트와 비슷하지만, 한 가지 큰 차이점이 있는데요.
바로 업데이트 실패 시 롤백하지 않고, 사용자가 입력한 내용을 로컬 상태(useState)에 그대로 유지한다는 점입니다.
실패 알림만 보여주고 입력 내용은 보존해서, 사용자의 작업이 날아가지 않도록 보호하는 것이 핵심입니다.
7. 스트리밍 렌더링 (AI 채팅/진행률 표시)#
이 부분은 추후에 내용이 추가될 예정입니다.
마치며#
이번 글에서는 Next.js 15와 React 19 환경에서 데이터를 업데이트하는 다양한 방법들을 유스케이스별로 알아봤는데요.
데이터 업데이트는 사용자 경험에 직접적인 영향을 미치는 아주 중요한 부분입니다.
일반적인 업데이트부터 복잡한 낙관적 업데이트까지, 요구사항에 맞는 최적의 구현 방법을 선택하는 것이 핵심인데요.
특히 낙관적 업데이트는 사용자 경험을 크게 향상시키는 강력한 기법이지만, 데이터 정합성을 보장하는 장치와 함께 신중하게 구현해야 한다는 점을 꼭 기억해야 합니다.
끝까지 읽어주셔서 정말 감사합니다.