Engineering

React 19: useOptimistic으로 만드는 쫀득한 UX

Mike발행일 2026년 1월 24일읽는 시간 약 3

React 19: 답답한 로딩 바, 이제 굿바이

"전송 중..." 뱅글뱅글 도는 스피너. 아무리 예쁘게 만들어도 유저는 기다리는 걸 싫어합니다. React 19에 정식 도입된 useOptimistic 훅은 이 문제를 아주 우아하게 해결해 줍니다. 이름부터 마음에 들지 않나요? "**낙관적(Optimistic)<strong>". 세상이 다 잘 될 거라고 믿는 거죠.

하지만 React 19의 변화는 useOptimistic 하나가 아닙니다. 이번 메이저 업데이트는 "</strong>개발자가 비동기 상태와 서버 상호작용을 더 쉽게 관리하게 하자**"라는 큰 방향성을 갖고 있습니다. 주요 변경사항들을 하나씩 뜯어봅시다.

"일단 보여주고, 뒷수습은 나중에": useOptimistic

원리는 간단합니다. 사용자가 '좋아요' 버튼을 딱 누르는 순간, 서버에 요청을 보내기도 전에 화면의 하트부터 빨갛게 칠해버리는 겁니다. "어? 그러다 서버 에러 나면 어떡해요?" 그때를 위해 React가 뒤에서 다 알아서 해줍니다. 서버 요청이 실패하면 묻지도 따지지도 않고 원래 상태로 롤백(Rollback) 시켜줍니다. 개발자는 복잡한 try-catch 지옥에서 해방되는 거죠.

예전엔 이거 구현하려면, 로컬 상태 따로 만들고, 서버 상태 따로 관리하고, 에러 나면 다시 되돌리고... 코드가 스파게티가 되기 십상이었습니다. 근데 이제 훅 하나로 정리가 됩니다.

이 한 줄이면 끝납니다. UX는 쫀득해지고, 코드는 깔끔해집니다.

어디서 써먹을까?

낙관적 업데이트가 특히 효과적인 상황들이 있습니다:

  1. 좋아요/북마크: 가장 클래식한 사례. 버튼을 누르는 순간 UI가 바뀌고, 뒤에서 조용히 서버에 반영합니다.
  2. 메시지 전송: 카카오톡, 슬랙처럼 메시지를 보내면 즉시 채팅 목록에 나타나고, 서버 확인 후에 "전송됨" 체크가 뜹니다.
  3. 드래그 앤 드롭 정렬: 할 일 목록이나 칸반 보드에서 아이템을 드래그하면 즉시 UI에 반영하고, 서버 문제가 생기면 원래 위치로 되돌립니다.
  4. 장바구니: 상품을 추가/제거하면 즉시 수량이 변경되고, 서버 동기화는 백그라운드에서 처리합니다.

느린 네트워크에서도 사용자는 "이 앱 반응이 빠르네"라고 느끼게 됩니다. 이것이 체감 성능의 힘입니다.

useActionState: 폼 상태 관리의 정석

React 19에서 새로 도입된 useActionState는 Server Actions와 완벽하게 궁합이 맞는 훅입니다. 폼 제출의 세 가지 핵심 상태를 한 번에 관리합니다:

  1. 결과 데이터: 서버에서 돌아온 응답
  2. 폼 액션: 실행할 Server Action 함수
  3. 대기 상태: 현재 제출 중인지 여부

이전에는 useState로 로딩 상태, useEffect로 에러 처리, 별도 변수로 결과 저장... 이런 보일러플레이트 코드가 매 폼마다 반복되었습니다. useActionState 하나면 이 모든 게 정리됩니다.

특히 좋은 점은 Progressive Enhancement입니다. JavaScript가 로드되기 전에도 폼이 동작합니다. HTML의 기본 폼 동작을 활용하기 때문이죠. 접근성 측면에서도 큰 장점입니다.

use() Hook: 프로미스를 직접 읽는다

React 19의 또 다른 혁신은 use() 훅입니다. 이 녀석은 <strong>프로미스(Promise)</strong>를 컴포넌트 안에서 직접 "읽을" 수 있게 해줍니다.

기존에는 데이터를 가져오려면 useEffect + useState를 조합해야 했습니다. 로딩 상태 관리, 에러 핸들링, 렌더링 타이밍... 매번 같은 패턴을 반복하는 게 정말 지겨웠죠.

use()를 쓰면 그냥 프로미스를 직접 전달합니다. React가 알아서 해당 프로미스가 해결(resolve)될 때까지 Suspense 경계에서 대기합니다. 코드가 극적으로 단순해집니다.

그리고 use()는 Context에도 쓸 수 있습니다. 기존 useContext를 대체할 수 있는데, 차이점은 use()<strong>조건부(if문 안)</strong>에서도 호출할 수 있다는 겁니다. 기존 훅의 "최상위에서만 호출해야 한다"는 규칙에서 자유로워진 거죠.

useTransition과의 환상적인 듀오

여기에 useTransition까지 곁들이면 금상첨화입니다. "이 작업은 좀 걸릴 수 있어, 근데 UI는 멈추지 마." 라고 React에게 힌트를 주는 거죠.

React 19에서 useTransition이 더욱 강력해진 이유는 비동기 함수를 직접 지원하기 때문입니다. 이전에는 동기적인 상태 업데이트만 트랜지션으로 감쌀 수 있었는데, 이제는 async/await를 사용하는 비동기 작업도 트랜지션에 넣을 수 있습니다.

함께 읽으면 좋은 글

Engineering

Next.js 인증 리다이렉트 루프 방지 플레이북: 로그인 UX와 보안을 동시에 지키는 운영 설계

2026-03-03
Engineering

Next.js 서버 액션 장애 복구 플레이북: 실패를 사용자 이탈로 번지지 않게 막는 실무 설계

2026-03-01

본문 작성/검수 원칙은 편집 원칙에서 확인할 수 있습니다.

← 목록으로 돌아가기