ν”„λ‘œκ·Έλž˜λ°/React JS

[React] ν•¨μˆ˜(Function) μ»΄ν¬λ„ŒνŠΈ vs 클래슀(Class) μ»΄ν¬λ„ŒνŠΈ 차이가 λ­˜κΉŒμš”?

μš©λ‡½ 2023. 11. 17. 16:58
λ°˜μ‘ν˜•

[React] ν•¨μˆ˜(Function) μ»΄ν¬λ„ŒνŠΈ vs 클래슀(Class) μ»΄ν¬λ„ŒνŠΈ 차이가 λ­˜κΉŒμš”?

πŸ“– λ“€μ–΄κ°€λ©°

ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈ λ„μž…

λ¦¬μ•‘νŠΈμ—μ„œ ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈλŠ” λ¦¬μ•‘νŠΈκ°€ 처음 생겼을 λ•ŒλΆ€ν„° μžˆμ—ˆμ§€λ§Œ, μ²˜μŒμ—λŠ” μƒνƒœκ΄€λ¦¬λ‚˜ 생λͺ…μ£ΌκΈ° λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•  수 μ—†μ–΄μ„œ 주둜 λ‹¨μˆœν•œ ν‘œν˜„μ„ μœ„ν•œ μš©λ„λ‘œ μ‚¬μš©λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

 

ν•˜μ§€λ§Œ, λ¦¬μ•‘νŠΈ 16.8 λ²„μ „μ—μ„œ Hooksκ°€ λ„μž…λ˜λ©΄μ„œ ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈμ—μ„œλ„ μƒνƒœκ΄€λ¦¬μ™€ 생λͺ…μ£ΌκΈ° λ©”μ†Œλ“œμ™€ μœ μ‚¬ν•œ κΈ°λŠ₯을 ν™œμš©ν•  수 있게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

 

Hooks의 λ„μž…μ€ ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈμ˜ ν™œμš© λ²”μœ„λ₯Ό 크게 ν™•μž₯μ‹œν‚€κ³ , ν˜„μž¬λŠ” ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈλ§ŒμœΌλ‘œλ„ λŒ€λΆ€λΆ„μ˜ κΈ°λŠ₯을 κ΅¬ν˜„ν•  수 있게 λ˜μ—ˆκ³ , κ·Έ κ²°κ³Ό ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈμ˜ μ‚¬μš©μ΄ 크게 λŠ˜μ—ˆμŠ΅λ‹ˆλ‹€.


μ‹€μ œλ‘œ λ¦¬μ•‘νŠΈ κ³΅μ‹λ¬Έμ„œμ—μ„œλ„ 클래슀 μ»΄ν¬λ„ŒνŠΈλ³΄λ‹€ ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈλ‘œ μž‘μ„±ν•˜λŠ” 것을 ꢌμž₯ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

ν•¨μˆ˜ν˜•(Functional)이 μ•„λ‹ˆλΌ ν•¨μˆ˜(Function) μ»΄ν¬λ„ŒνŠΈλΌκ³  λΆ€λ₯΄λŠ” 이유
ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°(Functional Programming)μ—μ„œ 'ν•¨μˆ˜ν˜•'μ΄λΌλŠ” μš©μ–΄λŠ” 순수 ν•¨μˆ˜(pure function), λΆˆλ³€μ„±(immutability), μ‚¬μ΄λ“œ μ΄νŽ™νŠΈ μ—†μŒ(side-effect free) λ“±κ³Ό 같은 νŠΉμ„±μ„ κ°•μ‘°ν•˜λŠ”λ°, λ¦¬μ•‘νŠΈ ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈλŠ” 순수 ν•¨μˆ˜κ°€ μ•„λ‹ˆκ³  μ‚¬μ΄λ“œ μ΄νŽ™νŠΈκ°€ μΌμ–΄λ‚˜λŠ” λ“±κ³Ό 같은 이유둜 λ¦¬μ•‘νŠΈλŠ” 'ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈ'λΌλŠ” 말이 μš©μ–΄κ°€ 더 μ •ν™•ν•©λ‹ˆλ‹€.

클래슀 μ»΄ν¬λ„ŒνŠΈμ˜ μ‚¬μš© κ°μ†Œ

  • ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈμ— λΉ„ν•΄ μƒλŒ€μ μœΌλ‘œ λ³΅μž‘ν•©λ‹ˆλ‹€.
  • 클래슀 문법, this 바인딩, 생λͺ…μ£ΌκΈ° λ©”μ†Œλ“œ 등을 이해해야 ν•˜λ©° μ½”λ“œκ°€ 더 κΈΈμ–΄μ§€λ©΄μ„œ ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈμ— 가독성이 λΉ„ν•΄ λ–¨μ–΄μ§‘λ‹ˆλ‹€.
  • ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈμ— λ„μž…λœ Hooks둜 인해 클래슀 μ»΄ν¬λ„ŒνŠΈμ—μ„œλ§Œ κ°€λŠ₯ν–ˆλ˜ μƒνƒœκ΄€λ¦¬μ™€ 라이프사이클과 μœ μ‚¬ν•œ λ™μž‘μ„ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • 클래슀 μ»΄ν¬λ„ŒνŠΈλŠ” μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜κΈ° λ•Œλ¬Έμ— 각 μΈμŠ€ν„΄μŠ€λ§ˆλ‹€ λ©”λͺ¨λ¦¬λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€. 반면, ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈλŠ” μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ΄ μ μŠ΅λ‹ˆλ‹€.
  • 클래슀 μ»΄ν¬λ„ŒνŠΈλŠ” 클래슀의 κ΄€λ ¨λœ λ‹€μ–‘ν•œ λ©”μ†Œλ“œμ™€ ν”„λ‘œν† νƒ€μž…μ„ 가지고 μžˆμ–΄μ„œ, λ²ˆλ“€λ§λœ 결과물의 크기가 컀질 수 μžˆμŠ΅λ‹ˆλ‹€. 반면, ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈλŠ” ν•„μš”ν•œ κΈ°λŠ₯λ§Œμ„ Hooks둜 가져와 μ‚¬μš©ν•˜λ―€λ‘œ λ²ˆλ“€λ§λœ 결과물의 크기λ₯Ό 쀄일 수 μžˆμŠ΅λ‹ˆλ‹€.

μ΄λŸ¬ν•œ 이유둜 μ΅œκ·Όμ—λŠ” ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈλ₯Ό 더 μ„ ν˜Έν•˜λŠ” μΆ”μ„Έκ°€ λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

ν•˜μ§€λ§Œ, 아직도 클래슀 μ»΄ν¬λ„ŒνŠΈλ₯Ό μ‚¬μš©ν•˜λŠ” κ²½μš°λ„ 많기 λ•Œλ¬Έμ— 클래슀 μ»΄ν¬λ„ŒνŠΈμ— λŒ€ν•œ 이해도 μ€‘μš”ν•©λ‹ˆλ‹€.

클래슀(Class)  μ»΄ν¬λ„ŒνŠΈ

클래슀 μ»΄ν¬λ„ŒνŠΈλŠ” ES6의 클래슀λ₯Ό μ‚¬μš©ν•˜μ—¬ μ •μ˜λ˜λ©°, 생λͺ…μ£ΌκΈ° λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λ˜ν•œ λ‚΄λΆ€ μƒνƒœ(state)λ₯Ό κ°€μ§ˆ 수 μžˆμ–΄ 데이터 변경에 λ”°λ₯Έ λ Œλ”λ§μ„ 관리할 수 μžˆμŠ΅λ‹ˆλ‹€.

생λͺ…μ£ΌκΈ°(Lfie Cycle)

생λͺ…μ£ΌκΈ° λ©”μ†Œλ“œλŠ” μ»΄ν¬λ„ŒνŠΈκ°€ 생성, μ—…λ°μ΄νŠΈ, μ‚­μ œλ˜λŠ” κ³Όμ •μ—μ„œ νŠΉμ • μ‹œμ μ— ν˜ΈμΆœλ˜λŠ” λ©”μ†Œλ“œμž…λ‹ˆλ‹€.

  • constructor(): μ»΄ν¬λ„ŒνŠΈμ˜ μƒμ„±μž λ©”μ†Œλ“œλ‘œ, μ»΄ν¬λ„ŒνŠΈκ°€ 생성될 λ•Œ κ°€μž₯ λ¨Όμ € μ‹€ν–‰λ©λ‹ˆλ‹€. 초기 μƒνƒœλ₯Ό μ„€μ •ν•˜λŠ” λ“± μ»΄ν¬λ„ŒνŠΈμ˜ μ΄ˆκΈ°ν™” μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.
  • componentDidMount(): μ»΄ν¬λ„ŒνŠΈκ°€ λ Œλ”λ§ 후에 μ‹€ν–‰λ˜λŠ” λ©”μ†Œλ“œμž…λ‹ˆλ‹€.
  • shouldComponentUpdate(): μ»΄ν¬λ„ŒνŠΈκ°€ μ—…λ°μ΄νŠΈλ˜κΈ° 전에 μ‹€ν–‰λ˜λŠ” λ©”μ†Œλ“œμž…λ‹ˆλ‹€. 이 λ©”μ†Œλ“œκ°€ falseλ₯Ό λ°˜ν™˜ν•˜λ©΄ ν•΄λ‹Ή μ—…λ°μ΄νŠΈλŠ” μ·¨μ†Œλ©λ‹ˆλ‹€.
  • componnetDidUpdate(): μ»΄ν¬λ„ŒνŠΈκ°€ μ—…λ°μ΄νŠΈλœ 후에 μ‹€ν–‰λ˜λŠ” λ©”μ†Œλ“œμž…λ‹ˆλ‹€.
  • componentWillUnmount(): μ»΄ν¬λ„ŒνŠΈκ°€ 제거되기 전에 μ‹€ν–‰λ˜λŠ” λ©”μ†Œλ“œμž…λ‹ˆλ‹€.

React Life Cycle

클래슀 μ»΄ν¬λ„ŒνŠΈμ˜ μ‚¬μš© μ˜ˆμ‹œ

클래슀 μ»΄ν¬λ„ŒνŠΈμ˜ κΈ°λ³Έ ꡬ쑰

import React from "react";

class MyClassComponent extends React.Component {
  render() {
    return <h1>Hello, World!</h1>;
  }
}

export default MyClassComponent;

κ°€μž₯ 기본적인 클래슀 μ»΄ν¬λ„ŒνŠΈμ˜ κ΅¬μ‘°μž…λ‹ˆλ‹€.

React.Componnetλ₯Ό 상속받아 클래슀λ₯Ό μ •μ˜ν•˜κ³  render() λ©”μ†Œλ“œ μ•ˆμ—μ„œ JSXλ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

μƒνƒœ(state)λ₯Ό κ°€μ§€λŠ” 클래슀 μ»΄ν¬λ„ŒνŠΈ

import React from "react";

class MyClassComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  incrementCount = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={this.incrementCount}>Click me</button>
      </div>
    );
  }
}

export default MyClassComponent;

constructor()μ—μ„œ 초기 μƒνƒœλ₯Ό μ„€μ •ν•˜κ³ , incrementCount λ©”μ†Œλ“œλ₯Ό 톡해 μƒνƒœλ₯Ό λ³€κ²½ν•©λ‹ˆλ‹€.

render() λ©”μ†Œλ“œμ—μ„œλŠ” μƒνƒœλ₯Ό 화면에 ν‘œμ‹œν•˜κ³ , λ²„νŠΌ 클릭 μ‹œ incrementCount λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ μƒνƒœλ₯Ό λ³€κ²½ν•©λ‹ˆλ‹€.

생λͺ…μ£ΌκΈ°(Lfie Cycle)λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” 클래슀 μ»΄ν¬λ„ŒνŠΈ

import React from "react";

class MyClassComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      date: new Date(),
    };
  }

  componentDidMount() {
    this.timerID = setInterval(() => this.tick(), 1000);
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date(),
    });
  }

  render() {
    return <div>ν˜„μž¬ μ‹œκ°„: {this.state.date.toLocaleTimeString()}</div>;
  }
}

export default MyClassComponent;

coumpononetDidMount()μ—μ„œ 타이머λ₯Ό μ„€μ •ν•˜μ—¬ tick() λ©”μ†Œλ“œλ₯Ό 주기적으둜 ν˜ΈμΆœν•˜κ²Œ ν•©λ‹ˆλ‹€.

componentWillMount()μ—μ„œ 타이머λ₯Ό μ œκ±°ν•©λ‹ˆλ‹€.

ν•¨μˆ˜(Function) μ»΄ν¬λ„ŒνŠΈ

ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈλŠ” ν•¨μˆ˜λ‘œ μ •μ˜λ˜λ©°, 본래 μƒνƒœμ™€ 생λͺ…μ£ΌκΈ° λ©”μ†Œλ“œλ₯Ό 가지지 μ•ŠλŠ”λ‹€λŠ” νŠΉμ§•μ„ 가지고 μžˆμ—ˆμ§€λ§Œ Hooks의 λ„μž…μœΌλ‘œ μƒνƒœ 관리와 생λͺ…주기와 μœ μ‚¬ν•œ κΈ°λŠ₯을 κ°€μ§ˆ 수 있게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€.


Hooksλž€ ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈμ—μ„œλ„ μƒνƒœ 관리와 생λͺ…μ£ΌκΈ° κΈ°λŠ₯을 μ‚¬μš©ν•  수 있게 ν•΄μ£ΌλŠ” κΈ°λŠ₯μž…λ‹ˆλ‹€.

λŒ€ν‘œμ μœΌλ‘œ useState(), useEffect() 등이 μžˆμŠ΅λ‹ˆλ‹€.

ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈμ˜ κΈ°λ³Έ ꡬ쑰

import React from 'react';

function MyFunctionComponent() {
  return <h1>Hello, World!</h1>;
}

export default MyFunctionComponent;

κ°€μž₯ 기본적인 ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈμ˜ κ΅¬μ‘°μž…λ‹ˆλ‹€. ν•¨μˆ˜λ₯Ό μ •μ˜ν•˜κ³  κ·Έ μ•ˆμ—μ„œ JSXλ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈμ—μ„œ 생λͺ…μ£ΌκΈ°

클래슀 μ»΄ν¬λ„ŒνŠΈμ—μ„œ μ‚¬μš©λœ 생λͺ…μ£ΌκΈ° λ©”μ†Œλ“œλ“€μ΄ ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈμ—μ„œλŠ” μ–΄λ–»κ²Œ λ™μž‘ν• κΉŒμš”?

  • useState(): μƒνƒœλ₯Ό μƒμ„±ν•˜κ³  κ΄€λ¦¬ν•˜λŠ” Hook으둜, 클래슀 μ»΄ν¬λ„ŒνŠΈμ˜ this.state와 this.setState()에 ν•΄λ‹Ήν•©λ‹ˆλ‹€.
  • useEffect(): 생λͺ…μ£ΌκΈ°(Life Cycle)λ₯Ό κ΄€λ¦¬ν•˜λŠ” Hook으둜, componentDidMount, componentDidUpdate, componentWillUnmount의 역할을 ν•˜λ‚˜λ‘œ ν•©μΉœ κ²ƒμž…λ‹ˆλ‹€.

μ»΄ν¬λ„ŒνŠΈ 마운트 / μ–Έλ§ˆμš΄νŠΈ μ‹œμ μ— μ‹€ν–‰λ˜λŠ” useEffect

import React, { useEffect } from 'react';

function MyComponent() {
  useEffect(() => {
    console.log('Component Did Mount');

    return () => {
      console.log('Component Will Unmount');
    };
  }, []); // 빈 배열을 λ„£μ–΄ 마운트 μ‹œμ μ—λ§Œ μ‹€ν–‰

  return <div>My Component</div>;
}

export default MyComponent;

νŠΉμ • μƒνƒœμ˜ 변경을 κ°μ§€ν•˜λŠ” useEffect

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

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

  useEffect(() => {
    console.log(`Count has changed to ${count}`);
  }, [count]); // count의 변경을 감지

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

export default MyComponent;

πŸ“• 마무리

클래슀 μ»΄ν¬λ„ŒνŠΈμ™€ ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈλŠ” μ„ μ–Έ 방식, μƒνƒœ 관리 방식, 생λͺ…μ£ΌκΈ° λ©”μ†Œλ“œ μ‚¬μš© 방식 λ“±μ—μ„œ 차이가 μžˆμŠ΅λ‹ˆλ‹€.

 

ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈμ™€ HooksλŠ” 클래슀 μ»΄ν¬λ„ŒνŠΈμ˜ 라이프사이클과 μƒνƒœ 관리 방식을 λ‹¨μˆœν™”ν•˜κ³ , 가독성을 높이고, ν…ŒμŠ€νŠΈμ™€ μœ μ§€λ³΄μˆ˜μ—μ„œ μš©μ΄ν•¨ λ“± μ—¬λŸ¬ 가지 μž₯점을 μ œκ³΅ν•©λ‹ˆλ‹€.

 

ν•˜μ§€λ§Œ 이런 μž₯점을 μ΅œλŒ€ν•œ ν™œμš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” 기본적으둜 클래슀 μ»΄ν¬λ„ŒνŠΈμ™€ ν•΄λ‹Ή 생λͺ…μ£ΌκΈ°(Life Cycle)κ°€ μ–΄λ–»κ²Œ λ™μž‘ν•˜λŠ”μ§€ μ΄ν•΄ν•˜λŠ” 것이 μ€‘μš”ν•©λ‹ˆλ‹€.

 

λ”°λΌμ„œ, 클래슀 μ»΄ν¬λ„ŒνŠΈμ˜ μ΄ν•΄λŠ” ν•¨μˆ˜ μ»΄ν¬λ„ŒνŠΈμ™€ Hooks μ‚¬μš©μ— μžˆμ–΄μ„œ μ€‘μš”ν•œ 기반 지식이 λ©λ‹ˆλ‹€.

λ°˜μ‘ν˜•