본문으로 건너뛰기
프론트엔드 기초집LESSON 04

JSX와 컴포넌트

난이도초급
예상 시간35분
선수지식이전 강의

4강. JSX와 컴포넌트

1. 이번 강의에서 해결할 문제​

App 하나에 모든 마크업을 넣으면 화면이 커질수록 수정 위치를 찾기 어렵습니다. 반복될 게임 카드와 페이지 헤더를 의미 있는 컴포넌트로 나눕니다.

2. 학습 목표​

  • JSX의 기본 규칙을 적용한다.
  • 함수 컴포넌트를 만들고 조합한다.
  • 컴포넌트의 책임 경계를 정한다.

3. 핵심 개념​

JSX는 JavaScript 안에 마크업을 표현하는 문법입니다. 반환할 형제 요소는 하나의 부모 또는 Fragment로 감싸고, 태그는 반드시 닫습니다. HTML의 class 대신 className을 씁니다. 중괄호 안에는 JavaScript 표현식을 넣을 수 있습니다.

JSX는 브라우저가 그대로 실행하는 HTML 파일이 아닙니다. Vite의 변환 과정을 거쳐 React가 이해하는 JavaScript 호출이 되고, React는 그 결과를 실제 DOM에 반영합니다. 그래서 HTML과 닮았지만 JavaScript 예약어인 class 대신 className을 쓰고, 모든 태그를 명확히 닫습니다.

용어 정리​

용어뜻예
JSXJavaScript 안에서 UI 구조를 표현하는 문법<h1>{serviceName}</h1>
함수 컴포넌트JSX를 반환하는 대문자 이름의 함수SiteHeader
표현식하나의 값으로 계산되는 JavaScript 코드{serviceName}, {2 + 3}
Fragment불필요한 DOM 요소 없이 형제 JSX를 묶는 요소<>...</>
export/import다른 파일의 컴포넌트를 공개하고 가져오는 모듈 문법export function, import { ... }

가장 작은 예제로 시작하기​

function Greeting() {
const learnerName = '민지';
return <p>안녕하세요, {learnerName}님!</p>;
}

learnerName은 JavaScript 문자열이고, JSX의 중괄호가 그 값을 문장 사이에 넣습니다. 화면에는 중괄호나 변수 이름이 아니라 안녕하세요, 민지님!이 나타납니다. 중괄호 안에는 if 문 전체가 아니라 값으로 계산되는 표현식을 넣습니다.

컴포넌트 이름은 대문자로 시작해야 React가 일반 HTML 태그와 구분합니다. 컴포넌트는 한 가지 UI 책임에 집중시키되 단순한 div 하나마다 분리하지는 않습니다.

4. 단계별 코드 예시​

파일 경로: game-release-dashboard/src/components/SiteHeader.tsx

아래 세 파일을 작성하기 전에는 App 하나만 화면을 담당합니다. 작성 후에는 헤더와 카드가 독립 파일에 있고 App은 두 컴포넌트를 조합하는 역할만 맡습니다.

export function SiteHeader() {
const serviceName = 'Release Radar';

return (
<header>
<p>GAME RELEASE DASHBOARD</p>
<h1>{serviceName}</h1>
</header>
);
}

파일 경로: game-release-dashboard/src/components/GameCard.tsx

export function GameCard() {
return (
<article className="game-card">
<p>2026년 9월 18일</p>
<h2>Star Harbor</h2>
<p>PC · 탐험</p>
<button type="button">관심 게임</button>
</article>
);
}

파일 경로: game-release-dashboard/src/App.tsx

import { GameCard } from './components/GameCard';
import { SiteHeader } from './components/SiteHeader';

function App() {
return (
<>
<SiteHeader />
<main>
<GameCard />
</main>
</>
);
}

export default App;

중요한 줄을 따라가기​

  1. export function SiteHeader()는 다른 파일에서 가져올 수 있는 컴포넌트를 공개합니다.
  2. {serviceName}은 문자열 값을 h1 내용으로 계산합니다. 따옴표로 "{serviceName}"이라 쓰면 변수 값이 아니라 글자가 됩니다.
  3. className="game-card"는 최종 DOM의 class="game-card"가 되어 CSS 선택자와 연결됩니다.
  4. <button type="button">은 폼 안에서도 의도치 않은 제출을 만들지 않는 일반 버튼입니다.
  5. import { GameCard } ...의 중괄호는 이름 있는 export를 가져온다는 뜻입니다. 경로와 대소문자가 실제 파일과 같아야 합니다.
  6. Fragment <>...</>가 SiteHeader와 main을 하나의 반환값으로 묶지만 개발자 도구 DOM에는 별도 태그를 만들지 않습니다.

실행 결과와 컴포넌트 호출 흐름​

브라우저에는 서비스 머리말 다음에 한 게임 카드가 표시됩니다.

GAME RELEASE DASHBOARD
Release Radar

2026년 9월 18일
Star Harbor
PC · 탐험
[관심 게임]

호출 순서는 App() 계산 → <SiteHeader />에서 SiteHeader() 계산 → <GameCard />에서 GameCard() 계산 → React가 결과 DOM 반영입니다. React 개발자 도구를 사용하면 App > SiteHeader, App > GameCard 계층을 확인할 수 있습니다.

5. 코드가 동작하는 이유​

<SiteHeader />를 만나면 React는 SiteHeader 함수를 실행해 반환된 JSX를 그 위치에 배치합니다. 두 컴포넌트는 export되고 App에서 import되어 모듈 경계를 넘습니다. Fragment는 불필요한 DOM 요소 없이 형제 요소를 묶습니다.

6. 자주 하는 실수와 해결법​

  • 컴포넌트 이름을 소문자로 시작: React가 HTML 태그로 해석합니다. PascalCase를 사용합니다.
  • 여러 최상위 요소를 그대로 반환: Fragment 또는 의미 있는 부모 요소로 감쌉니다.
  • 이벤트 버튼에 type 생략: 폼 안에서 제출 버튼이 될 수 있으므로 일반 버튼은 type="button"을 명시합니다.

7. 직접 실습​

실습 목표​

DashboardIntro 컴포넌트를 만들고 export/import해 기존 컴포넌트 사이에 배치합니다.

시작 전 상태​

src/components/SiteHeader.tsx, GameCard.tsx와 src/App.tsx가 예제와 같은지 확인하고 개발 서버를 실행합니다. 변경 전 화면을 한 번 확인합니다.

1단계: 따라 하기​

src/components/DashboardIntro.tsx를 만들고 서비스 설명 문단과 h2 제목을 반환합니다. 이름 있는 export를 사용해 App.tsx에서 import하고 SiteHeader와 main 사이에 배치합니다.

2단계: 값을 바꾸고 결과 비교​

컴포넌트 안에 const currentMonth = '9월';을 만들고 제목을 {currentMonth} 출시작으로 바꿉니다. 값을 10월로 변경했을 때 해당 제목만 바뀌는지 확인합니다.

3단계: 직접 적용​

GameCard를 한 번 더 배치해 카드가 반복되는지 확인합니다. 두 카드 내용이 똑같은 이유를 설명하고, 서로 다른 데이터를 전달하려면 다음 강의의 props가 필요하다고 기록합니다.

4단계: 스스로 확인​

  • 모든 컴포넌트 이름이 대문자로 시작하나요?
  • export 방식과 import 중괄호가 서로 맞나요?
  • 개발자 도구 DOM에 Fragment용 불필요한 div가 생기지 않았나요?

막혔을 때​

  • Failed to resolve import: 상대 경로, 파일명 대소문자, 확장 위치를 확인합니다.
  • 컴포넌트가 소문자 태그로 보임: 함수와 JSX 사용 이름을 PascalCase로 바꿉니다.
  • Adjacent JSX elements 오류: 형제 요소를 Fragment나 의미 있는 부모로 감쌉니다.
  • 화면이 비어 있음: 브라우저 Console과 터미널의 첫 오류 파일·줄 번호부터 확인합니다.

8. 이해 점검 질문 3개​

  1. 사용자 이름 변수를 제목에 표시해야 할 때 JSX의 어느 문법을 사용하며, if 문 전체를 바로 넣을 수 없는 이유는 무엇인가요?
  2. 컴포넌트 이름이 대문자로 시작해야 하는 이유는 무엇인가요?
  3. Fragment는 실제 div와 어떤 차이가 있나요?

9. 핵심 요약​

JSX는 데이터와 마크업을 함께 표현하고, 컴포넌트는 UI 책임을 이름 있는 함수로 분리합니다. 다음 강의에서는 고정된 GameCard에 외부 데이터를 전달합니다.

다음 강의 연결​

5강. Props로 데이터 전달에서는 지금 두 번 배치하면 같은 내용만 보이는 GameCard에 서로 다른 제목·출시일·플랫폼을 전달합니다.

MINI QUIZ

JSX와 컴포넌트 미니 퀴즈

선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.

0 / 2
  1. 문제 1“JSX와 컴포넌트” 실행 결과를 설명하는 내용으로 가장 알맞은 것은 무엇인가요?
  2. 문제 2“JSX와 컴포넌트”에서 ‘이벤트 버튼에 type 생략’ 문제가 생겼습니다. 가장 알맞은 진단 또는 대응은 무엇인가요?
LESSON STATUS

학습을 마쳤나요?

직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.

4강. JSX와 컴포넌트 미완료 상태