[React/Next.js/React-hook-form] 입력값에따라 동적으로 너비가 조정되는 input 컴포넌트 만들기

퀴즈를 제작하는 폼에서 선택지를 입력하는 컴포넌트를 개발하면서, 사용자가 입력하는 텍스트 길이에따라 input의 너비가 자동으로 조정되는 기능이 필요했습니다. 그 과정에대한 간단한 기록입니다!

 

시도 1 - css ch 단위 이용

처음에는 단순히 CSS의 ch 단위를 활용했습니다. ch 단위는 현재 사용중인 폰트의 '0' 문자 너비를 기준으로 하는 단위로, 문자 개수에 비례한 너비를 설정하기위해 활용하고 현재 폼에 react-hook-form을 사용하고 있었기 때문에 watch를 사용해 입력된 값을 확인했습니다.

 

const inputValue = watch(`problem.item`);

// 텍스트 길이에 따라 너비 계산
const inputWidth = Math.max(15, (inputValue?.length || 0) + 3);

return (
  <input
    ...react-hook-form 관련 설정
    style={{ 
      width: `${inputWidth}ch`, 
      minWidth: '150px',
      maxWidth: '100%'
    }}
  />
);

 

문제 - 너비 차이 누적으로 예상과 다른 너비 설정

하지만 실제로 사용해보니 입력되는 문자의 너비와 '0' 너비의 차이가 누적되면서 실제 문자 길이와 차이가 생겼습니다.

한글은 실제 너비보다 input이 좁아서 가려지고 영어 소문자는 실제 너비보다 input이 더 넓어서 점점 여백이 커지고..

 

시도 2 - 별도 span 태그 활용

이 문제를 해결하기 위해 span 태그에 텍스트를 입력하고 그 너비를 input에 적용하는 방법을 활용했습니다.

const spanRef = useRef<HTMLSpanElement>(null);
const [inputWidth, setInputWidth] = useState(140);  // 기본 너비

const inputValue = watch(`problems.${problemIndex}.item.${itemIndex}.text`) || '';

// 실제 텍스트 너비 측정 및 input 너비 갱신
useEffect(() => {
  if (spanRef.current) {
    const textWidth = spanRef.current.getBoundingClientRect().width;
    const calculatedWidth = Math.max(140, textWidth + 20); // 최소 140px, 여유분 20px
    setInputWidth(calculatedWidth);
  }
}, [inputValue]); // 입력값 변경시마다 계산

return (
  <>
    {/* 숨겨진 너비 측정용 span */}
    <span
      ref={spanRef}
      className='(input과 동일한 스타일 적용) absolute pointer-events-none opacity-0 whitespace-pre'
    >
      {inputValue || '선지를 입력하세요.'}
    </span>

    {/* 실제 input */}
    <input
      placeholder='선지를 입력하세요.'
      className='...생략'
      {...register(`problems.${problemIndex}.item.${itemIndex}.text`)}
      style={{
        width: `${inputWidth}px`, // 측정된 실제 너비 적용
        maxWidth: '100%', // 컨테이너 너비 제한
      }}
    />
  </>
);

 

한글, 영어 등 실제로 입력된 문자에 관계없이 실제 너비가 잘 반영됩니다!

주요 사항

- input과 동일하게 폰트, 크기, 여백 등 span 스타일링을 설정

- span 태그 스타일에 whiteSpace: pre로 설정해서 공백 문자도 정확히 측정하도록 설정

- 입력값 변경시마다 너비를 계산할 수 있도록 watch 활용

- 입력값 변경시에만 계산할 수 있도록 useEffect 의존성배열 활용

- 커서 공간 + 약간의 여백 추가

 

마무리

한글, 영어뿐만 아니라 다른 외국어도 많이 입력될 수 있는 서비스여서 사용자 경험에 중요한 개선이었다고 생각됩니다

끝!