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

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

1.2. 컴포넌트 완전 정복

1.2.1. 컴포넌트의 기본 구조

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

1
// 가장 간단한 컴포넌트
2
function Hello() {
3
return <div>안녕!</div>;
4
}
5
6
// 화살표 함수로도 작성 가능
7
const Hello = () => {
8
return <div>안녕!</div>;
9
};
10
11
// return이 한 줄이면 괄호 생략 가능
12
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해야 한다. 아무것도 반환하지 않으면 오류 발생.

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

요약

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

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

Button.jsx
1
function Button() {
2
return <button>클릭</button>;
3
}
4
5
export default Button; // 다른 파일에서 import 가능
6
7
// 또는 한 줄로
8
export default function Button() {
9
return <button>클릭</button>;
10
}

1.2.3. 컴포넌트 사용하기

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

1
// Button 컴포넌트 정의
2
function Button() {
3
return <button>클릭하세요</button>;
4
}
5
6
// App 컴포넌트에서 Button 사용
7
function App() {
8
return (
9
<div>
10
<h1>내 앱</h1>
11
<Button /> {/* Button 컴포넌트를 HTML 태그처럼 사용 */}
12
<Button /> {/* 여러 번 재사용 가능 */}
13
</div>
14
);
15
}

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

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

1
function 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 파일을 만들고 다음 내용을 작성:

1
function 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
15
export default Profile;

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

1
import Profile from './Profile';
2
3
function App() {
4
return <Profile />;
5
}

1.3. JSX 문법 완전 정복

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

1.3.1. JSX의 본질

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

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

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

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

1
// 오류 발생! - 두 개의 형제 태그
2
function App() {
3
return (
4
<h1>제목</h1>
5
<p>내용</p>
6
);
7
}
8
9
// 해결책 1: div로 감싸기
10
function App() {
11
return (
12
<div>
13
<h1>제목</h1>
14
<p>내용</p>
15
</div>
16
);
17
}
18
19
// 해결책 2: Fragment 사용 (권장)
20
function 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)**을 넣을 수 있다.

1
function 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는 표현식이 아님
2
return <div>{if (true) { "참" }}</div>;
3
4
// 대신 삼항 연산자 사용
5
return <div>{true ? "참" : "거짓"}</div>;

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

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

HTMLJSX이유
classclassNameclass는 JS 예약어
forhtmlForfor는 JS 예약어
onclickonClick카멜케이스 사용
tabindextabIndex카멜케이스 사용
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
// 변수로 분리하면 더 깔끔
7
const myStyle = {
8
color: 'red',
9
fontSize: '20px',
10
backgroundColor: '#f0f0f0' // background-color → backgroundColor
11
};
12
13
return <div style={myStyle}>텍스트</div>;

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

1
function 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 문법을 연습해보자:

1
function 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
42
export default Card;

1.4. 컴포넌트 분리 실습

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

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

요약

언제 분리해야 할까?

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

요약

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

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

src/Header.jsx

1
function 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
18
export default Header;

src/MainContent.jsx

1
function 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
25
export default MainContent;

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

1
import Header from './Header';
2
import MainContent from './MainContent';
3
import Footer from './Footer';
4
5
function 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
19
export 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 밖에서 처리하고 변수에 저장하는 것이 더 깔끔하다.

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