본문 바로가기

Language/React

[개념] React의 개념, 컴포넌트, JSX, Props, State

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의 핵심 사용 이유
  1. 컴포넌트 기반 아키텍처
    • 컴포넌트를 사용UI를 구성
    • 각 컴포넌트는 독립적이며 HTML, CSS, JS를 포함하여 재사용 가능한 단위로 설계 됨
    • 복잡한 UI를 소규모 컴포넌트로 나누어 개발 및 유지보수를 단순화
  2. 선언적 프로그래밍
    • 선언적 접근법을 사용
      → 개발자는 "어떻게 변경할지"보다 "어떤 결과를 얻고 싶은지"를 작성
      Ex) state나 props를 변경하면 React가 자동으로 DOM을 업데이트
    • 전통적인 방식처럼 DOM 조작 코드를 작성할 필요가 없음
  3. 가상 DOM (Virtual DOM)
    • Virtual DOM을 통해 효율적으로 UI를 업데이트
    • 데이터 변경 시 전체 UI를 다시 렌더링하는 대신 Virtual DOM과 실제 DOM을 비교해 필요한 부분만 변경
    • 이는 전통적인 DOM 조작에 비해 성능이 뛰어나며, 특히 복잡한 애플리케이션에서 효과적
      → 효과적인 이유는 바로 아래의 가상 DOM의 동작 방식이 설명
  4. 상태 관리
    • 컴포넌트 내부 상태 관리를 통해 데이터와 UI를 효율적으로 동기화
    • 상태가 변경되면 React가 자동으로 UI를 다시 렌더링하므로, 별도의 DOM 업데이트 코드가 필요없음
  5. 생태계와 확장성
    • 다양한 라이브러리 및 도구(예: 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의 특징
  1. HTML과 유사한 문법
    • JSX는 JavaScript 코드 안에서 HTML처럼 보이는 코드를 작성 가능
      Ex) <div>Hello, World!</div>
  2. JavaScript와의 통합
    • JSX 내에서 JavaScript 표현식을 사용할 때중괄호 {} 사용
      Ex) {userName}
  3. 컴파일 과정
    • JSX는 브라우저에서 직접 실행되지 않고, Babel과 같은 트랜스파일러를 통해 일반 JavaScript로 변환
  4. 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;
  1. userName 변수에 문자열 'Best Developer' 를 저장
  2. JSX는 <div>, <h1>, <p>와 같은 HTML 태그처럼 보이지만, 실제로는 React.createElement()로 변환
  3. {userName}를 통해 JavaScript 변수 값을 JSX 내에서 사용 가능
JSX의 장점
  1. 가독성 향상 : HTML과 비슷한 구조로 UI를 설계 가능
  2. 동적 콘텐츠 처리 용이 : JavaScript 코드를 쉽게 통합 가능
  3. 오류 감지 : 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의 특징
  1. 단방향 데이터 흐름
    • 부모 컴포넌트 → 자식 컴포넌트로만 데이터를 전달 가능
    • 데이터는 위에서 아래로(단방향) 흐름
  2. 읽기 전용
    • Props 자식 컴포넌트에서 변경 불가
    • 자식 컴포넌트props를 수정하려고 시도하면 오류가 발생
  3. 재사용 가능성
    • Props를 사용하면 동일한 컴포넌트를 다양한 데이터로 재사용 가능
  4. 동적 데이터 전달
    • 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의 특징
  1. 컴포넌트 내부에서 관리
    • 컴포넌트가 동적으로 관리하는 데이터
    • 이 데이터는 컴포넌트 내에서만 변경할 수 있으며, 외부에서 직접 수정 불가
  2. 변경 시 컴포넌트 재렌더링
    • State가 변경되면 React는 해당 컴포넌트를 다시 렌더링하여 UI를 최신 상태로 유지
  3. 기본값 설정 가능
    • 초기값을 설정 가능
    • 상태를 변경하는 방법은 주로 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;

주요 개념 정리
  1. State 초기화
    • useState는 상태의 초기값을 매개변수로 받음
    • 위 코드에서는 useState(0)으로 counter의 초기값을 0으로 설정
  2. State 읽기
    • const [state, setState] = useState(initialValue)에서 state는 현재 상태 값을 나타냄
    • 컴포넌트 내에서 state를 사용하면 최신 상태를 얻을 수 있음
  3. State 변경
    • setState()state를 변경하는 함수
    • 함수를 호출하면 상태가 업데이트되고, React는 해당 컴포넌트를 다시 렌더링
  4. 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를 동적으로 업데이트하는 데 사용