JSX는 JavaScript 파일 내에서 HTML과 유사한 마크업을 작성할 수 있는 JavaScript 확장 구문이다.
JSX: JavaScript에 마크업 넣기
웹은 HTML, CSS, JavaScript를 기반으로 구축된다. 그래서 우리는 배울 때 각자의 역할을 HTML은 콘텐츠, CSS는 디자인, 로직은 JavaScript로 분리해서 작성하였다.
하지만 웹이 점점 반응형, 상호작용을 중요시 하게 됨으로써 JavaScript가 HTML을 담당해가기 시작했다. 이것이 React에서 렌더링 요소와 마크업이 같은 위치, 즉 컴포넌트에 함께 존재하는 이유이다.
위의 왼쪽 예시처럼 동적으로 조건 판단을 요하는 요소가 있다면 JSX를 이용해 동적으로 렌더링이 가능하다. 또한 오른쪽 예시처럼 이벤트 리스너를 JS 파일로 따로 빼는 대신 컴포넌트 내부에 같이 작성하게 되면 항상 동기화가 가능하다.
JSX의 규칙
1. 단일 루트 요소 반환
컴포넌트에서 여러 요소를 반환하려면 단일 상위 태그로 요소를 감싸야한다.
예를 들어 가장 많이 사용하는 것은 <div></div>, <></>, <React.Fragment></React.Fragment>가 있다.
<div>
<h1>Hedy Lamarr's Todos</h1>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
class="photo"
>
<ul>
...
</ul>
</div>
<>
<h1>Hedy Lamarr's Todos</h1>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
class="photo"
>
<ul>
...
</ul>
</>
<React.Fragment>
<h1>Hedy Lamarr's Todos</h1>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
class="photo"
>
<ul>
...
</ul>
</React.Fragment>
그럼 위 세가지의 차이점은 무엇일까?
1. <div> ... </div>
만약 여러 요소를 <div>로 감싸서 반환하게 되면 HTML 트리에 <div>요소가 남게 된다. 이를 확인할 수 있는 방법은 크롬의 개발자도구에서 element 탭을 보면 우리가 감싼 요소들이 <div>로 감싸져 있는 것을 볼 수 있다. 하지만 이는 단점이 있다. 보통 우리는 단지 여러 요소들을 반환하고 싶을 뿐이지 그 요소를 감싸기 위해서만 존재하는 <div>를 HTML 트리에 보이게 하고 싶지 않을 수 있다. 따라서 <div>로 감싸서 반환하게 되면 굳이 보이고 싶지 않은 DOM element 요소를 보게 될 수도 있다.
function App() {
return (
<div>
<h1>Minjun's Todos</h1>
<ul>
<li>Todo1</li>
<li>Todo2</li>
<li>Todo3</li>
</ul>
</div>
);
}
export default App;
2. <> ... </>
사실 이 빈태그는 <React.Fragment>의 단축형이다. 기능상 큰 차이는 없다. 그러면 위의 <div>로 감싸는것과의 차이점은 무엇일까? 바로 HTML 트리에 요소가 삽입되지 않는다는것이다. 위에서 <div>로 감싸게 되면 HTML 트리에 <div> 요소가 삽입된다. 하지만 <>로 감싸게 되면 <div>로 감쌀때 굳이 원하지 않았는데 감싸기 위해서만 존재하는 태그가 보이는것을 없앨 수 있다. 크롬의 개발자도구의 element 탭을 보면 <>로 감쌌을 때는 감싼 태그가 보이지 않는다. 가장 많이 사용한다!
function App() {
return (
<>
<h1>Minjun's Todos</h1>
<ul>
<li>Todo1</li>
<li>Todo2</li>
<li>Todo3</li>
</ul>
</>
);
}
export default App;
3. <React.Fragment> ... </React.Fragment>
앞서 기술한 <> ... </>가 <React.Fragment>의 단축형이라고 말했다. 따라서 둘 사이에는 큰 차이가 없다. 실제로 위의 방식이 더 짧고 간편하기 때문에 더 많이 사용한다.
import React from "react";
function App() {
return (
<React.Fragment>
<h1>Minjun's Todos</h1>
<ul>
<li>Todo1</li>
<li>Todo2</li>
<li>Todo3</li>
</ul>
</React.Fragment>
);
}
export default App;
그러면 이걸 사용할 필요가 굳이 있을까? 나도 거의 없긴 했지만... 다음과 같은 상황에 쓰인다. 일단 우리는 태그에 여러 속성을 붙일 수 있다. 예시로는 이벤트 리스너, 스타일, ... 등등이 있다. 그리고 우리는 위와 같이 리스트를 렌더링할 때 예를 들어 배열의 요소로 .map()을 사용해서 반복문으로 렌더링하려고 한다면 key 속성을 붙여야 할 것이다. 여러 요소를 반복문으로 해결해야 할 때, 가장 상위 태그에는 key 속성을 붙여야 하는데 빈 태그로 할 경우 속성을 붙일 수가 없다. 물론 <div> 태그로 감싸서 <div>에 key 속성을 붙일 수 있겠지만, 위에서도 말했듯이 그냥 리스트 요소만 렌더링 하고 싶을 경우에는 <React.Fragment>에 key 속성을 붙여서 렌더링하면 된다.
import React from 'react';
function App() {
const TODO_LIST = ['Todo1', 'Todo2', 'Todo3'];
return (
<React.Fragment>
<h1>Minjun's Todos</h1>
<ul>
{TODO_LIST.map((todo, idx) => {
return (
<div>
<span>{idx + 1}번째</span>
<li>{todo}</li>
</div>
);
})}
</ul>
</React.Fragment>
);
}
export default App;
위와 같은 간단한 Todo List를 만들었다고 해보자. 리스트를 반복문으로 화면에 렌더링 하기 위해 JSX 구문으로 .map() 함수를 이용한 것을 볼 수 있다. 화면에 렌더링은 잘 되지만 에러가 난다.
바로 .map()에서 반환하는 요소에 key가 없다는 것이다. 그래서 다음과 같이 고쳐줘야 한다. (임의로 인덱스를 key로 사용하자)
import React from 'react';
function App() {
const TODO_LIST = ['Todo1', 'Todo2', 'Todo3'];
return (
<React.Fragment>
<h1>Minjun's Todos</h1>
<ul>
{TODO_LIST.map((todo, idx) => {
return (
<div key={idx}> {/* key 속성 */}
<span>{idx + 1}번째</span>
<li>{todo}</li>
</div>
);
})}
</ul>
</React.Fragment>
);
}
export default App;
위와 같이 하면 더이상 에러는 볼 수 없을 것이다. 그러면 개발자도구의 element 에선 어떻게 보일까?
굳이 필요하지 않은 div가 각 리스트 요소마다 한개씩 생겨났다. 굳이 <div>로 감싸고 싶진 않아서 다음과 같이 빈 태그로 감싸보았다.
import React from 'react';
function App() {
const TODO_LIST = ['Todo1', 'Todo2', 'Todo3'];
return (
<React.Fragment>
<h1>Minjun's Todos</h1>
<ul>
{TODO_LIST.map((todo, idx) => {
return (
<> {/* 빈 태그로 감쌌다 */}
<span>{idx + 1}번째</span>
<li>{todo}</li>
</>
);
})}
</ul>
</React.Fragment>
);
}
export default App;
그러면 우리가 원하는대로 굳이 불필요한 <div> 없이 HTML 트리에 원하는대로 렌더링을 마쳤다. 하지만 console 창에는 여전히 key가 존재하지 않기 때문에 에러메시지를 보여준다. 이럴 때 우리는 <React.Fragment>를 사용해야 한다. 빈 태그에 key 속성을 넣을 수 있을까?
위와 같이 에러가 난다. 애초에 태그가 없는데 속성을 지정한다는 것이 말이 안된다. 따라서 이 때 key 속성을 넣기 위해 단축형으로 쓰지말고 React.Fragment로 사용하는 것이다.
import React from 'react';
function App() {
const TODO_LIST = ['Todo1', 'Todo2', 'Todo3'];
return (
<React.Fragment>
<h1>Minjun's Todos</h1>
<ul>
{TODO_LIST.map((todo, idx) => {
return (
<React.Fragment key={idx + 1}> {/* React.Fragment + key */}
<span>{idx + 1}번째</span>
<li>{todo}</li>
</React.Fragment>
);
})}
</ul>
</React.Fragment>
);
}
export default App;
그러면 위와 같이 에러없이 원하는 결과를 얻을 수 있다.
2. 모든 태그 닫기
모든 태그에 닫는 태그가 존재해야 한다. <img>와 같이 자동 닫힘 태그는 <img />와 같이 닫힘 태그도 같이 써주어야 한다.
<>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
class="photo"
/>
<ul>
<li>Invent new traffic lights</li>
<li>Rehearse a movie scene</li>
<li>Improve the spectrum technology</li>
</ul>
</>
3. 대부분 카멜 케이스로 작성
JSX는 HTML이 아니라 JavaScript의 확장형이다. 따라서 태그에 class를 넣기 위해 HTML 에서와 같이 class를 넣으면 안된다. 이미 JavaScript 에는 비슷한 변수나 함수를 묶을 수 있는 class를 선언하는 class 키워드가 따로 존재하기 때문이다. 따라서 class 대신 className으로 대체하여 사용한다.
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
className="photo"
/>
'React 공식문서 (번역, 공부) > Describing the UI' 카테고리의 다른 글
Passing Props to a Component (0) | 2023.05.08 |
---|---|
JavaScript in JSX with Curly Braces (2) | 2023.05.08 |
Importing and Exporting Components (0) | 2023.05.07 |
Your First Component (0) | 2023.05.07 |