Coding Archive

[Next.js] ๊ฒŒ์‹œ๊ธ€ ์—…๋กœ๋“œ ํŽ˜์ด์ง€: ๋™์ ์œผ๋กœ element ๋†’์ด ์กฐ์ •ํ•˜๊ธฐ(custom hook, throttle) ๋ณธ๋ฌธ

๐Ÿ’ป Programming/Next.js & project

[Next.js] ๊ฒŒ์‹œ๊ธ€ ์—…๋กœ๋“œ ํŽ˜์ด์ง€: ๋™์ ์œผ๋กœ element ๋†’์ด ์กฐ์ •ํ•˜๊ธฐ(custom hook, throttle)

์ฝ”๋“ฑ์–ด 2024. 11. 22. 18:03

๊ฒŒ์‹œ๊ธ€ ์—…๋กœ๋“œ ํŽ˜์ด์ง€: ๋™์ ์œผ๋กœ element ๋†’์ด ์กฐ์ •ํ•˜๊ธฐ(custom hook, throttle)

 

โš ๏ธ ๋ฐฐ๊ฒฝ

๊ฒŒ์‹œ๊ธ€ ์—…๋กœ๋“œ ํŽ˜์ด์ง€๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด์„œ, ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•˜๊ธฐ ์œ„ํ•œ textarea์˜ ๋†’์ด๊ฐ€ ๊ณ ์ •๋˜์–ด ์žˆ์ง€ ์•Š๊ณ , ์ž…๋ ฅ๋œ ํ…์ŠคํŠธ์˜ ์–‘์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ๋†’์ด๊ฐ€ ํ™•์žฅ๋˜๋Š” UI๊ฐ€ ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

1. ํ…์ŠคํŠธ ๊ธธ์ด์— ๋”ฐ๋ฅธ ๋†’์ด ๋ณ€ํ™”   

์‚ฌ์šฉ์ž๊ฐ€ ์ž‘์„ฑํ•˜๋Š” ํ…์ŠคํŠธ์˜ ์–‘์€ ๊ณ ์ •๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉฐ, ์ž‘์„ฑ ๋„์ค‘์— ๊ณ„์† ์ฆ๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๊ฐ์†Œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ textarea์˜ ๋†’์ด๊ฐ€ ํ…์ŠคํŠธ ์–‘์— ๋งž์ถฐ ์œ ์—ฐํ•˜๊ฒŒ ์กฐ์ •๋˜์–ด์•ผ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX)์ด ๊ฐœ์„ ๋ฉ๋‹ˆ๋‹ค.

2. CSS๋งŒ์œผ๋กœ๋Š” ํ•ด๊ฒฐ๋˜์ง€ ์•Š๋Š” ์Šคํฌ๋กค ๋ฌธ์ œ   

min-height์™€ max-height๋ฅผ CSS๋กœ ์„ค์ •ํ•˜๋ฉด ๋†’์ด๋ฅผ ์ผ์ • ๋ฒ”์œ„ ๋‚ด์—์„œ ๊ณ ์ •ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ํ…์ŠคํŠธ๊ฐ€ ์ฆ๊ฐ€ํ•ด๋„ textarea์˜ ํฌ๊ธฐ๊ฐ€ ์ž๋™์œผ๋กœ ํ™•์žฅ๋˜์ง€ ์•Š๊ณ  ํ…์ŠคํŠธ๊ฐ€ ์ž˜๋ฆฌ๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

 

-> ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด, ๋†’์ด๋ฅผ ํ…์ŠคํŠธ์˜ ์–‘์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ์กฐ์ •ํ•˜๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

์ด ๊ธ€์—์„œ๋Š” onInput ์ด๋ฒคํŠธ๋ฅผ ํ™œ์šฉํ•œ ๊ธฐ๋ณธ์ ์ธ ๊ตฌํ˜„๋ถ€ํ„ฐ, ์ปค์Šคํ…€ ํ›…์œผ๋กœ ๋กœ์ง์„ ์ถ”์ƒํ™”ํ•˜๊ณ  ์„ฑ๋Šฅ ์ด์Šˆ๋ฅผ throttle ํ†ตํ•ด ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•๊นŒ์ง€ ์ƒ์„ธํžˆ ๋‹ค๋ค„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 


๐Ÿ“ ๊ตฌํ˜„ ๋ฐฉํ–ฅ

textarea์˜ ๋†’์ด๋ฅผ ํ…์ŠคํŠธ ์–‘์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ์กฐ์ •ํ•˜๋Š” ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

 

  • ํ…์ŠคํŠธ ์ž…๋ ฅ ์ด๋ฒคํŠธ ํ™œ์šฉ (onInput) : ์‚ฌ์šฉ์ž๊ฐ€ ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•  ๋•Œ๋งˆ๋‹ค textarea์˜ ๋†’์ด๋ฅผ ์กฐ์ •ํ•˜๋„๋ก ์„ค์ •.
  • scrollHeight ์†์„ฑ ํ™œ์šฉ : textarea.scrollHeight๋Š” ํ…์ŠคํŠธ ์ฝ˜ํ…์ธ ์˜ ์ „์ฒด ๋†’์ด๋ฅผ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋Š” ์†์„ฑ์œผ๋กœ ์ด๋ฅผ ํ™œ์šฉํ•ด ์ฝ˜ํ…์ธ  ๋†’์ด์— ๋งž๊ฒŒ textarea์˜ ๋†’์ด๋ฅผ ์กฐ์ •.
  • style.height ์ดˆ๊ธฐํ™” ํ›„ ์žฌ์„ค์ • : ๊ธฐ์กด ๋†’์ด๋ฅผ ์ดˆ๊ธฐํ™”(style.height = 'auto')ํ•˜์—ฌ ํ…์ŠคํŠธ๊ฐ€ ์ค„์–ด๋“œ๋Š” ๊ฒฝ์šฐ์—๋„ ์ ์ ˆํžˆ ๋†’์ด๋ฅผ ๋‹ค์‹œ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฒ˜๋ฆฌ.

 

 

๊ธฐ๋ณธ ๊ตฌํ˜„: onInput๋ฅผ ํ™œ์šฉํ•œ ๋™์  ๋†’์ด ์กฐ์ •

 

textarea์˜ ๋†’์ด๋ฅผ ์ž…๋ ฅ ๋‚ด์šฉ์— ๋งž๊ฒŒ ์ž๋™์œผ๋กœ ์กฐ์ •ํ•˜๋ ค๋ฉด ononInput ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. onInput์€ ์‚ฌ์šฉ์ž๊ฐ€ ํ…์ŠคํŠธ๋ฅผ ์ž…๋ ฅํ•  ๋•Œ๋งˆ๋‹ค ์ด๋ฒคํŠธ๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ๋˜์–ด scrollHeight๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋†’์ด๋ฅผ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

// UploadForm.tsx
import { useRef } from 'react';

interface IUploadFormProps {
  className?: string;
}

export default function UploadForm({ className }: IUploadFormProps) {
  const textareaRef = useRef<HTMLTextAreaElement>(null);

  const handleTextChange = () => {
    const textarea = textareaRef.current;
    if (textarea) {
      textarea.style.height = 'auto'; // ๋†’์ด ์ดˆ๊ธฐํ™”
      textarea.style.height = `${textarea.scrollHeight}px`; // ์ž…๋ ฅ ๋‚ด์šฉ์— ๋”ฐ๋ผ ๋†’์ด ์กฐ์ •
    }
  };

  return (
    <textarea
      ref={textareaRef}
      onInput={handleTextChange}
      rows={1}
      placeholder="๋‚ด์šฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”"
      className="resize-none w-full"
    />
  );
}
  • textarea์˜ ๋†’์ด๋ฅผ scrollHeight ๊ฐ’์œผ๋กœ ์„ค์ •ํ•˜์—ฌ ์ž…๋ ฅ ๋‚ด์šฉ์— ๋”ฐ๋ผ ๋†’์ด๊ฐ€ ์กฐ์ •๋ฉ๋‹ˆ๋‹ค.(textarea.scrollHeight: ์ฝ˜ํ…์ธ  ์ „์ฒด ๋†’์ด) 
  • textarea.style.height = 'auto'๋Š” ๋†’์ด๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜์—ฌ ๊ธฐ์กด ๋†’์ด๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ๋†’์ด ์กฐ์ •์˜ ๋ถ€๋“œ๋Ÿฌ์›€์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ์ตœ์†Œ ๋†’์ด๋Š” rows={1}๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

 


๐Ÿ› ๏ธ ๋ฆฌํŒฉํ† ๋ง: ์ปค์Šคํ…€ ํ›…์œผ๋กœ ๋กœ์ง ์ถ”์ƒํ™”

๋†’์ด ์ž๋™ ์กฐ์ •์„ ์œ„ํ•œ ๋กœ์ง์„ ๋ณ„๋„์˜ ํ›…(Hook)์œผ๋กœ ์ถ”์ƒํ™”ํ•˜์—ฌ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์˜ ๋‹ค์–‘ํ•œ DOM ์š”์†Œ์—์„œ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ถ„๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค. 

useAutoResizeHeight ํ›…์€ ํ•ด๋‹น ์š”์†Œ์— ๋Œ€ํ•œ ์ฐธ์กฐ์™€ ํ•จ๊ป˜ ์ž…๋ ฅ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋†’์ด๋ฅผ ์ž๋™์œผ๋กœ ์กฐ์ •ํ•˜๋Š” ๋กœ์ง์„ ์บก์Аํ™”ํ•ฉ๋‹ˆ๋‹ค.

 

 

useAutoResizeHeight ์ปค์Šคํ…€ ํ›… ์ œ์ž‘
// useAutoResizeHeight.ts
import { useRef, useCallback } from 'react';

export function useAutoResizeHeight<T extends HTMLElement>() {
  const elementRef = useRef<T>(null);

  const adjustHeight = useCallback(() => {
    const element = elementRef.current;
    if (element) {
      element.style.height = 'auto'; // ๋†’์ด ์ดˆ๊ธฐํ™”
      element.style.height = `${element.scrollHeight}px`; // ์ฝ˜ํ…์ธ ์— ๋งž๊ฒŒ ๋†’์ด ์กฐ์ •
    }
  }, []);

  return { elementRef, resizeToFitContent };
}
  • ์ œ๋„ค๋ฆญ ์‚ฌ์šฉ (T extends HTMLElement): textarea๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋‹ค๋ฅธ DOM ์š”์†Œ์—์„œ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • useCallback์œผ๋กœ ์ตœ์ ํ™”: ๋™์ผํ•œ ํ•จ์ˆ˜๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์ƒ์„ฑํ•˜์ง€ ์•Š๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

 

 

useAutoResizeHeight ํ›…์„ UploadForm์—์„œ ํ™œ์šฉ

 

UploadForm์—์„œ useAutoResizeTextarea ํ›…์„ ํ˜ธ์ถœํ•˜์—ฌ elementRef์™€ resizeToFitContent์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

// UploadForm.tsx
function UploadForm() {
  const { elementRef, resizeToFitContent } = useAutoResizeHeight<HTMLTextAreaElement>();

  return (
    <textarea
      className="scrollbar-hidden w-full resize-none text-14px focus:outline-none"
      placeholder="๊ฒŒ์‹œ๊ธ€ ์ž…๋ ฅํ•˜๊ธฐ..."
      ref={elementRef}
      onInput={resizeToFitContent}
      rows={1}
    />
  );
}

 

๊ฐœ์„  ํ›„ ํšจ๊ณผ

 

  1. UploadForm์—์„œ UI ๋กœ์ง๋งŒ ์ฒ˜๋ฆฌํ•˜๊ณ , ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ํ›…์œผ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ฐ€๋…์„ฑ์ด ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  2. ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์—์„œ๋„ useAutoResizeHeight๋ฅผ ํ˜ธ์ถœํ•ด ๋™์ผํ•œ ๋™์ž‘์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์–ด ์žฌ์‚ฌ์šฉ์„ฑ์ด ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ DOM ์š”์†Œ์˜ ํƒ€์ž…์„ ์ œ๋„ค๋ฆญ์œผ๋กœ ๋ฐ›์•„ ํƒ€์ž… ์˜ค๋ฅ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ณ , ๋‹ค์–‘ํ•œ UI ์ปดํฌ๋„ŒํŠธ์—์„œ ํ™œ์šฉ ๊ฐ€๋Šฅํ•˜๋„๋ก ์ถ”์ƒํ™”ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  3. useAutoResizeHeight๊ฐ€ ๋…๋ฆฝ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋ฏ€๋กœ, ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•  ๋•Œ ํ•ด๋‹น ํ›…๋งŒ ์ˆ˜์ •ํ•˜๋ฉด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

 

 

but, ์ด ๋ฐฉ์‹์€ ๊ฐ„๋‹จํ•˜๋ฉด์„œ๋„ ์œ ์šฉํ•˜์ง€๋งŒ ์ž…๋ ฅ์ด ๋งŽ์•„์งˆ ๊ฒฝ์šฐ ์„ฑ๋Šฅ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด ์„ฑ๋Šฅ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.

 


โœจ ์„ฑ๋Šฅ ์ตœ์ ํ™”:  throttle ํ™œ์šฉ

onInput ์ด๋ฒคํŠธ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•  ๋•Œ๋งˆ๋‹ค ํŠธ๋ฆฌ๊ฑฐ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ž…๋ ฅ ์†๋„๊ฐ€ ๋น ๋ฅด๊ฑฐ๋‚˜ ๋งŽ์•„์ง€๊ฒŒ ๋˜๋ฉด ์„ฑ๋Šฅ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด throttle์„ ์ ์šฉํ•ด ํ˜ธ์ถœ ๋นˆ๋„๋ฅผ ์ œํ•œํ•˜๋„๋ก ์ˆ˜์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

 

throttle์ด๋ž€?

 

throttle์€ ์ฃผ๊ธฐ์ ์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ์ผ์ • ๊ฐ„๊ฒฉ์œผ๋กœ ์ œํ•œํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๋Š” ์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ธฐ๋ฒ•์ž…๋‹ˆ๋‹ค.

์ฆ‰, ์ด๋ฒคํŠธ๊ฐ€ ์•„๋ฌด๋ฆฌ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋”๋ผ๋„ ์„ค์ •๋œ ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ ๋‚ด์—์„œ๋Š” ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

 

 

throttle์˜ ๋™์ž‘ ์›๋ฆฌ

 

  1. ์ง€์†์ ์œผ๋กœ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒ : ์‚ฌ์šฉ์ž๊ฐ€ ๋น ๋ฅด๊ฒŒ ํƒ€์ดํ•‘ํ•˜๊ฑฐ๋‚˜ ์Šคํฌ๋กคํ•˜๋Š” ๊ฒฝ์šฐ์ฒ˜๋Ÿผ, ์ด๋ฒคํŠธ๊ฐ€ ๋งค์šฐ ์งง์€ ๊ฐ„๊ฒฉ์œผ๋กœ ๋ฐ˜๋ณต ๋ฐœ์ƒ.
  2. ์ผ์ •ํ•œ ๊ฐ„๊ฒฉ์œผ๋กœ ์ด๋ฒคํŠธ ์‹คํ–‰ : ์„ค์ •๋œ ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ์— ๋”ฐ๋ผ ์ด๋ฒคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ณ , ๊ทธ ์‚ฌ์ด์˜ ์ด๋ฒคํŠธ๋Š” ๋ฌด์‹œ.

-> ์ผ์ •ํ•œ ์ฃผ๊ธฐ๋กœ ์ด๋ฒคํŠธ๊ฐ€ ์‹คํ–‰๋˜๋ฏ€๋กœ, ๊ณผ๋„ํ•œ ์—ฐ์‚ฐ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”.

 

 

throttle์„ ์ ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ

 

throttle์€ ์ด๋ฒคํŠธ๊ฐ€ ์ง€์†์ ์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” ์ƒํ™ฉ์—์„œ ์ผ์ • ๊ฐ„๊ฒฉ์œผ๋กœ๋งŒ ์‹คํ–‰์ด ํ•„์š”ํ•  ๋•Œ ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

  • ์Šคํฌ๋กค ์ด๋ฒคํŠธ : ์‚ฌ์šฉ์ž๊ฐ€ ํŽ˜์ด์ง€๋ฅผ ์Šคํฌ๋กคํ•  ๋•Œ ์ด๋ฒคํŠธ๊ฐ€ ์ˆ˜๋ฐฑ ๋ฒˆ ํ˜ธ์ถœ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์Šคํฌ๋กค ์œ„์น˜๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ฑฐ๋‚˜ ํŠน์ • ์œ„์น˜์— ๋„๋‹ฌํ–ˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋กœ์ง์— ์ ์šฉ.
  • ์œˆ๋„์šฐ ๋ฆฌ์‚ฌ์ด์ฆˆ ์ด๋ฒคํŠธ : ๋ธŒ๋ผ์šฐ์ € ์ฐฝ ํฌ๊ธฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ๋ ˆ์ด์•„์›ƒ์„ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋กœ์ง์— ์ง€๋‚˜์น˜๊ฒŒ ๋นˆ๋ฒˆํ•œ ํ˜ธ์ถœ์„ ์ œํ•œํ•˜์—ฌ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”.
  • ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ : ์‚ฌ์šฉ์ž๊ฐ€ ๋“œ๋ž˜๊ทธํ•  ๋•Œ ๋งˆ์šฐ์Šค ์œ„์น˜๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋กœ์ง์— ์ ์šฉํ•ด UI ์—…๋ฐ์ดํŠธ ๋นˆ๋„๋ฅผ ์ œํ•œ.
  • ์‹ค์‹œ๊ฐ„ ์ž…๋ ฅ ์ฒ˜๋ฆฌ : ์ž…๋ ฅ ์ค‘์— ์• ๋‹ˆ๋ฉ”์ด์…˜์ด๋‚˜ ํ™”๋ฉด ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ(textarea์˜ ๋†’์ด๋ฅผ ์กฐ์ •ํ•˜๊ฑฐ๋‚˜, ์ž…๋ ฅ ๋‚ด์šฉ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ๋ฏธ๋ฆฌ๋ณด๊ธฐ)์— ์ ์šฉ.

 

throttle vs debounce

 

throttle์€์ด๋ฒคํŠธ ๋ฐœ์ƒ ๊ฐ„๊ฒฉ์„ ์ œํ•œ, debounce๋Š” ์ด๋ฒคํŠธ ์‹คํ–‰์„ ์ง€์—ฐํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๊ธฐ๋ฒ•์˜ ์ฐจ์ด๋ฅผ ์ดํ•ดํ•˜๋ฉด ์ ์ ˆํ•œ ์ƒํ™ฉ์—์„œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ตฌ๋ถ„ throttle debounce
๋™์ž‘ ๋ฐฉ์‹ ์ง€์ •๋œ ๊ฐ„๊ฒฉ๋งˆ๋‹ค ์ด๋ฒคํŠธ ์‹คํ–‰ ์ด๋ฒคํŠธ ๋ฐœ์ƒ ํ›„ ์ง€์ •๋œ ์‹œ๊ฐ„ ๋™์•ˆ ๋Œ€๊ธฐ ํ›„ ์‹คํ–‰
์‚ฌ์šฉ ์‚ฌ๋ก€ ์Šคํฌ๋กค, ๋ฆฌ์‚ฌ์ด์ฆˆ, ๋“œ๋ž˜๊ทธ, ์‹ค์‹œ๊ฐ„ UI ์—…๋ฐ์ดํŠธ ๊ฒ€์ƒ‰ ์ž๋™์™„์„ฑ, API ์š”์ฒญ, ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅ์„ ๋ฉˆ์ท„์„ ๋•Œ ์‹คํ–‰
์ด๋ฒคํŠธ ์‹คํ–‰ ์ง€์†์ ์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ ์ค‘ ์ผ์ • ๊ฐ„๊ฒฉ์œผ๋กœ ์‹คํ–‰ ๋งˆ์ง€๋ง‰ ์ด๋ฒคํŠธ ํ›„ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰
์ ํ•ฉํ•œ ๊ฒฝ์šฐ ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ๊ฐ€ ๋๋‚œ ํ›„ ์ž‘์—…์ด ํ•„์š”ํ•  ๊ฒฝ์šฐ

 

 

Lodash throttle ์ ์šฉ

 

throttle์„ ์ ์šฉํ•˜์—ฌ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋”๋ผ๋„, ๋งˆ์ง€๋ง‰ ์‹คํ–‰ ํ›„ ์ตœ์†Œ wait ์‹œ๊ฐ„์ด ์ง€๋‚˜์•ผ ๋‹ค์‹œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

throttle์€ ๋‚ด๋ถ€ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๋ฏ€๋กœ, React๊ฐ€ ์ปดํฌ๋„ŒํŠธ ์–ธ๋งˆ์šดํŠธ ์‹œ ์ด๋ฅผ ํด๋ฆฐ์—…ํ•ด์•ผ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

1. Lodash throttle์˜ ๋‚ด๋ถ€ ๋™์ž‘

Lodash์˜ throttle์€ ์ด๋ฒคํŠธ ์‹คํ–‰์„ ์ œํ•œํ•˜๊ธฐ ์œ„ํ•ด ๋‚ด๋ถ€ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

  • ๋งˆ์ง€๋ง‰์œผ๋กœ ์‹คํ–‰๋œ ์‹œ๊ฐ„(lastRan)

  • ํƒ€์ด๋จธ(timeout): ์•„์ง ์‹คํ–‰๋˜์ง€ ์•Š์€ ๋Œ€๊ธฐ ์ค‘์ธ ํ•จ์ˆ˜

์ด๋Ÿฌํ•œ ์ƒํƒœ๋Š” throttle๋กœ ์ƒ์„ฑ๋œ ํ•จ์ˆ˜๊ฐ€ ๊ณ„์† ์œ ์ง€๋˜๋ฉฐ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

 

2. React ์ปดํฌ๋„ŒํŠธ์˜ ์ƒ๋ช… ์ฃผ๊ธฐ

• React ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ธ๋งˆ์šดํŠธ๋  ๋•Œ, throttle ํ•จ์ˆ˜ ๋‚ด๋ถ€์˜ ํƒ€์ด๋จธ(timeout)๊ฐ€ ์—ฌ์ „ํžˆ ๋™์ž‘ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

• ์˜ˆ๋ฅผ ๋“ค์–ด textarea์˜ ์ž…๋ ฅ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒ ์ค‘์ธ๋ฐ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ธ๋งˆ์šดํŠธ๋˜๋ฉด, ๋Œ€๊ธฐ ์ค‘์ด๋˜ throttle ํ•จ์ˆ˜๊ฐ€ ์—ฌ์ „ํžˆ ์‹คํ–‰๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

3. Cleanup์˜ ํ•„์š”์„ฑ

• ์ปดํฌ๋„ŒํŠธ ์–ธ๋งˆ์šดํŠธ ์‹œ throttle ํ•จ์ˆ˜์˜ ํƒ€์ด๋จธ์™€ ์ƒํƒœ๋ฅผ ์ •๋ฆฌํ•ด ์ฃผ์–ด์•ผ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜์™€ ๋ถˆํ•„์š”ํ•œ ์—ฐ์‚ฐ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

• Lodash์˜ throttle์€ cancel() ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•˜์—ฌ ํƒ€์ด๋จธ์™€ ๋‚ด๋ถ€ ์ƒํƒœ๋ฅผ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import { useRef, useCallback } from 'react';
import { throttle } from 'lodash';

export function useAutoResizeHeight<T extends HTMLElement>(throttleTime = 100) {
  const elementRef = useRef<T|null>(null);

  const resizeToFitContent = useCallback(() => {
    const element = elementRef.current;
    if (element) {
      element.style.height = 'auto'; 
      element.style.height = `${element.scrollHeight}px`; 
    }
  }, []);

  // Throttled function ์ƒ์„ฑ
  const throttledResize = useMemo(() => throttle(resizeToFitContent, throttleTime), [
    resizeToFitContent,
    throttleTime,
  ]);

  // Cleanup: Throttle ํ•จ์ˆ˜ ์ทจ์†Œ
  useEffect(() => {
    return () => {
      throttledResize.cancel(); // Throttled ํ•จ์ˆ˜ ์ •๋ฆฌ
    };
  }, [throttledResize]);

  return { elementRef, throttledResize };

_throttle ์บ์‹ฑ

• useMemo๋กœ throttle๋กœ ์ƒ์„ฑ๋œ ํ•จ์ˆ˜๋ฅผ ์บ์‹ฑํ•ด ๋งค๋ฒˆ ์ƒˆ๋กœ ์ƒ์„ฑ๋˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

• ๋ฉ”๋ชจ์ด์ œ์ด์…˜์„ ํ†ตํ•ด ๋™์ผํ•œ ์˜์กด์„ฑ ๋ฐฐ์—ด(deps)์„ ๊ฐ€์ง€๋Š” ๋™์•ˆ ๊ธฐ์กด์˜ throttle ํ•จ์ˆ˜๊ฐ€ ์žฌ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

• ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์–ธ๋งˆ์šดํŠธ๋˜๊ธฐ ์ „์—๋Š” throttle ํ•จ์ˆ˜์˜ ๋‚ด๋ถ€ ์ƒํƒœ(์˜ˆ: ํƒ€์ด๋จธ)๊ฐ€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

 

_Cleanup ์ฒ˜๋ฆฌ

• useEffect Cleanup์„ ํ™œ์šฉํ•ด throttledResize.cancel()์„ ํ˜ธ์ถœํ•˜์—ฌ ์–ธ๋งˆ์šดํŠธ ์‹œ throttle์˜ ํƒ€์ด๋จธ(timeout)์™€ ๋‚ด๋ถ€ ์ƒํƒœ๋ฅผ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

• ๋ฉ”๋ชจ์ด์ œ์ด์…˜์€ ์œ ์ง€ํ•˜๋ฉด์„œ๋„ ์ปดํฌ๋„ŒํŠธ ์ƒ๋ช… ์ฃผ๊ธฐ์— ๋”ฐ๋ผ throttle์„ ์™„์ „ํžˆ ์ข…๋ฃŒํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

 

 

throttle ์ ์šฉ ํ›„ ํšจ๊ณผ
  1. ๊ณผ๋„ํ•œ ์ด๋ฒคํŠธ ํ˜ธ์ถœ์ด ์ œํ•œ๋˜์–ด ๋ถˆํ•„์š”ํ•œ DOM ์กฐ์ž‘์„ ๋ฐฉ์ง€ํ•˜์—ฌ ์„ฑ๋Šฅ์„ ๋ณด๋‹ค ํšจ์œจ์ ์œผ๋กœ ๊ฐœ์„ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  2. ์‚ฌ์šฉ์ž ์ž…๋ ฅ ์†๋„๊ฐ€ ๋นจ๋ผ๋„ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋™์ž‘ํ•˜์—ฌ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  3. ๋น ๋ฅด๊ฒŒ ํƒ€์ดํ•‘ํ•  ๋•Œ๋„ ์ง€์ •๋œ ๊ฐ„๊ฒฉ์œผ๋กœ๋งŒ ์ด๋ฒคํŠธ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ CPU ๋ถ€ํ•˜๋ฅผ ์ค„์—ฌ ๊ฒฐ๊ณผ์ ์œผ๋กœ ํšจ์œจ์ ์œผ๋กœ ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  4. throttle์„ ํ™œ์šฉํ•˜์—ฌ ๋ณต์žกํ•œ ๋กœ์ง ์—†์ด ๊ฐ„๋‹จํžˆ ์ตœ์ ํ™” ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

 

๋งค๋ฒˆ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋งˆ๋‹ค ๋А๋ผ๋Š” ๊ฑฐ์ง€๋งŒ ๋‹จ์ˆœํžˆ ๋™์ž‘ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์—์„œ ๊ทธ์น˜์ง€ ์•Š๊ณ , ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ์œ ์ง€๋ณด์ˆ˜์„ฑ๊นŒ์ง€ ๊ณ ๋ คํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ์ค‘์š”์„ฑ์„ ์‹ค๊ฐํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

textarea์˜ ๋™์  ๋†’์ด ์กฐ์ •์€ ์ž‘์€ UX ๊ฐœ์„ ์ด์ง€๋งŒ, ์ปค์Šคํ…€ ํ›…๊ณผ ์ตœ์ ํ™”๋ฅผ ํ†ตํ•ด ํ™•์žฅ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋™์‹œ์— ๊ฐ–์ถ˜ ์†”๋ฃจ์…˜์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. 

 

Comments