🎯 학습 목표
- JSX가 JavaScript로 변환되는 과정을 이해한다
- Fragment, null/false/undefined 렌더링 규칙을 익힌다
- JSX와 HTML의 속성 차이(className, htmlFor, style)를 정확히 구분한다
&&단락 평가의 pitfall을 알고 안전하게 사용한다- JSX 표현식 안에서 JavaScript를 활용하는 패턴을 익힌다
📖 핵심 개념 1 — JSX는 문법 설탕(Syntactic Sugar)
JSX는 JavaScript 확장 문법입니다. 브라우저는 JSX를 직접 이해하지 못하므로, Babel(또는 Vite의 경우 esbuild/swc)이 JSX를 순수 JavaScript로 변환합니다.
// 개발자가 작성하는 JSX
function Greeting({ name }) {
return (
<div className="greeting">
<h1>안녕하세요, {name}님!</h1>
<p>React 강좌에 오신 것을 환영합니다.</p>
</div>
);
}
// Babel이 변환한 실제 JavaScript
function Greeting({ name }) {
return React.createElement(
'div',
{ className: 'greeting' },
React.createElement('h1', null, '안녕하세요, ', name, '님!'),
React.createElement('p', null, 'React 강좌에 오신 것을 환영합니다.')
);
}
이 변환 과정을 알면 JSX의 제약 조건이 이해됩니다. React.createElement()는 단 하나의 루트 요소만 반환할 수 있기 때문에, JSX도 항상 하나의 루트 요소로 감싸야 합니다.
📖 핵심 개념 2 — Fragment
루트 요소로 감싸야 하는 제약 때문에 불필요한 <div>가 생기는 문제가 있었습니다. Fragment로 해결합니다.
// ❌ 불필요한 div가 DOM에 추가됨
function List() {
return (
<div> {/* 이 div는 레이아웃에 영향을 줄 수 있음 */}
<dt>React</dt>
<dd>UI 라이브러리</dd>
</div>
);
}
// ✅ Fragment 사용 — DOM에 추가 요소 없음
function List() {
return (
<> {/* 단축 문법 */}
<dt>React</dt>
<dd>React는 UI 라이브러리입니다</dd>
</>
);
}
// key prop이 필요할 때는 명시적 Fragment 사용
function ItemList({ items }) {
return items.map(item => (
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</React.Fragment>
));
}
단축 문법 <>...</>은 key prop을 사용할 수 없습니다. 리스트 렌더링처럼 key가 필요한 경우에는 <React.Fragment key={...}>를 사용해야 합니다. 패널에서 이 차이를 모르고 에러를 만나는 초보자가 많다고 지적했습니다.
📖 핵심 개념 3 — null, false, undefined 렌더링 규칙
JSX에서 null, false, undefined는 아무것도 렌더링하지 않습니다. 조건부 렌더링에서 자주 활용됩니다.
function Example() {
return (
<div>
{null} {/* 아무것도 출력 안 함 */}
{false} {/* 아무것도 출력 안 함 */}
{undefined} {/* 아무것도 출력 안 함 */}
{0} {/* ⚠️ 주의! 숫자 0은 "0"으로 렌더링됨 */}
{""} {/* 빈 문자열은 아무것도 출력 안 함 */}
</div>
);
}
⚠️ && 단락 평가 pitfall
const items = []; // 빈 배열
// ❌ 잘못된 패턴 — 화면에 "0"이 출력됨
function BadExample() {
return (
<div>
{items.length && <ItemList items={items} />}
{/* items.length가 0이면 0 && ... = 0 → "0"이 렌더링됨! */}
</div>
);
}
// ✅ 올바른 패턴 1 — Boolean으로 변환
function GoodExample1() {
return (
<div>
{items.length > 0 && <ItemList items={items} />}
</div>
);
}
// ✅ 올바른 패턴 2 — 삼항 연산자
function GoodExample2() {
return (
<div>
{items.length ? <ItemList items={items} /> : null}
</div>
);
}
이 pitfall은 경험 많은 개발자도 자주 실수합니다. 패널에서 “조건부 렌더링 시 항상 비교 연산자(> 0)를 쓰거나 삼항 연산자를 쓰는 것을 팀 컨벤션으로 정하라”고 권고했습니다.
📖 핵심 개념 4 — JSX 속성 차이
JSX는 HTML과 거의 같아 보이지만, 몇 가지 속성 이름이 다릅니다. JavaScript 예약어와 충돌을 피하기 위해서입니다.
// HTML vs JSX 속성 차이
function FormExample() {
return (
<form>
{/* class → className (class는 JS 예약어) */}
<div className="form-group">
{/* for → htmlFor (for는 JS 예약어) */}
<label htmlFor="email">이메일</label>
{/* 이벤트 핸들러는 camelCase */}
<input
id="email"
type="email"
onChange={(e) => console.log(e.target.value)}
/>
</div>
{/* style은 문자열이 아닌 객체 — CSS 속성도 camelCase */}
<button
style={{
backgroundColor: '#0070f3', // background-color → backgroundColor
fontSize: '16px', // font-size → fontSize
borderRadius: '4px', // border-radius → borderRadius
color: 'white',
padding: '8px 16px',
cursor: 'pointer',
}}
>
제출
</button>
</form>
);
}
💻 코드 예제 — JSX 표현식 활용
import { useState } from 'react';
// JSX 안에서 다양한 JavaScript 표현식 활용
function JsxExpressions() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const user = { name: '김개발', role: 'admin' };
const scores = [85, 92, 78, 95];
return (
<div className="container">
{/* 변수 */}
<h1>{user.name}님의 대시보드</h1>
{/* 삼항 연산자 */}
<p>상태: {isLoggedIn ? '로그인됨' : '로그아웃됨'}</p>
{/* 함수 호출 */}
<p>평균 점수: {(scores.reduce((a, b) => a + b, 0) / scores.length).toFixed(1)}점</p>
{/* 배열 렌더링 */}
<ul>
{scores.map((score, index) => (
<li key={index}>{index + 1}회차: {score}점</li>
))}
</ul>
{/* 조건부 렌더링 */}
{user.role === 'admin' && (
<div className="admin-panel">관리자 패널</div>
)}
<button onClick={() => setIsLoggedIn(!isLoggedIn)}>
{isLoggedIn ? '로그아웃' : '로그인'}
</button>
</div>
);
}
export default JsxExpressions;
⚠️ 흔한 실수 (よくあるミス)
- JSX 안에서 if 문 사용: JSX 중괄호
{}안에는 표현식(expression)만 올 수 있습니다.if문은 명령문(statement)이므로 사용할 수 없습니다. 삼항 연산자나&&를 사용하세요. - style에 문자열 전달:
style="color: red"는 HTML에서는 되지만 JSX에서는 오류입니다. 반드시 객체로 전달해야 합니다:style={{ color: 'red' }} - 자기 닫는 태그 누락: HTML에서는
<br>이 유효하지만 JSX에서는<br />처럼 반드시 닫아야 합니다.<input>,<img>,<hr>등도 마찬가지입니다. - 숫자 0의 렌더링: 위에서 설명한
&&pitfall.items.length &&대신items.length > 0 &&를 사용하세요. - camelCase 잊기:
onclick대신onClick,onchange대신onChange. JSX 이벤트 핸들러는 모두 camelCase입니다.
💡 실무 팁
- JSX를 반환하는 함수는 괄호로 감싸기: 멀티라인 JSX는
return (...)으로 감싸지 않으면 ASI(자동 세미콜론 삽입)로 인해return undefined가 될 수 있습니다. - 복잡한 JSX는 변수로 분리: JSX 표현식이 길어지면 별도 변수에 저장하여 가독성을 높이세요.
const header = <h1>...</h1>; - Prettier 설정: JSX 포매팅을 자동화하면 팀 전체 코드 스타일이 일관됩니다.
.prettierrc에"singleQuote": true를 추가하는 것을 권장합니다. - eslint-plugin-react-hooks 설치: JSX/React 관련 ESLint 규칙을 설정하면 흔한 실수를 코딩 단계에서 잡을 수 있습니다.