Contents
0. 실행하기1. JSX문법 1. 태그는 하나만 return2. import문법2. className문법 3. 컴포넌트를 만들어서 import해서 사용하기문법4. 변수 바인딩 (expression)문법5. 접근 제어자 (= export/import 제어)3. 비교 연산자: == vs === 문법6. 서로 다른 중괄호 {{}} expression과 js object문법 7. Props 전달 (컴포넌트 인수 전달하기)문법 8. 구조 분해 할당 (Destructuring Assignment)문법 9. 깊은 복사로 컬렉션 다루기Cursor 세팅






0. 실행하기
npm start



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

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>
</>
);


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 문법을 지원 X2. 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을 확장해서 다음 기능을 제공:
import './App.css'→ webpack loader가 CSS 읽어서 JS 안으로 주입.
import image from './logo.png'→ 번들러가 이미지 처리 후 URL 문자열로 변환.
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>;
}



문법 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 함수 호출 구조로 변환하는 것이 핵심.




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



=

문법5. 접근 제어자 (= export/import 제어)
export default→import 이름 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로



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 // true3. JSX에서의 활용
- JSX 안의
{}표현식에서 조건문, 삼항 연산자 등을 쓸 때 비교 연산자를 자주 쓰게 됨.
- JS에서는
==보다는===를 쓰는 게 안전 ==는 타입을 강제로 맞추는 과정(형변환) 때문에 의도하지 않은 true/false 결과가 나오기 때문.
4. 정리
==→ 값만 비교 (자동 형변환 O)
===→ 값 + 타입 비교 (자동 형변환 X, 더 엄격)
- 실무에서는 항상
===를 기본으로 쓰는 걸 권장.

문법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-size→fontSizebackground-color→backgroundColor
- 값은 문자열 또는 숫자로 작성할 수 있음.
<div style={{ backgroundColor: 'red', marginTop: 20 }}>내용</div>

문법 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는 읽기 전용, 수정 불가.


문법 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); // kim4. 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를 구조 분해 할당으로 받아 쓰는 게 일반적.
- 코드 간결성, 가독성, 유지보수성 모두 향상.

문법 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 같은 리스트를 불변성 유지하며 관리해야 함.




Share article