리액트 기초 강좌 #3 JSX란 무엇인가?
지난 시간에는 Vite로 첫 리액트 프로젝트를 만들고 개발 서버를 띄웠습니다. 그 과정에서 우리는 자연스럽게 다음과 같은 코드를 봤습니다.
function App() {
return (
<div>
<h1>안녕하세요, 리액트!</h1>
</div>
);
}함수 안에서 HTML처럼 생긴 코드를 그냥 return하고 있습니다. 자바스크립트 안에 HTML이라니, 어딘가 어색하지 않습니까? 이번 시간에는 이 신기한 문법인 JSX가 무엇이고, 어떻게 사용하는지 알아보도록 하겠습니다.
JSX는 무엇인가? #
**JSX(JavaScript XML)**는 자바스크립트에 HTML과 비슷한 마크업 문법을 합쳐 놓은 것입니다. 자바스크립트의 공식 문법은 아니지만, 페이스북이 리액트를 발표할 때 함께 제안한 확장 문법이며, 오늘날 거의 모든 리액트 프로젝트가 JSX를 사용합니다.
사실 JSX 없이도 리액트 코드를 작성할 수 있습니다. 다음 두 코드는 정확히 동일한 결과를 만듭니다.
function App() {
return <h1>Hello</h1>;
}function App() {
return React.createElement('h1', null, 'Hello');
}위쪽이 훨씬 읽기 쉽다는 데 누구나 동의할 것입니다. 그래서 우리는 JSX를 사용합니다.
브라우저는 JSX를 모른다 #
브라우저의 자바스크립트 엔진은 JSX를 직접 이해하지 못합니다. 우리가 JSX로 작성한 코드는 빌드 과정에서 일반 자바스크립트로 변환된 후 브라우저로 전달됩니다. 지난 시간에 사용한 Vite 같은 빌드 도구가 이 변환을 자동으로 처리해주기 때문에, 우리는 신경쓰지 않고 JSX를 마음껏 사용할 수 있습니다.
JSX의 기본 규칙 #
JSX는 HTML과 비슷해 보이지만 몇 가지 다른 규칙이 있습니다. 처음 배우시는 분들이 가장 많이 헷갈리는 부분이니 차근차근 살펴보겠습니다.
규칙 1. 반드시 하나의 부모 요소로 감싸야 한다 #
JSX는 결국 함수가 무언가를 return하는 것이므로, 한 번에 하나의 값만 반환할 수 있습니다. 따라서 여러 요소를 한꺼번에 반환하려면 반드시 하나의 부모로 감싸야 합니다.
function App() {
return (
<h1>안녕하세요</h1>
<p>오늘도 즐거운 하루 보내세요</p>
);
}function App() {
return (
<div>
<h1>안녕하세요</h1>
<p>오늘도 즐거운 하루 보내세요</p>
</div>
);
}<div>로 감싸면 되긴 하는데, 의미 없는 <div>가 늘어나는 것이 마음에 들지 않을 때가 있습니다. 그럴 때는 빈 태그인 Fragment를 사용할 수 있습니다.
function App() {
return (
<>
<h1>안녕하세요</h1>
<p>오늘도 즐거운 하루 보내세요</p>
</>
);
}<>...</>는 React.Fragment의 짧은 표기로, 실제 DOM에는 어떤 요소도 만들지 않으면서 여러 자식을 묶어주는 역할을 합니다.
규칙 2. 모든 태그는 닫혀야 한다 #
HTML에서는 <img>, <br>, <input> 같은 일부 태그를 닫지 않고 사용해도 됐습니다. 하지만 JSX에서는 모든 태그를 닫아야 합니다. 자식이 없는 태그는 끝에 /를 붙여 자체 닫음(self-closing)으로 처리합니다.
<img src="/cat.png" alt="고양이" />
<br />
<input type="text" />규칙 3. class 대신 className
#
HTML에서 CSS 클래스를 지정할 때 사용하던 class 속성은 자바스크립트의 예약어와 겹치기 때문에 JSX에서는 **className**을 사용합니다.
<div className="card">카드입니다</div>같은 이유로 <label>의 for 속성도 htmlFor로 바뀝니다.
<label htmlFor="email">이메일</label>
<input id="email" type="email" />규칙 4. 속성 이름은 camelCase #
HTML에서는 속성 이름이 모두 소문자였지만 (onclick, tabindex, readonly), JSX에서는 camelCase를 사용합니다.
<button onClick={handleClick} tabIndex={0}>클릭</button>
<input readOnly value="고정값" />JSX 안에 자바스크립트 표현식 넣기 #
JSX의 진짜 힘은 마크업 안에 자바스크립트 표현식을 자유롭게 끼워 넣을 수 있다는 데 있습니다. 중괄호 { } 안에 들어가는 모든 자바스크립트 표현식은 평가되어 그 결과가 화면에 출력됩니다.
function App() {
const name = '철수';
const age = 30;
return (
<div>
<h1>안녕하세요, {name}님!</h1>
<p>나이: {age}세</p>
<p>10년 후에는: {age + 10}세</p>
</div>
);
}함수 호출도, 삼항 연산자도, 배열의 메소드도 모두 표현식이라면 사용할 수 있습니다.
function App() {
const isLoggedIn = true;
const items = ['사과', '바나나', '체리'];
return (
<div>
<h1>{isLoggedIn ? '환영합니다!' : '로그인 해주세요'}</h1>
<ul>
{items.map(item => <li key={item}>{item}</li>)}
</ul>
</div>
);
}if 문이나 for 루프 같은 **문(statement)**은 직접 넣을 수 없습니다. 조건부 출력이 필요하면 삼항 연산자나 && 연산자를, 반복 출력이 필요하면 map 메소드를 사용하는 식입니다. 자세한 내용은 #7 조건부 렌더링과 #8 리스트와 key에서 다루겠습니다.인라인 스타일 #
JSX에서 직접 스타일을 지정하려면 객체 형태로 전달합니다. CSS 속성 이름도 마찬가지로 camelCase입니다.
function App() {
return (
<h1 style={{ color: 'tomato', fontSize: '32px' }}>
스타일 적용된 제목
</h1>
);
}중괄호가 두 개인 것이 처음에는 어색해 보일 수 있는데, 바깥 { }는 “JSX 안의 자바스크립트 표현식"을 의미하고, 안쪽 { }는 “자바스크립트 객체 리터럴"이기 때문입니다.
JSX 안의 주석 #
JSX 안에서 주석을 달려면 자바스크립트 표현식이므로 중괄호로 감싸야 합니다.
function App() {
return (
<div>
{/* 이것은 JSX 주석입니다 */}
<h1>제목</h1>
</div>
);
}직접 해보기 #
지난 시간에 만든 프로젝트의 src/App.jsx를 다음과 같이 바꿔보세요.
function App() {
const name = '철수';
const fruits = ['사과', '바나나', '체리'];
return (
<>
<h1>안녕하세요, {name}님!</h1>
<p>오늘의 과일은 총 {fruits.length}개입니다:</p>
<ul>
{fruits.map(fruit => <li key={fruit}>{fruit}</li>)}
</ul>
</>
);
}
export default App;저장하면 HMR로 즉시 화면이 갱신될 것입니다. 변수의 값을 바꾸거나 배열에 새 과일을 추가해보면서, 화면이 어떻게 반응하는지 직접 확인해보세요.
마무리 #
이번 글에서는 리액트의 핵심 문법인 JSX를 소개하고, 기본 규칙 4가지(부모 요소로 감싸기, 태그 닫기, className/htmlFor, camelCase 속성), 자바스크립트 표현식 넣기, 인라인 스타일, 주석을 살펴봤습니다. 이제 JSX 코드를 보고 어색함이 많이 줄었기를 바랍니다.
다음 글인 “리액트 기초 강좌 #4 컴포넌트와 props"에서는 화면을 작은 단위로 나누는 핵심 개념인 컴포넌트와, 컴포넌트에 데이터를 전달하는 통로인 props를 다뤄보도록 하겠습니다.