Type something to search...

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)함으로써 화면에 무엇을 그릴지 정의한다.

// 컴포넌트: Greeting이라는 함수
function Greeting() {
// JSX: 이 컴포넌트가 그릴 UI를 JSX 문법으로 작성
return <h1>안녕하세요!</h1>;
}
// 위 JSX는 실제로 이렇게 변환됨
function Greeting() {
return React.createElement('h1', null, '안녕하세요!');
}

1.2. 컴포넌트 완전 정복

1.2.1. 컴포넌트의 기본 구조

React 컴포넌트는 함수형 컴포넌트를 기본으로 사용한다. (과거에는 클래스형도 있었지만 지금은 거의 사용 안 함)

// 가장 간단한 컴포넌트
function Hello() {
return <div>안녕!</div>;
}
// 화살표 함수로도 작성 가능
const Hello = () => {
return <div>안녕!</div>;
};
// return이 한 줄이면 괄호 생략 가능
const Hello = () => <div>안녕!</div>;

1.2.2. 컴포넌트 작성 규칙 (매우 중요!)

요약

규칙 1: 대문자로 시작

컴포넌트 이름은 반드시 대문자로 시작해야 한다.

올바른 예: Header, MyButton, UserProfile 잘못된 예: header, myButton, userProfile

이유: React는 소문자로 시작하면 일반 HTML 태그로 인식한다. <div>는 HTML div 태그, <Div>는 Div 컴포넌트

요약

규칙 2: JSX 반환

함수는 반드시 JSX를 return해야 한다. 아무것도 반환하지 않으면 오류 발생.

// 올바른 예
function Button() {
return <button>클릭</button>;
}
// 오류 발생 - 아무것도 반환 안 함
function Button() {
console.log("버튼");
}

요약

규칙 3: 파일당 하나의 기본 내보내기

다른 파일에서 사용하려면 export default로 내보내야 한다.

Button.jsx
function Button() {
return <button>클릭</button>;
}
export default Button; // 다른 파일에서 import 가능
// 또는 한 줄로
export default function Button() {
return <button>클릭</button>;
}

1.2.3. 컴포넌트 사용하기

만든 컴포넌트는 HTML 태그처럼 사용한다. 이를 **“컴포넌트 렌더링”**이라고 한다.

// Button 컴포넌트 정의
function Button() {
return <button>클릭하세요</button>;
}
// App 컴포넌트에서 Button 사용
function App() {
return (
<div>
<h1>내 앱</h1>
<Button /> {/* Button 컴포넌트를 HTML 태그처럼 사용 */}
<Button /> {/* 여러 번 재사용 가능 */}
</div>
);
}

1.2.4. 컴포넌트 안에서 로직 작성하기

컴포넌트는 자바스크립트 함수이므로, return 전에 일반 JS 코드를 작성할 수 있다.

function Greeting() {
// return 전에 자바스크립트 로직 작성
const name = "철수";
const hour = new Date().getHours();
const greeting = hour < 12 ? "좋은 아침" : "안녕하세요";
// JSX에서 변수 사용
return (
<div>
<h1>{greeting}, {name}님!</h1>
<p>현재 시각은 {hour}시입니다.</p>
</div>
);
}

요약

[실습 1] 자기소개 컴포넌트 만들기

src/Profile.jsx 파일을 만들고 다음 내용을 작성:

function Profile() {
const name = "홍길동";
const age = 25;
const hobby = "독서";
return (
<div style={{ padding: '20px', border: '1px solid #ccc' }}>
<h2>{name}의 프로필</h2>
<p>나이: {age}세</p>
<p>취미: {hobby}</p>
</div>
);
}
export default Profile;

그리고 App.jsx에서 불러와서 사용:

import Profile from './Profile';
function App() {
return <Profile />;
}

1.3. JSX 문법 완전 정복

JSX는 HTML처럼 보이지만 실제로는 자바스크립트다. 그래서 HTML과는 다른 규칙들이 있다.

1.3.1. JSX의 본질

// 우리가 작성하는 JSX
const element = <h1 className="title">안녕!</h1>;
// Vite(Babel)가 변환한 실제 자바스크립트
const element = React.createElement(
'h1',
{ className: 'title' },
'안녕!'
);

JSX는 결국 React.createElement() 함수를 쉽게 쓰기 위한 **문법 설탕(Syntactic Sugar)**이다.

1.3.2. JSX 규칙 1: 하나의 최상위 태그

컴포넌트는 반드시 하나의 덩어리만 반환해야 한다. 형제 태그를 나란히 둘 수 없다.

// 오류 발생! - 두 개의 형제 태그
function App() {
return (
<h1>제목</h1>
<p>내용</p>
);
}
// 해결책 1: div로 감싸기
function App() {
return (
<div>
<h1>제목</h1>
<p>내용</p>
</div>
);
}
// 해결책 2: Fragment 사용 (권장)
function App() {
return (
<> {/* Fragment: 화면에 렌더링되지 않는 투명한 태그 */}
<h1>제목</h1>
<p>내용</p>
</>
);
}

요약

Fragment를 쓰는 이유:

불필요한 <div>가 많아지면 HTML 구조가 복잡해지고 CSS 스타일링이 어려워진다. Fragment(<></>)는 실제 DOM에 렌더링되지 않으므로 깔끔한 HTML 구조를 유지할 수 있다.

1.3.3. JSX 규칙 2: 모든 태그는 닫아야 함

HTML에서는 일부 태그를 닫지 않아도 됐지만, JSX에서는 모든 태그를 반드시 닫아야 한다.

// HTML에서는 OK, JSX에서는 오류
<input type="text">
<br>
<img src="image.jpg">
// JSX에서는 이렇게 닫아야 함 (Self-closing tag)
<input type="text" />
<br />
<img src="image.jpg" />
<hr />

1.3.4. JSX 규칙 3: 중괄호로 자바스크립트 표현식 삽입

중괄호 {} 안에 자바스크립트 **표현식(expression)**을 넣을 수 있다.

function Welcome() {
const name = "철수";
const age = 20;
const isAdult = age >= 18;
return (
<div>
{/* 변수 */}
<h1>안녕, {name}!</h1>
{/* 계산식 */}
<p>10년 후 나이: {age + 10}</p>
{/* 삼항 연산자 */}
<p>{isAdult ? "성인" : "미성년자"}</p>
{/* 함수 호출 */}
<p>대문자 이름: {name.toUpperCase()}</p>
</div>
);
}

요약

중요: 표현식만 가능!

중괄호 안에는 값을 반환하는 표현식만 넣을 수 있다.

올바른 예: 변수, 계산식, 삼항 연산자, 함수 호출 잘못된 예: if문, for문, switch문 등의 문장(statement)

// 오류! - if는 표현식이 아님
return <div>{if (true) { "참" }}</div>;
// 대신 삼항 연산자 사용
return <div>{true ? "참" : "거짓"}</div>;

1.3.5. JSX 규칙 4: 속성명 주의사항

JSX는 자바스크립트이므로, HTML 속성명이 약간 다르다.

HTMLJSX이유
classclassNameclass는 JS 예약어
forhtmlForfor는 JS 예약어
onclickonClick카멜케이스 사용
tabindextabIndex카멜케이스 사용
// JSX 방식
<div className="container">
<label htmlFor="name">이름</label>
<button onClick={handleClick}>클릭</button>
</div>

1.3.6. JSX 규칙 5: 인라인 스타일은 객체로

HTML에서는 문자열로 스타일을 지정했지만, JSX에서는 객체로 지정한다.

// JSX 방식 (객체 사용, 속성명은 카멜케이스)
<div style={{ color: 'red', fontSize: '20px' }}>텍스트</div>
// ↑ 첫 번째 중괄호: JS 표현식
// ↑ 두 번째 중괄호: 객체 리터럴
// 변수로 분리하면 더 깔끔
const myStyle = {
color: 'red',
fontSize: '20px',
backgroundColor: '#f0f0f0' // background-color → backgroundColor
};
return <div style={myStyle}>텍스트</div>;

1.3.7. JSX 규칙 6: 주석 작성법

function App() {
return (
<div>
{/* JSX 안에서 주석: 중괄호 안에 /* */ 사용 */}
<h1>제목</h1>
{/*
여러 줄 주석도 가능
이렇게 작성합니다
*/}
</div>
);
}

요약

[실습 2] JSX 문법 연습

src/Card.jsx 파일을 만들고 JSX 문법을 연습해보자:

function Card() {
const title = "React 학습";
const progress = 75;
const completed = progress >= 100;
const cardStyle = {
border: '2px solid #007bff',
borderRadius: '10px',
padding: '20px',
margin: '10px',
backgroundColor: completed ? '#d4edda' : '#fff3cd'
};
return (
<>
<div style={cardStyle}>
<h2 className="card-title">{title}</h2>
<p>진행률: {progress}%</p>
{/* 진행 바 */}
<div style={{
width: '100%',
height: '20px',
backgroundColor: '#e0e0e0',
borderRadius: '10px'
}}>
<div style={{
width: `${progress}%`,
height: '100%',
backgroundColor: '#007bff',
borderRadius: '10px'
}} />
</div>
{/* 조건부 렌더링 */}
<p>{completed ? "✅ 완료!" : "🔄 진행 중"}</p>
</div>
</>
);
}
export default Card;

1.4. 컴포넌트 분리 실습

하나의 파일(App.jsx)에 모든 코드를 넣는 것은 좋지 않다. 역할별로 파일을 나누면 코드 관리가 쉬워진다.

  • 재사용성: 한 번 만든 컴포넌트를 여러 곳에서 사용
  • 유지보수: 문제가 생기면 해당 컴포넌트 파일만 수정
  • 협업: 팀원들이 각자 다른 컴포넌트를 작업 가능
  • 테스트: 개별 컴포넌트를 독립적으로 테스트 가능

요약

언제 분리해야 할까?

  1. 재사용될 것 같은 UI: 버튼, 카드, 입력 폼 등
  2. 독립적인 기능: 로그인 폼, 검색 바, 장바구니 등
  3. 너무 길어진 컴포넌트: 100줄 이상이면 분리 고려
  4. 명확한 책임이 있는 UI: 헤더, 푸터, 사이드바 등

요약

[실습 3] 컴포넌트 분리하기

1단계: 컴포넌트 파일 생성

src/Header.jsx

function Header() {
return (
<header style={{
backgroundColor: '#282c34',
padding: '20px',
color: 'white'
}}>
<h1>내 웹사이트</h1>
<nav>
<a href="#home" style={{ color: 'white', margin: '0 10px' }}>홈</a>
<a href="#about" style={{ color: 'white', margin: '0 10px' }}>소개</a>
<a href="#contact" style={{ color: 'white', margin: '0 10px' }}>연락</a>
</nav>
</header>
);
}
export default Header;

src/MainContent.jsx

function MainContent() {
return (
<main style={{
padding: '40px',
minHeight: '500px'
}}>
<h2>메인 콘텐츠</h2>
<p>여기에 주요 내용이 들어갑니다.</p>
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: '20px',
marginTop: '20px'
}}>
<div style={{ border: '1px solid #ddd', padding: '20px' }}>
<h3>카드 1</h3>
<p>첫 번째 카드 내용</p>
</div>
</div>
</main>
);
}
export default MainContent;

2단계: App.jsx에서 조립하기

import Header from './Header';
import MainContent from './MainContent';
import Footer from './Footer';
function App() {
return (
<div style={{
display: 'flex',
flexDirection: 'column',
minHeight: '100vh'
}}>
<Header />
<MainContent />
<Footer />
</div>
);
}
export default App;

1.5. 조건부 렌더링

상황에 따라 다른 UI를 보여줘야 할 때가 많다. JSX에서는 자바스크립트의 조건 연산자를 활용한다.

1.5.1. 방법 1: 삼항 연산자 (조건 ? 참 : 거짓)

참/거짓 두 가지 경우를 모두 처리할 때 사용한다.

{isLoggedIn ? (
<button>로그아웃</button>
) : (
<button>로그인</button>
)}

1.5.2. 방법 2: AND 연산자 (&&)

조건이 참일 때만 보여주고, 거짓이면 아무것도 안 보여줄 때 사용한다.

{messageCount > 0 && (
<div>새 메시지 {messageCount}개</div>
)}

1.5.3. 방법 3: 변수에 저장하기

조건이 복잡하면 JSX 밖에서 처리하고 변수에 저장하는 것이 더 깔끔하다.

let content;
if (user.age < 18) {
content = <p>미성년자는 이용할 수 없습니다.</p>;
} else {
content = <p>환영합니다!</p>;
}
return <div>{content}</div>;