현재 오즈코딩스쿨 강의를 통해 프론트엔드를 학습하고 있습니다.
본 포스트는 해당 강의에 대한 내용 정리를 목적으로 합니다.
React는 Facebook에서 만든 JavaScript 라이브러리다. 웹 애플리케이션의 UI(사용자 인터페이스)를 효율적으로 만들 수 있도록 도와준다.
신뢰할 수 있는 라이브러리
오픈 소스 라이브러리
방대한 커뮤니티
컴포넌트 기반 → UI를 작은 컴포넌트 단위로 나눠서 재사용할 수 있다.
가상 DOM(Virtual DOM) → 실제 DOM을 직접 수정하지 않고, 변경 사항을 가상의 DOM에서 먼저 처리한 후, 필요한 부분만 업데이트한다.
선언형 프로그래밍 → 화면을 직접 조작하지 않고, 상태(State)가 바뀌면 자동으로 렌더링된다.
선언적 프로그래밍 vs 명령적 프로그래밍의 차이
- 선언적 프로그래밍 (React로 조작하기)
- UI가 어떤 상태(State)일 때, 어떻게 보일지를 선언하면 React가 알아서 화면을 렌더링한다.
- 개발자는 UI 업데이트 로직을 직접 조작하지 않고, 상태만 관리하면 된다.
- 가상 DOM을 활용하여 변경된 부분만 효율적으로 업데이트한다.
- 명령적 프로그래밍 (DOM으로 직접 조작하기)
- UI를 직접 조작해야 한다.
- 상태가 바뀔 때마다 수동으로 DOM을 수정하는 코드를 작성해야 한다.
- 코드가 복잡해지고, 유지보수가 어려워질 수 있다.
- 단방향 데이터 흐름 → 데이터가 부모에서 자식으로 전달되므로 구조가 명확하다.
import React from "react";
function Hello() {
return <h1>Hello, React!</h1>;
}
export default Hello;
이렇게 컴포넌트를 만들고 App.js에서 <Hello />를 호출하면 화면에 출력된다.
React는 웹 개발에서 많이 쓰이고, 모바일 앱을 개발할 수 있는 React Native도 있다.
CRA는 Create React App의 약자로, React 프로젝트를 빠르게 생성할 수 있는 공식 툴이다.모듈 번들러로 Webpack 사용, 개발 환경에서도 번들링 진행 가능. 서버 시작과 리로딩 시간 느리다. (웹팩의 다양한 기능 이용 가능)
👉 "React 개발 환경을 쉽게 세팅해주는 도구"라고 생각하면 된다.
터미널을 열고

npx create-react-app cra-react 입력

npx create-react-app cra-react


vs코드로 열기


my-app/
│── node_modules/ # 설치된 패키지들
│── public/ # 정적 파일 (favicon, index.html 등)
│── src/ # 소스 코드 (컴포넌트, 스타일 등)
│ ├── App.js # 메인 컴포넌트
│ ├── index.js # 앱 진입점
│ ├── ...
│── package.json # 프로젝트 설정 및 의존성 관리
│── .gitignore # Git에서 제외할 파일 목록
│── README.md # 프로젝트 설명
src/ 폴더 → 주요 React 코드가 들어가는 곳public/ 폴더 → 정적인 HTML, 이미지 파일들이 위치package.json → 설치된 패키지 목록과 설정 정보🚫 React에서 Create-react-app(CRA)를 추천하지 않는 이유
느린 개발 환경 (Slow Development)
번들 크기가 큼 (Large Bundle Size)
Eject 문제 (Limited Customization)
eject를 해야 함.eject를 하면 프로젝트가 무거워지고, 되돌리기 어려움.vite.config.js에서 간단하게 설정 변경 가능.유지보수 중단 가능성 (Deprecation Risk)
최신 React 기능 지원이 느림
Vite(비트)는 빠르고 가벼운 프론트엔드 빌드 도구다.
기존의 Webpack이나 CRA(Create React App)보다 더 빠르게 개발 서버를 실행하고 빌드 속도도 빠르다.
모듈 번들러로 Rollup 사용, 개발 환경에서는 번들링 진행 불가, 서버 시작과 리로딩 시간 빠름(빠른 개발 속도에 유리)
👉 한마디로 "React, Vue 등 프레임워크의 개발 환경을 더 빠르게 만들어주는 도구"다.
개발 서버 속도가 빠름
빠른 HMR(Hot Module Replacement)
가벼운 번들링 & 최적화된 빌드
플러그인 시스템 & 확장성
터미널을 열고

npm create vite@latest 입력

npm create vite@latest







vs코드로 열기


my-app/
│── node_modules/ # 설치된 패키지들
│── public/ # 정적 파일 (favicon, index.html 등)
│── src/ # 소스 코드
│ ├── App.jsx # 메인 컴포넌트
│ ├── main.jsx # 앱 진입점
│ ├── ...
│── index.html # 기본 HTML 파일
│── package.json # 프로젝트 설정 및 의존성 관리
│── vite.config.js # Vite 설정 파일
vite.config.js → 프로젝트 설정을 변경할 수 있는 파일.src/main.jsx → React 앱이 실행되는 시작점.
CRA : npm run start

Vite : npm run dev

| 항목 | create-react-app (CRA) | Vite |
|---|---|---|
| 초기 빌드 속도 | 느림 (WebPack 기반) | 매우 빠름 (ESBuild 기반) |
| 핫 리로드 속도 | 비교적 느림 | 즉각적 반영 (HMR 성능 우수) |
| 번들러 | Webpack | ESBuild + Rollup |
| 출력 번들 크기 | 크기가 다소 큼 | 더 작고 최적화됨 |
| 설정 유연성 | 제한적 (Eject 필요) | 설정 가능 (vite.config.js) |
| 지원하는 프레임워크 | React 전용 | React, Vue, Svelte 등 다양한 프레임워크 지원 |
| 사용법 | npx create-react-app my-app | npm create vite@latest my-app -- --template react |
| 빌드 속도 | 느림 | 매우 빠름 |
| 개발 경험 | 기본 설정이 많아 진입장벽 낮음 | 빠른 속도로 개발 효율성 높음 |
| 사용 용도 | 기존 React 프로젝트나 빠른 시작용 | 최신 프로젝트, 빠른 개발 환경이 필요한 경우 |
| 구성 방식 | Webpack 기반의 설정이 기본 값으로 제공됨. | Rollup과 ESBuild를 활용해 빠르게 번들링됨. |
| 개발 서버 속도 | 코드 변경 시 반영 속도가 상대적으로 느림. | HMR(Hot Module Replacement)이 즉각적으로 반영됨. |
| 공식 지원 여부 | React 공식 팀에서 유지보수 하지만, 더 이상 추천되지 않음. | React 공식 문서에서 권장하는 방식. |
React 컴포넌트(Component)는 UI를 구성하는 독립적인 블록이다.
👉 쉽게 말해 "버튼, 네비게이션 바, 카드, 입력 폼 같은 UI 조각들"을 의미한다.
React의 핵심 개념 = 컴포넌트 기반 개발
함수(function)를 사용하여 만든 컴포넌트.
useState, useEffect 같은 React Hook을 사용 가능.
최신 React에서는 함수형 컴포넌트를 주로 사용.
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
</div>
);
}
export default Counter;
render() 메서드가 필요하지만, 함수형 컴포넌트는 그냥 return하면 됨.useState, useEffect 같은 Hooks를 사용하면 상태 관리와 라이프사이클 관리가 쉬워짐.this.state, this.setState 등을 사용해야 해서 코드가 길어짐.useEffect)componentDidMount, componentDidUpdate, componentWillUnmount 등 여러 개의 라이프사이클 메서드를 사용해야 했음.useEffect 하나로 라이프사이클을 관리할 수 있음.React.memo, useCallback, useMemo)React.memo를 활용해 불필요한 렌더링을 방지할 수 있음.useCallback과 useMemo를 사용해 최적화가 쉬움.class 문법


class를 사용하여 만든 컴포넌트.
state와 라이프사이클 메서드(componentDidMount 등) 사용 가능.
React 16.8 이후, 함수형 컴포넌트 + Hooks가 도입되면서 거의 사용하지 않음.
import React, { Component } from "react";
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return (
<div>
<p>현재 카운트: {this.state.count}</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
증가
</button>
</div>
);
}
}
export default Counter;
function Counter() {
return <h1>카운터 컴포넌트</h1>;
}
export default Counter;
import Counter from "./Counter"; // Counter 컴포넌트 불러오기
function App() {
return (
<div>
<h1>React 앱</h1>
<Counter /> {/* Counter 컴포넌트 사용 */}
</div>
);
}
export default App;
Counter 컴포넌트를 <Counter />처럼 사용하면 자동으로 렌더링됨.
| 구분 | 함수형 컴포넌트 | 클래스형 컴포넌트 |
|---|---|---|
| 작성 방식 | function 사용 | class 사용 |
| React Hook 사용 가능 | ✅ 가능 (useState, useEffect 등) | ❌ 불가능 (대신 라이프사이클 메서드 사용) |
| 코드가 간결함 | ✅ 짧고 직관적 | ❌ 코드가 길어짐 |
| 사용 여부 | ✅ 최신 React에서 주로 사용 | 🚫 거의 사용 안 함 (옛날 방식) |
JSX는 JavaScript 코드 안에서 HTML과 유사한 문법을 사용할 수 있도록 만든 React의 문법 확장이다.
React에서 UI를 구성할 때 JSX를 사용하면 더 직관적으로 컴포넌트를 작성할 수 있다.
JSX에서 여러 개의 요소를 반환할 때는 반드시 하나의 부모 요소로 감싸야 한다.
// ❌ 오류 발생 (부모 요소 없음)
return (
<h1>안녕하세요!</h1>
<p>React 너무 재밌어요!</p>
);
// ✅ 올바른 코드 (div로 감싸기)
return (
<div>
<h1>안녕하세요!</h1>
<p>React 너무 재밌어요!</p>
</div>
);
// ✅ Fragment 사용 (불필요한 div 방지)
return (
<>
<h1>안녕하세요!</h1>
<p>React 너무 재밌어요!</p>
</>
);
💡 <></>는 React.Fragment의 단축 문법이므로, 불필요한 <div>를 추가하지 않고도 여러 요소를 반환할 수 있다.
JSX 내부에서는 {} 중괄호를 사용하여 JavaScript 코드를 실행할 수 있다.
const name = "React";
return <h1>안녕하세요, {name}!</h1>; // 결과: 안녕하세요, React!
함수 호출도 가능하다.
return `반가워요, ${name}!`;
}
return <h1>{getGreeting("상희")}</h1>; // 결과: 반가워요, 상희!
JSX에서는 class 대신 className을 사용해야 한다.
// ❌ 오류 발생
return <button class="btn">클릭</button>;
// ✅ 올바른 코드
return <button className="btn">클릭</button>;
JavaScript에서 class는 예약어이기 때문에 JSX에서는 className을 사용해야 한다.
JSX에서 inline 스타일을 적용할 때는 객체 형태로 작성해야 한다.
const styles = {
color: "blue",
fontSize: "20px"
};
return <p style={styles}>이 텍스트는 파란색이에요!</p>;
💡 CSS 속성명은 camelCase(background-color → backgroundColor)로 작성해야 한다.
return <div style={{ backgroundColor: "yellow", padding: "10px" }}>스타일 적용</div>;
JSX에서 조건문을 사용할 때 if를 직접 사용할 수 없기 때문에, 삼항 연산자 (? :) 또는 && 연산자를 활용한다.
const isLoggedIn = true;
return <h1>{isLoggedIn ? "환영합니다!" : "로그인이 필요합니다."}</h1>;
const isAdmin = true;
return <>{isAdmin && <button>관리자 페이지 이동</button>}</>;
💡isAdmin이 true일 때만 <button>이 렌더링됨.
React에서 리스트를 렌더링할 때 map()을 사용하여 동적으로 요소를 생성할 수 있다.
const fruits = ["사과", "바나나", "포도"];
return (
<ul>
{fruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
);
💡 key 속성은 리스트에서 요소를 구별하는데 사용되며, 성능 최적화를 위해 필요하다.
createElement 함수는 JSX 코드를 JavaScript로 변환하는 역활을 한다.// JSX 코드
const element = <h1>Hello, JSX!</h1>;
// 변환된 JavaScript 코드
const element = React.createElement("h1", null, "Hello, JSX!");
➡ 따라서 JSX를 사용하려면 Babel과 같은 트랜스파일러(Transpiler)가 필요하다.
JSX에서 두 줄 이상 작성할 경우 소괄호 ()로 묶어야 한다.
그렇지 않으면 JavaScript가 자동으로 세미콜론을 삽입하는 과정에서 예상치 못한 오류가 발생할 수 있다.
return (
<div>
<h1>안녕하세요!</h1>
<p>React 너무 재밌어요!</p>
</div>
);
➡ 오류 발생: return 문이 종료된 것으로 인식되기 때문에 <div> 태그가 반환되지 않는다.
vsCode ➡ ESLint
JavaScript 문법 작성할 때 문법에러를 잡아주는 모듈"eslint"
HTML 태그를 JavaScript 안에서 직접 작성하듯이 사용할 수 있어서 구조를 한눈에 파악하기 쉬움.
중괄호 {} 안에 JavaScript 코드를 자유롭게 사용할 수 있어서 동적인 UI 작성이 쉬움.
React.createElement()로 변환되기 때문에 문제 발생 위치 추적이 비교적 쉬움.map() 함수는 JavaScript의 배열 메서드 중 하나로, 배열의 각 요소를 변환하여 새로운 배열을 반환하는 함수다.
const newArray = oldArray.map((element, index) => {
return 새로운값;
});
element → 배열의 각 요소
index → 현재 요소의 인덱스
기존 배열을 변경하지 않고 새로운 배열을 반환
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((num) => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
➡ numbers 배열의 각 요소를 2배로 변환한 새로운 배열을 반환함.
const fruits = ["🍎 사과", "🍌 바나나", "🍇 포도"];
function FruitList() {
return (
<ul>
{fruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
);
}
fruits.map()을 이용해 <li> 태그를 동적으로 생성
각 요소에는 key 속성을 추가해야 함 (React의 성능 최적화를 위해 필요)
React에서는 리스트를 렌더링할 때 key 속성을 추가해야 한다.
이는 React가 변경된 항목을 효율적으로 감지하고 업데이트하기 위해 필요하기 때문이다.
const names = ["철수", "영희", "민수"];
return (
<ul>
{names.map((name, index) => (
<li key={index}>{name}</li>
))}
</ul>
);
💡 가능하면 고유한 ID 값을 key로 설정하는 것이 더 좋다.
const users = [
{ id: 1, name: "철수" },
{ id: 2, name: "영희" },
{ id: 3, name: "민수" },
];
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
➡ index를 key로 쓰는 것은 권장되지 않음. (데이터가 변경될 경우 예측하지 못한 문제 발생 가능)
const products = [
{ id: 1, name: "노트북", price: 1200000 },
{ id: 2, name: "키보드", price: 150000 },
{ id: 3, name: "마우스", price: 70000 },
];
function ProductList() {
return (
<div>
<h2>상품 목록</h2>
<ul>
{products.map((product) => (
<li key={product.id}>
{product.name} - {product.price.toLocaleString()}원
</li>
))}
</ul>
</div>
);
}
💡 toLocaleString()을 사용하면 숫자를 천 단위 콤마 형식으로 변환할 수 있다.
const todos = [
{ id: 1, text: "React 공부하기", done: true },
{ id: 2, text: "운동하기", done: false },
{ id: 3, text: "책 읽기", done: false },
];
function TodoList() {
return (
<ul>
{todos.map((todo) => (
<li key={todo.id} style={{ color: todo.done ? "gray" : "black" }}>
{todo.text} {todo.done ? "✔️" : "❌"}
</li>
))}
</ul>
);
}
💡 todo.done이 true이면 글자를 회색으로 표시하고 ✔️ 체크 아이콘을 추가함.
filter() 함수는 배열에서 특정 조건을 만족하는 요소들만 걸러내어 새로운 배열을 반환하는 함수다.
React에서 데이터를 필터링할 때 자주 사용되며, 원본 배열을 변경하지 않는다.
const newArray = oldArray.filter((element, index) => {
return 조건식;
});
element → 배열의 각 요소
index → 현재 요소의 인덱스
조건식이 true인 요소들만 새로운 배열에 포함됨
const numbers = [1, 2, 3, 4, 5, 6];
// 짝수만 필터링
const evenNumbers = numbers.filter((num) => num % 2 === 0);
console.log(evenNumbers); // [2, 4, 6]
➡ num % 2 === 0 조건을 만족하는 값만 새로운 배열에 포함됨.
📌 완료된 할 일만 필터링
const todos = [
{ id: 1, text: "React 공부하기", done: true },
{ id: 2, text: "운동하기", done: false },
{ id: 3, text: "책 읽기", done: true },
];
// 완료된 항목만 필터링
const completedTodos = todos.filter((todo) => todo.done);
console.log(completedTodos);
// [{ id: 1, text: "React 공부하기", done: true }, { id: 3, text: "책 읽기", done: true }]
➡ done: true인 요소만 새로운 배열에 저장됨.
✔ 특정 조건을 만족하는 데이터만 렌더링
const products = [
{ id: 1, name: "노트북", price: 1200000 },
{ id: 2, name: "키보드", price: 150000 },
{ id: 3, name: "마우스", price: 70000 },
];
// 10만 원 이상인 제품만 필터링
const expensiveProducts = products.filter((product) => product.price >= 100000);
function ProductList() {
return (
<ul>
{expensiveProducts.map((product) => (
<li key={product.id}>
{product.name} - {product.price.toLocaleString()}원
</li>
))}
</ul>
);
}
💡 toLocaleString()을 사용하여 숫자를 천 단위 콤마(1,200,000원) 형식으로 변환함.
function TodoApp() {
const [todos, setTodos] = React.useState([
{ id: 1, text: "React 공부하기", done: false },
{ id: 2, text: "운동하기", done: true },
{ id: 3, text: "책 읽기", done: false },
]);
// 특정 ID의 할 일을 삭제하는 함수
const deleteTodo = (id) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>
{todo.text}
<button onClick={() => deleteTodo(todo.id)}>삭제</button>
</li>
))}
</ul>
);
}
💡 filter()를 사용하여 선택한 항목을 제외한 새로운 배열을 만들어 setTodos()로 업데이트함.
function SearchableList() {
const [query, setQuery] = React.useState("");
const items = ["사과", "바나나", "포도", "오렌지"];
// 입력한 검색어를 포함하는 항목만 필터링
const filteredItems = items.filter((item) =>
item.includes(query)
);
return (
<div>
<input
type="text"
placeholder="검색어 입력"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<ul>
{filteredItems.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
➡ 사용자가 입력한 검색어를 포함하는 항목만 리스트에 표시됨.
여지것..나는 요령없이 공부를 했구나..ㅠㅠ
강의를 보면서 블로그에 필기하듯이 하면 될 것을..아흑..