React 19의 기능들이 하나씩 구체화되면서, 팀은 사용자 경험의 또 다른 차원으로 눈을 돌렸다. 그것은 바로 ‘인지 속도(Perceived Speed)’의 영역이었다. 실제 데이터 처리가 끝나는 속도와는 별개로, 사용자가 ‘시스템이 즉각적으로 반응한다’고 느끼게 만드는 방법에 대한 고민이었다.
문제의 발단은 흔한 ‘좋아요’ 버튼이었다.
로렌 탄은 일반적인 ‘좋아요’ 버튼의 동작 시퀀스를 화이트보드에 그렸다.
- 사용자가 ‘좋아요’ 버튼을 클릭한다.
- 클라이언트가 서버에 API 요청을 보낸다. (
POST /api/posts/123/like
) - 클라이언트는 로딩 스피너를 보여주며 서버의 응답을 기다린다.
- 서버는 데이터베이스를 업데이트하고, 성공 응답을 보낸다.
- 클라이언트는 성공 응답을 받고, 로딩 스피너를 없앤 뒤 버튼의 UI를 ‘좋아요’가 눌린 상태로 바꾼다. (예: 하트 아이콘 채우기)
“이 과정에서 네트워크 지연 시간이 500밀리초라고 가정해봅시다.” 로렌이 말했다. “사용자는 버튼을 누르고 나서 0.5초 동안 아무런 시각적 피드백을 받지 못하거나, 기껏해야 작은 스피너만 보게 됩니다. 이 짧은 지연이 사용자에게는 ‘앱이 느리다’ 혹은 ‘내 클릭이 무시당했나?’라는 느낌을 주기에 충분합니다.”
인간-컴퓨터 상호작용 연구에 따르면, 시스템이 0.1초(100밀리초) 안에 반응할 때 사용자는 자신의 행동이 즉각적인 결과를 낳았다고 느낀다. 500밀리초는 그 기준을 한참 벗어나는 시간이었다.
“만약….”
그녀의 눈빛이 빛났다.
“만약 우리가 서버의 응답을 기다리지 않는다면 어떨까요? 일단 서버 요청이 성공할 것이라고 ‘낙관적으로’ 가정하고, UI를 먼저 업데이트해버리는 겁니다.”
그녀는 새로운 시퀀스를 그렸다.
- 사용자가 ‘좋아요’ 버튼을 클릭한다.
- 즉시 UI를 ‘좋아요’가 눌린 상태로 업데이트한다.
- 동시에, 백그라운드에서 서버에 API 요청을 보낸다.
“이러면 사용자는 버튼을 누르는 즉시 하트가 채워지는 것을 보게 됩니다. 0.1초의 마법이 실현되는 거죠. 사용자 경험은 비교할 수 없이 향상됩니다.”
“하지만 만약 서버 요청이 실패하면 어떡하죠?” 한 엔지니어가 곧바로 반문했다. “네트워크 오류나 다른 이유로요. 그러면 UI는 성공한 것처럼 보이는데 실제 데이터는 그렇지 않은, 불일치 상태에 빠지게 됩니다.”
“바로 그게 핵심입니다.” 로렌이 답했다. “요청이 실패했을 때, 우리는 조용히 UI를 이전의 원래 상태로 되돌리면 됩니다. 마치 아무 일도 없었던 것처럼요.”
이것이 바로 ‘낙관적 업데이트(Optimistic Update)’의 핵심 아이디어였다. 대부분의 경우 요청은 성공할 것이라는 합리적인 가정 하에, 사용자에게 즉각적인 피드백을 먼저 제공하여 인지 속도를 극대화하는 기법.
이 아이디어는 새롭지 않았다. 많은 개발자가 이 패턴을 구현하기 위해 복잡한 상태 관리 로직을 직접 짜왔다. 임시 상태를 만들고, 실제 상태와 동기화하며, 실패 시 롤백하는 코드는 번거롭고 버그를 유발하기 쉬웠다.
“우리는 이 패턴을 React가 직접 지원해야 합니다. 개발자가 단 한 줄의 훅으로 이 모든 복잡성을 처리할 수 있게 만들어야 해요.”
그 순간, useOptimistic
이라는 새로운 훅의 청사진이 그려졌다. 이 훅은 개발자에게 ‘실제 상태’와 별개로, 잠시 동안만 유효한 ‘낙관적 상태’를 관리할 수 있는 능력을 부여할 터였다. 그리고 그 모든 동기화와 롤백 과정은 React가 알아서 처리해 줄 것이다.
React 19는 이제 단순히 로딩 시간을 줄이는 것을 넘어, 사용자의 심리까지 파고들 준비를 하고 있었다. 사용자의 시간을 0.1초라도 더 가치 있게 만들어주기 위한, 가장 인간적인 차원의 혁신이 시작되고 있었다.