JSX 문법 이해하기
안녕하세요! 첫 번째 리액트 앱을 성공적으로 만들어보신 것을 다시 한번 축하드립니다. 1장 마지막에서 잠시 언급했듯이, 리액트 컴포넌트를 작성할 때 우리는 HTML과 매우 유사하게 생긴 특별한 문법을 사용했습니다. 바로 JSX(JavaScript XML) 입니다.
이번 장에서는 리액트 개발의 핵심이자 필수적인 JSX 문법에 대해 깊이 있게 알아보는 시간을 갖겠습니다. JSX는 처음 볼 때는 낯설게 느껴질 수 있지만, 익숙해지면 리액트 컴포넌트를 매우 직관적이고 효율적으로 작성할 수 있도록 돕는 강력한 도구입니다.
JSX란 무엇인가?
JSX는 JavaScript XML의 약자로, 이름에서 알 수 있듯이 자바스크립트 코드 내부에 XML/HTML과 유사한 구문을 작성할 수 있게 해주는 문법 확장입니다. 리액트에서 UI를 정의할 때 사용되며, 내부적으로는 자바스크립트 객체로 변환됩니다.
JSX의 특징
- HTML처럼 보이지만 자바스크립트입니다: JSX는 겉보기에는 HTML 태그처럼 보이지만, 실제로는 자바스크립트 코드의 일부입니다. 덕분에 자바스크립트의 모든 기능을 JSX 내부에서 활용할 수 있습니다.
- 컴파일 과정: 웹 브라우저는 JSX를 직접 이해하지 못합니다. 따라서 JSX 코드는 바벨(Babel)과 같은 트랜스파일러(Transpiler)에 의해 일반 자바스크립트 코드로 변환된 후 브라우저에서 실행됩니다. 우리가
create-react-app
으로 프로젝트를 생성할 때 이미 이러한 변환 과정이 자동으로 설정되어 있습니다. - UI를 선언적으로 표현: JSX는 "무엇을 보여줄 것인가"를 직관적으로 선언할 수 있게 하여 UI 구조를 한눈에 파악하기 용이하게 만듭니다.
JSX의 기본 규칙
JSX는 HTML과 유사하지만 몇 가지 중요한 규칙을 따릅니다. 이 규칙들을 정확히 이해하는 것이 중요합니다.
항상 하나의 루트(Root) 요소 반환
리액트 컴포넌트는 JSX를 반환할 때, 반드시 하나의 최상위(Root) 요소로 감싸져야 합니다. 두 개 이상의 요소가 나란히 존재할 수 없습니다.
// ⭕ 올바른 예시: 하나의 div로 감싸져 있음
function MyComponent() {
return (
<div>
<h1>안녕하세요!</h1>
<p>리액트를 배워봅시다.</p>
</div>
);
}
// ❌ 잘못된 예시: 두 개의 최상위 요소
// function MyComponent() {
// return (
// <h1>안녕하세요!</h1>
// <p>리액트를 배워봅시다.</p>
// );
// }
만약 불필요한 <div>
태그를 추가하고 싶지 않다면, 프래그먼트(Fragment) 를 사용할 수 있습니다. 프래그먼트는 <></>
또는 <React.Fragment></React.Fragment>
와 같이 작성하며, 실제 DOM에는 렌더링되지 않는 '유령' 같은 요소입니다.
// ⭕ 올바른 예시: 프래그먼트 사용
import React from 'react'; // React.Fragment를 사용하려면 React를 임포트해야 합니다.
function MyComponent() {
return (
<> {/* 단축 문법: <React.Fragment>와 동일 */}
<h1>안녕하세요!</h1>
<p>리액트를 배워봅시다.</p>
</>
);
}
// 또는 명시적으로
function MyComponentWithFragment() {
return (
<React.Fragment>
<h1>안녕하세요!</h1>
<p>리액트를 배워봅시다.</p>
</React.Fragment>
);
}
프래그먼트는 특히 여러 컴포넌트를 묶어 반환하거나, 리스트를 렌더링할 때 유용하게 사용됩니다.
HTML 속성 이름의 CamelCase 규칙
JSX에서는 HTML 속성(Attribute) 이름을 작성할 때 자바스크립트의 CamelCase(카멜 케이스) 규칙을 따릅니다. 이는 자바스크립트에서 DOM 속성을 다룰 때도 유사한 규칙을 따르기 때문입니다.
class
$\rightarrow$className
for
$\rightarrow$htmlFor
tabindex
$\rightarrow$tabIndex
onclick
$\rightarrow$onClick
예시
function MyButton() {
return (
<button className="my-button" onClick={() => console.log('클릭!')}>
클릭해주세요
</button>
);
}
// HTML에서는 다음과 같았겠죠?
// <button class="my-button" onclick="console.log('클릭!')">
// 클릭해주세요
// </button>
자식 요소가 없는 태그는 항상 닫기
HTML에서는 <img>
, <input>
, <br>
과 같이 자식 요소가 없는 태그를 단일 태그로 작성할 수 있었습니다. JSX에서는 이러한 태그를 사용할 때 항상 스스로 닫는(Self-closing) 형태로 작성해야 합니다.
// ⭕ 올바른 예시
<img src="logo.png" alt="로고" />
<input type="text" />
<br />
// ❌ 잘못된 예시
// <img src="logo.png" alt="로고">
// <input type="text">
// <br>
자식 요소가 있는 태그는 HTML과 동일하게 시작 태그와 종료 태그를 사용합니다.
<div>
<p>이것은 단락입니다.</p>
</div>
자바스크립트 표현식 삽입: {}
중괄호
JSX 내부에 자바스크립트 변수, 함수 호출 결과, 또는 다른 자바스크립트 표현식을 삽입하고 싶다면 중괄호 {}
를 사용합니다.
function Greeting() {
const name = '김코딩';
const age = 30;
const isStudent = true;
function formatGreeting(userName) {
return `안녕하세요, ${userName}님!`;
}
return (
<div>
<h1>{formatGreeting(name)}</h1> {/* 함수 호출 결과 삽입 */}
<p>나이: {age}</p> {/* 변수 값 삽입 */}
{isStudent ? (
<p>저는 학생입니다.</p>
) : (
<p>저는 직장인입니다.</p>
)} {/* 조건부 렌더링 (삼항 연산자) */}
<p>현재 날짜: {new Date().toLocaleDateString()}</p> {/* 자바스크립트 객체 및 메서드 호출 */}
</div>
);
}
{변수명}
: 변수의 값을 렌더링합니다.{함수명()}
: 함수의 반환값을 렌더링합니다.{조건 ? 참일 때 : 거짓일 때}
: 삼항 연산자를 사용하여 조건부 렌더링을 할 수 있습니다.- JSX 내에서
if
문이나for
문과 같은 제어문은 직접 사용할 수 없습니다. 대신 삼항 연산자나 논리 연산자(&&
,||
), 배열의map()
메서드 등을 사용하여 조건을 처리하거나 반복적인 요소를 렌더링합니다. 이 부분은 뒤에서 더 자세히 다루겠습니다.
JSX 내부의 주석
JSX 내부에서 주석을 작성할 때는 자바스크립트 주석 문법과 약간 다릅니다. 중괄호 {}
안에 자바스크립트 주석을 작성해야 합니다.
function CommentExample() {
return (
<div>
{/* 이것은 JSX 내부의 주석입니다 */}
<h1>JSX 주석 예시</h1>
{
// 여러 줄 주석도 가능합니다.
// 이 주석은 화면에 렌더링되지 않습니다.
}
<p>주석은 코드를 설명하는 데 유용합니다.</p>
</div>
);
}
JSX의 활용 예시
간단한 예제를 통해 JSX의 활용법을 다시 한번 살펴보겠습니다.
import React from 'react';
function ProductCard(props) {
const { name, price, imageUrl, isInStock } = props; // props를 비구조화 할당
return (
<div className="product-card">
<img src={imageUrl} alt={name} className="product-image" />
<h2>{name}</h2>
<p>가격: {price}원</p>
{isInStock ? ( // 조건부 렌더링
<p style={{ color: 'green', fontWeight: 'bold' }}>재고 있음</p>
) : (
<p style={{ color: 'red' }}>재고 없음</p>
)}
<button onClick={() => alert(`${name}을(를) 장바구니에 담았습니다!`)}>
장바구니 담기
</button>
</div>
);
}
export default ProductCard;
위 ProductCard
컴포넌트는 name
, price
, imageUrl
, isInStock
과 같은 데이터를 props
로 받아 JSX를 통해 동적으로 UI를 구성하고 있습니다.
className="product-card"
:class
대신className
을 사용했습니다.src={imageUrl}
: 이미지src
속성에 자바스크립트 변수imageUrl
을 삽입했습니다.onClick={() => ...}
:onClick
이벤트 핸들러에 자바스크립트 함수를 직접 전달했습니다.style={{ color: 'green', fontWeight: 'bold' }}
:style
속성에 자바스크립트 객체 형태로 CSS 속성을 직접 부여했습니다. (CSS 속성 이름도 카멜 케이스를 사용합니다. 예:font-weight
$\rightarrow$fontWeight
)
JSX는 리액트 컴포넌트를 작성하는 데 있어 필수적인 문법입니다. 처음에는 규칙들이 낯설게 느껴질 수 있지만, 실제로 코드를 작성하면서 익숙해지는 것이 가장 좋습니다. 다음 장부터는 이 JSX를 활용하여 실제로 '컴포넌트'를 만들고, 데이터를 전달하고, 이벤트를 처리하는 방법에 대해 본격적으로 다루게 될 것입니다.
'JSX 문법 이해하기' 장은 여기까지입니다. JSX의 핵심 규칙들과 활용법을 명확하게 전달하고자 노력했습니다. 특히 카멜 케이스 규칙과 단일 루트 요소 반환, 그리고 중괄호를 이용한 자바스크립트 표현식 삽입에 대한 설명을 강조했습니다.
더 추가하거나 수정하고 싶은 부분이 있으신가요? 말씀해주시면 반영하겠습니다.