[React] JSX(TSX) 문법 정리
💡 JSX(TSX)란?
JSX는 JavaScript를 확장한 문법(JavaScript XML)입니다. 쉽게 말해, JavaScript 파일 안에서 HTML과 비슷한 코드를 작성할 수 있게 해주는 특별한 문법이죠. 우리가 보기엔 HTML 같지만, 사실 이 코드는 브라우저가 읽기 전에 바벨(Babel)이라는 도구를 통해 순수한 JavaScript 객체로 변환됩니다.
TSX도 JSX의 문법을 따릅니다.
- React는 UI를 만들기 위한 라이브러리입니다.
- JSX는 React에서 UI를 더 쉽고 직관적으로 표현하기 위해 사용하는 문법입니다.
예를 들어, 우리가 이렇게 JSX를 작성하면:
const element = <h1>안녕하세요, 리액트!</h1>;
바벨은 이 코드를 아래와 같은 순수 JavaScript 코드로 바꿔줍니다.
const element = React.createElement('h1', null, '안녕하세요, 리액트!');
결국 JSX는 복잡한 React.createElement 코드를 더 편하게 작성하기 위한 "문법적 설탕(Syntactic Sugar)"인 셈입니다. 이 덕분에 우리는 마치 HTML을 다루듯 익숙하게 UI 구조를 짤 수 있고, 동시에 JavaScript의 모든 강력한 기능을 활용할 수 있습니다.
🚀 JSX 핵심 문법
이제 본격적으로 JSX를 사용할 때 반드시 알아야 할 핵심 규칙을 살펴보겠습니다.
1. 모든 태그는 단 하나의 최상위 태그로 감쌀 것
JSX 표현식은 반드시 하나의 부모 요소만 반환해야 합니다. 여러 개의 태그를 병렬로 나열하면 오류가 발생합니다. 이는 컴포넌트가 반환해야 하는 결과물이 하나의 '덩어리'여야 한다는 React의 규칙 때문입니다.
// ⭕ 올바른 예시: div라는 하나의 부모 태그로 감싸기
return (
<div>
<h1>안녕하세요!</h1>
<p>JSX 문법을 배우고 있습니다.</p>
</div>
);
// ❌ 잘못된 예시: 부모 태그 없이 여러 태그를 반환
return (
<h1>안녕하세요!</h1><p>이 코드는 오류를 발생시킵니다.</p>
);
2. JSX 내부 주석
JSX 내부의 주석은 일반적인 HTML 주석(``)이나 JavaScript 주석(//, /* */)과 다릅니다. 중괄호 {} 안에 JavaScript의 여러 줄 주석 형태를 사용해야 합니다.
VS Code에서는 주석을 달고 싶은 부분을 선택하고 Alt + Shift + A 단축키를 사용하면 바로 JSX 주석을 만들 수 있습니다.
function MyComponent() {
return (
<div>
{/* 이렇게 한 줄 주석을 작성할 수 있습니다. */}
<p>이 문장은 화면에 보입니다.</p>
{/*
물론,
여러 줄에 걸쳐
주석을 작성하는 것도 가능합니다.
*/}
</div>
);
}`
3. 불필요한 <div> 대신 Fragment를 사용할 것
가끔 최상위 태그 규칙을 지키기 위해 의미 없는 <div>로 감싸야 할 때가 있습니다. 이렇게 불필요한 태그는 DOM 구조를 복잡하게 만들고 스타일링에 문제를 일으킬 수 있죠. 이럴 때 Fragment를 사용하면 좋습니다. <React.Fragment> 또는 축약형인 <>를 사용하면, 실제 DOM에는 추가되지 않는 가상의 부모 요소를 만들 수 있습니다.
import React from 'react';
function MyComponent() {
return (
// <div> 대신 Fragment(<>)를 사용해 깔끔한 DOM 구조를 유지합니다.
<>
<h1>안녕하세요!</h1>
<p>Fragment를 사용하고 있어요.</p>
</>
);
}
4. JavaScript 변수를 사용하려면 중괄호 {}
JSX 내부에서 JavaScript 변수나 표현식의 결과값을 보여주고 싶을 때는 중괄호 {}로 감싸주면 됩니다.
const name = '리액트';
const studyTopic = 'JSX 문법';
return (
<h1>{name}로 {studyTopic} 배우기</h1>
);
// 결과: <h1>리액트로 JSX 문법 배우기</h1>
5. 조건부 렌더링은 삼항 연산자를 활용하세요.
JSX 내부에서는 if 문을 직접 사용할 수 없습니다. 대신 삼항 연산자 (조건 ? 결과1 : 결과2)를 사용하여 조건에 따라 다른 UI를 렌더링하는 것이 일반적입니다.
const isLoggedIn = true;
return (
<div>
{isLoggedIn ? <p>환영합니다!</p> : <p>로그인이 필요합니다.</p>}
</div>
);
6. 렌더링할 내용이 없다면 null을 사용
특정 조건에서 아무것도 화면에 그리고 싶지 않다면 null을 반환하면 됩니다. React는 null이나 false를 렌더링하지 않습니다. 하지만 undefined를 반환하면 오류가 발생할 수 있으니 주의해야 합니다.
// 렌더링할 내용이 없을 때
if (someConditionIsFalse) {
return null; // 아무것도 렌더링하지 않음
}
// undefined 변수를 안전하게 처리하기
const value = undefined;
return <div>{value || '기본값'}</div>; // value가 undefined이면 '기본값'을 출력
7. 인라인 스타일은 객체 형태로 작성
JSX에서 HTML 요소에 직접 스타일을 적용할 때는 스타일 객체를 사용합니다. CSS 속성 이름은 하이픈(-) 대신 카멜 케이스(camelCase)로 작성해야 합니다. 예를 들어, background-color는 backgroundColor로, font-size는 fontSize로 바뀝니다.
const divStyle = {
backgroundColor: 'navy',
color: 'white',
fontSize: '24px',
padding: '1rem'
};
return <div style={divStyle}>스타일이 적용된 텍스트</div>;
8. CSS 클래스는 className을 사용
HTML에서는 class 속성을 사용해 CSS 클래스를 지정하지만, JSX에서는 class가 JavaScript의 예약어이기 때문에 **className**이라는 속성을 사용해야 합니다.
// 일반 CSS 파일에 .container 클래스가 정의되어 있다고 가정
import './App.css';
return <div className="container">컨텐츠</div>;
9. 태그는 반드시 닫기
HTML에서는 <img>나 <br>처럼 닫는 태그가 없는 태그들이 허용되지만, JSX는 XML 규칙을 따르기 때문에 모든 태그를 명시적으로 닫아야 합니다. 내용이 없는 태그는 셀프 클로징(self-closing) 형식인 <tag />로 닫아주세요.
// 일반 CSS 파일에 .container 클래스가 정의되어 있다고 가정
import './App.css';
return <div className="container">컨텐츠</div>;
// ⭕ 올바른 예시
<img src="image.jpg" alt="이미지" />
<input type="text" /><br />// ❌ 잘못된 예시 (오류 발생)
<img src="image.jpg" alt="이미지">
10. for, onclick 등 HTML 속성 이름의 차이
className과 마찬가지로, 몇몇 HTML 속성은 JavaScript 예약어와의 충돌을 피하기 위해 JSX에서 다른 이름을 사용합니다.
- for (라벨) → htmlFor
- onclick, onmouseover 등 이벤트 핸들러 → onClick, onMouseOver (카멜 케이스)
<>
<label htmlFor="my-input">입력:</label><input id="my-input" type="text" />
</>
11. 이벤트 핸들링은 함수를 직접 전달
React에서 이벤트를 처리할 때는 이벤트 핸들러 속성(예: onClick)에 문자열이 아닌 함수를 직접 전달해야 합니다.
function handleClick() {
alert('버튼이 클릭되었습니다!');
}
// 함수 자체를 전달합니다. handleClick()처럼 호출하면 안됩니다!
return <button onClick={handleClick}>클릭하세요</button>;
12. 중괄호 안에는 어떤 JavaScript 표현식이든 OK!
중괄호 {} 안에는 변수뿐만 아니라, 함수 호출, 계산 등 유효한 모든 JavaScript 표현식을 넣을 수 있습니다.
const name = 'React';
return (
<h1>{name.toUpperCase()} 배우기</h1> // "REACT 배우기" 출력
);
13. 배열을 렌더링할 땐 key 속성이 필수
map() 함수 등을 사용해 배열의 각 항목을 컴포넌트나 엘리먼트로 변환할 때, React는 각 항목을 식별하기 위해 고유한 key 속성을 요구합니다. key는 형제 요소들 사이에서만 고유하면 되며, 주로 데이터의 id를 사용합니다. key는 React가 어떤 항목이 변경, 추가, 삭제되었는지 효율적으로 파악하는 데 매우 중요합니다.
const items = [
{ id: 1, name: '사과' },
{ id: 2, name: '바나나' },
{ id: 3, name: '오렌지' }
];
const listItems = items.map(item => (
<li key={item.id}>{item.name}</li>
));
return <ul>{listItems}</ul>;
14. PropTypes와 defaultProps로 컴포넌트 안정성 높이기
- PropTypes: 다른 개발자나 미래의 내가 이 컴포넌트에 어떤 props를 어떤 타입으로 전달해야 하는지 명확히 알려주는 명세서와 같습니다. 타입이 맞지 않으면 콘솔에 경고가 나타나 버그를 예방하는 데 도움이 됩니다.
- defaultProps: props가 전달되지 않았을 경우 사용할 기본값을 설정합니다.
import PropTypes from 'prop-types';
function Greeting({ name }) {
return <h1>안녕하세요, {name}님!</h1>;
}
// props 타입 검증
Greeting.propTypes = {
name: PropTypes.string.isRequired // name은 반드시 문자열이어야 함
};
// props 기본값 설정
Greeting.defaultProps = {
name: '방문자'
};
15. 사용자 정의 컴포넌트는 항상 대문자로 시작
React는 태그의 시작이 소문자이면 일반 HTML 태그 (예: <div>, <p>)로, 대문자이면 사용자 정의 컴포넌트 (예: <MyComponent>)로 인식합니다. 이 규칙을 지키지 않으면 React가 컴포넌트를 제대로 렌더링하지 못합니다.
// ⭕ 올바른 예시: 컴포넌트 이름은 대문자로 시작
function MyComponent() {
return <div>사용자 정의 컴포넌트</div>;
}
// ❌ 잘못된 예시: 소문자로 시작하면 HTML 태그로 인식하려다 오류 발생
function myComponent() {
return <div>잘못된 컴포넌트</div>;
}
16. JSX 스프레드 연산자로 props 한번에 전달
객체에 여러 props를 담아두고, **스프레드 연산자(...)**를 사용해 한 번에 컴포넌트로 전달할 수 있습니다. 코드를 훨씬 간결하게 만들어 줍니다.
const buttonProps = {
className: 'btn-primary',
disabled: false,
onClick: () => console.log('클릭!')
};
function MyButton() {
return <button {...buttonProps}>멋진 버튼</button>;
}
17. 간단한 조건부 렌더링은 && 연산자
true && expression은 항상 expression으로 평가되고, false && expression은 항상 false로 평가되는 JavaScript의 특징을 이용한 방법입니다. 조건이 true일 때만 특정 요소를 렌더링하고 싶을 때 유용합니다.
const isLoggedIn = true;
const messageCount = 5;
return (
<div>
{isLoggedIn && <p>환영합니다!</p>}
{messageCount > 0 && <p>새 메시지가 {messageCount}개 있습니다.</p>}
</div>
);