React란 무엇인가?
- 다양한 자바스크립트 라이브러리 중 가장 인기있는 라이브러리
- 사용자 인터페이스(UI)를 쉽게 구축하기 위해 설계된 컴포넌트 기반의 선언적 JavaScript 라이브러리
- 대규모 애플리케이션에 적합
JavaScript의 다양한 라이브러리들
- UI 및 DOM 조작 : jQuery, GSAP, Three.js
- 빌드 및 번들링 : Webpack, Parcel, Vite
- 데이터 관리 및 상태 관리 : Lodash, Redux, RxJS
- 테스트 : Jest, Mocha, Chai
- HTTP 요청 : Axios, Fetch API
- Chart 및 데이터 시각화 : Chart.js, D3.js
- 실시간 및 WebSocket : Socket.IO
- 폼 검증 및 유효성 검사 : Yup, Validator.js
- CSS 및 스타일링 : Tailwind CSS, Emotion
- 프레임워크 : Next.js
React의 등장 배경
HTML, CSS, JS를 사용한 직접 구축의 한계
- 재사용성 부족
- HTML과 JavaScript 코드가 단순한 페이지에서 복잡한 애플리케이션으로 발전할수록 코드를 재사용하거나 모듈화하기 어려워짐
- 복잡한 DOM 업데이트
- UI를 동적으로 업데이트하려면 DOM 요소를 탐색하고 수동으로 수정해야 함
- 복잡한 상태 변경이 많은 애플리케이션에서는 유지보수가 어려움
- 데이터와 UI의 비효율적 연계
- 데이터가 바뀌면 그에 맞춰 UI를 다시 렌더링하는 코드를 일일이 작성해야 함
- React는 이러한 문제를 해결하기 위해 설계된 라이브러리
React의 핵심 사용 이유
- 컴포넌트 기반 아키텍처
- 컴포넌트를 사용해 UI를 구성
- 각 컴포넌트는 독립적이며 HTML, CSS, JS를 포함하여 재사용 가능한 단위로 설계 됨
- 복잡한 UI를 소규모 컴포넌트로 나누어 개발 및 유지보수를 단순화
- 선언적 프로그래밍
- 선언적 접근법을 사용
→ 개발자는 "어떻게 변경할지"보다 "어떤 결과를 얻고 싶은지"를 작성
Ex) state나 props를 변경하면 React가 자동으로 DOM을 업데이트 - 전통적인 방식처럼 DOM 조작 코드를 작성할 필요가 없음
- 선언적 접근법을 사용
- 가상 DOM (Virtual DOM)
- Virtual DOM을 통해 효율적으로 UI를 업데이트 함
- 데이터 변경 시 전체 UI를 다시 렌더링하는 대신 Virtual DOM과 실제 DOM을 비교해 필요한 부분만 변경
- 이는 전통적인 DOM 조작에 비해 성능이 뛰어나며, 특히 복잡한 애플리케이션에서 효과적
→ 효과적인 이유는 바로 아래의 가상 DOM의 동작 방식이 설명
- 상태 관리
- 컴포넌트 내부 상태 관리를 통해 데이터와 UI를 효율적으로 동기화 함
- 상태가 변경되면 React가 자동으로 UI를 다시 렌더링하므로, 별도의 DOM 업데이트 코드가 필요없음
- 생태계와 확장성
- 다양한 라이브러리 및 도구(예: Redux, React Router)와 함께 사용할 수 있어, 프로젝트의 요구사항에 맞게 확장 가능
- React를 사용하면 React Native를 통해 모바일 애플리케이션을 개발하거나, Next.js를 통해 서버사이드 렌더링을 쉽게 구현 가능
가상 DOM의 동작 방식
- 상태 변경 감지 : 애플리케이션의 상태(state)가 변경되면 React는 새로운 가상 DOM을 생성
- Diffing 알고리즘 적용 : 이전의 가상 DOM과 새로운 가상 DOM을 비교하여 변경된 부분을 식별
→ 이 과정을 '재조정(reconciliation)'이라고 함 - 필요한 부분만 실제 DOM에 업데이트 : 변경된 부분만 실제 DOM에 반영하여 업데이트
→ 이를 통해 불필요한 DOM 조작을 최소화하고, 렌더링 성능을 향상시킴
전통적인 방식 vs React 차이점 비교
특징 | 전통적인 방식(HTML, CSS, JS) | React |
코드 재사용성 | 제한적 | 컴포넌트 기반으로 높은 재사용성 |
DOM 조작 | 직접 조작 | 가상 DOM을 통해 자동화 |
유지보수 | 복잡한 애플리케이션에서 어려움 | 컴포넌트 단위로 유지보수 용이 |
상태 관리 | 직접 구현해야 함 | React에서 내장 상태 관리 사용 가능 |
선언적 vs 명령적 | 명령적 (How) | 선언적 (What) |
컴포넌트란 무엇인가?
- 사용자 인터페이스를 구성하는 독립적이고 재사용 가능한 빌딩 블럭
- 컴포넌트는 HTML 구조, CSS 스타일, JavaScript 로직을 하나의 단위로 캡슐화 함
- UI의 일부분을 정의하는 함수
구성 요소
- View (HTML) : 화면에 보이는 부분
- Logic (JS) : 사용자 상호작용 및 데이터 관리 로직
- Style (CSS) : 시각적 디자인을 담당
리액트 컴포넌트의 주요 장점
- 재사용성
- React 컴포넌트는 한 번 정의하면 여러 곳에서 재사용 가능
- 중복 코드 ↓, 유지보수 ↑
Ex) "버튼", "헤더", "폼" 같은 UI 요소를 컴포넌트화하면 필요할 때마다 재사용 가능
- 역할 분리(우려사항의 분리, Separation of Concerns)
- 하나의 컴포넌트는 특정 기능이나 역할에 집중
Ex) "검색창" 컴포넌트는 입력과 검색 동작만 처리, "결과 리스트" 컴포넌트는 결과를 보여주는 데만 집중 - 이점 : 코드의 가독성 향상, 디버깅 및 확장성 용이
- 하나의 컴포넌트는 특정 기능이나 역할에 집중
JSX(JavaScript XML)란 무엇인가?
- React에서 사용되는 문법으로 JavaScript 코드 안에 HTML과 비슷한 문법을 작성할 수 있게 해줌
- React.createElement()를 사용하여 DOM 요소를 생성하는 복잡한 과정을 추상화해, 코드의 가독성을 높이고 더 직관적으로 UI를 작성 가능하게 함
JSX의 특징
- HTML과 유사한 문법
- JSX는 JavaScript 코드 안에서 HTML처럼 보이는 코드를 작성 가능
Ex) <div>Hello, World!</div>
- JSX는 JavaScript 코드 안에서 HTML처럼 보이는 코드를 작성 가능
- JavaScript와의 통합
- JSX 내에서 JavaScript 표현식을 사용할 때는 중괄호 {} 사용
Ex) {userName}
- JSX 내에서 JavaScript 표현식을 사용할 때는 중괄호 {} 사용
- 컴파일 과정
- JSX는 브라우저에서 직접 실행되지 않고, Babel과 같은 트랜스파일러를 통해 일반 JavaScript로 변환
- React 요소 반환
- JSX를 사용하면 React 요소를 반환
- 이는 DOM에 렌더링될 수 있음
JSX 예시
import React from 'react';
function Welcome() {
const userName = 'Best Developer!';
return (
<div>
<h1>Hello, {userName}!</h1>
<p>Welcome to React!</p>
</div>
);
}
export default Welcome;
- userName 변수에 문자열 'Best Developer' 를 저장
- JSX는 <div>, <h1>, <p>와 같은 HTML 태그처럼 보이지만, 실제로는 React.createElement()로 변환
- {userName}를 통해 JavaScript 변수 값을 JSX 내에서 사용 가능
JSX의 장점
- 가독성 향상 : HTML과 비슷한 구조로 UI를 설계 가능
- 동적 콘텐츠 처리 용이 : JavaScript 코드를 쉽게 통합 가능
- 오류 감지 : JSX는 컴파일 시 에러를 미리 감지해 디버깅이 쉬움
JSX 없이 React 요소 작성하기 (비교 예시)
import React from 'react';
function Welcome() {
return React.createElement(
'div',
null,
React.createElement('h1', null, 'Hello, Best Developer!'),
React.createElement('p', null, 'Welcome to React!')
);
}
export default Welcome;
- JSX를 사용하면 HTML처럼 직관적이고 간결하게 작성할 수 있으며, 가독성이 크게 향상
Props의 개념
- React 컴포넌트로 데이터를 전달하는 데 사용되는 속성(properties)
- 부모 컴포넌트가 자식 컴포넌트에 데이터를 전달하기 위해 props를 사용 [ 부모 → 자식 데이터 전달 ]
- 이 데이터는 읽기 전용(immutable)이며, 자식 컴포넌트는 이를 변경 불가
Props의 특징
- 단방향 데이터 흐름
- 부모 컴포넌트 → 자식 컴포넌트로만 데이터를 전달 가능
- 데이터는 위에서 아래로(단방향) 흐름
- 읽기 전용
- Props는 자식 컴포넌트에서 변경 불가
- 자식 컴포넌트는 props를 수정하려고 시도하면 오류가 발생
- 재사용 가능성
- Props를 사용하면 동일한 컴포넌트를 다양한 데이터로 재사용 가능
- 동적 데이터 전달
- Props는 동적으로 데이터를 전달할 수 있어, 컴포넌트가 다양한 상황에 대응하도록 설계 가능
Props의 구조
- Props는 객체 형태로 전달되며, 컴포넌트의 첫 번째 매개변수로 접근 가능
function ComponentName(props) {
return <h1>{props.someProperty}</h1>;
}
Props 예제
- 간단한 Props 예제
- 부모 컴포넌트가 자식 컴포넌트로 데이터를 전달하는 간단한 예제
- App 컴포넌트가 Greeting 컴포넌트에 name이라는 데이터를 props로 전달
- Greeting 컴포넌트는 전달받은 name 데이터를 활용하여 사용자별로 인사를 출력
// App.js
import logo from './logo.svg';
import './App.css';
function Greeting(props) {
return <h1>Hello, {props.name}!</h1>;
}
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Greeting name="Alice" />
<Greeting name="Bob" />
<Greeting name="Charlie" />
</header>
</div>
);
}
export default App;
- Props로 컴포넌트 재사용
- 컴포넌트 재사용성을 높이는 방법을 보여줌
- 동일한 Card 컴포넌트를 다양한 데이터를 사용해 재사용
- Props를 통해 제목(title)과 내용(content)을 동적으로 전달
// App.js
import logo from './logo.svg';
import './App.css';
function Card(props) {
return (
<div style={{ border: "1px solid #ddd", padding: "10px", margin: "10px" }}>
<h2>{props.title}</h2>
<p>{props.content}</p>
</div>
);
}
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<div>
<Card title="React Basics" content="Learn the basics of React." />
<Card title="React Props" content="Understand how props work." />
<Card title="React State" content="Learn about state management." />
</div>
</header>
</div>
);
}
export default App;
Props 주요 사용 패턴
- 기본값 설정 (Default Props)
- 컴포넌트에 props가 전달되지 않았을 때 사용할 기본값을 설정 가능
- Greeting 컴포넌트는 name 값이 전달되지 않으면 기본값으로 "Guest" 사용
// App.js
import logo from './logo.svg';
import './App.css';
// 비구조적 할당
function Greeting(props) {
const { name = "Guest" } = props;
return <h1>Hello, {name}!</h1>;
}
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<div>
<Greeting />
<Greeting name="Alice" />
</div>
</header>
</div>
);
}
export default App;
- 구조 분해 할당으로 Props 사용
- 구조 분해 할당을 사용하면 코드를 더 간결하게 작성 가능
- props.name 대신 구조 분해를 통해 name만 바로 사용
- Greeting매개변수.name으로 접근하지 않아도 됨
- 결과는 위와 동일
import logo from './logo.svg';
import './App.css';
function Greeting({ name = "Guest" }) {
return <h1>Hello, {name}!</h1>;
}
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<div>
<Greeting />
<Greeting name="Alice" />
</div>
</header>
</div>
);
}
export default App;
State의 개념
- 컴포넌트 내부에서 관리되는 데이터로, 컴포넌트가 변경할 수 있는 데이터
- 컴포넌트가 변경할 수 있는 데이터를 다루며 컴포넌트가 렌더링될 때마다 해당 데이터가 변하면 React가 이를 감지하고, 컴포넌트를 다시 렌더링하여 UI를 최신 상태로 유지
- 주로 사용자 상호작용, 서버 응답 등의 동적인 데이터를 처리하는 데 사용
State의 특징
- 컴포넌트 내부에서 관리
- 컴포넌트가 동적으로 관리하는 데이터
- 이 데이터는 컴포넌트 내에서만 변경할 수 있으며, 외부에서 직접 수정 불가
- 변경 시 컴포넌트 재렌더링
- State가 변경되면 React는 해당 컴포넌트를 다시 렌더링하여 UI를 최신 상태로 유지
- 기본값 설정 가능
- 초기값을 설정 가능
- 상태를 변경하는 방법은 주로 setState() 메서드를 사용
State를 사용하는 방법
- React에서 State를 사용하려면 useState 훅을 사용하거나, 클래스형 컴포넌트에서는 this.state와 this.setState()를 사용
- 다음 코드는 버튼을 클릭할 때마다 횟수가 증가하는 코드
import React, { useState } from 'react';
function Counter() {
// useState를 사용하여 state 정의하기
// counter라는 state와 setCounter라는 setter 함수
const [counter, setCounter] = useState(0);
// 클릭 시 counter를 증가시키는 함수
const increment = () => {
setCounter(counter + 1);
};
return (
<div>
<h1>Current Count: {counter}</h1>
<button onClick={increment}>Increase</button>
</div>
);
}
export default Counter;
주요 개념 정리
- State 초기화
- useState는 상태의 초기값을 매개변수로 받음
- 위 코드에서는 useState(0)으로 counter의 초기값을 0으로 설정
- State 읽기
- const [state, setState] = useState(initialValue)에서 state는 현재 상태 값을 나타냄
- 컴포넌트 내에서 state를 사용하면 최신 상태를 얻을 수 있음
- State 변경
- setState()는 state를 변경하는 함수
- 이 함수를 호출하면 상태가 업데이트되고, React는 해당 컴포넌트를 다시 렌더링
- State는 비동기적
- 상태 변경은 비동기적으로 이루어짐
- setState를 여러 번 호출하더라도 한 번의 렌더링에서 모두 반영
상태를 여러 개 다루는 방법
- React에서 하나의 컴포넌트가 여러 개의 상태를 관리하는 경우도 많이 존재
- 이때는 useState를 여러 번 사용하여 각각의 상태를 관리 가능
- 다음의 코드는 name과 age라는 두 개의 상태를 관리
- 각각의 상태는 독립적으로 업데이트 됨
import React, { useState } from 'react';
function UserProfile() {
const [name, setName] = useState("John");
const [age, setAge] = useState(25);
return (
<div>
<h1>Name: {name}</h1>
<h2>Age: {age}</h2>
<button onClick={() => setName("Alice")}>Change Name</button>
<button onClick={() => setAge(age + 1)}>Increase Age</button>
</div>
);
}
export default UserProfile;
State를 객체나 배열로 다루기
- 상태가 더 복잡한 데이터 구조일 경우 객체나 배열로 관리 가능
- 이때 상태를 업데이트할 때 기존 상태를 그대로 유지하면서 새로운 값을 반영하는 방식으로 관리해야 함
- 다음 코드에서는 todos라는 배열 상태를 관리하고, 새로운 항목을 추가할 때 기존 항목을 보존하면서 새로운 항목을 추가
import React, { useState } from 'react';
function ToDoApp() {
// 상태를 배열로 관리
const [todos, setTodos] = useState(["Buy groceries", "Walk the dog"]);
const addTodo = () => {
setTodos([...todos, "New Task"]);
};
return (
<div>
<h1>To Do List</h1>
<ul>
{todos.map((todo, index) => (
<li key={index}>{todo}</li>
))}
</ul>
<button onClick={addTodo}>Add Task</button>
</div>
);
}
export default ToDoApp;
Props vs State 차이점
특징 | Props | State |
데이터 흐름 | 부모 → 자식 (단방향) | 컴포넌트 내부에서 관리 |
변경 가능 여부 | 읽기 전용 | 변경 가능 |
재사용성 | 컴포넌트를 재사용 가능하게 만듦 | 재사용성보단 개별 상태 관리 중심 |
관리 위치 | 부모 컴포넌트가 관리 | 컴포넌트 자체에서 관리 |
정리
- React : 사용자 인터페이스(UI)를 구축하고 관리하기 위한 JavaScript 라이브러리
- 컴포넌트 : UI의 독립적인 단위로 상태와 속성을 기반으로 화면을 렌더링하는 함수나 클래스
- JSX : JavaScript 안에서 HTML-like 문법을 사용하여 UI를 정의하는 방식
- Props : 부모 컴포넌트에서 자식 컴포넌트로 전달되는 읽기 전용 값
- State : 컴포넌트 내부에서 관리되는 데이터로, 변경될 수 있고 UI를 동적으로 업데이트하는 데 사용
'Language > React' 카테고리의 다른 글
[개념] List와 Key (0) | 2025.01.01 |
---|---|
[개념] 렌더링과 조건부 렌더링 (0) | 2024.12.31 |
[개념] React Hook (0) | 2024.12.30 |
[Node.js] npm과 npx, 전역 설치와 로컬 설치 (0) | 2024.12.17 |
[React] 리액트 프로젝트 생성 시 에러 발생 (1) | 2024.12.17 |