Menu

JSX란? 리액트 JSX 문법 규칙과 예제

JSX는 React 컴포넌트 안에 HTML 같은 마크업을 작성하게 해 주는 자바스크립트 문법 확장입니다. 하나의 루트 요소, 닫힌 태그, className, camelCase 속성, 중괄호, 인라인 스타일, 주석, 그리고 JSX가 무엇으로 컴파일되는지 배웁니다.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

JSX는 코드 안에 HTML 같은 마크업을 작성하게 해 주는 자바스크립트 문법 확장입니다. React 컴포넌트는 화면에 무엇이 나타나야 하는지 설명하기 위해 JSX를 반환하고, 빌드 도구가 이를 일반 자바스크립트 함수 호출로 컴파일합니다. HTML처럼 보이지만 몇 가지 규칙이 다르고, 중괄호를 쓰면 어떤 자바스크립트 값이든 마크업에 넣을 수 있습니다.

{} 안의 모든 것은 자바스크립트입니다. 속성, 메서드 호출, 배열을 목록 항목으로 바꾸는 map까지 가능합니다. skills 배열에 'design'을 추가하면 개수와 목록이 모두 업데이트됩니다.

JSX 규칙

하나의 루트 요소를 반환하기

컴포넌트는 하나의 값을 반환하므로, JSX에도 하나의 부모가 필요합니다. 최상위에 형제 요소 두 개가 있으면 문법 오류입니다. 페이지에 <div>를 하나 더 넣고 싶지 않다면 프래그먼트 <>...</>로 감싸세요.

// Error: Adjacent JSX elements must be wrapped in an enclosing tag
return (
    <h1>Title</h1>
    <p>Text</p>
);

// OK: a fragment groups them without adding a DOM element
return (
    <>
        <h1>Title</h1>
        <p>Text</p>
    </>
);

그룹에 key가 필요하면 긴 형태인 <Fragment key={id}>를 사용하세요. 프래그먼트를 참고하세요.

모든 태그 닫기

HTML에서는 일부 태그를 열어 둔 채로 둘 수 있지만 JSX에서는 안 됩니다. 자식이 없는 요소는 />로 스스로 닫습니다: <img />, <br />, <input />, 그리고 <Avatar /> 같은 직접 만든 컴포넌트도 마찬가지입니다.

className, htmlFor, camelCase 속성 사용하기

JSX 속성은 자바스크립트 객체의 키가 되므로 자바스크립트 이름 규칙을 따릅니다. class와 for는 자바스크립트의 예약어이기 때문에 JSX는 className과 htmlFor를 씁니다. 여러 단어로 된 속성과 모든 이벤트 핸들러는 camelCase입니다: tabIndex, maxLength, autoFocus, onClick, onChange. 두 가지 예외는 대시를 유지합니다. aria-*와 data-* 속성은 HTML과 똑같이 씁니다.

className="field"를 class="field"로 바꿔 보세요. React가 어쨌든 속성을 설정하기 때문에 라벨의 스타일은 유지되지만, 개발 빌드에서는 "Invalid DOM property class. Did you mean className?"이라는 로그가 남습니다. 미리보기는 프로덕션 빌드처럼 실행되므로 여기서는 그 경고가 보이지 않습니다. className을 사용하세요. 문자열 속성은 따옴표를 쓰고(type="email"), 그 밖의 값은 중괄호를 씁니다(maxLength={40}, disabled={false}).

중괄호에는 문이 아니라 표현식이 들어갑니다

{} 안에는 값을 만들어 내는 것이면 무엇이든 쓸 수 있습니다. 변수, 산술 연산, 함수 호출, 삼항 연산자, &&, 템플릿 리터럴 등입니다. if, for, const 같은 문은 값을 만들지 않으므로 그 자리에 올 수 없습니다. return 전에 작업을 끝내거나 표현식 형태를 사용하세요.

클릭하기 전에 잘 보세요. 페이지에 엉뚱한 0이 보입니다. count && <p>...</p>는 카운트가 0이면 0으로 평가되고, React는 숫자를 렌더링합니다. 0도 예외가 아닙니다. count > 0 && <p>You have mail.</p>로 바꾸면 0이 사라집니다. false, null, undefined, true는 아무것도 렌더링하지 않으므로 && 왼쪽에 불리언을 두는 것은 안전합니다. 조건부 렌더링 페이지에서 패턴들을 다룹니다.

인라인 스타일은 객체를 사용합니다

style 속성은 CSS 문자열이 아니라 자바스크립트 객체를 받습니다. 그래서 "이중 중괄호" 모양이 됩니다. 바깥 중괄호는 "여기는 자바스크립트"라는 뜻이고, 안쪽 중괄호는 객체 리터럴입니다. 속성 이름은 camelCase(backgroundColor, fontSize)이며, 크기 속성 대부분에서 일반 숫자는 픽셀을 뜻합니다.

스타일이 객체이므로 다른 값과 마찬가지로 상태에서 계산할 수 있습니다. fontSize: big ? 24 : 16을 fontSize: big ? '2em' : '1em'으로 바꿔 보세요. 문자열은 단위와 함께 그대로 전달됩니다.

주석

JSX 안의 주석은 중괄호 안에 넣습니다: {/* this is a comment */}. HTML 주석(<!-- -->)은 문법 오류입니다. 마크업 바깥의 함수 본문에서는 일반 // 주석이 평소처럼 동작합니다.

return (
    <div>
        {/* The header is hidden on small screens */}
        <Header />
    </div>
);

HTML을 JSX로 변환하기

HTML을 컴포넌트에 붙여 넣는 것은 흔한 첫 단계이고, 대부분은 그대로 동작합니다. 보통 고쳐야 하는 부분은 다음과 같습니다.

  • class는 className이 되고, for는 htmlFor가 됩니다.
  • 빈 태그는 스스로 닫습니다: <br>은 <br />이 되고, <img src="a.png">는 <img src="a.png" />가 됩니다.
  • style="color: red; font-size: 14px"는 style={{ color: 'red', fontSize: 14 }}가 됩니다.
  • 이벤트 속성은 문자열이 아니라 함수를 받습니다: onclick="save()"는 onClick={save}가 됩니다.
  • 속성 이름은 camelCase가 됩니다: tabindex는 tabIndex가 되고, SVG의 stroke-width는 strokeWidth가 됩니다.
  • <!-- comments -->는 {/* comments */}가 됩니다.
  • 최상위 요소가 여러 개라면 하나의 부모나 프래그먼트가 필요합니다.

온라인 HTML to JSX 변환기가 이 단계들을 대신해 주지만, 규칙을 알고 있으면 하나를 놓쳤을 때 오류 메시지를 쉽게 읽을 수 있습니다.

JSX는 무엇으로 컴파일될까

브라우저는 JSX를 실행할 수 없습니다. 컴파일러(Vite와 프레임워크가 대신 실행해 주는 Babel, SWC, Oxc 등)가 각 태그를 react/jsx-runtime의 함수 호출로 바꿉니다.

// What you write
const element = <h1 className="title">Hello, {name}</h1>;
const button = <Button size="small">OK</Button>;
// What the compiler produces (roughly)
import { jsx as _jsx, jsxs as _jsxs } from 'react/jsx-runtime';

const element = _jsxs('h1', { className: 'title', children: ['Hello, ', name] });
const button = _jsx(Button, { size: 'small', children: 'OK' });

소문자 태그는 문자열('h1', HTML 요소)이 되고, 대문자로 시작하는 태그는 직접 만든 함수에 대한 참조(Button)가 되며, 속성은 props 객체가 되고, 내용은 children이 됩니다. 예전 설정에서는 대신 React.createElement(type, props, ...children)으로 컴파일되었기 때문에, 오래된 파일은 React를 전혀 언급하지 않더라도 import React from 'react'로 시작합니다. 이 호출은 React 요소라고 부르는 일반 객체를 반환합니다. 직접 로그로 찍어 볼 수 있습니다.

콘솔을 보면 heading은 단지 데이터라는 것을 알 수 있습니다. 타입 'h3'와 props 객체입니다. JSX를 변수에 저장하고, 함수에 넘기고, 함수에서 반환할 수 있는 것도 이 때문입니다.

JSX는 텍스트를 자동으로 이스케이프합니다

중괄호에 넣은 문자열은 HTML이 아니라 항상 텍스트로 렌더링됩니다. 사용자가 댓글 입력창에 <img src=x onerror=alert(1)>를 입력하면, React는 이미지 태그를 만드는 대신 그 문자들을 화면에 그대로 보여 줍니다. 이 덕분에 가장 흔한 형태의 크로스 사이트 스크립팅(XSS)이 기본으로 차단됩니다.

첫 번째 문단은 태그를 일반 문자로 출력합니다. 두 번째 문단은 dangerouslySetInnerHTML을 사용하는데, 이것은 이스케이프를 건너뛰고 문자열을 실제 HTML로 삽입하므로 "our own"이 굵게 표시됩니다. 긴 이름 자체가 경고입니다. 직접 만들었거나 DOMPurify 같은 새니타이저로 정리한 HTML만 넘기세요. 사용자 입력을 그곳에 넘기면 JSX가 막아 둔 XSS 구멍이 다시 열립니다.

자주 묻는 질문

JSX는 무엇의 약자인가요?

JSX는 JavaScript XML의 약자입니다. 자바스크립트 코드 안에 HTML처럼 보이는 마크업을 작성하게 해 주는 문법 확장입니다.

JSX는 HTML인가요?

아닙니다. JSX는 HTML처럼 보이지만 React 요소를 만드는 자바스크립트 함수 호출로 컴파일됩니다. 그래서 class 대신 className을 쓰고, 속성 이름은 camelCase이며, 모든 태그를 닫아야 합니다.

JSX 없이 React를 사용할 수 있나요?

네. JSX는 선택 사항입니다. react의 createElement('h1', { className: 'title' }, 'Hello')를 직접 호출할 수 있습니다. 중첩된 마크업이 훨씬 읽기 쉽기 때문에 거의 모든 React 코드베이스가 JSX를 사용합니다.

JSX 안에서 if 문을 쓸 수 없는 이유는 무엇인가요?

JSX의 중괄호는 값을 만들어 내는 표현식을 받는데, if는 문(statement)입니다. 삼항 연산자(cond ? a : b)나 &&를 쓰거나, return 전에 변수에 값을 계산해 두세요.

JSX에서 주석은 어떻게 작성하나요?

자바스크립트 주석을 중괄호로 감싸세요: {/* like this */}. HTML 주석(<!-- -->)은 JSX에서 문법 오류입니다.

Coddy 프로그래밍 언어 일러스트

Coddy로 코딩 배우기

시작하기