React 독학 가이드 3 - JSX로 UI 표현하기
React 코드를 처음 보면 좀 당황스러워요. JavaScript인데 HTML이 들어있거든요.
const element = <h1>안녕하세요!</h1>;
이게 뭐지? JavaScript에서 HTML을? 이게 바로 JSX입니다.
JSX가 뭔가요?
JSX는 JavaScript XML의 약자예요. JavaScript 안에서 HTML과 비슷한 문법을 쓸 수 있게 해주는 거죠.
사실 브라우저는 JSX를 이해 못 해요. Babel이라는 도구가 JSX를 일반 JavaScript로 변환해줍니다.
// 우리가 쓰는 JSX
const element = <h1>안녕하세요!</h1>;
// 실제로 변환되는 코드
const element = React.createElement('h1', null, '안녕하세요!');
근데 React.createElement 쓰면 너무 복잡하잖아요? 그래서 JSX를 쓰는 거예요. 훨씬 직관적이니까요.
JSX 기본 문법
1. 하나의 루트 요소
JSX는 반드시 하나의 요소로 감싸야 해요.
// ❌ 에러!
function App() {
return (
<h1>제목</h1>
<p>내용</p>
);
}
// ⭕ div로 감싸기
function App() {
return (
<div>
<h1>제목</h1>
<p>내용</p>
</div>
);
}
// ⭕ Fragment 사용 (빈 태그)
function App() {
return (
<>
<h1>제목</h1>
<p>내용</p>
</>
);
}
<> </>를 쓰면 불필요한 div 없이 여러 요소를 묶을 수 있어요.
2. 모든 태그는 닫아야 함
HTML에서는 <img>, <br>, <input> 같은 태그를 안 닫아도 돼요. 근데 JSX에서는 무조건 닫아야 해요.
// ❌ 에러!
<img src="photo.jpg">
<input type="text">
<br>
// ⭕ self-closing 태그
<img src="photo.jpg" />
<input type="text" />
<br />
/>로 닫아주세요.
3. className 사용
HTML에서는 class를 쓰지만, JSX에서는 className을 씁니다.
// ❌ 에러 (class는 JavaScript 예약어)
<div class="container">
// ⭕ className 사용
<div className="container">
class가 JavaScript에서 클래스 선언에 쓰이는 예약어라서 그래요.
4. 카멜케이스 속성
HTML 속성 중 하이픈(-)이 들어간 것들은 카멜케이스로 바꿔야 해요.
// HTML
<div onclick="handleClick()" tabindex="0">
<label for="name">
// JSX
<div onClick={handleClick} tabIndex="0">
<label htmlFor="name">
주요 변환:
onclick→onClickonchange→onChangetabindex→tabIndexfor→htmlFormaxlength→maxLength
JavaScript 표현식 넣기
JSX의 진짜 장점은 JavaScript를 바로 넣을 수 있다는 거예요. **중괄호 {}**를 사용합니다.
변수 출력
function Greeting() {
const name = "홍길동";
const age = 25;
return (
<div>
<h1>안녕하세요, {name}님!</h1>
<p>나이: {age}세</p>
</div>
);
}
실행 결과:
안녕하세요, 홍길동님!
나이: 25세
JavaScript 변수가 JSX 안에서 출력된 결과입니다
계산식
function Calculator() {
const a = 10;
const b = 20;
return (
<div>
<p>{a} + {b} = {a + b}</p>
<p>{a} × {b} = {a * b}</p>
</div>
);
}
함수 호출
function formatDate(date) {
return `${date.getFullYear()}년 ${date.getMonth() + 1}월 ${date.getDate()}일`;
}
function Today() {
return (
<p>오늘 날짜: {formatDate(new Date())}</p>
);
}
삼항 연산자
function LoginStatus({ isLoggedIn }) {
return (
<div>
{isLoggedIn ? (
<p>환영합니다!</p>
) : (
<p>로그인이 필요합니다.</p>
)}
</div>
);
}
실행 결과:
환영합니다!
isLoggedIn = true
로그인이 필요합니다.
isLoggedIn = false
조건에 따라 다른 내용이 렌더링됩니다
조건부 렌더링
특정 조건에서만 요소를 보여주고 싶을 때가 있어요.
삼항 연산자
function Notification({ count }) {
return (
<div>
{count > 0 ? (
<span className="badge">{count}</span>
) : null}
</div>
);
}
&& 연산자 (더 간단)
function Notification({ count }) {
return (
<div>
{count > 0 && <span className="badge">{count}</span>}
</div>
);
}
&& 앞이 true면 뒤의 요소를 렌더링하고, false면 아무것도 안 나와요.
주의: 0은 렌더링됨
// ⚠️ count가 0이면 "0"이 화면에 나옴!
{count && <span>{count}</span>}
// ⭕ 명확하게 비교
{count > 0 && <span>{count}</span>}
&& 쓸 때 숫자 0은 falsy지만 렌더링되니까 주의하세요.
리스트 렌더링
배열을 화면에 보여줄 때는 map()을 씁니다.
function FruitList() {
const fruits = ["사과", "바나나", "오렌지", "포도"];
return (
<ul>
{fruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
);
}
실행 결과:
- 사과
- 바나나
- 오렌지
- 포도
배열의 각 항목이 리스트로 렌더링됩니다
key가 뭐예요?
key는 React가 어떤 항목이 바뀌었는지 알 수 있게 해주는 고유 식별자예요.
// ⚠️ index를 key로 쓰는 건 비추천 (순서가 바뀔 수 있으면)
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
// ⭕ 고유 ID가 있으면 그걸 사용
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
데이터에 고유 ID가 있으면 그걸 쓰세요. 없으면 index 써도 되긴 한데, 목록 순서가 바뀔 수 있으면 문제가 생길 수 있어요.
인라인 스타일
JSX에서 인라인 스타일은 객체로 넣어요.
// ❌ HTML 방식
<div style="color: red; font-size: 20px;">
// ⭕ JSX 방식 (객체로)
<div style={{ color: 'red', fontSize: '20px' }}>
중괄호가 두 개인 이유:
- 바깥
{}: JSX에서 JavaScript 쓰겠다 - 안쪽
{}: JavaScript 객체
스타일 속성도 카멜케이스예요:
font-size→fontSizebackground-color→backgroundColormargin-top→marginTop
스타일 객체 분리
function StyledBox() {
const boxStyle = {
width: '200px',
height: '200px',
backgroundColor: '#3498db',
borderRadius: '10px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
color: 'white',
fontSize: '18px'
};
return <div style={boxStyle}>스타일 박스</div>;
}
주석 달기
JSX 안에서 주석은 이렇게 써요:
function App() {
return (
<div>
{/* JSX 안에서의 주석 */}
<h1>안녕하세요</h1>
{/*
여러 줄
주석도 가능
*/}
<p>내용</p>
</div>
);
}
{/* */} 형태로 써야 해요. 일반 // 주석은 JSX 안에서 안 돼요.
실전 예제: 상품 카드
배운 거 다 써서 상품 카드를 만들어볼게요.
function ProductCard() {
const product = {
id: 1,
name: "에어팟 프로 2",
price: 359000,
discount: 15,
inStock: true,
tags: ["무선", "노이즈캔슬링", "Apple"]
};
const discountedPrice = product.price * (1 - product.discount / 100);
const cardStyle = {
border: '1px solid #ddd',
borderRadius: '12px',
padding: '20px',
maxWidth: '300px'
};
return (
<div style={cardStyle}>
{/* 상품명 */}
<h2>{product.name}</h2>
{/* 가격 정보 */}
<div>
<span style={{ textDecoration: 'line-through', color: '#999' }}>
{product.price.toLocaleString()}원
</span>
<span style={{ color: 'red', marginLeft: '10px' }}>
{product.discount}% OFF
</span>
</div>
<p style={{ fontSize: '24px', fontWeight: 'bold' }}>
{Math.floor(discountedPrice).toLocaleString()}원
</p>
{/* 재고 상태 */}
{product.inStock ? (
<span style={{ color: 'green' }}>✓ 재고 있음</span>
) : (
<span style={{ color: 'red' }}>품절</span>
)}
{/* 태그 목록 */}
<div style={{ marginTop: '15px' }}>
{product.tags.map((tag) => (
<span
key={tag}
style={{
background: '#f0f0f0',
padding: '4px 8px',
borderRadius: '4px',
marginRight: '5px',
fontSize: '12px'
}}
>
#{tag}
</span>
))}
</div>
</div>
);
}
다음 단계
JSX 문법을 익혔으니, 다음 글에서는 Props를 배울 거예요. Props를 쓰면 컴포넌트에 데이터를 전달할 수 있어요. 위의 상품 카드도 Props를 쓰면 여러 상품을 쉽게 보여줄 수 있죠.
JSX가 처음엔 어색할 수 있는데, 쓰다 보면 오히려 HTML보다 편하다고 느껴질 거예요!
React 시리즈 탐색:
← 블로그 목록으로