[React] 3. JSX 문법

김주희's avatar
Aug 18, 2025
[React] 3. JSX 문법
 
정리
 
Cursor 세팅
notion image
notion image
notion image
notion image
notion image
 
notion image
 

0. 실행하기

npm start
 
notion image
notion image
notion image
 
react-lab 위치가 아닌 myappp 위치에서 실행해야 한다!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
notion image
 

1. JSX

  • 자바스크립트의 공식 문법은 아님
  • React에서 UI를 구성하기 위해 사용하는 문법
  • JavaScript XML
  • HTML 태그처럼 작성하면 React가 문자열로 변환하여 반환
 

문법 1. 태그는 하나만 return

  • return 안에는 반드시 하나의 태그만 존재해야 함.
  • 여러 개의 태그를 넣으면 오류 발생.
  • 여러 개의 요소를 넣고 싶으면 <div> 같은 부모 태그로 감싸야 함.
  • 불필요한 div 대신 <> ... </> (Fragment) 문법도 자주 사용.
  • 규칙: JSX는 html과 비슷하지만 정확히 html은 아니고, “JS 안에서 XML 문법을 쓰는 것”
 
// ❌ 오류 return ( <h1>안녕</h1> <h2>하세요</h2> ); // ✅ 올바른 방법 (div로 감싸기) return ( <div> <h1>안녕</h1> <h2>하세요</h2> </div> ); // ✅ Fragment 사용 return ( <><h1>안녕</h1> <h2>하세요</h2> </> );
 
notion image
 
notion image
 
notion image
 

2. import

  • import는 js 문법

1. ES Modules 등장 이전 (CommonJS / AMD / Script 태그 시절)

(1) Script 태그 방식 (초창기 웹)
  • HTML에서 그냥 <script src="..."> 여러 개 불러옴.
  • 전역 스코프에 다 붙기 때문에 충돌 많음.
  • 의존성 관리가 어려움.
<script src="jquery.js"></script> <script src="app.js"></script>
(2) 모듈 시스템의 필요
  • JS가 커지고 규모가 커지면서, "모듈" 개념 필요해짐.
  • 그래서 비표준 모듈 시스템들이 먼저 등장함:
    • CommonJS (Node.js 환경에서 사용 → require, module.exports)
    • AMD/RequireJS (브라우저 환경 → define, require)
// CommonJS (Node.js) const fs = require('fs'); module.exports = myFunc;
이때까지는 브라우저 자체적으로 import 문법을 지원 X

2. ES Modules (ES2015, ES6) 등장 이후

(1) ES 표준에 import / export 추가
// math.js export function add(a, b) { return a + b; } // app.js import { add } from './math.js'; console.log(add(2, 3));
  • import, export 가 언어 차원에서 표준화됨.
  • 브라우저도 <script type="module"> 로 ES Modules 실행 가능해짐.
  • Node.js도 점점 ESM 지원 (.mjs 확장자, "type": "module" 옵션 등).
(2) 하지만 여기엔 제약이 있음
  • 표준 ESM에서는 JS 파일만 import 가능.
  • 즉, import './style.css' 같은 건 순수 ESM 사양에는 없음.
  • 브라우저는 여전히 CSS는 <link> 태그로 불러야 함.

3. React / 번들러 환경에서의 발전

React + CRA, Vite 같은 개발환경은 표준 ESM을 확장해서 다음 기능을 제공:
  1. import './App.css' → webpack loader가 CSS 읽어서 JS 안으로 주입.
  1. import image from './logo.png' → 번들러가 이미지 처리 후 URL 문자열로 변환.
  1. import data from './data.json' → JSON 파싱해서 객체로 변환.
즉, ESM 문법(import)은 사용하지만, **진짜 ESM 스펙 그대로가 아니라 번들러가 확장한 "가짜 ESM"**을 쓰는 셈

4. 요약 비교

시대
방식
특징
ESM 이전
Script 태그, CommonJS, AMD
브라우저 표준 아님, 혼란스러움
ESM 등장
import/export 표준화
JS 모듈화 가능 (JS 파일만 import)
React/번들러 환경
확장된 ESM
CSS, 이미지, JSON 등도 import 가능 (webpack, Vite 덕분)

import './App.css'순수 ESM 덕분은 아니고, ESM 문법 + 번들러 확장 기능 덕분에 가능
 

문법2. className

  • JSX는 HTML이 아니라 JavaScript 확장 문법임.
  • class는 이미 JS 예약어(클래스 정의 키워드)이므로 그대로 쓰면 충돌이 발생.
  • 따라서 JSX에서는 html의 class 속성을 사용할 수 없음.
  • class 대신 className이라는 속성을 정의함.
  • React가 렌더링 시 className="..." → 실제 DOM에서는 <div class="..."> 로 변환해줌.
  • 따라서 개발자는 className을 쓰지만, 브라우저가 받는 최종 HTML에는 원래의 class 속성이 적용됨.
  • 예시: <h1 className="blue">안녕</h1>
  • import './App.css' 같은 방식으로 css를 불러올 수 있음. (이건 JS의 ES Module 문법)
  • css는 나중에 컴포넌트별로 분리하거나 라이브러리(styled-components 등)를 많이 사용.
 
import './App.css'; function App() { return <h1 className="blue">안녕</h1>; }
 
notion image
 
notion image
 
notion image
 
notion image
 

문법 3. 컴포넌트를 만들어서 import해서 사용하기

  • src/components 폴더를 만들고, 새로운 JS 파일을 생성해서 컴포넌트를 정의.
  • 컴포넌트 이름은 반드시 대문자 시작 (React 컨벤션).
  • 정의한 컴포넌트는 export default로 내보내고, 다른 파일에서 import<컴포넌트명 /> 형태로 사용.
  • React의 핵심: UI 조각을 재사용 가능한 컴포넌트 단위로 만들 수 있음.
 

1. 컴포넌트(Component)의 개념

  • React에서는 UI를 컴포넌트 단위로 쪼개서 듦.
  • 함수형 컴포넌트를 많이 사용:
    • // src/components/Logo.js export default function Logo() { return <h1>React Study</h1>; }
      // src/components/Logo.js function Hello() { return <h1>안녕</h1>; } export default Hello;
  • 만든 컴포넌트를 다른 파일에서 불러와서 사용할 수 있음:
    • // App.js import Logo from './components/Logo'; function App() { return ( <div> <Logo /> </div> ); }
 

2. JSX의 역할

  • JSX는 HTML처럼 보이는 문법이지만 사실은 HTML이 아님.
  • 브라우저는 JSX를 직접 이해하지 못함.
  • JSX는 결국 JavaScript 함수 호출(React.createElement) 로 변환되어 동작.
// JSX <h1>안녕</h1> // Babel이 변환한 JS React.createElement("h1", null, "안녕");
 

3. Babel의 역할

  • 우리가 JSX 문법을 사용하면 Babel이 이를 순수 JavaScript 코드로 변환해줌.
  • = “너는 JSX를 써. 내가 HTML로(정확히는 JS → DOM으로) 바꿔줄게”.
  • 이런 점에서 템플릿 엔진과 비슷.
  • 하지만 실제로는 템플릿만 바꾸는 게 아니라, JSX → JS 함수 호출 구조로 변환하는 것이 핵심.
 
notion image
 
notion image
 
notion image
notion image
 

문법4. 변수 바인딩 (expression)

  • JSX 내부에서는 중괄호 {} 안에 JS 변수를 넣어 표현할 수 있음.
  • ex) <h1>{num}</h1>
  • 중괄호 부분 = Expression(표현식).
    • → 값이 반환되는 것만 가능. (if문, for문은 불가능, 삼항연산자는 가능)
  • 정리: JSX 중괄호 안에서는 “변수에 담을 수 있는 값(표현식)”만 넣을 수 있음.

1. Expression의 정의

  • 값을 만들어내는 것을 expression.
  • 즉, return을 해서 변수에 담을 수 있는 것은 expression.
  • 예시:
    • let num = 10 + 20; // 10+20이 expression let name = "Hello"; // 문자열도 expression

2. 조건문과 Expression

  • 조건문도 expression으로 쓸 수 있음.
  • 예: 삼항 연산자 → expression.
    • let isSame = (num === 10) ? '10이다' : '아니다';
  • 하지만 if문은 expression이 아님.
    • let isSame = if (num === 10) { return '10이다'; } // ❌ 이렇게는 불가능 → if문은 statement이기 때문
  • 이유: if문은 return을 하지만, 값을 그대로 변수에 대입할 수는 없음.

3. for문과 Expression

  • 언어마다 다름.
  • Dart에서는 for문이 expression으로 가능.
  • 하지만 JavaScript에서는 for문은 expression이 아님.
    • 대신 map, filter, reduce 같은 함수로 대체.
  • 예:
    • let ss = for (let i = 0; i < 3; i++) { return "fdfsdf"; } // ✅ Dart에서는 이런 식으로 expression으로 가능

4. Function과 Expression

  • JavaScript에서는 function도 expression으로 사용할 수 있음.
  • 즉, 함수를 값처럼 변수에 할당 가능.
  • 예:
    • let ss = function() { return "Hello"; }; console.log(ss()); // Hello

5. 정리

  • Expression: 값을 리턴할 수 있어 변수에 담을 수 있는 것.
  • Statement: 실행은 되지만 값을 리턴하지 않음 → 변수에 담을 수 없음.
  • JSX 안에서 중괄호 {}로 감싸서 쓸 수 있는 건 expression만 가능.
 

(추가) 1급 객체 & 메모리 (App.js 변수 위치에 따른 차이)

  1. App.js 최상단(함수 밖)에서 선언한 변수
    1. let num = 10; function App() { return <h1>{num}</h1>; }
      • 이렇게 함수 밖에서 선언된 변수는 **전역(모듈 스코프)**에 존재.
      • React 컴포넌트 클래스 안에 넣지 않아도 메모리에 상주함.
      • 즉, App이 호출될 때마다 새로 생기는 게 아니라, 한 번 선언되고 계속 살아있음.
      • 여기서 num 같은 것도 결국 1급 객체로 취급되므로, 값처럼 다루거나 다른 함수에 넘길 수 있음.
  1. App 함수 안에서 선언한 변수
    1. function App() { let num = 10; return <h1>{num}</h1>; }
      • 이렇게 컴포넌트 함수 내부에서 선언하면, 함수 실행 시마다 스택(stack) 영역에 잡힘.
      • 즉, App 컴포넌트가 렌더링될 때마다 새로 만들어졌다가 사라짐.
      • 지역 변수이므로 함수가 종료되면 사라지는 특징을 가짐.
  1. 정리
      • 밖에서 선언: 모듈 레벨 → 프로그램이 실행되는 동안 메모리에 상주 (힙/전역). (메모리에 항상 떠있음.)
      • 안에서 선언: 함수 호출 때마다 stack에 잡혔다가 사라짐. (stack 변수, 함수 실행할 때마다 새로 생겼다가 사라짐.)
      • 이 차이를 이해해야, React에서 상태(state)를 useState로 관리해야 하는 이유도 명확해짐. (지역 변수만 쓰면 함수 호출 끝나면 날아가기 때문)
 
notion image
 
notion image
 
notion image
=
notion image
 

문법5. 접근 제어자 (= export/import 제어)

  • export defaultimport 이름 from "경로"
  • export (이름 지정) → import { 이름 } from "경로"
    • default 가 아닌것은 중괄호로 import

1. export

  • 모듈에서 특정 함수, 변수, 클래스를 외부에 공개하는 키워드.
  • export를 붙여야 다른 파일에서 import 가능.
// math.js export function add(a, b) { return a + b; } export const num = 10;

2. import

  • 공개된(export된) 대상을 다른 파일에서 가져올 때 사용.
import { add, num } from './math.js'; console.log(add(num, 5));

3. default export

  • 한 모듈당 하나만 지정 가능.
  • 가져올 때 중괄호 없이 원하는 이름으로 import 가능.
// Hello.js export default function Hello() { return <h1>안녕</h1>; } // App.js import Hello from './Hello'; // 이름 마음대로 가능

4. named export

  • 여러 개 가능. 가져올 때 반드시 원래 이름과 일치해야 함.
  • 중괄호 {} 로 감싸서 import
// utils.js export function sum(a, b) { return a + b; } export function mul(a, b) { return a * b; } // App.js import { sum, mul } from './utils';

5. “접근 제어자” 의미

  • 즉, 무엇을 외부에 공개할지 (public) / 내부에만 둘지 (private) 제어하는 개념.
  • Java의 private/public처럼 정식 접근 제어자는 아니지만, JS에서는 export 여부가 사실상 접근 제어자 역할.
 
이거 말고 로고.js로
notion image
 
notion image
 
notion image
 

3. 비교 연산자: == vs ===

1. == (동등 비교, equality)

  • 두 값의 값만 비교.
  • 타입이 다르면 JS가 형변환(type coercion) 해서 비교.
10 == "10" // true 0 == false // true null == undefined // true
  • 암묵적 형변환 때문에 예측하기 어려운 경우가 많음.

2. === (일치 비교, strict equality)

  • 두 값의 값과 타입을 모두 비교.
  • 형변환을 하지 않고, 타입까지 같아야 true.
10 === "10" // false (타입 다름: number vs string) 0 === false // false (number vs boolean) null === undefined // false 10 === 10 // true

3. JSX에서의 활용

  • JSX 안의 {} 표현식에서 조건문, 삼항 연산자 등을 쓸 때 비교 연산자를 자주 쓰게 됨.
  • JS에서는 == 보다는 ===를 쓰는 게 안전
    • ==는 타입을 강제로 맞추는 과정(형변환) 때문에 의도하지 않은 true/false 결과가 나오기 때문.

4. 정리

  • == → 값만 비교 (자동 형변환 O)
  • === → 값 + 타입 비교 (자동 형변환 X, 더 엄격)
  • 실무에서는 항상 ===를 기본으로 쓰는 걸 권장.
 
notion image
 

문법6. 서로 다른 중괄호 {{}} expression과 js object

  • JSX의 inline style은 JS 객체로 작성.
  • 중괄호 두 개는 **expression(바깥) + object literal(안쪽)**의 조합.
  • CSS 속성명은 camelCase로 표기.
  • ⇒ React가 style을 문자열이 아니라 JS 객체로 다루기 때문.

1. style 속성은 문자열이 아님

  • HTML에서는 이렇게 쓸 수 있음:
    • <h1 style="font-size: 50px; color: blue;">안녕</h1>
  • 하지만 JSX에서는 style에 문자열을 직접 주지 않고, JS 객체(Object) 로 작성해야 함.
  • React는 DOM을 JS로 제어하기 때문에, style도 JS 객체 형태로 다루는 것.

2. 중괄호 두 개 {{ }} 를 쓰는 이유

  • 예시:
    • <h1 style={{ fontSize: 50, color: 'blue' }}>안녕</h1>
  • 바깥쪽 { }: JSX에서 expression을 쓰기 위한 문법.
    • JSX 안에서 {} 는 “JS 코드(표현식)를 쓰겠다”는 의미.
  • 안쪽 { }: 실제 JS에서의 객체 리터럴(object literal).
    • style 속성을 담는 객체 → { fontSize: 50, color: 'blue' }.
  • 그래서 expression 안에 객체를 바로 넣는 구조라서 중괄호가 두 번 필요해진 것.

3. expression vs object

  • JSX는 HTML이 아니라 JS 확장 문법이므로,
    • style="..." 처럼 문자열을 직접 쓰면 JSX 입장에서는 단순 문자열 literal일 뿐.
  • React에서는 style을 JS 객체로 다루기 때문에 expression 안에 객체를 넣어야 함.
  • 즉:
    • expression: JSX에서 {} 로 JS 코드를 실행하는 문맥.
    • object: 실제로 style 속성값을 담고 있는 JS 객체.
  • 이 둘이 합쳐져서 style={{ ... }} 구조가 된 것.

4. camelCase 규칙

  • CSS 속성 이름은 JSX에서 camelCase로 바꿔야 함.
  • 예:
    • font-sizefontSize
    • background-colorbackgroundColor
  • 값은 문자열 또는 숫자로 작성할 수 있음.
    • <div style={{ backgroundColor: 'red', marginTop: 20 }}>내용</div>
 
notion image
 
notion image
 

문법 7. Props 전달 (컴포넌트 인수 전달하기)

1. Property의 개념

  • HTML 태그에 붙는 속성 = property(속성).
    • <input type="text" placeholder="이름 입력" />
  • type="text", placeholder="이름 입력" 같은 값들이 property.
  • React에서는 이 property 개념이 컴포넌트에도 적용됨.
  • 즉, 컴포넌트 호출 시 property를 주면 그 값이 props로 전달.
  • props = property의 줄임말.

2. Props 전달하기

  • 컴포넌트에 값을 전달할 수 있음.
  • 컴포넌트 내부에서는 props 객체로 접근 가능.
function Hello(props) { return <h1>안녕 {props.name}</h1>; } function App() { return <Hello name="철수" />; }
  • <Hello name="철수" />name="철수" 가 property → props.name 으로 접근 가능.

3. Props는 객체(Object)

  • 여러 속성을 넘기면 props 객체 안에 key–value 형태로 담김.
function Hello(props) { console.log(props); // { name: "철수", age: 20 } return <h1>{props.name} ({props.age})</h1>; } function App() { return <Hello name="철수" age={20} />; }

4. Props는 읽기 전용

  • 부모가 내려주는 값이므로, 자식 컴포넌트에서 직접 수정할 수 없음.
  • “props는 그냥 받는 거지, 건드리는 게 아니다.”
  • 값 변경이 필요하다면 state를 사용해야 함.

5. 정리

  • property는 HTML 속성, React에서는 props라는 이름으로 컴포넌트 인수 전달에 사용됨.
  • props는 항상 객체 형태.
  • <컴포넌트 속성="값" /> 으로 전달, 내부에서는 props.속성명 으로 사용.
  • props는 읽기 전용, 수정 불가.
 
notion image
 
notion image
 

문법 8. 구조 분해 할당 (Destructuring Assignment)

1. props 접근의 불편함

  • 매번 props.name, props.age 처럼 접근하는 건 번거로움.
  • 코드가 장황해지고 가독성이 떨어짐.

2. 구조 분해 할당으로 props 꺼내쓰기

  • 함수 매개변수에서 바로 props를 구조 분해 할당 가능.
function Hello({ name, age }) { return <h1>{name} ({age})</h1>; } function App() { return <Hello name="철수" age={20} />; }
  • 위 코드에서 props.name, props.age 대신 name, age를 바로 사용.

3. 객체 구조 분해 일반 문법

  • props 말고도 일반 객체에서 자주 쓰이는 문법.
const user = { id: 1, username: "kim", email: "test@test.com" }; // 구조 분해 할당 const { id, username } = user; console.log(id); // 1 console.log(username); // kim

4. Java에는 없는 문법

  • 구조 분해 할당은 Java 같은 정적 언어에는 없는 문법.
  • 이유: Java에서는 변수를 선언할 때 반드시 타입(type) 을 지정해야 함.
  • 하지만 구조 분해는 타입 추론(type inference) 을 전제로 하기 때문에, JS처럼 동적 언어에서 가능.
  • JS에서는 var, let, const 같은 선언 키워드만 쓰면, 오른쪽 객체에 맞춰 자동으로 타입/구조를 추론할 수 있음.
  • 그래서 타입 추론이 가능한 언어(JS, Python, Kotlin, Swift 등)에서만 구조 분해 할당이 존재.

5. React에서의 장점

  • props 구조 분해를 쓰면 코드가 간결해지고 직관적.
  • JSX 안에서 {name}처럼 바로 접근 가능.
  • 유지보수할 때 props 객체 안에 어떤 값이 필요한지 함수 시그니처에서 바로 보임.

6. 정리

  • 구조 분해 할당은 객체의 속성을 풀어서 변수에 바로 할당하는 문법.
  • Java에는 없고, 타입 추론이 가능한 언어여야 가능
  • React에서는 props를 구조 분해 할당으로 받아 쓰는 게 일반적.
  • 코드 간결성, 가독성, 유지보수성 모두 향상.
notion image
 

문법 9. 깊은 복사로 컬렉션 다루기

1. 기존 컬렉션 수정의 문제

let datas = [1, 2, 3]; datas.push(4); console.log(datas);
  • 이렇게 하면 기존 배열 datas 자체가 수정됨.
  • 즉, 원본 메모리가 바뀌어버림 → 이전 값과 달라짐을 추적하기 어려워짐.
  • React에서는 상태 관리(state management)에서 “불변성”이 중요하기 때문에, 기존 데이터를 직접 수정하지 않고 새로운 데이터를 만들어야 함.

2. 깊은 복사 (전개 연산자 사용)

let datas = [1, 2, 3]; let addDatas = [...datas, 4];
  • 전개 연산자(...)는 타입 껍데기를 벗기고 안에 있는 원소를 흩뿌려 줌.
  • 그걸 다시 [] 안에 담아서 새로운 배열을 만든 것.
  • 이렇게 하면 원본은 변하지 않고, 복사된 새 배열이 만들어짐.

3. 데이터 가공하기 (map 활용)

  • 단순히 추가만 하는 게 아니라, 데이터를 가공하고 싶으면 map 같은 고차 함수 사용.
let datas = [1, 2, 3]; let mapped = datas.map(x => x * 2); console.log(mapped); // [2, 4, 6]
  • map은 새로운 배열을 리턴하기 때문에, 역시 불변성을 유지할 수 있음.
  • 추가적인 문법 공부는 스스로 GPT와 함께하기

4. Expression과 return

  • React JSX에서는 expression만 쓸 수 있음.
  • 즉, return 해서 값을 내놓는 형태여야 화면에 표시할 수 있다.
  • 깊은 복사(예: map, 전개 연산자)는 새로운 배열을 return하기 때문에 expression으로 사용 가능.
  • 반대로 for 문은 statement라서 return 값을 직접 만들지 못함.
  • 따라서 JSX 안에서는 for 문 대신 map을 써야 함.
❌ 불가능:
for (let i = 0; i < datas.length; i++) { return <div>{datas[i]}</div>; }
✅ 가능:
{datas.map(data => <div>{data}</div>)}

5. props란? (기본 개념)

  • JSX에서 컴포넌트 호출 시 props.~~~ 로 값을 넘길 수 있음.
<Product name="사과" price={1000} />
  • 이때 props 자체가 객체로 넘어감 → { name: "사과", price: 1000 }.

6. props 개별 전달 방식

  • map으로 돌린 배열의 원소를 각각 props로 전달하는 방식.
{products.map((item) => ( <ListItem id={item.id} title={item.title} /> ))}
  • JSX 코드 안에서 중괄호 {} → JS 문법.
  • 그 안에서 <컴포넌트 /> 같은 소괄호 () → JSX 문법.
  • return 자리에는 JSX 문법을 쓸 수 있음.
  • 즉, map 안에서 JS 문법과 JSX 문법을 섞어 쓰는 게 가능.

7. props 객체 한 방에 전달 방식

  • 위처럼 하나하나 넘기는 대신, 객체 자체를 통째로 넘길 수도 있음.
{products.map((item) => ( <ListItem item={item} /> ))}
  • 이 경우 ListItem 컴포넌트 내부에서 props.item으로 받게 되고, 구조 분해 할당을 활용할 수 있음.
export default function ListItem(props) { const { id, title } = props.item; return ( <div> {id} {title} </div> ); }
  • 장점:
    • props가 많을 때 일일이 써주지 않아도 됨.
    • 컴포넌트 쪽에서 필요한 값만 구조 분해 해서 쓰면 됨.
    • 코드가 간결해지고 확장성도 좋아짐.

8. 상태 관리와 연결

  • 나중에 products 같은 리스트를 다룰 때,
    • push 로 직접 수정하지 말고 전개 연산자 / map 같은 방식으로 새 배열을 만들어야 함.
  • 그렇게 해야 React의 상태 관리에서 변경 사항을 감지하고 UI를 올바르게 갱신할 수 있음.

9. 정리

  • 기존 배열 수정(push)은 원본 메모리 변경 → React 상태 관리에 적합하지 않음.
  • 전개 연산자(...)로 깊은 복사하여 새 배열 생성.
  • 데이터 가공은 map 활용 → 새로운 배열을 return 하므로 expression으로 사용 가능.
  • JSX 안에서는 for 대신 map을 써야 함 (for는 statement라 expression 아님).
  • props는 객체 형태로 전달되며, 나중에 상태 관리할 때 products 같은 리스트를 불변성 유지하며 관리해야 함.
 
notion image
 
notion image
 
notion image
 
notion image
 
 
Share article

jay0628