[React.js] 리액트 공부[1] ~ useState

iskra·2023년 5월 26일
0

본문 내용

  • 리액트 사용 이유
  • 단점
  • 함께 사용할 수 있는 도구
  • 중요한 개념들(지금까지 공부한 내용)
  • 다양한 Hooks

리액트 사용 이유

컴포넌트 기반 구조

리액트는 컴포넌트라는 개념을 도입함. 이는 코드 재사용성을 높이고 유지보수를 편리하게 해줌. 각 컴포넌트는 독립적으로 동작하고 조합 가능해 복잡한 UI를 쉽게 구축할 수 있음.

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Welcome name="Alice" />
      <Welcome name="Bob" />
      <Welcome name="Charlie" />
    </div>
  );
}

Virtual DOM

리액트는 Virtual DOM을 사용함. DOM 조작은 느리고 비효율적인 작업인데, 리액트는 이를 최적화함. 이는 불필요한 렌더링을 최소화하여 성능을 향상시킴.

단방향 데이터 흐름

리액트의 데이터는 항상 위에서 아래로(부모에서 자식으로) 흐름. 이로 인해 앱의 동작을 예측하기 쉬움.

리액트의 단점

학습 곡선

리액트 자체는 상대적으로 간단하지만, 리액트와 함께 사용하는 기술들(예: Redux, Router)을 익히는 것은 어려울 수 있음.

라이브러리로서의 한계

리액트는 라이브러리로서, 전체 애플리케이션을 구축하는 데 필요한 모든 기능을 제공하지 않음. 상태 관리나 라우팅 등 추가 기능을 위해서는 외부 라이브러리가 필요함.

참고로 CRA(Create-React-App)을 사용하면, 본적으로 리액트 라이브러리와 리액트 DOM, 테스트 라이브러리, 웹팩, Babel 등 필요한 환경을 제공함. 다만 React Router나 Redux와 같은 라이브러리를 기본적으로 포함하고 있지 않음. 필요에 따라 개발자가 추가로 설치하여 사용해야 함.

최적화 필요성

리액트의 성능은 기본적으로 우수하지만, 큰 규모의 애플리케이션에서는 추가적인 최적화가 필요할 수 있음. 가상 DOM 또한 비용이 들고, 이에 대한 이해가 필요함.

리액트와 함께 사용할 수 있는 도구

리액트의 라이브러리로서의 한계를 극복하기 위해 다양한 툴과 라이브러리를 사용할 수 있음. 주로 사용되는 몇 가지를 소개함.

상태 관리

  • Redux : 가장 널리 사용되는 상태 관리 라이브러리 중 하나임. 애플리케이션의 전체 상태를 관리하는데 유용하고, 시간여행 디버거 등 고급 디버깅 기능을 제공함.

  • MobX : 간단하고 빠른 방법으로 애플리케이션 상태를 관리할 수 있음. 'reactive'한 상태 관리를 제공해 자동으로 최소한의 업데이트를 수행함.

  • Context API : 리액트 자체에서 제공하는 상태 관리 기능임. Redux나 MobX보다 간단한 상황에서 사용하기 적합함.

라우팅

  • React Router : 리액트 애플리케이션에 라우팅 기능을 제공하는 가장 대표적인 라이브러리임. 웹 브라우저에서의 페이지 전환 효과를 쉽게 구현할 수 있음.

  • Next.js : Next.js는 서버 사이드 렌더링(SSR)과 정적 사이트 생성을 지원하는 리액트 프레임워크임. 자체적인 라우팅 시스템을 가지고 있어 별도의 라우팅 라이브러리가 필요 없음.

  • Reach Router : Reach Router는 리액트를 위한 라우팅 라이브러리로, 접근성에 중점을 두고 있음. 간단하고 예측 가능한 라우팅을 제공함.

  • Gatsby : Gatsby는 정적 사이트 생성기로서, 리액트 기반임. 동적 라우팅을 지원하며, 페이지 생성 시에 미리 경로를 정의함.

UI 라이브러리

  • Material-UI, Ant Design 등의 라이브러리를 통해 미리 디자인된 컴포넌트를 사용할 수 있음. 이를 통해 UI 개발 시간을 단축시킬 수 있음.

리액트 공부시 중요한 개념들

컴포넌트의 개념

리액트에서 컴포넌트는 UI의 독립적인, 재사용 가능한 부분을 나타냄. 각각의 컴포넌트는 자체적인 마크업과 로직을 가짐.

간단한 예제로 Button 컴포넌트를 만들어 보겠음.

// Button.js
import React from 'react';

function Button(props) {
  return (
    <button>{props.label}</button>
  );
}

export default Button;

Button 컴포넌트는 재사용 가능하고 독립적으로 동작함. label이라는 props를 통해 버튼의 텍스트를 지정할 수 있음. 이 컴포넌트를 사용하려면 다음과 같이 사용하면 됨.

// App.js
import React from 'react';
import Button from './Button';

function App() {
  return (
    <div>
      <Button label="Click Me!" />
      <Button label="Another Button" />
    </div>
  );
}

export default App;

위의 코드에서 Button 컴포넌트는 두 번 사용되었지만, 각각 다른 라벨을 가짐. 이처럼 컴포넌트는 재사용이 가능하고, 필요에 따라 다르게 동작하도록 설정할 수 있음.

useState의 개념

useState는 리액트의 Hook 중 하나로, 컴포넌트의 상태 관리를 가능하게 함. useState를 이용하면 함수형 컴포넌트 내에서 상태를 가질 수 있게 됨.

간단한 예제로 버튼을 클릭할 때마다 카운트가 증가하는 Counter 컴포넌트를 만들어 보겠음.

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Clicked {count} times</p>
      <button 
        // 이전 상태를 직접 참조하는 방법
        onClick={() => setCount(count + 1)}
      >
        Click me
      </button>
      {/* 이전 상태를 함수를 통해 참조하는 방법도 있음. 이 방법이 더 안전함.
      <button onClick={() => setCount((prevCount) => prevCount + 1)}>
        Click me
      </button>
      */}
    </div>
  );
}

export default Counter;

여기서 useState(0)을 호출하면 초기값 0을 가진 상태 변수 count와 해당 상태를 변경하는 함수 setCount가 반환됨. 이들을 배열 비구조화 할당 (배열 구조분해라고도 함. Array Destructuring. 위의 [count, setCount] 부분) 으로 추출함.

버튼을 클릭하면 onClick 핸들러 함수가 실행되고, setCount 함수를 통해 count 상태 값이 1 증가함. 이렇게 상태가 변경되면 리액트는 해당 컴포넌트를 다시 렌더링하고, 업데이트된 count 값을 화면에 표시함.

단방향 데이터 흐름과 상태 끌어올리기 개념

리액트에서 데이터 흐름은 상위에서 하위로 이루어짐. 이를 단방향 데이터 흐름(One-way data flow) 또는 top-down 또는 unidirectional 데이터 흐름이라 함. 이는 상위 컴포넌트가 하위 컴포넌트에게 props를 통해 데이터를 전달하고, 이 데이터는 읽기 전용이라는 원칙을 가지고 있음.

그런데 경우에 따라서는 자식 컴포넌트에서 생성된 데이터를 상위 컴포넌트로 전달해야 할 필요가 생김. 이때 사용하는 기법이 '상태 끌어올리기(Lifting State Up)'임.

상태 끌어올리기란, 공유되어야 하는 state를 가장 가까운 공통 상위 컴포넌트로 이동시키는 것을 말함. 그리고 이 상위 컴포넌트에서 상태 변경 함수를 하위 컴포넌트로 전달하여 상태를 변경하게 함.

다음은 상태 끌어올리기의 간단한 예제임.

import React, { useState } from 'react';

// 자식 컴포넌트. 상위 컴포넌트로부터 value와 onValueChange를 props로 받음
function Child({ value, onValueChange }) {
  const handleChange = (event) => {
    onValueChange(event.target.value);
  };
// 입력 컴포넌트. 상위 컴포넌트의 상태를 value로, 상태 변경 함수를 onChange 핸들러로 사용함
  return <input value={value} onChange={handleChange} />;
}

// 상위 컴포넌트. 자식 컴포넌트의 입력값 상태를 관리함
function App() {
  const [value, setValue] = useState("");

// 자식 컴포넌트에 상태와 상태 변경 함수를 props로 전달함
// p 태그를 사용하여 현재 입력값 상태를 보여줌
  return (
    <div>
      <Child value={value} onValueChange={setValue} />
      <p>The input value is: {value}</p>
    </div>
  );
}

export default App;

참고로 아래 코드 예제는 위의 코드 예제와 동일하게 작동함. 화살표 함수를 이용하고, 자식 컴포넌트에 props를 전달하는 방식이 다를 뿐임.

import React, { useState } from 'react';

const Child = (props) => {
  const handleChange = (event) => {
    props.onValueChange(event.target.value);
  };

  return <input value={props.value} onChange={handleChange} />;
}

const App = () => {
  const [value, setValue] = useState("");

  return (
    <div>
      <Child value={value} onValueChange={setValue} />
      <p>The input value is: {value}</p>
    </div>
  );
}

export default App;

여기서 App 컴포넌트는 상태 value와 해당 상태를 변경하는 setValue 함수를 가지고 있음. 이 두 값을 Child 컴포넌트에 props로 전달함. Child 컴포넌트에서는 inputonChange 이벤트를 통해 setValue 함수를 호출하여 value를 변경함. 이렇게 하면 Child 컴포넌트에서 생성된 입력값이 App 컴포넌트로 전달됨.

리액트의 다양한 Hooks

Hooks는 리액트 16.8에서 도입된 기능으로, 함수형 컴포넌트에서도 상태 관리와 라이프사이클 기능을 사용할 수 있게 해줌. 기본적인 몇 가지 Hooks를 알아보겠음.

1. useState

useState는 컴포넌트 내에서 상태를 가지고 있게 해주는 Hook임. 컴포넌트의 로컬 상태를 선언하고, 관리할 수 있게 해줌.

const [state, setState] = useState(initialState);

2. useEffect

useEffect는 사이드 이펙트를 수행하는 Hook임. 데이터 가져오기, 구독 설정 및 해제, 수동으로 리액트 컴포넌트의 DOM을 수정하는 작업 등, 컴포넌트가 렌더링 이후에 수행되어야 하는 작업을 관리함.

useEffect(() => {
  // side effect를 수행하는 코드
}, [dependencies]);

3. useContext

useContext는 컴포넌트를 중첩하지 않고도 React 컨텍스트를 구독할 수 있게 해주는 Hook임.

const value = useContext(MyContext);

4. useReducer

useReducer는 복잡한 컴포넌트 상태 로직을 관리하거나, 상태를 구성요소 트리로 전달해야 할 때 useState보다 우수한 대안이 될 수 있는 Hook임.

const [state, dispatch] = useReducer(reducer, initialState);

5. useRef

useRef는 .current 프로퍼티로 전달된 인자(initialValue)를 가리키는 변경 가능한 ref 객체를 반환함.

const refContainer = useRef(initialValue);

각각의 Hooks는 서로 다른 문제를 해결하는데 사용되며, 함께 사용하면 더 강력한 기능을 구현할 수 있음.


끝.

profile
프로그래밍 개인공부 공간입니다.

0개의 댓글