JSX는 JavaScript 코드 내에서 UI 구조를 직관적으로 표현할 수 있게 해주는 JavaScript의 확장 문법입니다. 복잡하고 긴 DOM API 코드를 사용하는 대신, HTML과 유사한 형태로 UI를 작성할 수 있어 코드의 가독성과 생산성을 크게 높여줍니다.
DOM API 코드
const element = document.createElement("h1");
element.className = "greeting";
element.textContent = "Hello, World!";
document.getElementById("root").appendChild(element);
JSX 코드
const element = <h1 className="greeting">Hello, World!</h1>;
ReactDOM.createRoot(document.getElementById("root")).render(element);
React 컴포넌트는 여러 개의 JSX 요소를 동시에 반환할 수 없습니다. 모든 요소는 반드시 하나의 부모 태그로 감싸져야 합니다. 불필요한 <div> 태그를 피하고 싶을 때는 Fragment 태그(<>...</>)를 사용할 수 있습니다.
❌ 잘못된 코드 (Error)
// 에러: 인접한 JSX 요소들은 반드시 하나의 부모 태그로 감싸져야 합니다.
const element = (
<h1>Title</h1>
<p>Paragraph</p>
);
✅ 올바른 코드 (Fragment 사용)
const element = (
<>
<h1>Title</h1>
<p>Paragraph</p>
</>
);
HTML과 달리 JSX는 모든 태그를 명시적으로 닫아야 합니다. 내용이 없는 단일 태그(Self-closing tag)는 끝에 /를 붙여주어야 합니다.
❌ 잘못된 코드
const element = (
<div>
<h1>Title
<p>Content
<br>
<input type="text">
</div>
);
✅ 올바른 코드
const element = (
<div>
<h1>Title</h1>
<p>Content</p>
<br />
<input type="text" />
</div>
);
JSX에서 HTML 속성을 사용할 때는 JavaScript 문법과의 충돌을 피하기 위해 대부분 카멜케이스 표기법을 따릅니다.
class → className (가장 흔한 예시, class는 JavaScript의 예약어)onclick → onClicktabindex → tabIndexreadonly → readOnly예외:
data-*와aria-*속성
data-testid같은data-*속성과aria-label같은aria-*웹 접근성 속성은 카멜케이스로 변환하지 않고 원래의 하이픈(-) 표기법을 그대로 사용합니다.
function Component() {
const handleClick = () => {
console.log("Button clicked!");
};
return (
<div className="container">
<button
onClick={handleClick} // 카멜케이스
aria-label="Close button" // 예외: 하이픈 유지
data-testid="close-btn" // 예외: 하이픈 유지
>
Click Me
</button>
<input type="text" readOnly={true} />
</div>
);
}
style 속성은 객체 형태로 작성합니다.인라인 스타일을 적용할 때 style 속성에는 문자열이 아닌 JavaScript 객체를 전달해야 합니다. CSS 속성명 또한 카멜케이스로 작성합니다.
style={{...}}: 바깥쪽 중괄호는 JSX의 JavaScript 표현식 삽입을 의미하고, 안쪽 중괄호는 스타일을 정의하는 JavaScript 객체를 의미합니다.function StyledComponent() {
return (
<div
style={{
// background-color → backgroundColor
backgroundColor: "#f0f0f0",
// font-size → fontSize
fontSize: "20px",
// border-radius → borderRadius
borderRadius: "8px",
padding: "16px",
}}
>
Styled Div
</div>
);
}
보간법은 중괄호 {}를 사용하여 JSX 코드 내부에 JavaScript 변수나 표현식의 결과값을 동적으로 삽입하는 방법입니다.
const name = "김철수";
const age = 25;
const element = (
<>
<h1>안녕하세요, {name}님!</h1>
<p>내년 나이는 {age + 1}세 입니다.</p>
<p>성인 여부: {age >= 18 ? "성인" : "미성년자"}</p>
</>
);
function getGreeting(userName) {
return `Welcome, ${userName}!`;
}
const element = <h1>{getGreeting("React Developer")}</h1>;
const user = {
name: "이영희",
avatarUrl: "/path/to/avatar.jpg",
};
const colors = ["빨강", "파랑", "초록"];
const element = (
<>
<h2>{user.name}</h2>
<img src={user.avatarUrl} alt={user.name} />
<p>가장 좋아하는 색: {colors[0]}</p>
</>
);
if, for와 같은 문(Statement)은 사용할 수 없습니다: 오직 값으로 평가되는 표현식(Expression)만 가능합니다. (삼항 연산자나 배열의 map 메서드는 사용 가능)
객체는 직접 렌더링할 수 없습니다: {user}와 같이 객체 자체를 렌더링하면 Objects are not valid as a React child 오류가 발생합니다. 반드시 {user.name}처럼 특정 속성에 접근해야 합니다.
배열은 직접 렌더링이 가능합니다: React는 배열을 만나면 내부의 항목들을 순서대로 모두 렌더링합니다. 하지만 실무에서는 주로 map 메서드를 사용하여 배열의 각 항목을 JSX 요소로 변환하여 목록을 만드는 방식을 사용합니다.
const users = [
{ id: 1, name: "김철수" },
{ id: 2, name: "이영희" },
];
const userList = (
<ul>
{/* 배열 map을 사용한 렌더링. 각 요소에 고유한 key를 제공해야 합니다. */}
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);