"input.value = 'foo' 설정. UI 가 'foo' 보임. User 가 submit click 하고 server 가 빈 문자열 받음. React 가 이제 input 소유, 순진한 set 이 tracker 에 invisible. Lesson 5 가 production 에서 만날 모든 framework 에 fix 하는 한 줄 trick."
순진한 set 이 실패하는 이유
React 가 HTMLInputElement 와 HTMLTextAreaElement 의 prototype 을 patch 해서 custom setter 통해 value 변경 추적. input.value = 'foo' 쓰면, patched setter 가 변경이 React component lifecycle 바깥에서 온 거 노트하고 change tracker 에서 silently discard. Visible value 가 update (underlying property 가 여전히 움직임), 하지만 React 의 internal state 가 뭐였든 유지, 다음 render 가 snap back.
Vue, Svelte, Solid 도 유사 mechanism 을 살짝 다른 shape 으로 가짐. Fix 는 같음: framework 가 install 한 어떤 wrapper 든 우회하고 native setter 에 직접 write.
Native setter trick
Framework 가 overwrite 할 기회 갖기 전 prototype 에서 원본 setter 얻기:
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
'value'
).set;
nativeInputValueSetter.call(input, 'foo');
input.dispatchEvent(new Event('input', { bubbles: true }));
두 가지 일어남:
- Native setter 가 실제 property write. Tracker wrap 없음; DOM 이 소란 없이 update.
- Synthetic
inputevent fire. React (와 다른 모든 framework) 가 internal state update 위해 nativeinputevent listen. Bubbling 이 중요 — 대부분 framework 가 input 자체 아닌 root 에 listener attach.
함께 실제 keystroke 와 구별 안 됨. React 의 onChange 가 돔, internal state update, 다음 render 가 새 value 렌더. Submit 동작.
Textarea 와 contenteditable
<textarea> 엔 HTMLTextAreaElement.prototype 사용:
const nativeTextareaValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLTextAreaElement.prototype,
'value'
).set;
contenteditable 요소 (Slack, Notion, Gmail 의 compose) 엔 trick 이 다름:
el.focus();
document.execCommand('insertText', false, 'foo');
document.execCommand 가 deprecated 지만 모든 browser 에서 여전히 동작. Selection-API-based 대체 (navigator.clipboard.write + paste, 또는 InputEvent dispatch) 가 더 복잡하고 framework 별 다양. ClipDeck 엔 execCommand 가 실용적 선택.
이게 중요할 때
ClipDeck v1 이 input 안 채움 — clip 이 read-out, paste-in 아님. v2 의 roadmap 에 user 가 snippet 수집해서 text editor 나 chat app 에 paste 하는 workflow 위한 'focus 된 input 에 이 clip paste'. 그게 정확히 native setter trick 중요할 때; 그것 없이 Slack 이나 Gmail 에 paste 가 UI 에 텍스트 보이지만 실제 보낸 메시지 빈 채.
감지 — 이게 framework-managed 인가?
빠른 확인: input 의 __reactProps 나 __reactInternalInstance read (React 의 internal field; 정확한 이름이 React 버전에 따라 다양). 있으면 framework 가 React. Vue 가 element 에 __vueParentComponent 사용. Svelte 는 obvious marker 없지만 input 이 native event 에 정상 응답, trick 이 detection 없이도 여전히 동작.
Feature-detect 도 가능: 순진한 set 시도, tick 후 value back read. Revert 되면 framework 가 소유. 실용에선 항상 native setter 사용 — 둘 다 케이스에서 올바름.
모두 cover 하는 한 helper
Wrap:
function fillInput(el, value) {
if (el.tagName === 'INPUT') {
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
setter.call(el, value);
el.dispatchEvent(new Event('input', { bubbles: true }));
} else if (el.tagName === 'TEXTAREA') {
const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
setter.call(el, value);
el.dispatchEvent(new Event('input', { bubbles: true }));
} else if (el.isContentEditable) {
el.focus();
document.execCommand('insertText', false, value);
}
}
세 branch, 하나의 consistent caller. React / Vue / Svelte / Solid 와 대부분 contenteditable rich editor 에 동작.