좀비 차일드 문제와의 조우

4

발행일: 2025년 05월 07일

“대체… 왜 이러는 거야?”

새벽 2시. 카토의 쉰 목소리가 텅 빈 사무실에 낮게 울렸다. 며칠 밤낮을 괴롭히던 유령 같은 버그는 여전히 그의 손아귀를 교묘하게 빠져나가고 있었다.

문제는 특정 사용자 시나리오에서 발생했다. 사용자가 빠르게 장바구니에 상품을 추가했다가 즉시 삭제하는 경우, 간헐적으로 삭제된 상품이 화면에 그대로 남아있는 현상. 마치 유령처럼, 사라져야 할 것이 사라지지 않고 버젓이 자리를 차지하고 있었다.

처음에는 단순한 상태 업데이트 로직 실수라고 생각했다. 리듀서 코드를 수십 번 검토하고, 액션 디스패치 순서를 확인하고, 비동기 처리 로직을 샅샅이 뒤졌다. 하지만 코드 상으로는 아무런 문제가 없어 보였다. 상태는 분명히 정상적으로 업데이트되고 있었다.

“로그에는… 삭제됐다고 나오는데, 왜 화면에는 그대로 남아있지?”

카토는 미간을 찌푸리며 React DevTools를 노려봤다. 컴포넌트의 props와 state를 실시간으로 추적했지만, 버그가 발생하는 순간을 포착하기란 하늘의 별 따기였다. 마치 그의 눈을 피해 숨바꼭질하듯, 버그는 재현될 때도 있고, 안 될 때도 있었다.

혹시나 싶어 관련 컴포넌트의 렌더링 로직을 뜯어보았다. useEffect 훅 안에서 상태를 구독하고, 클린업 함수에서 구독을 해제하는 기본적인 패턴. 특별히 문제 될 만한 부분은 없어 보였다.

“혹시… 타이밍 문제인가?”

카토는 퍼뜩 떠오른 생각에 손가락을 빠르게 움직였다. 상태 업데이트 요청(dispatch)과 실제 컴포넌트 리렌더링 사이의 미묘한 시간차. 그 찰나의 순간에 무언가 잘못되고 있는 것은 아닐까?

그는 인터넷 검색창에 관련 키워드를 입력하기 시작했다. ‘React Context state update lag’, ‘useReducer inconsistent state’, ‘component renders with stale state’… 수많은 개발자들의 질문과 답변, 그리고 고통의 흔적들이 화면을 채웠다.

그러다 그의 눈에 특정 용어가 들어왔다.

‘좀비 차일드(Zombie Child)’

섬뜩한 이름이었지만, 그 설명은 카토가 겪고 있는 현상과 놀랍도록 일치했다.

핵심은 이랬다. 컴포넌트가 언마운트되거나 리렌더링을 준비하는 과정에서, 아직 처리되지 않은 이전 상태 업데이트(Context로부터 오는)를 뒤늦게 수신하는 경우가 발생할 수 있다는 것. 특히 상태 변경이 매우 빠르게 연속적으로 일어날 때, 혹은 Concurrent Mode 같은 비동기 렌더링 환경에서 더욱 두드러질 수 있는 문제였다.

마치 무덤에서 잠시 깨어난 좀비처럼, 죽었어야 할(혹은 새로운 정보로 갱신되었어야 할) 컴포넌트 인스턴스가 예전의 낡은 상태(stale state)를 가지고 잠시 화면에 나타났다가 사라지는 현상.

“이거였군…”

카토는 허탈한 웃음을 터뜨렸다. 범인은 특정 코드 라인이 아니었다. React의 렌더링 메커니즘과 Context API의 구독 방식이 만들어내는, 교묘하고 예측 불가능한 부작용. 디버깅이 어려웠던 것은 당연했다. 이 ‘좀비’는 아무 때나 나타나는 것이 아니라, 여러 조건이 기막히게 맞아떨어지는 특정 순간에만 잠시 출몰했다 사라지니 말이다.

불필요한 렌더링, 번거로운 보일러플레이트에 이어 이제는 예측 불가능한 상태 오류까지. Context API와 useReducer 조합은 마치 판도라의 상자처럼 계속해서 새로운 문제들을 쏟아내고 있었다.

‘단순히 성능 문제가 아니었어. 이건… 애플리케이션의 안정성 자체를 위협하는 문제잖아.’

카토는 등골이 서늘해지는 것을 느꼈다. 사용자는 그저 버튼 몇 번 눌렀을 뿐인데, 개발자는 밤을 새워 유령을 쫓아야 한다? 이건 뭔가 단단히 잘못되었다.

“React의 철학은 간결함인데, 왜 상태 관리는 이렇게 복잡한 지뢰밭이 되어야만 하는 거지?”

그의 목소리에는 깊은 실망과 분노가 뒤섞여 있었다. 더 이상 기존 방식에 기댈 수 없다는 확신이 들었다. 이 지긋지긋한 문제들을 근본적으로 해결할 새로운 방법. React의 철학에 부합하면서도, 개발자를 고통스럽게 하지 않는, 그런 상태 관리 방식이 필요했다.

모니터의 푸른빛이 그의 얼굴을 비췄다. 밤은 깊었지만, 그의 눈빛은 오히려 더욱 또렷하게 빛나기 시작했다. 좀비 차일드와의 끔찍한 조우는 그에게 좌절을 안겼지만, 동시에 새로운 길을 찾아야 한다는 강력한 동기를 부여했다.

벽에 부딪혔는가? 그렇다면, 그 벽을 부수거나, 혹은 새로운 길을 찾아 돌아가면 된다. 카토의 머릿속에서 혁신의 톱니바퀴가 서서히, 하지만 분명하게 돌아가기 시작했다. 그의 여정은 이제 막 진짜 시작점에 선 것일지도 몰랐다.