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

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

## 1. JSX 개념 바로잡기

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

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

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

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

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

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

* 위 코드에서 내가 html 영역이라고 생각했던 부분이 바로 jsx 영역이다.
* **즉, `jsx`는 html처럼 생겼지만 html이 아니라 `자바스크립트`다.**
  * 자바스크립트 확장판이 딱 적절한 말이다.
* 리액트 개발을 쉽게 하기 위해서, HTML 과 비슷한 문법인 jsx 작성을 하면 이를 `React.createElement` 를 사용하는 자바스크립트 형태로 변환해왔던 것이다.

`jsx` 작성 규칙 등 더 자세한 내용은 [벨로퍼트님 블로그](https://velopert.com/3626)를 참고하면 좋다.

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

![image](https://user-images.githubusercontent.com/37580034/89106209-d560dd00-d462-11ea-960a-98eeb0119dbb.png)

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

```markup
<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개로 줄여줬다.

```javascript
<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

* [Array() 생성자](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/Array)
* [fill() 메서드](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/fill)\\

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

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

```javascript
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()을 사용하는 더 좋은 방식도 존재한다. 이곳 [벨로퍼트님 블로그](https://velopert.com/1896) 참고!
