[JS/React] input ํ๋์ ์ซ์๋ง ์ ๋ ฅ ํ์ฉํ๊ธฐ (ํ๊ธ ์ ๋ ฅ ๋ฐฉ์ง)
text ํ์ ์ผ ๋
onKeyDown
, onKeyPress
์ด๋ฒคํธ๋ ํธ๋ค๋ฌ ์์์ e.preventDefault()
๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ์
๋ ฅ์ ๋ง์ ์ ์๋ค. ํ์ง๋ง ํ๊ธ์ ํฌํจํ CJK ๋ฌธ์(์กฐํฉ ๋ฌธ์)๋ ๋ฉ์๋๋ฅผ ํธ์ถํด๋ ๊ทธ๋๋ก ์
๋ ฅ๋๋ค. ์ด ๋ฐฉ๋ฒ์ ์ซ์๋ ์์ด ์
๋ ฅ์ ๋ง์๋๋ง ์ ํจํ๋ค. ์๋ ๋ฐฉ๋ฒ์ผ๋ก ํด๊ฒฐํ ์ ์๋ค.
export default function App() {
const OnKeyDown = (e) => {
e.preventDefault();
};
return (
<div className="App">
<h1>Only allow numbers in input</h1>
<label>
<span className="label">Text Type</span>
<input type="text" onKeyDown={OnKeyDown} />
</label>
</div>
);
}
๐ก React์์ onKeyDown
→ onKeyPress
→ onChange
(value ๋ณ๊ฒฝ๋จ) → onKeyUp
(value ๋ณ๊ฒฝ๋จ) ์ด๋ฒคํธ ์์๋๋ก ํธ๋ค๋ฌ๊ฐ ํธ์ถ๋๋ค. ์ผ๋ฐ ์๋ฐ์คํฌ๋ฆฝํธ์์ change
์ด๋ฒคํธ๋ value
์์ฑ์ด ๋ณ๊ฒฝ๋๊ณ ํฌ์ปค์ค๋ฅผ ํด์ ํด์ผ ํธ์ถ๋์ง๋ง, React์์ Input ์ด๋ฒคํธ์ฒ๋ผ value
์์ฑ ๊ฐ์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ํธ์ถ๋๋ค.
๋ฐฉ๋ฒ 1 — value ๊ต์ฒด (uncontrolled)
๐ก ์ ๊ท์์์ \d
๋ฉํ ๋ฌธ์๋ 10์ง์([0-9]
์ ๋์ผ), \D
๋ 10์ง์๊ฐ ์๋ ๋ฌธ์๋ฅผ ๊ฐ๋ฆฌํจ๋ค.
onChange
์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด ํธ๋ค๋ฌ ์์์ ์ซ์๊ฐ ์๋ ๋ชจ๋ value
๋ฅผ ๋น๋ฌธ์์ด๋ก ๋์ฒดํ๋ค. onKeyDown
์ด๋ฒคํธ๋ value
์์ฑ์ด ๋ฐ๋๊ธฐ ์ ์ ํธ์ถํ๋ฏ๋ก ์ฌ์ฉํ ์ ์๊ณ , onKeyUp
์ด๋ฒคํธ๋ ํ๊ธ(CJK ๋ฌธ์)์ด ์ง์์ง๋ ๊ณผ์ ์ ์ก์์ผ๋ก ํ์ธ ํ ์ ์์ด์ UX์ ์ข์ง ์๋ค. ๋ฐ๋ผ์ onChange
์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ๋ค.
const onChange = (e) => {
// onChange ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๋๋ง๋ค 10์ง์๊ฐ ์๋ ๋ฌธ์๋ฅผ ๋ชจ๋ ๋น๋ฌธ์์ด๋ก ๋์ฒด
// ๋ฐฉํฅํค, ๋ฐฑ์คํ์ด์ค ๋ฑ๋ ์ ์๋ํ๋ค
e.target.value = e.target.value.replaceAll(/\D/g, "");
};
return (
<label>
<span className="label">Text Type</span>
<input type="text" onChange={onChange} />
</label>
);
๋ฐฉ๋ฒ 2 — ์ซ์์ผ๋๋ง ์ํ ๋ณ๊ฒฝ (controlled)
onChange
์ด๋ฒคํธ๊ฐ ๋ฐ์ํด์ ํธ๋ค๋ฌ๋ฅผ ํธ์ถํ๋ ์์ ์ value
์์ฑ ๊ฐ์ด ์ด๋ฏธ ๋ฐ๋์ด ์๋ ์ํ๋ค. ์๋ฅผ๋ค์ด ํ์ฌ Input์ 88
์ด ์
๋ ฅ๋ ์ํ์์ a
๋ฅผ ์
๋ ฅํ๋ฉด ํธ๋ค๋ฌ ์์์ e.target.value
๋ 88a
๊ฐ ๋๋ค.
์ ๊ท์์ ์ด์ฉํด์ e.target.value
์ 10์ง์ ๋ฌธ์๋ง ์์๋ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ฉด โ์ซ์๋ง ํ์ฉํ๋ฉด์ โ๋ถํ์ํ ๋ ๋๋ง์ ๋ฐฉ์งํ ์ ์๋ค. value
์์ฑ ๊ฐ์ด ๋ณ๊ฒฝ๋ ํ onChange
์ด๋ฒคํธ๋ฅผ ์ ์ดํ๋ฏ๋ก ๋ฐฉํฅํค๋ ๋ฐฑ์คํ์ด์ค ๋ฑ์ ๋ชจ๋ ์ ์์ ์ผ๋ก ์ด์ฉํ ์ ์๋ค.
const [textValue, setTextValue] = useState('');
const onChange = (e) => {
// '88'.match(/\D/g) -> null
// !'88'.match(/\D/g) -> true -> ์กฐ๊ฑด ํต๊ณผ ํ ์ํ ๋ณ๊ฒฝ
// '88a'.match(/\D/g) -> ['a']
// !'88a'.match(/\D/g) -> false -> ์กฐ๊ฑด ํต๊ณผ ๋ชปํด์ ์ํ ๋ณ๊ฒฝ ์ํจ
if (!e.target.value.match(/\D/g)) setTextValue(e.target.value);
};
return (
<label>
<span className="label">Text Type</span>
<input type="text" value={textValue} onChange={onChange} />
</label>
);
number ํ์ ์ผ ๋
<input type="number" />
์ผ ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ซ์๋ง ์
๋ ฅํ ์ ์๋ค. ํ์ง๋ง CJK ๋ฌธ์๋ ์๋ ์ด๋ฏธ์ง์ฒ๋ผ ์
๋ ฅ ํ๋์ ๊ทธ๋๋ก ํ์๋๋ ๋ฌธ์ ๊ฐ ์๋ค. number
ํ์
Input์์ CJK ๋ฌธ์ ์
๋ ฅ ๋ฐฉ์ง๋ ์๊ฐ๋ณด๋ค ๊น๋ค๋กญ๋ค.
์ผ๋จ CJK ๋ฌธ์ ์
๋ ฅ์ onChange
, onKeyPress
์ด๋ฒคํธ๊ฐ ๊ฐ์งํ์ง ๋ชปํ๋ค. ์ฆ, ํ๊ธ์ ์
๋ ฅํด๋ ํธ๋ค๋ฌ๊ฐ ํธ์ถ๋์ง ์๋๋ค. ๋์ onKeyDown
, onKeyUp
์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํด์ผ CJK ๋ฌธ์ ์
๋ ฅ์ ๊ฐ์งํ ์ ์๋ค.
๋ฐฉ๋ฒ 1 — value ๊ต์ฒด (uncontrolled)
onKeyDown
์ด๋ฒคํธ๋ value
๊ฐ์ด ๋ฐ๋๊ธฐ ์ ์ ํธ์ถ๋๋ฏ๋ก uncontrolled ๋ฐฉ์์์ ํ์ฉํ ์ ์๋ค. ํ๊ธ ์
๋ ฅ์ ๊ฐ์งํ ์ ์๋ onKeyUp
์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค. ๋ฐฉ๋ฒ์ text
ํ์
์์ value
๊ต์ฒด ๋ฐฉ๋ฒ๊ณผ ๋์ผํ๋ค.
const onKeyUp = (e) => {
e.target.value = e.target.value.replaceAll(/\D/g, '');
};
return (
<label>
<span className="label">Number Type</span>
<input type="number" onKeyUp={onKeyUp} />
</label>
);
โดํ๊ธ, ์์ด์ฒ๋ผ ์ซ์๊ฐ ์๋ ํค๋ฅผ ์
๋ ฅํ๊ณ onKeyUp
ํธ๋ค๋ฌ ์์์ e.target.value
๊ฐ์ ์กฐํํด๋ณด๋ฉด ํญ์ ''
๋น ๋ฌธ์์ด๋ง ๋์จ๋ค. ์ด๋ฏธ ์ซ์๊ฐ ์
๋ ฅ๋ ์ํ์์๋ ๋น ๋ฌธ์์ด์ ์ถ๋ ฅํ๋ค. ์ฆ, ์ซ์๊ฐ ์๋ ํค๋ฅผ ์
๋ ฅํ๋ฉด ์
๋ ฅ ํ๋๋ ํญ์ ๋น ๋ฌธ์์ด์ด ๋๋ค. โต๊ฒ๋ค๊ฐ onKeyUp
์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ๋ฉด ํ๊ธ์ด ์ง์์ง๋ ๊ณผ์ ์ ๊ทธ๋๋ก ํ์ธํ ์ ์์ผ๋ฏ๋ก UX์ ์ข์ง ์๋ค.
๋ฐฉ๋ฒ 2 — Composition ์ด๋ฒคํธ ํ์ฉ (controlled/uncontrolled) โก๏ธ
CJK ๊ฐ์ ์กฐํฉ๋ฌธ์๋ ๋ฌธ์ ์ ๋ ฅ์ Composition ์ด๋ฒคํธ๋ฅผ ํธ์ถํ๋ค. CJK ๋ฌธ์๋ฅผ ์ ๋ ฅํ๋ฉด ์กฐํฉ์ ์์ํด์ ๋ฐ์ค์ด ํ์๋๋ค. ์คํ์ด์ค / ๋ฐฉํฅํค๋ฅผ ๋๋ฅด๋ฉด ์กฐํฉ์ ์๋ฃํด์ ๋ฐ์ค์ด ์ฌ๋ผ์ง๋ค. ์์ฃผ ์ฌ์ฉํ๋ ์กฐํฉ ๊ด๋ จ ์ด๋ฒคํธ๋ ์๋ 3๊ฐ๊ฐ ์๋ค. ์ด๋ฆ ๊ทธ๋๋ก ์กฐํฉ ์์/๊ฐฑ์ /์ข ๋ฃ๋ ๋ฐ์ํ๋ค. ์ ๋ ฅ์ ๋ฐ๋ผ ์ด๋ฒคํธ๊ฐ ์ด๋ป๊ฒ ๋ฐ์ํ๋์ง๋ MDN์์ ์ง์ ์ ๋ ฅํด๋ณด๋ฉด์ ํ์ธํ ์ ์๋ค.
compositionstart
(onCompositionStart) : ์กฐํฉ ์์compositionupdate
(onCompositionUpdate) : ์กฐํฉ ๋์คcompositionend
(onCompositionEnd) : ์กฐํฉ ์ข ๋ฃ
CJK ๋ฌธ์๋ event.preventDefault()
๋ฉ์๋๋ฅผ ํธ์ถํด๋ ์
๋ ฅ์ ๋ง์ ์ ์์ผ๋ฏ๋ก ํฌ์ปค์ค ์ด๋ฒคํธ๋ฅผ ํ์ฉํด์ผ ํ๋ค. CJK ๋ฌธ์ ์
๋ ฅ์ ์์ํ์ ๋ onCompositionStart
์ด๋ฒคํธ ํธ๋ค๋ฌ ์์์ ํฌ์ปค์ค๋ฅผ ํด์ ํ ํ, ๋ค์ ํ๋ ์(requestAnimation
๋ฉ์๋ ํ์ฉ)์ ๋ค์ ํฌ์ปค์ค๋ฅผ ์ฃผ๋ ๋ฐฉ์์ผ๋ก ํ๊ธ ์
๋ ฅ์ ๋ฐฉ์งํ ์ ์๋ค.
requestAnimationFrame
๋ฉ์๋๋ ๊ฐ ํ๋ ์์ ์์ ์์ (1000ms/์ฃผ์ฌ์จ, ์ฃผ์ฌ์จ์ ๋ณดํต 60fps)์ ์ ๋๋ฉ์ด์
์ ์
๋ฐ์ดํธํ๋ ์๋ฐ์คํฌ๋ฆฝํธ(์ฝ๋ฐฑ)๊ฐ ์คํ๋๋๋ก ๋ณด์ฅํ๋ค. ์ฃผ๋ก ํ๋ ์ ๋๋ฝ์ ๋ฐฉ์งํ ๋ ์ฌ์ฉํ๋ค.
๋ง์ฝ requestAnimation
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฉด ํ ํ๋ ์์์(ํธ๋ค๋ฌ ์) ํฌ์ปค์ค ํด์ → ํฌ์ปค์ค ์ค์ ์ด ๊ฐ์ด ์ด๋ค์ง๊ธฐ ๋๋ฌธ์ ์
๋ ฅํ ๋ฌธ์๊ฐ ๊ทธ๋๋ก Input์ ๋ฐ์๋๋ค.
๐ก ์ฐธ๊ณ ๋ด์ฉ
Composition
์ด๋ฒคํธ๋ CJK ๋ฌธ์๋ฅผ ์ ๋ ฅํ์ ๋๋ง ํธ์ถ๋๋ค.onKeyDown
์ด๋ฒคํธ๊ฐonCompositionStart
์ด๋ฒคํธ๋ณด๋ค ๋จผ์ ํธ์ถ๋๋ค.
requestAnimation ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ๋
- CJK ๋ฌธ์ ์ ๋ ฅ
onCompositionStart
์ด๋ฒคํธ ํธ๋ค๋ฌ ์คํ- ํฌ์ปค์ค ํด์
requestAnimationFrame
์ฝ๋ฐฑ ์์ฝ
- ํธ๋ค๋ฌ ์ข ๋ฃ
- DOM ์ ๋ฐ์ดํธ → ํฌ์ปค์ค ํด์ ์ํ๋ฏ๋ก Input์ ์๋ฌด๊ฒ๋ ์ ๋ ฅ ์๋จ
- ๋ค์ ํ๋ ์์ผ๋ก ๋์ด๊ฐ์ ์ฝ๋ฐฑ ์คํ → ํฌ์ปค์ค ์ค์
requestAnimation ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ง ์์ ๋
- CJK ๋ฌธ์ ์ ๋ ฅ
onCompositionStart
์ด๋ฒคํธ ํธ๋ค๋ฌ ์คํ- ํฌ์ปค์ค ํด์
- ํฌ์ปค์ค ์ค์
- ํธ๋ค๋ฌ ์ข ๋ฃ
- DOM ์ ๋ฐ์ดํธ → ํฌ์ปค์ค๋ ์ํ๋ฏ๋ก ์ ๋ ฅํ ๋ฌธ์๊ฐ Input์ ๊ทธ๋๋ก ๋ฐ์๋จ
์ ์ฉ ์ฝ๋
uncontrolled ๋ฐฉ์ โผ
const onCompositionStart = (e) => {
e.target.blur(); // ํฌ์ปค์ค ํด์
requestAnimationFrame(() => e.target.focus()); // ํธ๋ค๋ฌ ์ข
๋ฃ ํ ๋ค์ ํ๋ ์์ ๋ค์ ํฌ์ปค์ค
};
return (
<label>
<span className="label">Number Type</span>
<input type="number" onCompositionStart={onCompositionStart} />
</label>
);
controlled ๋ฐฉ์ โผ
const [numberValue, setNumberValue] = useState('');
const onChange = (e) => {
setNumberValue(e.target.value);
};
const onCompositionStart = (e) => {
e.target.blur();
requestAnimationFrame(() => e.target.focus());
};
return (
<label>
<span className="label">Number Type</span>
<input
type="number"
value={numberValue}
onCompositionStart={onCompositionStart}
onChange={onChange}
/>
</label>
);
CodeSandbox
๋ ํผ๋ฐ์ค
- Element: compositionupdate event - Web APIs | MDN
- [JS] input ํ๊ทธ์ ํ๊ธ ์ ๋ ฅ ๋ง๊ธฐ
- [JS] input ์ซ์๋ง ์ ๋ ฅํ๊ธฐ
๊ธ ์์ ์ฌํญ์ ๋ ธ์ ํ์ด์ง์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๋ฐ์๋ฉ๋๋ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[React] ๋ฆฌ์กํธ Profiler ํ๋กํ์ผ๋ฌ์ Hook ์์
[React] ๋ฆฌ์กํธ Profiler ํ๋กํ์ผ๋ฌ์ Hook ์์
2024.05.16 -
[DevTools] iPhone ์์ดํฐ ์ฌํ๋ฆฌ์์ ๋๋ฒ๊น / ์ฝ์ ์ฌ์ฉํ๊ธฐ
[DevTools] iPhone ์์ดํฐ ์ฌํ๋ฆฌ์์ ๋๋ฒ๊น / ์ฝ์ ์ฌ์ฉํ๊ธฐ
2024.05.16 -
[JS] Promise ํ๋ก๋ฏธ์ค ๋ณ๋ ฌ์ฒ๋ฆฌ ๋ฉ์๋ ํบ์๋ณด๊ธฐ
[JS] Promise ํ๋ก๋ฏธ์ค ๋ณ๋ ฌ์ฒ๋ฆฌ ๋ฉ์๋ ํบ์๋ณด๊ธฐ
2024.05.14 -
[JS] ํ์ ์ด๋ฆ์ ๋ฐํํ๋ getType ์ ํธ ํจ์
[JS] ํ์ ์ด๋ฆ์ ๋ฐํํ๋ getType ์ ํธ ํจ์
2024.05.14