3 JSX 문법과 컴포넌트
1. JSX 문법과 컴포넌트
React의 핵심인 컴포넌트를 만드는 법과, 이를 위해 사용하는 JSX 문법을 완벽히 익힌다.
1.1. 컴포넌트와 JSX의 관계
1.1.1. 컴포넌트란?
**컴포넌트(Component)**는 React에서 UI를 만드는 기본 단위다. 레고 블록처럼 작은 부품들을 조립해서 전체 화면을 만든다고 생각하면 된다.
- 자바스크립트 함수: React 컴포넌트는 그냥 자바스크립트 함수다
- UI 반환: 이 함수는 화면에 그릴 내용을 반환한다
- 재사용 가능: 한 번 만들면 여러 곳에서 사용할 수 있다
1.1.2. JSX란?
**JSX(JavaScript XML)**는 자바스크립트 안에서 HTML처럼 생긴 코드를 작성할 수 있게 해주는 문법이다.
- HTML + JavaScript: HTML 구조 안에 자바스크립트 코드를 넣을 수 있다
- 변환 필요: 브라우저는 JSX를 이해 못함 → Vite(Babel)가 일반 JS로 변환
- 문법 설탕: 실제로는
React.createElement()함수 호출로 변환됨
1.1.3. 둘의 관계
요약
핵심 개념:
컴포넌트는 **“무엇”**이고, JSX는 **“어떻게”**다.
• 컴포넌트: UI를 만드는 함수 (설계도) • JSX: 컴포넌트 안에서 UI를 표현하는 문법 (재료와 조립법)
컴포넌트는 JSX를 반환(return)함으로써 화면에 무엇을 그릴지 정의한다.
1// 컴포넌트: Greeting이라는 함수2function Greeting() {3 // JSX: 이 컴포넌트가 그릴 UI를 JSX 문법으로 작성4 return <h1>안녕하세요!</h1>;5}6
7// 위 JSX는 실제로 이렇게 변환됨8function Greeting() {9 return React.createElement('h1', null, '안녕하세요!');10}1.2. 컴포넌트 완전 정복
1.2.1. 컴포넌트의 기본 구조
React 컴포넌트는 함수형 컴포넌트를 기본으로 사용한다. (과거에는 클래스형도 있었지만 지금은 거의 사용 안 함)
1// 가장 간단한 컴포넌트2function Hello() {3 return <div>안녕!</div>;4}5
6// 화살표 함수로도 작성 가능7const Hello = () => {8 return <div>안녕!</div>;9};10
11// return이 한 줄이면 괄호 생략 가능12const Hello = () => <div>안녕!</div>;1.2.2. 컴포넌트 작성 규칙 (매우 중요!)
요약
규칙 1: 대문자로 시작
컴포넌트 이름은 반드시 대문자로 시작해야 한다.
올바른 예: Header, MyButton, UserProfile
잘못된 예: header, myButton, userProfile
이유: React는 소문자로 시작하면 일반 HTML 태그로 인식한다.
<div>는 HTML div 태그, <Div>는 Div 컴포넌트
요약
규칙 2: JSX 반환
함수는 반드시 JSX를 return해야 한다. 아무것도 반환하지 않으면 오류 발생.
1// 올바른 예2function Button() {3 return <button>클릭</button>;4}5
6// 오류 발생 - 아무것도 반환 안 함7function Button() {8 console.log("버튼");9}요약
규칙 3: 파일당 하나의 기본 내보내기
다른 파일에서 사용하려면 export default로 내보내야 한다.
1function Button() {2 return <button>클릭</button>;3}4
5export default Button; // 다른 파일에서 import 가능6
7// 또는 한 줄로8export default function Button() {9 return <button>클릭</button>;10}1.2.3. 컴포넌트 사용하기
만든 컴포넌트는 HTML 태그처럼 사용한다. 이를 **“컴포넌트 렌더링”**이라고 한다.
1// Button 컴포넌트 정의2function Button() {3 return <button>클릭하세요</button>;4}5
6// App 컴포넌트에서 Button 사용7function App() {8 return (9 <div>10 <h1>내 앱</h1>11 <Button /> {/* Button 컴포넌트를 HTML 태그처럼 사용 */}12 <Button /> {/* 여러 번 재사용 가능 */}13 </div>14 );15}1.2.4. 컴포넌트 안에서 로직 작성하기
컴포넌트는 자바스크립트 함수이므로, return 전에 일반 JS 코드를 작성할 수 있다.
1function Greeting() {2 // return 전에 자바스크립트 로직 작성3 const name = "철수";4 const hour = new Date().getHours();5 const greeting = hour < 12 ? "좋은 아침" : "안녕하세요";6
7 // JSX에서 변수 사용8 return (9 <div>10 <h1>{greeting}, {name}님!</h1>11 <p>현재 시각은 {hour}시입니다.</p>12 </div>13 );14}요약
[실습 1] 자기소개 컴포넌트 만들기
src/Profile.jsx 파일을 만들고 다음 내용을 작성:
1function Profile() {2 const name = "홍길동";3 const age = 25;4 const hobby = "독서";5
6 return (7 <div style={{ padding: '20px', border: '1px solid #ccc' }}>8 <h2>{name}의 프로필</h2>9 <p>나이: {age}세</p>10 <p>취미: {hobby}</p>11 </div>12 );13}14
15export default Profile;그리고 App.jsx에서 불러와서 사용:
1import Profile from './Profile';2
3function App() {4 return <Profile />;5}1.3. JSX 문법 완전 정복
JSX는 HTML처럼 보이지만 실제로는 자바스크립트다. 그래서 HTML과는 다른 규칙들이 있다.
1.3.1. JSX의 본질
1// 우리가 작성하는 JSX2const element = <h1 className="title">안녕!</h1>;3
4// Vite(Babel)가 변환한 실제 자바스크립트5const element = React.createElement(6 'h1',7 { className: 'title' },8 '안녕!'9);JSX는 결국 React.createElement() 함수를 쉽게 쓰기 위한 **문법 설탕(Syntactic Sugar)**이다.
1.3.2. JSX 규칙 1: 하나의 최상위 태그
컴포넌트는 반드시 하나의 덩어리만 반환해야 한다. 형제 태그를 나란히 둘 수 없다.
1// 오류 발생! - 두 개의 형제 태그2function App() {3 return (4 <h1>제목</h1>5 <p>내용</p>6 );7}8
9// 해결책 1: div로 감싸기10function App() {11 return (12 <div>13 <h1>제목</h1>14 <p>내용</p>15 </div>16 );17}18
19// 해결책 2: Fragment 사용 (권장)20function App() {21 return (22 <> {/* Fragment: 화면에 렌더링되지 않는 투명한 태그 */}23 <h1>제목</h1>24 <p>내용</p>25 </>26 );27}요약
Fragment를 쓰는 이유:
불필요한 <div>가 많아지면 HTML 구조가 복잡해지고 CSS 스타일링이 어려워진다.
Fragment(<></>)는 실제 DOM에 렌더링되지 않으므로 깔끔한 HTML 구조를 유지할 수 있다.
1.3.3. JSX 규칙 2: 모든 태그는 닫아야 함
HTML에서는 일부 태그를 닫지 않아도 됐지만, JSX에서는 모든 태그를 반드시 닫아야 한다.
1// HTML에서는 OK, JSX에서는 오류2<input type="text">3<br>4<img src="image.jpg">5
6// JSX에서는 이렇게 닫아야 함 (Self-closing tag)7<input type="text" />8<br />9<img src="image.jpg" />10<hr />1.3.4. JSX 규칙 3: 중괄호로 자바스크립트 표현식 삽입
중괄호 {} 안에 자바스크립트 **표현식(expression)**을 넣을 수 있다.
1function Welcome() {2 const name = "철수";3 const age = 20;4 const isAdult = age >= 18;5
6 return (7 <div>8 {/* 변수 */}9 <h1>안녕, {name}!</h1>10
11 {/* 계산식 */}12 <p>10년 후 나이: {age + 10}</p>13
14 {/* 삼항 연산자 */}15 <p>{isAdult ? "성인" : "미성년자"}</p>16
17 {/* 함수 호출 */}18 <p>대문자 이름: {name.toUpperCase()}</p>19 </div>20 );21}요약
중요: 표현식만 가능!
중괄호 안에는 값을 반환하는 표현식만 넣을 수 있다.
올바른 예: 변수, 계산식, 삼항 연산자, 함수 호출
잘못된 예: if문, for문, switch문 등의 문장(statement)
1// 오류! - if는 표현식이 아님2return <div>{if (true) { "참" }}</div>;3
4// 대신 삼항 연산자 사용5return <div>{true ? "참" : "거짓"}</div>;1.3.5. JSX 규칙 4: 속성명 주의사항
JSX는 자바스크립트이므로, HTML 속성명이 약간 다르다.
| HTML | JSX | 이유 |
|---|---|---|
class | className | class는 JS 예약어 |
for | htmlFor | for는 JS 예약어 |
onclick | onClick | 카멜케이스 사용 |
tabindex | tabIndex | 카멜케이스 사용 |
1// JSX 방식2<div className="container">3 <label htmlFor="name">이름</label>4 <button onClick={handleClick}>클릭</button>5</div>1.3.6. JSX 규칙 5: 인라인 스타일은 객체로
HTML에서는 문자열로 스타일을 지정했지만, JSX에서는 객체로 지정한다.
1// JSX 방식 (객체 사용, 속성명은 카멜케이스)2<div style={{ color: 'red', fontSize: '20px' }}>텍스트</div>3// ↑ 첫 번째 중괄호: JS 표현식4// ↑ 두 번째 중괄호: 객체 리터럴5
6// 변수로 분리하면 더 깔끔7const myStyle = {8 color: 'red',9 fontSize: '20px',10 backgroundColor: '#f0f0f0' // background-color → backgroundColor11};12
13return <div style={myStyle}>텍스트</div>;1.3.7. JSX 규칙 6: 주석 작성법
1function App() {2 return (3 <div>4 {/* JSX 안에서 주석: 중괄호 안에 /* */ 사용 */}5 <h1>제목</h1>6
7 {/*8 여러 줄 주석도 가능9 이렇게 작성합니다10 */}11 </div>12 );13}요약
[실습 2] JSX 문법 연습
src/Card.jsx 파일을 만들고 JSX 문법을 연습해보자:
1function Card() {2 const title = "React 학습";3 const progress = 75;4 const completed = progress >= 100;5
6 const cardStyle = {7 border: '2px solid #007bff',8 borderRadius: '10px',9 padding: '20px',10 margin: '10px',11 backgroundColor: completed ? '#d4edda' : '#fff3cd'12 };13
14 return (15 <>16 <div style={cardStyle}>17 <h2 className="card-title">{title}</h2>18 <p>진행률: {progress}%</p>19
20 {/* 진행 바 */}21 <div style={{22 width: '100%',23 height: '20px',24 backgroundColor: '#e0e0e0',25 borderRadius: '10px'26 }}>27 <div style={{28 width: `${progress}%`,29 height: '100%',30 backgroundColor: '#007bff',31 borderRadius: '10px'32 }} />33 </div>34
35 {/* 조건부 렌더링 */}36 <p>{completed ? "✅ 완료!" : "🔄 진행 중"}</p>37 </div>38 </>39 );40}41
42export default Card;1.4. 컴포넌트 분리 실습
하나의 파일(App.jsx)에 모든 코드를 넣는 것은 좋지 않다. 역할별로 파일을 나누면 코드 관리가 쉬워진다.
- 재사용성: 한 번 만든 컴포넌트를 여러 곳에서 사용
- 유지보수: 문제가 생기면 해당 컴포넌트 파일만 수정
- 협업: 팀원들이 각자 다른 컴포넌트를 작업 가능
- 테스트: 개별 컴포넌트를 독립적으로 테스트 가능
요약
언제 분리해야 할까?
- 재사용될 것 같은 UI: 버튼, 카드, 입력 폼 등
- 독립적인 기능: 로그인 폼, 검색 바, 장바구니 등
- 너무 길어진 컴포넌트: 100줄 이상이면 분리 고려
- 명확한 책임이 있는 UI: 헤더, 푸터, 사이드바 등
요약
[실습 3] 컴포넌트 분리하기
1단계: 컴포넌트 파일 생성
src/Header.jsx
1function Header() {2 return (3 <header style={{4 backgroundColor: '#282c34',5 padding: '20px',6 color: 'white'7 }}>8 <h1>내 웹사이트</h1>9 <nav>10 <a href="#home" style={{ color: 'white', margin: '0 10px' }}>홈</a>11 <a href="#about" style={{ color: 'white', margin: '0 10px' }}>소개</a>12 <a href="#contact" style={{ color: 'white', margin: '0 10px' }}>연락</a>13 </nav>14 </header>15 );16}17
18export default Header;src/MainContent.jsx
1function MainContent() {2 return (3 <main style={{4 padding: '40px',5 minHeight: '500px'6 }}>7 <h2>메인 콘텐츠</h2>8 <p>여기에 주요 내용이 들어갑니다.</p>9
10 <div style={{11 display: 'grid',12 gridTemplateColumns: 'repeat(3, 1fr)',13 gap: '20px',14 marginTop: '20px'15 }}>16 <div style={{ border: '1px solid #ddd', padding: '20px' }}>17 <h3>카드 1</h3>18 <p>첫 번째 카드 내용</p>19 </div>20 </div>21 </main>22 );23}24
25export default MainContent;2단계: App.jsx에서 조립하기
1import Header from './Header';2import MainContent from './MainContent';3import Footer from './Footer';4
5function App() {6 return (7 <div style={{8 display: 'flex',9 flexDirection: 'column',10 minHeight: '100vh'11 }}>12 <Header />13 <MainContent />14 <Footer />15 </div>16 );17}18
19export default App;1.5. 조건부 렌더링
상황에 따라 다른 UI를 보여줘야 할 때가 많다. JSX에서는 자바스크립트의 조건 연산자를 활용한다.
1.5.1. 방법 1: 삼항 연산자 (조건 ? 참 : 거짓)
참/거짓 두 가지 경우를 모두 처리할 때 사용한다.
1{isLoggedIn ? (2 <button>로그아웃</button>3) : (4 <button>로그인</button>5)}1.5.2. 방법 2: AND 연산자 (&&)
조건이 참일 때만 보여주고, 거짓이면 아무것도 안 보여줄 때 사용한다.
1{messageCount > 0 && (2 <div>새 메시지 {messageCount}개</div>3)}1.5.3. 방법 3: 변수에 저장하기
조건이 복잡하면 JSX 밖에서 처리하고 변수에 저장하는 것이 더 깔끔하다.
1let content;2if (user.age < 18) {3 content = <p>미성년자는 이용할 수 없습니다.</p>;4} else {5 content = <p>환영합니다!</p>;6}7
8return <div>{content}</div>;