나는 지금까지 리액트 컴포넌트가 자바스크립트
부분과, html
부분으로 구성되어 있다고 생각해왔었다.
export default function App() {
//return 바깥 부분이 js 영역
return (
//return 내부가 html 영역
<div>
<input type="text">
</div>
);
}
그래서 컴포넌트 작성과 관련된 문제를 해결하기 위해 구글링할 때마다 헷갈렸나보다.
왜 리액트 파일은 .js 파일인데 자바스크립트도 쓰이고 html도 쓰이는건지, 어떤 경우에 리액트에서 html에 자바스크립트 문법을 사용할 수 있는건지, 또 왜 어쩔때는 안되는건지 등등. 리액트에서 쓰이는 문법을 정확히 이해하지 못하고 있었기 때문에 구글링도 중구난망 엉망진창 비효율적일 수밖에 없었다.
답변의 키워드는 항상 jsx
였고, 그래서 jsx를 막연히 리액트에서 쓰이는 자바스크립트라고 이해하고 있었다. 이 말은 반은 맞고 반은 틀린 말이다.
이제 내 잘못된 개념을 바로잡자면,
jsx
는 html처럼 생겼지만 html이 아니라 자바스크립트
다.React.createElement
를 사용하는 자바스크립트 형태로 변환해왔던 것이다.jsx
작성 규칙 등 더 자세한 내용은 벨로퍼트님 블로그를 참고하면 좋다.
예발자닷컴연간 캘린더를 보면 지금은 월 단위로만 구분선이 존재하는데, 원래는 각 월마다 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>
<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()을 사용하는 더 좋은 방식도 존재한다. 이곳 벨로퍼트님 블로그 참고!