FEInterview Prep

YKSS

리액트에서 flushSync로 포커스 관리 마스터하기

setShow(true) 직후 inputRef.current?.focus() 가 동작하지 않는 본질을 짚고, flushSync 로 강제 동기화하는 패턴을 EditableText 예시로 보여 준다.

2025-09-15·6분 읽기
React
원문 보기 ↗

핵심 요약

문제: setShow(true); inputRef.current?.focus() — 배칭 때문에 <input> 이 아직 DOM 에 없어 focus 실패. 해결: flushSync(() => setShow(true)); inputRef.current?.focus(). 응용 패턴 4가지: ① edit 진입 시 input focus + select, ② submit 시 button 으로 focus 복귀, ③ ESC 시 button focus, ④ blur 시 button focus. 단 flushSync 는 React 의 배칭 최적화를 포기하므로 "꼭 필요한 곳" 에만 사용.

React 의 상태 업데이트는 배칭(batching) 되어 핸들러가 끝난 뒤 한꺼번에 적용된다. 따라서 "상태 변경 → 새로 생긴 DOM 에 즉시 접근" 시점이 어긋난다. flushSync 는 그 배칭을 명시적으로 깨고 "이번 업데이트는 지금 반영" 을 강제하는 탈출구 다.

포커스 관리는 키보드/스크린 리더 사용자에게는 기본기다. setTimeout(focus, 10) 같은 임시방편은 브라우저·기기 따라 깨진다. flushSync 를 정확히 알고 있으면 modal 열기/edit 모드 진입/blur 후 복귀처럼 상태와 DOM 의존 이 얽힌 흐름을 안정적으로 짤 수 있다.

학습 포인트

면접 답변으로 연결할 학습 포인트입니다.

왜 `setState` 직후 `ref.current` 가 비어 있을 수 있나

React 의 setState 는 "지금 변경하라" 가 아니라 "이번 핸들러 끝나면 변경 예약" 이다. 따라서 같은 핸들러 안에서 ref.current?.focus() 를 부르면 새 DOM 노드가 아직 생성되지 않은 상태일 수 있다.

batchingschedulingrefcommit phase
자주 하는 오해

setState 가 동기적으로 DOM 을 바꾼다고 가정. 실제로는 scheduler 가 batch 후 commit 한다.

`setTimeout(focus, 10)` 같은 임시방편이 깨지는 이유

10ms 는 매직 넘버다. 기기·브라우저·동시 작업 부하에 따라 commit 이 더 늦을 수도 있고, 그 사이 다른 이벤트가 끼어들 수도 있다. 즉 "운에 의존" 한다.

setTimeoutrequestAnimationFramerace condition
자주 하는 오해

requestAnimationFrame 으로 바꾸면 안전하다고 믿는 것. rAF 는 다음 frame 보장이지 commit 직후 보장이 아니다.

`flushSync` 의 비용과 사용 기준

flushSync(() => setShow(true)); inputRef.current?.focus() 처럼 쓰면 콜백 안의 setState 를 즉시 commit 한다. 단 React 의 자동 batching 최적화를 포기하므로 "DOM 에 즉시 접근해야만 하는 곳" 에만. 대표 케이스: 포커스 관리, onbeforeprint 같이 DOM 이 즉시 갱신돼야 하는 외부 API.

flushSyncbatchingperformance
자주 하는 오해

단순 "동기 setState" 라며 도처에 사용. 성능 비용을 동반하므로 명확한 동기가 있을 때만.

읽는 순서

  1. 1이론

    React 공식 flushSync 문서 를 읽고, batching·commit 단계와 어떻게 충돌하는지 한 문단으로 정리.

  2. 2구현

    본문 EditableText 예제를 직접 구현하고, edit 진입 → ESC → blur → submit 4 케이스에서 포커스가 모두 의도한 곳으로 가는지 검증.

  3. 3실무

    현재 프로젝트에서 modal/drawer 열림 시 첫 입력 필드로 가는 포커스가 보장되는지 키보드만으로 점검. 깨지는 곳에 flushSync 도입 가능성 평가.

  4. 4설명

    "flushSyncuseLayoutEffect 중 무엇을 쓸까" 의사결정 기준을 5분 안에 설명. "effect 로 가능하면 effect 우선" 이 핵심 메시지.

면접 연결 질문

medium`setState` 호출 직후 `ref.current?.focus()` 가 실패하는 원인을 React 내부 동작으로 설명해 주세요.
힌트

[감점 답변] "비동기라서" 로 끝. [좋은 답변] scheduler 가 setState 를 batch 했다가 핸들러 종료 후 reconcile + commit 하기 때문. 따라서 새로 mount 될 <input> 의 ref 가 같은 tick 에서는 비어 있다.

hard`flushSync` 와 `useLayoutEffect` 의 사용 시점은 어떻게 다른가요?
힌트

[좋은 답변] useLayoutEffectcommit 직후 paint 전 동기 hook. flushSync이벤트 핸들러 안에서 batch 를 깨는 도구. 즉 "effect 로 풀 수 있으면 effect, 핸들러 안에서 즉시 DOM 을 만져야 하면 flushSync".

easy포커스 관리가 깨졌을 때 어떤 사용자 그룹이 가장 큰 영향을 받나요? 구체 시나리오로 답해 주세요.
힌트

[좋은 답변] 키보드 only / 스크린 리더 사용자. 예: edit 모드 진입 후 입력 필드로 포커스가 가지 않으면 Tab 으로 다시 찾아가야 하고, blur 후 버튼으로 돌아오지 않으면 컨텍스트가 사라진다.

자기 점검

`flushSync` 를 "항상 쓰면 안 되는" 이유를 한 줄로 설명해 보세요.
batching성능최적화
자주 하는 오해

"동기라서 좋다" 는 오해. 자동 batching 의 렌더 횟수 최소화 이점을 잃는다.

`setTimeout(focus, 0)` 으로 우회하는 패턴이 깨지는 시나리오를 한 가지 들어 보세요.
race condition기기 성능다른 이벤트
자주 하는 오해

"0ms 면 충분" 이라는 믿음. 실제론 commit 이 그 후일 수도 있다.