Textarea 자동줄 바꿈 - Textarea jadongjul bakkum

<textarea><input type="text">와 달리 여러 줄을 입력할 수 있지만, 줄바꿈에 따라 입력 칸 높이가 자동으로 조절되지는 않는다. 기본적으로는 내용을 두 줄 이상 입력하면 한 줄 높이의 칸에 스크롤이 생긴다. 하지만 내가 원하는 것은 두 줄 이상 입력하면 두 줄을 입력하면 스크롤 없이 칸이 두 줄 높이로 늘어나는 것이었다.

useRef를 이용한다면

textareaRef.current.style.height = textareaRef.current.scrollHeight + "px";

이렇게 높이를 scrollHeight로 지정해주면 줄 수에 따라 칸이 늘어나지만, 백스페이스를 눌러서 내용을 지웠을 때 줄이 줄어들어도 칸은 그에 맞춰 줄어들지 않는다는 문제점이 있었다. 한 번 늘어난 scrollHeight는 글자 수가 줄어들어도 자동으로 줄어들지 않나 보다. 그래서 내용을 지웠을 때 칸 높이가 줄어들게 하는 로직도 필요했다.
또, styled-components에서 state를 높이로 지정할 수 있기 때문에 굳이 ref를 쓰지 않아도 되었다.

textarea 줄바꿈에 따라 높이 자동 조절하는 코드

참고: https://mila00a.tistory.com/37

1. useState

const [textareaHeight, setTextareaHeight] = useState({
  row: 1,
  lineBreak: {},
});

row는 텍스트가 몇 줄인지 나타내는 값이고,
lineBreak는 텍스트에서 줄바꿈이 일어나는 지점을 담는 객체이다.

2. resize 함수

줄바꿈이 일어나면 row에 줄 수를 하나 추가하고, lineBreak에 줄바꿈 일어난 지점 값을 true로 기록한다.
텍스트를 지워서 줄바꿈이 취소되면 row에서 줄 수를 하나 빼고, lineBreak에서 줄바꿈 일어난 지점을 false로 바꾼다.
onInput 이벤트 시 실행한다.

const resizeTextarea = e => {
  const { scrollHeight, clientHeight, value } = e.target;
  
  // 줄바꿈이 일어날 때
  if (scrollHeight > clientHeight) {
    setTextareaHeight(prev => ({
      row: prev.row + 1,
      lineBreak: { ...prev.lineBreak, [value.length - 1]: true },
    }));
  }

  // 텍스트 지워서 줄바꿈 지점에 도달했을 때
  if (textareaHeight.lineBreak[value.length]) {
    setTextareaHeight(prev => ({
      row: prev.row - 1,
      lineBreak: { ...prev.lineBreak, [value.length]: false },
    }));
  }
};

3. 엔터 키 처리 함수

엔터 쳤을 때 row에 한 줄 더해 주고, 엔터 친 지점을 lineBreak에 추가한다.
onKeyDown 이벤트 시 실행한다.

const onKeyEnter = e => {
  if (e.code === 'Enter') {
    setTextareaHeight(prev => ({
      row: prev.row + 1,
      lineBreak: { ...prev.lineBreak, [e.target.value.length]: true },
    }));
  }
};

4. JSX

return (
  <InputText
    autoComplete="off"
    onInput={onInput}
    onKeyDown={onKeyEnter}
    row={textareaHeight.row}
  />
);

5. Styled-Components

const InputText = styled.textarea`
  all: unset;
  display: block;
  width: 100%;
  height: ${({ row, theme }) => +theme.listSize * row + 4}px;
  overflow-wrap: break-word;
  word-break: break-all;
  white-space: pre-wrap;
  resize: none;
`;

줄바꿈 관련 CSS 속성

overflow-wrap: break-word

보통 안 바꿔지는 단어들을 한 줄에서 대신 줄을 바꿀 만한 지점이 없을 시 임의의 지점에서 줄바꿈.

word-break: break-all

오버플로우될 때 문자 단위로 줄바꿈. (keep-all은 한글 단어 단위로 줄바꿈)

white-space: pre-wrap

연속 공백 유지. 한 줄이 너무 길어서 넘칠 경우 자동으로 줄바꿈.

결과 화면

잘 작동하지만, 긴 텍스트를 복사/붙여넣기하거나 여러 줄을 한꺼번에 선택해서 지웠을 때는 자동 높이 조절이 안 된다는 한계가 있다.

Textarea 자동줄 바꿈 - Textarea jadongjul bakkum

react-textarea-autosize

react-textarea-autosize 라이브러리를 사용하면 간편하게 해결할 수 있다.