tags:

  • study
  • react
  • js

React에서 줄바꿈이 다르게 동작하는 이유

목표: JSX Attribute Literal(JSX 문자열), JS String Literal(JS 문자열), Template Literal의 차이를 명확하게 이해하기

React에서 문자열을 props로 전달하는 방식은 여러 가지가 있습니다. 보통은 “text”, {“text”}, `text` 모두 동일하게 동작할 것 같지만, 실제로는 줄바꿈 처리나 whitespace 스타일링 (Tailwind whitespace-pre-line)에서 서로 다른 결과를 만듭니다.

프로젝트 개발 중 사용자 입력을 줄바꿈 포함 문자열로 보여주기 위해 whitespace-pre-line을 사용했습니다. 그런데 문자열을 넘기는 방식에 따라 줄바꿈이 되기도 하고, 되지 않기도 했습니다.

이 글에서는 React에서 문자열을 어떤 방식으로 전달하면 줄바꿈이 어떻게 처리되는지, 그리고 동일해 보이는 문자열이 왜 서로 다른 결과를 만드는지를 정리합니다.

1. 문제 상황: 같은 문자열인데 줄바꿈이 안 된다

아래처럼 텍스트를 출력하는 컴포넌트를 만들었습니다.

function TextBlock({ text }) {
  return <div className="whitespace-pre-line">{text}</div>;
}

그리고 다음 세가지 방식으로 문자열을 넘겼습니다.

// 1
<TextBlock text="lorem\nipsum" />

// 2
<TextBlock text={"lorem\nipsum"} />

// 3
<TextBlock text={`lorem\nipsum`} />

제 의도는 세 경우 모두 lorem과 ipsum 으로 줄바꿈되어 출력되는 것이었습니다. 하지만 결과는 달랐습니다.

  1. 줄바꿈 없음
  2. 줄바꿈 (의도대로 동작)
  3. 줄바꿈 (의도대로 동작)

2. 원인: JSX 문자열과 JS 문자열은 완전히 다르다

차이를 이해하려면 JSX 문자열이 어떻게 해석되는지 이해해야 합니다.

2.1 "lorem\nipsum”는 JS 문자열이 아니다

JSX 코드에서 text=“…” 형태로 문자열을 넣으면, 이는 JS 문법이 아니라 HTML/XML 스타일 속성 문자열입니다.

  • JSX 문자열은 HTML attribute 처럼 파싱된다.
  • HTML attribute에서는 \n이 escape sequence가 아니다.
  • 따라서 \n은 역슬래시 문자 + n 문자로 들어간다.

2.2 {"lorem\nipsum”}는 JS 문자열이다

중괄호 {}로 감싸면, JSX는 해당 영역을 JS 문자열으로 해석합니다. 즉,

  • {“…”} 내부는 JavaScript 엔진이 처리한다.
  • {"lorem\nipsum”}는 JS 문자열이므로 \n은 실제 줄바꿈 문자로 변환된다.

2.3 `lorem\nipsum`도 JS 문자열이다

템플릿 리터럴은 더 명확합니다.

  • 백틱 문자열 또한 JS 표현식 내부에서 JS 엔진이 평가한다.
  • \n은 실제 줄바꿈 문자로 변환된다.

3. Tailwind whitespace-pre-line과의 상호작용

Tailwind의 whitespace-pre-line은 CSS의 white-space: pre-line; 을 의미합니다.

white-space: pre-line;

이 속성은 다음 규칙을 갖습니다.

  • 연속 공백: 1개만 표시
  • 텍스트 wrapping: 허용
  • 줄바꿈 문자(\n): 새로운 줄로 렌더링 따라서 CSS는 텍스트 안에 실제 줄바꿈 문자(U+000A) 가 있어야만 줄바꿈을 생성할 수 있습니다. 그런데 1번 방식에서는 줄바꿈 문자가 없고, 2번과 3번에서는 줄바꿈 문자가 있습니다. 이 차이가 최종적으로 다른 렌더링 결과를 만듭니다.

4. 언제 어떤 방식을 사용해야 할까?

문자열에 \n을 사용해야 한다면 -> 무조건 JS 표현식

아래 두 방식 중 선택하면 됩니다.

text={"lorem\nipsum"}
text={`lorem\nipsum`}

사용자 입력(Markdown, textarea 등)을 그대로 보여줄 때

사용자의 입력에는 실제 줄바꿈 문자가 포함됩니다. 따라서 JSX attribute일 필요가 없습니다.

<TextBlock text={userInput} />

문자열이 길어 여러 줄이 필요할 때

템플릿 리터럴을 사용하는 것이 가독성에 좋습니다.

text={`
안녕하세요
여기는 여러 줄 텍스트입니다.
`}