5. [React 리팩토링] JSX로 HTML 렌더링하기

JSX에서 <br/>태그를 사용하면, 적용되지 않은 채 문자열 그대로 렌더링 된다. React는 무조건 텍스트형태로만 페이지를 렌더링하도록 설계되어있기 때문이다.

1. JSX 개념 바로잡기

나는 지금까지 리액트 컴포넌트가 자바스크립트 부분과, html 부분으로 구성되어 있다고 생각해왔었다.

export default function App() {
    //return 바깥 부분이 js 영역
    return (
    //return 내부가 html 영역
      <div>
      <input type="text">
    </div>
    );
}

그래서 컴포넌트 작성과 관련된 문제를 해결하기 위해 구글링할 때마다 헷갈렸나보다.

왜 리액트 파일은 .js 파일인데 자바스크립트도 쓰이고 html도 쓰이는건지, 어떤 경우에 리액트에서 html에 자바스크립트 문법을 사용할 수 있는건지, 또 왜 어쩔때는 안되는건지 등등. 리액트에서 쓰이는 문법을 정확히 이해하지 못하고 있었기 때문에 구글링도 중구난망 엉망진창 비효율적일 수밖에 없었다.

답변의 키워드는 항상 jsx였고, 그래서 jsx를 막연히 리액트에서 쓰이는 자바스크립트라고 이해하고 있었다. 이 말은 반은 맞고 반은 틀린 말이다.

이제 내 잘못된 개념을 바로잡자면,

  • 위 코드에서 내가 html 영역이라고 생각했던 부분이 바로 jsx 영역이다.

  • 즉, jsx는 html처럼 생겼지만 html이 아니라 자바스크립트다.

    • 자바스크립트 확장판이 딱 적절한 말이다.

  • 리액트 개발을 쉽게 하기 위해서, HTML 과 비슷한 문법인 jsx 작성을 하면 이를 React.createElement 를 사용하는 자바스크립트 형태로 변환해왔던 것이다.

jsx 작성 규칙 등 더 자세한 내용은 벨로퍼트님 블로그를 참고하면 좋다.

2.반복문으로 HTML 태그 렌더링하기

예발자닷컴연간 캘린더를 보면 지금은 월 단위로만 구분선이 존재하는데, 원래는 각 월마다 4개씩 선이 있어서 주단위 까지 구분지을 수 있도록 디자인했었다. 그리고 구분선은 span 태그를 쓴 뒤 보더라인에 색을 넣는 방식으로 만들었었다. 아래 코드처럼.

<div className={classNames({[styles.gantt__row]: true, [styles.gantt__row__months]: true})}>
  <div className={styles.gantt__row__first}></div>
  <span>1월</span><span>2월</span><span>3월</span>
  <span>4월</span><span>5월</span><span>6월</span>
  <span>7월</span><span>8월</span><span>9월</span>
  <span>10월</span><span>11월</span><span>12월</span>
</div>
<div className={classNames({[styles.gantt__row]: true, [styles.gantt__row__lines]: true})}>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
  <span></span><span></span><span></span>
</div>

총 54개의 span 을 넣었고, 당연히 리팩토링 1순위였다. 자바스크립트 변수에 span태그를 넣고 string 더하듯 for문을 써서 하나씩 더해보고 싶었는데 틀린 방법인건지, 잘 되지 않아서 map()을 사용했다.

Array().fill() 메서드로 빈 배열을 만들어주고, 그 크기만큼 특정 값을 리턴하는 map()을 사용해 span태그를 리턴시켰다. 이제 주 단위 구분선도 사용하지 않으니 span 태그도 54개에서 12개로 줄여줬다.

<div className={classNames({[styles.gantt__row]: true, [styles.gantt__row__months]: true})}>
    <div className={styles.gantt__row__first}></div>
    {Array(12).fill('').map((v, idx) => <span>{idx+1}월</span>)}
</div>
<div className={classNames({[styles.gantt__row]: true, [styles.gantt__row__lines]: true})}>
    {Array(12).fill('').map((v, idx) => <span></span>)}
</div>

새로 배운 javascript

3. HTML <br/> 태그 적용시켜 렌더링하기

연간 캘린더 더미데이터에는 아래처럼 줄바꿈 태그를 사용하는 데이터가 존재한다.

period: "1차 코딩테스트 : 7/04 <br/> 2차 코딩테스트 : 7/20",

문제는 이 period key값을 jsx에서 사용하면 br 태그가 먹히지 않고 문자열 그대로 렌더링 된다. React에서는 cross-site scripting (XSS) 공격을 막기 위해 무조건 텍스트형태로만 페이지를 렌더링하도록 설계되어 있기 때문이다.

나름 보안 취약점을 해결하기 위한 방안인데, 이걸 무시하고 html을 렌더링 하는 방법이 있다. 바로 dangerouslySetInnerHTML 이다.

<span className={styles.calendar_entry__date}>data.period</span>

위 코드를 아래처럼 바꿔주면 된다.

<span className={styles.calendar_entry__date} dangerouslySetInnerHTML={{__html: data.period}}></span>

보안이 걱정된다면 map()을 사용하는 더 좋은 방식도 존재한다. 이곳 벨로퍼트님 블로그 참고!

Last updated