JSX란?
- JavaScript XML의 약자
- JavaScript에 XML/HTML-like 문법을 추가하여 HTML과 비슷한 마크업을 작성할 수 있게 해주는 문법
- JavaScript 코드 안에서 UI 관련 작업을 할 수 있게 해줌
- React에서 UI를 표현할 때 사용
1. 기본적인 JSX 사용법
JSX는 다음과 같은 규칙을 따른다.
- 반드시 하나의 부모 요소로 감싸져야 함
- 모든 태그는 닫혀야 함
- JavaScript 표현식은 중괄호{}로 감싸야 함
- 속성명은 camelCase 사용
- class → className
- for → htmlFor
// 1. 간단한 JSX 예제
const hello = <h1>안녕하세요!</h1>;
// 2. 변수 사용하기
const name = "홍길동";
const greeting = <h1>안녕하세요, {name}님!</h1>;
// 3. 기본적인 컴포넌트
function Welcome() {
return <h1>환영합니다!</h1>;
}
HTML을 자바스크립트에서 직접 리터럴 형태로 타이핑 할 수 있다는 점에서 일부 하드 코딩적인 성격도 가진다.
2. JSX와 일반 HTML의 차이점
// HTML
<div class="container">
<label for="name">이름:</label>
<input type="text">
</div>
// JSX
<div className="container">
<label htmlFor="name">이름:</label>
<input type="text" />
</div>
// 주요 차이점
// 1. class → className
// 2. for → htmlFor
// 3. 모든 태그는 닫혀야 함
- class 속성은 className으로 대체한다.
- 이유: JavaScript의 예약어 'class'와 충돌 방지
- 예약어란?
- for 속성은 htmlFor로 대체한다. 여기서 for은 반복문이 아니라 html의 라벨 태그에서 사용되는 for이다.
- JSX에서 모든 태그는 닫혀야 한다. 간단히 말하자면 태그가 끝났다는 의미의 /> 이라는 부분이 무조건 들어가야한다.기존 html에서는 명시적으로 닫았다는 표현을 사용하지 않아도 됐던 태그들(link, img 등)까지 전부 다 해당한다.
<!-- HTML에서 닫지 않아도 되는 태그들 -->
<img src="image.jpg">
<input type="text">
<br>
<hr>
<meta charset="UTF-8">
<link rel="stylesheet" href="style.css">
<area>
<base>
<col>
<embed>
<source>
<track>
<!-- JSX에서는 반드시 닫아야 함 -->
<img src="image.jpg" />
<input type="text" />
<br />
<hr />
<meta charset="UTF-8" />
<link rel="stylesheet" href="style.css" />
<area />
<base />
<col />
<embed />
<source />
<track />
3. JSX에서 JavaScript 사용하기
function Greeting() {
// JavaScript 변수 선언
const user = {
name: "홍길동",
age: 20
};
// JavaScript 로직 사용
const isAdult = user.age >= 20;
return (
<div>
<h1>{user.name}님 안녕하세요!</h1>
{/* 조건문 사용 */}
{isAdult ? <p>성인입니다.</p> : <p>미성년자입니다.</p>}
</div>
);
}
jsx에 코드만 작성한다고 html에 반영되는 것은 아니고 실제로는 jsx에 적힌 저부분을 html로 변환해주는 과정을 거치게된다.
이 모든 과정은
- JSX → JavaScript 변환 (빌드 타임)
- React Element 생성 (런타임)
- Virtual DOM 처리 (런타임)
- 실제 DOM 업데이트 (런타임)
순서로 진행된다.
1. jsx 작성
function App() {
return (
<div className="greeting">
<h1>Hello!</h1>
</div>
);
}
2. 컴파일러(Babel/esbuild) 변환
// JavaScript 코드로 변환
function App() {
return React.createElement(
'div',
{ className: 'greeting' },
React.createElement('h1', null, 'Hello!')
);
}
3. React Element 생성
// 실제 React Element 객체
{
type: 'div',
props: {
className: 'greeting',
children: {
type: 'h1',
props: {
children: 'Hello!'
}
}
}
}
4. Virtual DOM 생성/업데이트
5. 실제 DOM 반영
<!-- 최종적으로 HTML에 추가되는 코드 -->
<div class="greeting">
<h1>Hello!</h1>
</div>
물론 해당 변환 과정은 자동으로 진행되며 사용자가 작성하는 것은 JSX부분과 HTML에 반영하기 위한 컴포넌트 사용 부분이다.
1. 프로젝트 설정
# Vite를 사용한 React 프로젝트 생성
npm create vite@latest my-app -- --template react
cd my-app
npm install
node.js를 먼저 설치해야 npm 기능을 사용할 수 있다.
2. 루트 컴포넌트 작성(App.jsx)
// App.jsx
function App() {
return (
<div className="greeting">
<h1>Hello!</h1>
</div>
);
}
export default App;
실제 html에 반영하고자 하는 레이아웃을 작성하는 부분이다.
코드 작성 후에 export default 함수 이름; 을 통해서 디폴트 값으로 반영되는 함수를 export 해줘야한다. 그래야 다른 jsx에서 해당 jsx의 정보를 받아올 수 있다.
3. React 앱 마운트(main.jsx)
// main.jsx
import ReactDOM from 'react-dom/client'
import App from './App'
ReactDOM.createRoot(document.getElementById('root')).render(
<App />
)
실제로 html를 렌더링 하는 jsx와 렌더링jsx를 받아서 html로 정보를 전달하는 jsx를 각각 별개로 파일로 관리한다. 반드시 필요한 과정은 아니고 통합해서 관리하는 것보다 분리하는 것이 재사용성이나 기능 분리 측면에서 훨씬 유용하기 때문이다.
✅ import 부분
1️⃣ import ReactDOM from 'react-dom/client' 부분을 통해서
ReactDOM 변수가 react-dom/client 모듈의 디폴트 값을 참조하도록 만든다.
2️⃣ import App from './App' 부분을 통해서
App 변수가 ./App 모듈의 디폴트 값을 참조하도록 만든다.
💡 디폴트 값
- 해당 모듈의 기본 내보내기(Default Export) 값이다.
- 정확히 말하자면 해당 모듈에서 export default로 내보낸 값이다.
'react-dom/client'는 리액트 라이브러리의 클라이언트 측 렌더링 관련 모듈이다.
이 모듈은 아래 기능들을 수행한다.
- 웹 브라우저에서 React 애플리케이션을 렌더링하기 위한 기능 제공
- createRoot() 같은 클라이언트 전용 메서드 포함
- 브라우저 환경에서 React 컴포넌트를 DOM에 렌더링하는 데 필요한 기능 제공
라이브러리에서 자체적으로 내장된 부분이어서 사용자가 건드릴 필요는 없다.
✅ App.jsx를 기준으로 한다면
// App.jsx
function App() {
return (
<div className="greeting">
<h1>Hello!</h1>
</div>
);
}
export default App;
App 함수가 기본 내보내기(Default Export) 값이다.
✅ ReactDOM 이하 render 부분
ReactDOM.createRoot(document.getElementById('root')).render()를 통해서 지정된 HTML 요소에 React 컴포넌트를 마운트(렌더링)한다.
각각의 단어는 아래와 같은 의미를 가지고 있다.
1. ReactDOM
- React의 DOM 관련 메서드를 포함하는 객체
- 웹 브라우저에서 React 컴포넌트를 렌더링하는 역할
- react-dom/client 모듈의 기본 내보내기(Default Export) 값을 참조한다
2. .createRoot()
- React 18에서 도입된 메서드
- 애플리케이션의 루트를 생성하는 함수
- 새로운 렌더링 방식 제공
3. document
- 웹 페이지의 전체 HTML 문서를 나타내는 객체
- 웹 페이지의 요소에 접근할 수 있게 해줌
4. .getElementById('')
- HTML에서 특정 id를 가진 요소를 선택하는 메서드
- 'root' id를 가진 DOM 요소를 찾아 반환
- 'root'가 아니라 다른 id여도 상관없다.
5. .render()
- 선택된 루트 요소에 React 컴포넌트를 렌더링하는 메서드
- 실제로 화면에 컴포넌트를 그리는 역할
전체 구조는 "React DOM에서 'root' id를 가진 요소를 루트로 설정하고, 그 위에 컴포넌트를 렌더링한다"는 의미이다.
1️⃣ import ReactDOM from 'react-dom/client' 부분을 좀 더 보충 설명하자면
ReactDOM은 임의의 변수명이다. 때문에 이름을 바꿔도 되지만 실제로는 거의 모든 개발자가 ReactDOM 이라는 이름을 그대로 사용한다.
마치 숙어와 마찬가지로 React 공식 문서, 각종 튜토리얼, 커뮤니티 등에서 ReactDOM이라는 이름을 사용하여 이미 업계 표준 형식의 🤝관례가 되어서 이를 따르는 것이 일반적이고 효율적이다.
4. HTML 파일 설정 (index.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="src/main.jsx" ></script>
</body>
</html>
가장 중요한 부분이다.
<div id="root"></div> 부분으로 main.jsx에서 받아온 정보를 통해 실제 html이 렌더링 되는 div 요소를 만들어줘야 한다.
앞서 말했듯이 id는 아무거나 사용해도 무관하다. 또한 태그가 꼭 <div>일 필요도 없다.
<div>같은 블록 레벨 요소를 권장해서 사용하긴 하지만 이론적으로는 모든 태그를 사용가능하긴 하다.
그렇지만 가급적 <div>를 사용하는 것이 좋다.
<div> 태그는 특별한 의미 없이 내용을 그룹화하거나 스타일링을 위한 컨테이너 역할을 하는 의미 없는(non-semantic) 태그지만
<p>같은 태그는 문단(Paragraph)을 나타내는 의미 있는(semantic) 태그이다. 문단을 구성하기 위한 스타일이나 기능 등도 포함하고 있는 개념이다.
때문에 특별한 기능이 있는 태그를 루트 콘테이너로 사용하게 되면 오류가 발생할 가능성이 무척 높다. 물론 텍스트만 렌더링하거나 하면 별 문제가 없겠지만 html에서 텍스트만 사용하는 경우는 거의 없다.
때문에 <div>가 가장 권장되는 태그이며 그나마 사용될만한 태그가 <main> 태그이다.
<script type="module" src="src/main.jsx" ></script> 부분은 React 애플리케이션을 특정 HTML root 요소에 렌더링하기 위한 모듈 스크립트를 로드하는 태그이다.
src/main.jsx 부분에서 내보내기 된 데이터를 가져온다.
해당 데이터는 3번 단계에서 선언한
ReactDOM.createRoot(document.getElementById('root')).render(<App />) 부분인데 html의 root에 App의 데이터를 가져와서 html 을render한다.
즉
"<script type="module" src="src/main.jsx"></script> 태그는 src/main.jsx 스크립트 파일을 로드하고 실행시키는 역할을 한다.
이 스크립트 파일 내부의 ReactDOM.createRoot(document.getElementById('root')).render(<App />) 코드는
- 먼저 document.getElementById('root')를 통해 루트로 사용할 HTML 요소의 참조를 얻는다.
- 여기서는 <div id="root"></div> 태그이다.
- 그 다음 ReactDOM.createRoot로 React 루트를 생성한다.
- 마지막으로 render(<App />)를 호출하여 <App /> 컴포넌트가 정의하는 UI를 해당 #root 요소 내부에 렌더링한다.
최종적으로는 아래와 같은 html이 업데이트된다.
과정은 모듈 로드(APP) → root 요소 선택(<div id root></div>) → App 컴포넌트 렌더링(HTML 업데이트)의 과정으로 진행된다.
<div id="root">
<div class="greeting">
<h1>Hello!</h1>
</div>
</div>
4. jsx 사용시 지켜야 하는 요소
앞서 jsx를 사용할 때 지켜야 하는 요소들에 대해 언급했는데
- 반드시 하나의 부모 요소로 감싸져야 함
- 모든 태그는 닫혀야 함
- JavaScript 표현식은 중괄호{}로 감싸야 함
- 속성명은 camelCase 사용
- class → className
- for → htmlFor
그중 반드시 하나의 부모 요소로 감싸져야 한다는 제약을 설명해보고자 한다.
// 1. 반드시 하나의 부모 요소로 감싸야 함
function Good() {
return (
<div>
<h1>제목</h1>
<p>내용</p>
</div>
);
}
기본적으로 jsx는 html로 렌더링 되는 과정에서 부모 요소로 감싸져 있어야만 오류가 나지 않는다.
이는 jsx 자체의 의도적인 제약이다. 그런데 항상 <div> 태그를 부모요소로만 사용할 필요는 없다.
다른 대안 중 하나가 바로 fragment(프래그먼트)이다.
// 2. Fragment 사용 가능
function AlsoGood() {
return (
<>
<h1>제목</h1>
<p>내용</p>
</>
);
}
리액트 기초 01 - 핵심 요소 2 : JSX 요소 Fragment
리액트는 html을 JS 내에서 구현할 수 있도록 JSX(JavaScript XML) 문법을 따른다. 때문에 컴포넌트가 반환하는 여러 요소들을 무조건 하나의 부모 요소로 감싸야한다. 그런데 부모 요소로 div 태그를
lamarck009.tistory.com
'React > 기초' 카테고리의 다른 글
리액트 기초 01 - 핵심 요소 2 : 함수형 컴포넌트 (0) | 2025.04.06 |
---|---|
리액트 기초 01 - 핵심 요소 2 : 🧩 컴포넌트 (Component) 란? (0) | 2025.04.06 |
리액트 기초 01 - 핵심 요소 1 : JSX 요소 Fragment (0) | 2025.04.06 |
리액트 기초 01 - 핵심 요소 1 : JSX의 기본적 사용법 (0) | 2025.04.06 |
리액트란 무엇인가? (0) | 2025.04.04 |