(SK쉴더스)AI 활용한 클라우드 & 보안 전문가 양성 캠프

[새싹 성동 2기] 4-2. 리액트 컴포넌트(2)

hunm719 2024. 11. 18. 20:45

목차

 

1. 리액트 컴포넌트의 기본 구조            

2. 컴포넌트의 상태 관리                       

3. 컴포넌트의 동작과 생명 주기           

4. 컴포넌트 간의 상태 전달 방법          

5. 컴포넌트의 DOM 참조                     

6. 컴포넌트의 동작과 사용자 인터렉션 

 

[1] 리액트 컴포넌트의 기본 구조

 

(1) 부모-자식 컴포넌트

 - 부모 컴포넌트는 다른 컴포넌트를 포함하고, 자식 컴포넌트에 데이터를 전달하는 역할

 - 부모는 자식 컴포넌트를 렌더링하고, 그들에게 필요한 props를 전달하여 UI를 동적으로 구성함

// 부모 컴포넌트
function Parent() {
  const [message, setMessage] = useState("Hello, React!");

  const handleMessageChange = () => {
    setMessage("Updated message from Parent");
  };

  return (
    <div>
      <h1>Parent Component</h1>
      <Child message={message} onMessageChange={handleMessageChange} />  // Parent는 Child에게 message를 props로 전달하고, onMessageChange라는 콜백 함수도 전달
    </div>
  );
}

// 자식 컴포넌트
function Child({ message, onMessageChange }) {
  return (
    <div>
      <h2>{message}</h2>
      <button onClick={onMessageChange}>Change Message</button>  // Child는 message를 화면에 출력하고, 버튼을 클릭하면 onMessageChange 함수를 호출하여 부모 컴포넌트의 상태를 업데이트
    </div>
  );
}

 

(2) Props(=Properties)

 - 부모 컴포넌트에서 자식 컴포넌트로 전달되는 읽기 전용 데이터로, 모든 자바스크립트 값을 props로 전달할 수 있음

 - props는 읽기 전용이므로, 자식 컴포넌트는 부모 컴포넌트에게서 전달 받은 props를 수정할 수 없음

 - 주로 컴포넌트 간에 데이터를 전달하려는 목적으로 사용됨

 - 컴포넌트는 props가 변경될 때마다 다시 렌더링(=리렌더링)

// 부모 컴포넌트
function Parent() {
  const message = "Hello, World!";
  return (
    <div>
      <Child message={message} />	// Parent는 message라는 데이터를 Child 컴포넌트에 props로 전달
    </div>
  );
}

// 자식 컴포넌트
function Child(props) {
  return <h2>{props.message}</h2>;	// Child는 props.message를 사용하여 데이터를 렌더링
}

 

 

[2] 컴포넌트의 상태 관리

 

State(=상태 = 상태 변수)

 - 컴포넌트 내부에서 읽고 업데이트할 수 있는 값으로, 컴포넌트의 상태를 나타냄

 - 일반 변수와 달리, 상태 변수는 변경되면 해당 상태를 참조하고 있는 컴포넌트는 자동으로 리렌더링

 - 클래스형 컴포넌트는 setState() 메서드를 이용해 상태 변수를 변경함

// 클래스형 컴포넌트에서 setState() 메서드 사용 방법
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };  // 초기 state 설정
  }

  increment = () => {
    this.setState({ count: this.state.count + 1 });  // 상태 변경(=업데이트)
  };

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increase</button>
      </div>
    );
  }
}

// 배열이나 객체를 상태로 사용할 때 = 전개 연산자(...)를 사용하여 원본을 복사한 후 새로운 값을 추가
function TodoList() {
  const [todos, setTodos] = useState([]);

  const addTodo = (newTodo) => {
    setTodos([...todos, newTodo]);  // 기존 배열을 복사하여 새로운 아이템 추가
  };

  return (
    <div>
      <ul>
        {todos.map((todo, index) => (
          <li key={index}>{todo}</li>
        ))}
      </ul>
      <button onClick={() => addTodo('New Task')}>Add Todo</button>
    </div>
  );
}

 - 함수형 컴포넌트는 useState 훅 함수가 반환하는 setter함수(=업데이트 함수)를 이용해 상태 변수를 변경함

       상태변수  세터함수 또는 업데이터   상태변수의 초기값    
        ~~~~~~~  ~~~~~~~~~~               ~~
const [ message, setMessage ] = useState( '' );
         ^        ^             ~~~~~~~~~~~~~~            
         |        |             상태 변수의 초기값과 상태 변수의 값을 변경할 때 사용할 함수를 배열로 반환
         |        |             return [ 상태 변수의 초기값, 상태 변수의 값을 변경할 때 사용할 함수 ]
         |        |                            |                        |
         +--------|----------------------------+                        |
                  +-----------------------------------------------------+  	⇐ 배열 비구조화

 

 

[3] 컴포넌트의 동작과 생명 주기

 

(1) hooks

 - 클래스형 컴포넌트에서 제공하던 기능(state, props, ref, life cycle method 등)을 함수형 컴포넌트에서 지원하기 위해 도입된 함수

 - 함수 이름은 use 접두어를 사용함(useState, useEffect, useRef, ...)

 

1. useState - 상태 관리

 - 함수형 컴포넌트에서 상태를 선언하고 관리할 수 있게 해주는 훅

 - 이 훅은 [상태변수의 초기값, 상태변수의 값을 변경하는 함수] 배열을 반환

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0); // 상태 초기값은 0

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increase</button>
    </div>
  );
}

// useState(initialValue) : 상태의 초기값을 설정
// setCount(value) : 상태를 변경하는 함수로, 이 함수가 호출될 때마다 해당 컴포넌트가 리렌더링

 

 

2. useEffect - 효과 관리

 - Side Effects를 관리하는 훅

 - 컴포넌트가 렌더링된 후에 비동기 작업이나 외부 데이터 가져오기, 구독 처리, 타이머 설정 등을 처리할 때 사용

import React, { useState, useEffect } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => setSeconds(prev => prev + 1), 1000);

    // 컴포넌트가 unmount 될 때 타이머를 클리어
    return () => clearInterval(timer);
  }, []); // 빈 배열을 넣으면 마운트될 때 한 번만 실행

  return <p>Time: {seconds}s</p>;
}

// useEffect(callback, dependencies) 로 구성
// callback은 부수효과를 처리하는 함수
// dependencies는 useEffect가 언제 실행될지 결정하는 의존성 배열로, 빈 배열[]은 마운트시 한 번만 실행

 - 후처리 (cleanup) 함수 : useEffect 내부에서 반환된 함수로, 컴포넌트가 언마운트되거나, 의존성 배열에 지정된 값이 변경될 때 호출되어 사이드 이펙트를 처리하는데 사용

import { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    // 초기화 작업: 이벤트 리스너 추가
    const timer = setInterval(() => {
      console.log('타이머 작동 중');
    }, 1000);

    // 후처리 함수: 컴포넌트가 언마운트되거나 의존성 배열이 변경될 때 호출
    return () => {
      clearInterval(timer); // 타이머 정리
      console.log('타이머가 종료되었습니다.');
    };
  }, []); // 빈 배열은 컴포넌트가 마운트될 때 한 번만 실행

  return <div>타이머가 작동 중입니다.</div>;
}

 

3. useRef - 참조 관리

 - DOM(Document Object Model : HTML 요소들에 대한 객체 모델)요소나 값을 기억하는데 사용되는 훅

 - 렌더링 없이 값을 지속적으로 저장하거나, DOM을 직접 조작할 때 사용

import React, { useRef } from 'react';

function FocusInput() {
  const inputRef = useRef();  // ref 객체를 반환하며, 해당 객체는 렌더링 간에 유지되는 값을 저장함

  const focusInput = () => {
    inputRef.current.focus(); // ref를 통해 DOM 요소에 직접 접근
  };

  return (
    <div>
      <input ref={inputRef} />
      <button onClick={focusInput}>Focus the input</button>
    </div>
  );
}

 

4. useMemo - 성능 최적화

 - 계산 비용이 큰 값을 메모이제이션(memoization)하여 성능을 최적화하는데 사용하는 훅

// const memoizedValue = useMemo( () => computeExpensiveValue(a, b), [a, b] );
//                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  ~~~~~~
//                                계산을 수행하는 함수               의존성 배열 → 배열의 값이 변경되었을 때만 함수를 실행
//                                                                  의존성 배열을 넣지 않는 경우, 
//                                                                  렌더링이 일어날 때마다 매번 함수를 실행

import React, { useMemo, useState } from 'react';

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

  const expensiveResult = useMemo(() => {
    console.log('Calculating...');
    return count * 2;
  }, [count]); // count가 변경될 때만 계산

  return (
    <div>
      <p>Result: {expensiveResult}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

 

5. useCallback - 함수 최적화

 - 함수를 메모이제이션하여 불필요한 함수 재생성을 방지하는 훅

 - 컴포넌트가 리렌더링될 때 동일한 콜백 함수가 사용되도록 하며, 자식 컴포넌트에 콜백 함수를 전달할 때 유용함

// const memorizedCallback = useCallback(() => { ... }, [ ... ]);
//                                       ~~~~~~~~~~~~~  ~~~~~~
//                                       콜백 함수      의존성 배열 ⇒ 배열의 값이 변경될 때만 콜백 함수를 새로 생성

import React, { useCallback, useState } from 'react';

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

  const increment = useCallback(() => setCount(count + 1), [count]);

  return (
    <div>
      <Child onClick={increment} />
      <p>Count: {count}</p>
    </div>
  );
}

function Child({ onClick }) {
  return <button onClick={onClick}>Increment</button>;
}

 

6. useReducer - 상태 관리

 - 상태와 상태를 업데이트하는 로직을 분리할 때 사용하는 훅

 - 단순 상태 관리는 useState로도 가능하지만, 상태가 다단계로 변하거나 관련된 여러 상태를 관리해야할 때 적합

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

// reducer : 현재 상태와 액션을 기반으로 새 상태를 반환하는 함수
// initialState : 초기 상태
// dispatch : 액션을 발생시키는 함수

 

7. useContext - 데이터 전달

 - 컴포넌트 트리 전체에 데이터를 전달해야 할 때 사용하는 훅

 - 리액트에서는 일반적으로 props를 통해 데이터를 전달하지만, 여러 컴포넌트 계층을 거쳐야 하면 코드가 복잡해질 수 있기 때문에 전역으로 데이터를 공유하고자 하는 경우에 적합

사용법
1. React.createContext()로 컨텍스트를 생성
2. Context.Provider로 데이터를 공급
3. 하위 컴포넌트에서 useContext로 데이터를 사용

 

 

(2) life cycle(생명 주기)

 - 컴포넌트가 생성, 업데이트, 소멸되는 과정에서 호출되는 메서드

 - 함수형 컴포넌트에서는 useEffect 훅을 사용함

 - 클래스형 컴포넌트에서는 생명 주기 메서드(DidMount, DidUpdate, WillUnmount)를 사용함

https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

 

요 생명 주기 단계

 (1) 마운트(Mount) : 컴포넌트가 생성되고, DOM에 추가되는 과정

   - 클래스형 컴포넌트에서:

     constructor : 컴포넌트 초기 설정. 초기 상태(state)와 인스턴스 변수를 정의함

     static getDerivedStateFromProps : 부모 컴포넌트로부터 전달된 props에 따라 state를 업데이트

     render : JSX를 반환하여 DOM에 렌더링

     componenetDidMount : 컴포넌트가 처음 DOM에 렌더링된 이후 호출됨. API 호출, 이벤트 리스너 등록 등에 사용됨

 

   - 함수형 컴포넌트에서:

     useEffect : 마운트 후의 작업을 처리함

 

 (2) 업데이트(Update) : 컴포넌트가 리렌더링되는 과정

   - 클래스형 컴포넌트에서:

     static getDerivedStateFromProps: 새로운 props에 따라 state를 업데이트

     shouldComponentUpdate: 컴포넌트 리렌더링 여부를 결정(true일 때 리렌더링)

     render : 컴포넌트가 업데이트될 때 다시 호출되어 DOM을 갱신

     componenetDidUpdate : 컴포넌트가 업데이트된 후 호출됨

 

   - 함수형 컴포넌트에서:

     useEffect : 상태나 props 변경 시 실행 됨

 

 (3) 언마운트(Unmount) : 컴포넌트가 DOM에서 제거되는 과정

   - 클래스형 컴포넌트에서:

     componentWillUnmount : 컴포넌트가 DOM에서 제거되기 직전에 호출

 

   - 함수형 컴포넌트에서:

     useEffect의 claenup 함수

life cycle에서의 두 컴포넌트 비교