๋ฐ˜์‘ํ˜•

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์—์„  onKeyDownonKeyPressonChange(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 ์กฐํ•ฉ ๋ฌธ์ž์—์„œ ์กฐํ•ฉ์„ ์‹œ์ž‘ํ–ˆ๊ฑฐ๋‚˜ ์กฐํ•ฉ์ค‘์ผ ๋•Œ ๋ฐ‘์ค„์ด ๋‚˜ํƒ€๋‚œ๋‹ค

์ผ๋‹จ 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 ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ

 

  1. CJK ๋ฌธ์ž ์ž…๋ ฅ
  2. onCompositionStart ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์‹คํ–‰
    1. ํฌ์ปค์Šค ํ•ด์ œ
    2. requestAnimationFrame ์ฝœ๋ฐฑ ์˜ˆ์•ฝ
  3. ํ•ธ๋“ค๋Ÿฌ ์ข…๋ฃŒ
  4. DOM ์—…๋ฐ์ดํŠธ → ํฌ์ปค์Šค ํ•ด์ œ ์ƒํƒœ๋ฏ€๋กœ Input์—” ์•„๋ฌด๊ฒƒ๋„ ์ž…๋ ฅ ์•ˆ๋จ
  5. ๋‹ค์Œ ํ”„๋ ˆ์ž„์œผ๋กœ ๋„˜์–ด๊ฐ€์„œ ์ฝœ๋ฐฑ ์‹คํ–‰ → ํฌ์ปค์Šค ์„ค์ •

 

requestAnimation ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๋•Œ

 

  1. CJK ๋ฌธ์ž ์ž…๋ ฅ
  2. onCompositionStart ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์‹คํ–‰
    1. ํฌ์ปค์Šค ํ•ด์ œ
    2. ํฌ์ปค์Šค ์„ค์ •
  3. ํ•ธ๋“ค๋Ÿฌ ์ข…๋ฃŒ
  4. 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


 

 

๋ ˆํผ๋Ÿฐ์Šค


 


๊ธ€ ์ˆ˜์ •์‚ฌํ•ญ์€ ๋…ธ์…˜ ํŽ˜์ด์ง€์— ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”

 

๋ฐ˜์‘ํ˜•