React 독학 가이드 3 - JSX로 UI 표현하기

learning by Seven Fingers Studio 16분
ReactJSXJavaScriptUI웹개발

React 코드를 처음 보면 좀 당황스러워요. JavaScript인데 HTML이 들어있거든요.

const element = <h1>안녕하세요!</h1>;

이게 뭐지? JavaScript에서 HTML을? 이게 바로 JSX입니다.

JSX가 뭔가요?

JSX는 JavaScript XML의 약자예요. JavaScript 안에서 HTML과 비슷한 문법을 쓸 수 있게 해주는 거죠.

사실 브라우저는 JSX를 이해 못 해요. Babel이라는 도구가 JSX를 일반 JavaScript로 변환해줍니다.

// 우리가 쓰는 JSX
const element = <h1>안녕하세요!</h1>;

// 실제로 변환되는 코드
const element = React.createElement('h1', null, '안녕하세요!');

근데 React.createElement 쓰면 너무 복잡하잖아요? 그래서 JSX를 쓰는 거예요. 훨씬 직관적이니까요.

JSX 기본 문법

1. 하나의 루트 요소

JSX는 반드시 하나의 요소로 감싸야 해요.

// ❌ 에러!
function App() {
  return (
    <h1>제목</h1>
    <p>내용</p>
  );
}

// ⭕ div로 감싸기
function App() {
  return (
    <div>
      <h1>제목</h1>
      <p>내용</p>
    </div>
  );
}

// ⭕ Fragment 사용 (빈 태그)
function App() {
  return (
    <>
      <h1>제목</h1>
      <p>내용</p>
    </>
  );
}

<> </>를 쓰면 불필요한 div 없이 여러 요소를 묶을 수 있어요.

2. 모든 태그는 닫아야 함

HTML에서는 <img>, <br>, <input> 같은 태그를 안 닫아도 돼요. 근데 JSX에서는 무조건 닫아야 해요.

// ❌ 에러!
<img src="photo.jpg">
<input type="text">
<br>

// ⭕ self-closing 태그
<img src="photo.jpg" />
<input type="text" />
<br />

/>로 닫아주세요.

3. className 사용

HTML에서는 class를 쓰지만, JSX에서는 className을 씁니다.

// ❌ 에러 (class는 JavaScript 예약어)
<div class="container">

// ⭕ className 사용
<div className="container">

class가 JavaScript에서 클래스 선언에 쓰이는 예약어라서 그래요.

4. 카멜케이스 속성

HTML 속성 중 하이픈(-)이 들어간 것들은 카멜케이스로 바꿔야 해요.

// HTML
<div onclick="handleClick()" tabindex="0">
<label for="name">

// JSX
<div onClick={handleClick} tabIndex="0">
<label htmlFor="name">

주요 변환:

  • onclickonClick
  • onchangeonChange
  • tabindextabIndex
  • forhtmlFor
  • maxlengthmaxLength

JavaScript 표현식 넣기

JSX의 진짜 장점은 JavaScript를 바로 넣을 수 있다는 거예요. **중괄호 {}**를 사용합니다.

변수 출력

function Greeting() {
  const name = "홍길동";
  const age = 25;

  return (
    <div>
      <h1>안녕하세요, {name}님!</h1>
      <p>나이: {age}세</p>
    </div>
  );
}

실행 결과:

안녕하세요, 홍길동님!

나이: 25세

JavaScript 변수가 JSX 안에서 출력된 결과입니다

계산식

function Calculator() {
  const a = 10;
  const b = 20;

  return (
    <div>
      <p>{a} + {b} = {a + b}</p>
      <p>{a} × {b} = {a * b}</p>
    </div>
  );
}

함수 호출

function formatDate(date) {
  return `${date.getFullYear()}년 ${date.getMonth() + 1}월 ${date.getDate()}일`;
}

function Today() {
  return (
    <p>오늘 날짜: {formatDate(new Date())}</p>
  );
}

삼항 연산자

function LoginStatus({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? (
        <p>환영합니다!</p>
      ) : (
        <p>로그인이 필요합니다.</p>
      )}
    </div>
  );
}

실행 결과:

환영합니다!

isLoggedIn = true

로그인이 필요합니다.

isLoggedIn = false

조건에 따라 다른 내용이 렌더링됩니다

조건부 렌더링

특정 조건에서만 요소를 보여주고 싶을 때가 있어요.

삼항 연산자

function Notification({ count }) {
  return (
    <div>
      {count > 0 ? (
        <span className="badge">{count}</span>
      ) : null}
    </div>
  );
}

&& 연산자 (더 간단)

function Notification({ count }) {
  return (
    <div>
      {count > 0 && <span className="badge">{count}</span>}
    </div>
  );
}

&& 앞이 true면 뒤의 요소를 렌더링하고, false면 아무것도 안 나와요.

주의: 0은 렌더링됨

// ⚠️ count가 0이면 "0"이 화면에 나옴!
{count && <span>{count}</span>}

// ⭕ 명확하게 비교
{count > 0 && <span>{count}</span>}

&& 쓸 때 숫자 0은 falsy지만 렌더링되니까 주의하세요.

리스트 렌더링

배열을 화면에 보여줄 때는 map()을 씁니다.

function FruitList() {
  const fruits = ["사과", "바나나", "오렌지", "포도"];

  return (
    <ul>
      {fruits.map((fruit, index) => (
        <li key={index}>{fruit}</li>
      ))}
    </ul>
  );
}

실행 결과:

  • 사과
  • 바나나
  • 오렌지
  • 포도

배열의 각 항목이 리스트로 렌더링됩니다

key가 뭐예요?

key는 React가 어떤 항목이 바뀌었는지 알 수 있게 해주는 고유 식별자예요.

// ⚠️ index를 key로 쓰는 건 비추천 (순서가 바뀔 수 있으면)
{items.map((item, index) => (
  <li key={index}>{item}</li>
))}

// ⭕ 고유 ID가 있으면 그걸 사용
{users.map((user) => (
  <li key={user.id}>{user.name}</li>
))}

데이터에 고유 ID가 있으면 그걸 쓰세요. 없으면 index 써도 되긴 한데, 목록 순서가 바뀔 수 있으면 문제가 생길 수 있어요.

인라인 스타일

JSX에서 인라인 스타일은 객체로 넣어요.

// ❌ HTML 방식
<div style="color: red; font-size: 20px;">

// ⭕ JSX 방식 (객체로)
<div style={{ color: 'red', fontSize: '20px' }}>

중괄호가 두 개인 이유:

  • 바깥 {}: JSX에서 JavaScript 쓰겠다
  • 안쪽 {}: JavaScript 객체

스타일 속성도 카멜케이스예요:

  • font-sizefontSize
  • background-colorbackgroundColor
  • margin-topmarginTop

스타일 객체 분리

function StyledBox() {
  const boxStyle = {
    width: '200px',
    height: '200px',
    backgroundColor: '#3498db',
    borderRadius: '10px',
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
    color: 'white',
    fontSize: '18px'
  };

  return <div style={boxStyle}>스타일 박스</div>;
}

주석 달기

JSX 안에서 주석은 이렇게 써요:

function App() {
  return (
    <div>
      {/* JSX 안에서의 주석 */}
      <h1>안녕하세요</h1>

      {/*
        여러 줄
        주석도 가능
      */}
      <p>내용</p>
    </div>
  );
}

{/* */} 형태로 써야 해요. 일반 // 주석은 JSX 안에서 안 돼요.

실전 예제: 상품 카드

배운 거 다 써서 상품 카드를 만들어볼게요.

function ProductCard() {
  const product = {
    id: 1,
    name: "에어팟 프로 2",
    price: 359000,
    discount: 15,
    inStock: true,
    tags: ["무선", "노이즈캔슬링", "Apple"]
  };

  const discountedPrice = product.price * (1 - product.discount / 100);

  const cardStyle = {
    border: '1px solid #ddd',
    borderRadius: '12px',
    padding: '20px',
    maxWidth: '300px'
  };

  return (
    <div style={cardStyle}>
      {/* 상품명 */}
      <h2>{product.name}</h2>

      {/* 가격 정보 */}
      <div>
        <span style={{ textDecoration: 'line-through', color: '#999' }}>
          {product.price.toLocaleString()}원
        </span>
        <span style={{ color: 'red', marginLeft: '10px' }}>
          {product.discount}% OFF
        </span>
      </div>
      <p style={{ fontSize: '24px', fontWeight: 'bold' }}>
        {Math.floor(discountedPrice).toLocaleString()}원
      </p>

      {/* 재고 상태 */}
      {product.inStock ? (
        <span style={{ color: 'green' }}>✓ 재고 있음</span>
      ) : (
        <span style={{ color: 'red' }}>품절</span>
      )}

      {/* 태그 목록 */}
      <div style={{ marginTop: '15px' }}>
        {product.tags.map((tag) => (
          <span
            key={tag}
            style={{
              background: '#f0f0f0',
              padding: '4px 8px',
              borderRadius: '4px',
              marginRight: '5px',
              fontSize: '12px'
            }}
          >
            #{tag}
          </span>
        ))}
      </div>
    </div>
  );
}

다음 단계

JSX 문법을 익혔으니, 다음 글에서는 Props를 배울 거예요. Props를 쓰면 컴포넌트에 데이터를 전달할 수 있어요. 위의 상품 카드도 Props를 쓰면 여러 상품을 쉽게 보여줄 수 있죠.

JSX가 처음엔 어색할 수 있는데, 쓰다 보면 오히려 HTML보다 편하다고 느껴질 거예요!


React 시리즈 탐색:

← 블로그 목록으로