리알못 React: 2. 컴포넌트 구성

Table of Content

이 글은 Code with Mosh의 Mastering React 과정과 리액트를 다루는 기술(김민준 저) 책을 공부하며 정리한 글입니다. 리알못이라 이상하거나 틀린 내용이 있을 것입니다…

Props

Props는 부모 컴포넌트가 자식 컴포넌트에게 값을 전달하기 위해 사용됩니다. 객체 데이터를 표시하거나 변경 사항 등을 알리기 위해 사용되며 읽기전용이므로 값을 수정할 수 없습니다.

다른 컴포넌트에 값 전달

Props를 이용하여 전달

아래 예제는 Props를 이용하여 부모 컴포넌트(App)에서 문자열을 자식 컴포넌트(MyComponent)로 전달하는 예제입니다.

/***** App.js (클래스형 컴포넌트) *****/

import React, { Component } from 'react'
import MyComponent from './components/MyComponent';

export default class App extends Component {
  state = {
    value: "프롭스!!!"
  }

  render() {
    return (
      <div>
        <p><MyComponent value="Props!!!" /></p>
        <p><MyComponent value={this.state.value} /></p>
      </div>
    )
  }
}
/***** App.js (함수형 컴포넌트) *****/

import React, { useState } from 'react';
import MyComponent from './components/MyComponent';

const App = () => {
  const [stateValue, setStateValue] = useState("프롭스!!!");

  return (
    <div>
      <p><MyComponent value="Props!!!" /></p>
      <p><MyComponent value={stateValue} /></p>
    </div>
  );
};

export default App;
/***** ./components/MyComponent.jsx (클래스형 컴포넌트) *****/

import React, { Component } from 'react'

export default class MyComponent extends Component {
  render() {
    return this.props.value;
  }
}
/***** ./components/MyComponent.jsx (함수형 컴포넌트) *****/

import React from 'react';

const MyComponent = (props) => {
  return props.value;
};

export default MyComponent;

props를 이용하여 state 배열의 각 요소 전달

/***** App.js (클래스형 컴포넌트) *****/

import React, { Component } from 'react';
import MyComponent from './components/MyComponent';

export default class App extends Component {
  state = {
    persons: [
      { id: 0, name: "ing-yeo", desc: "잉여인간" },
      { id: 1, name: "react", desc: "리액트" },
      { id: 2, name: "js", desc: "자바스크립트" }
    ]
  }

  render() {
    return (
      <div>
        {this.state.persons.map(person =>
          <MyComponent key={person.id} person={person} />
        )}
      </div>
    )
  }
}
/***** App.js (함수형 컴포넌트) *****/

import React, { useState } from 'react';
import MyComponent from './components/MyComponent';

const App = () => {
  const [persons, setPersons] = useState([
    { id: 0, name: "ing-yeo", desc: "잉여인간" },
    { id: 1, name: "react", desc: "리액트" },
    { id: 2, name: "js", desc: "자바스크립트" },
  ]);

  return (
    <div>
      {persons.map(person => (
        <MyComponent key={person.id} person={person} />
      ))}
    </div>
  );
};

export default App;
/***** ./components/MyComponent.jsx (클래스형 컴포넌트) *****/

import React, { Component } from 'react';

export default class MyComponent extends Component {
  render() {
    const person = this.props.person; // 전달받은 props

    return (
      <div>
        <h1>{person.name}</h1>
        <p>{person.desc}</p>
      </div>
    )
  }
}
/***** ./components/MyComponent.jsx (함수형 컴포넌트) *****/

import React from 'react';

const MyComponent = (props) => {
  const person = props.person;

  return (
    <div>
      <h1>{person.name}</h1>    
      <p>{person.desc}</p>
    </div>
  );
};

export default MyComponent;

구조분해 할당문법(Object Destructing)을 사용하면 코드를 좀 더 간결하게 줄일 수 있습니다.

import React from 'react';

const MyComponent = (props) => {
  const { name, desc } = props.person; // 비구조화 할당 문법

  return (
    <div>
      <h1>{name}</h1>
      <p>{desc}</p>
    </div>
  );
};

export default MyComponent;

props.children을 이용하여 전달

props.children는 특수한 props입니다. 다음과 같이 사용할 수 있습니다.

  • 부모 컴포넌트에서 자식 컴포넌트 태그를 명시하고 태그 내에 전달할 값을 구현
  • 자식 컴포넌트에서 props.children을 이용하여 전달 받은 값 표시
/***** App.js (클래스형 컴포넌트) *****/

import React, { Component } from 'react';
import MyComponent from './components/MyComponent';

export default class App extends Component {
  render() {
    return (
      <MyComponent value="Props!!!">
        {/* props.children 영역 내부*/}
        <h1>안녕요?ㅎ</h1>
        <p>props.children입니다.</p>
      </MyComponent>
    )
  }
}
/***** App.js (함수형 컴포넌트) *****/

import React from "react";
import MyComponent from "./components/MyComponent";

const App = () => {
  return (
    <MyComponent value="Props!!!">
      {/* props.children 영역 내부*/}
      <h1>안녕요?ㅎ</h1>
      <p>props.children입니다.</p>
    </MyComponent>
  );
};

export default App;
/***** ./components/MyComponent.jsx (클래스형 컴포넌트) *****/

import React, { Component } from 'react';

export default class MyComponent extends Component {
  render() {
    return (
      <div>
        {this.props.children} {/* 전달받은 props.children */}
        {this.props.value}
      </div>
    )
  }
}
/***** ./components/MyComponent.jsx (함수형 컴포넌트) *****/

import React from "react";

const MyComponent = ({ children, value }) => { // 구조분해 할당 문법
  return (
    <div>
      {children} {/* 전달받은 props.children */}
      {value}
    </div>
  );
};

export default MyComponent;

부모 컴포넌트에 이벤트 전달하기

부모 컴포넌트에 이벤트를 전달하기 위해 props를 사용합니다.

아래 예제는 해당 버튼을 클릭했을 때 자식 컴포넌트의 값을 0으로 초기화, 1씩 증가 및 삭제하는 예제입니다.

/***** App.js *****/

import React, { Component } from 'react';
import Counter from './components/Counter';

export default class App extends Component {
  state = {
    counters: [
      { id: 0, value: 0 },
      { id: 1, value: 1 },
      { id: 2, value: 2 },
    ]
  }

  handleReset = () => {
    const counters = this.state.counters.map(c => {
      c.value = 0;
      return c;
    });
    this.setState({ counters });
  }

  handleIncrement = (counter) => {
    const counters = [...this.state.counters];
    const index = counters.indexOf(counter);
    counters[index].value++;
    this.setState({ counters });
  }

  handleDelete = (counterId) => {
    const counters = this.state.counters.filter(counter =>
      counter.id !== counterId);
    this.setState({ counters });
  }

  render() {
    return (
      <React.Fragment>
        <p><button onClick={this.handleReset}>Reset</button></p>
        {this.state.counters.map(counter => (
            <Counter 
              key={counter.id} 
              counter={counter} // 배열 값을 props를 통해 통째로 전달
              onIncrement={this.handleIncrement} 
              onDelete={this.handleDelete} 
            />
        ))}
      </React.Fragment>
    )
  }
}
/***** App.js (함수형 컴포넌트) *****/

import React, { useState } from 'react';
import Counter from './components/Counter';

const App = () => {
  const [counters, setCounters] = useState([
    { id: 0, value: 0 },
    { id: 1, value: 1 },
    { id: 2, value: 2 },
  ]);

  const handleReset = () => {
    const newCounters = counters.map(counter => {
      counter.value = 0;
      return counter;
    })
    setCounters(newCounters);
  }

  const handleIncrement = (counter) => {
    const newCounters = [...counters];
    const index = newCounters.indexOf(counter);
    newCounters[index].value++;
    setCounters(newCounters);
  }

  const handleDelete = (counterId) => {
    const newCounters = counters.filter(counter => counter.id !== counterId);
    setCounters(newCounters);
  }

  return (
    <>
      <p><button onClick={handleReset}>Reset</button></p>
      {counters.map(counter => (
        <Counter
          key={counter.id} 
          counter={counter} // 배열 값을 props를 통해 통째로 전달 
          onIncrement={handleIncrement}
          onDelete={handleDelete}
        />
      ))}
    </>
  );
};

export default App;
/***** ./components/Counter.jsx (클래스형 컴포넌트) *****/

import React, { Component } from 'react';

export default class Counter extends Component {
  state = {}
  render() {
    return (
      <React.Fragment>
        <div>
          {this.props.counter.value}
          <button onClick={() => this.props.onIncrement(this.props.counter)}>Increment</button>
          <button onClick={() => this.props.onDelete(this.props.counter.id)}>Delete</button>
        </div>
      </React.Fragment>
    );
  }
}
/***** ./components/Counter.jsx (함수형 컴포넌트) *****/

import React from 'react';

const Counter = (props) => {
  return (
    <>
      <div>
        {props.counter.value}
        <button onClick={() => props.onIncrement(props.counter)}>Increment</button>
        <button onClick={() => props.onDelete(props.counter.id)}>Delete</button>
      </div>
    </>
  );
};

export default Counter;
  • 실행화면

(https://ing-yeo.net/wp-content/uploads/2019/08/react-beginner-2-1.png

간단한 Navigation Bar 만들기

먼저 리알못 React: 1. 컴포넌트 기초 글을 참고하여 Bootstrap을 사용합니다.

https://getbootstrap.com 사이트의 Documentation 메뉴 -> Components -> Navbar로 들어가면 Navbar 사용 예제가 나옵니다. 여기선 화면 상단에 간단한 네비게이션 바(Navigation Bar)를 배치해보겠습니다.

/***** App.js (클래스형 컴포넌트) *****/

import React, { Component } from 'react';
import NavBar from './components/NavBar';

export default class App extends Component {
  render() {
    return (
      <React.Fragment>
        <NavBar />
        <main className="container">안녕요?ㅎ</main>
      </React.Fragment>
    )
  }
}
/***** App.js (함수형 컴포넌트) *****/

import React from "react";
import NavBar from './components/NavBar';

const App = () => {
  return (
    <>
      <NavBar />
      <main className="container">안녕요?ㅎ</main>
    </>
  );
};

export default App;
/***** ./components/NavBar.jsx (클래스형 컴포넌트) *****/

import React, { Component } from 'react';

export default class NavBar extends Component {
  render() {
    return (
      <nav class="navbar navbar-light bg-light">
        <a class="navbar-brand" href="#">Navbar</a>
      </nav>
    );
  }
}
/***** ./components/NavBar.jsx (함수형 컴포넌트) *****/

import React from "react";

const NavBar = () => {
  return (
    <nav className="navbar navbar-light bg-light">
      <a className="navbar-brand" href="#">
        Navbar
      </a>
    </nav>
  );
};

export default NavBar;

https://ing-yeo.net/wp-content/uploads/2019/08/react-beginner-2-2.png

부모-자식 관계가 아닌 컴포넌트 간 정보 공유

부모-자식 관계가 아닌 컴포넌트 간에 정보를 공유하려면 App 컴포넌트를 최상위 컴포넌트로 둔 후 App 컴포넌트를 통해 정보를 공유하면 됩니다.

아래 예제는 값이 0 이상인 Counter 컴포넌트의 개수를 네비게이션 바에 출력하는 예제입니다. Counter 컴포넌트의 정보(이벤트)가 Counters -> App 컴포넌트로 전달되며 App 컴포넌트는 NavBar 컴포넌트로 그 정보를 전달합니다.

/***** App.js (클래스형 컴포넌트) *****/

import React, { Component } from 'react';
import NavBar from './components/NavBar';
import Counters from './components/Counters';

export default class App extends Component {
  state = {
    counters: [
      { id: 0, value: 0 },
      { id: 1, value: 1 },
      { id: 2, value: 2 },
    ]
  }

  handleReset = () => {
    const counters = this.state.counters.map(c => {
      c.value = 0;
      return c;
    });
    this.setState({ counters });
  }

  handleIncrement = (counter) => {
    const counters = [...this.state.counters];
    const index = counters.indexOf(counter);
    counters[index].value++;
    this.setState({ counters });
  }

  handleDelete = (counterId) => {
    const counters = this.state.counters.filter(counter =>
      counter.id !== counterId);
    this.setState({ counters });
  }

  render() {
    return (
      <React.Fragment>
        <NavBar totalCounters={this.state.counters.filter(counter => counter.value > 0).length} /> {/* 값이 0 이상인 Counter의 개수를 props를 통해 전달 */}
        <main className="container">
          <Counters
            counters={this.state.counters}
            onReset={this.handleReset}
            onIncrement={this.handleIncrement}
            onDelete={this.handleDelete}
          />
        </main>
      </React.Fragment>
    )
  }
}
/***** App.js (함수형 컴포넌트) *****/

import React, { useState } from "react";
import NavBar from "./components/NavBar";
import Counters from "./components/Counters";

const App = () => {
  const [counters, setCounters] = useState([
    { id: 0, value: 0 },
    { id: 1, value: 1 },
    { id: 2, value: 2 },
  ]);

  const handleReset = () => {
    const newCounters = counters.map((counter) => {
      counter.value = 0;
      return counter;
    });
    setCounters(newCounters);
  };

  const handleIncrement = (counter) => {
    const newCounters = [...counters];
    const index = newCounters.indexOf(counter);
    newCounters[index].value++;
    setCounters(newCounters);
  };

  const handleDelete = (counterId) => {
    const newCounters = counters.filter((counter) => counter.id !== counterId);
    setCounters(newCounters);
  };

  return (
    <>
      <NavBar totalCounters={counters.filter((counter) => counter.value > 0).length} /> {/* 값이 0 이상인 Counter의 개수를 props를 통해 전달 */}
      <main className="container">
        <Counters
          counters={counters}
          onReset={handleReset}
          onIncrement={handleIncrement}
          onDelete={handleDelete}
        />
      </main>
    </>
  );
};

export default App;
/***** ./components/Counters.js (클래스형 컴포넌트) *****/

import React, { Component } from 'react';
import Counter from './Counter';

export default class Counters extends Component {
  render() {
    return (
      <React.Fragment>
        <p><button onClick={this.props.onReset}>Reset</button></p>
        {this.props.counters.map(counter => (
          <Counter
            key={counter.id}
            counter={counter}
            onIncrement={this.props.onIncrement}
            onDelete={this.props.onDelete}
          />
        ))}
      </React.Fragment>
    )
  }
}
/***** ./components/Counters.js (함수형 컴포넌트) *****/

import React from 'react';
import Counter from './Counter';

const Counters = (props) => {
  return (
    <>
      <p><button onClick={props.onReset}>Reset</button></p>
      {props.counters.map(counter => (
        <Counter 
          key={counter.id} 
          counter={counter} 
          onIncrement={props.onIncrement} 
          onDelete={props.onDelete} 
        />
      ))}
    </>
  );
};

export default Counters;
/***** ./components/Counter.js (클래스형 컴포넌트) *****/

import React, { Component } from 'react';

export default class Counter extends Component {
  render() {
    return (
      <React.Fragment>
        <div>
          {this.props.counter.value}
          <button onClick={() => this.props.onIncrement(this.props.counter)}>Increment</button>
          <button onClick={() => this.props.onDelete(this.props.counter.id)}>Delete</button>
        </div>
      </React.Fragment>
    );
  }
}
/***** ./components/Counter.js (함수형 컴포넌트) *****/

import React from 'react';

const Counter = (props) => {
  return (
    <>
      <div>
        {props.counter.value}
        <button onClick={() => props.onIncrement(props.counter)}>Increment</button>
        <button onClick={() => props.onDelete(props.counter.id)}>Delete</button>
      </div>
    </>
  );
};

export default Counter;
/***** ./components/NavBar.jsx (클래스형 컴포넌트) *****/

import React, { Component } from 'react';

export default class NavBar extends Component {
  render() {
    return (
      <nav class="navbar navbar-light bg-light">
        <a class="navbar-brand" href="#">Navbar: {this.props.totalCounters}</a>
      </nav>
    );
  }
}
/***** ./components/NavBar.jsx (함수형 컴포넌트) *****/

import React from "react";

const NavBar = (props) => {
  return (
    <nav className="navbar navbar-light bg-light">
      <a className="navbar-brand" href="#">
        Navbar: {props.totalCounters}
      </a>
    </nav>
  );
};

export default NavBar;

https://ing-yeo.net/wp-content/uploads/2019/08/react-beginner-2-3.png

컴포넌트 라이프사이클(Lifecycle) API

컴포넌트 라이프사이클(Lifecycle) API는 컴포넌트가 생성(Mount), 업데이트(Update) 및 소멸(Unmount)되었을 때 자동적으로 호출되는 함수입니다.

함수형 컴포넌트(Stateless Functional Components)는 그 자체가 값을 반환하는 함수이므로 라이프사이클 API를 사용할 수 없습니다. 따라서 라이프사이클 API를 사용하려면 클래스형 컴포넌트를 사용해야 했었으나… Hooks를 사용하면 함수형 컴포넌트에서도 라이프사이클 API와 비슷한 기능을 사용할 수 있습니다.

Mount 단계

컴포넌트의 인스턴스가 생성되어 DOM에 입력되었을 때 constructor() -> render() -> componentDidMount() 순서대로 함수가 실행됩니다.

constructor()

주로 외부에서 전달받은 값으로 속성(state) 등을 초기화할 때 사용합니다. state가 생성되기 전이므로 setState() 함수를 사용하지 않고 state 값을 직접 지정해야 합니다.

export default class App extends Component {

  // constructor(): 컴포넌트가 생성되었을 때 호출
  constructor(props) {
    super(props);
    console.log('App - Constructor');
    this.state = this.props.something; // setState()를 사용하지 않고 직접 state를 지정해야 하며, constructor의 매개변수로 props를 전달받지 않으면 this.props는 undefined가 됨
  }

  // 생략
}

render()

컴포넌트에서 보여질 내용을 명시합니다.

export default class App extends Component {
  render() {
    console.log('App - Rendered');
    return <h1>안녕요?ㅎ</h1>
  }
}

componentDidMount()

컴포넌트가 DOM에 완전히 구현되었을 때 호출됩니다. 서버로부터 데이터를 얻어오는 작업(Ajax Call 등)을 할 때 주로 사용합니다.

import React, { Component } from 'react';
import MyComponent from './components/MyComponent';

export default class App extends Component {
  // componentDidMount(): 컴포넌트가 DOM에 완전히 구현되었을 때 호출
  componentDidMount() {
    console.log('App - Mounted');
    // this.setState({ something });
  }

  // 생략
}

Update

컴포넌트의 state나 props가 변경될 때 render() -> componentDidUpdate() 순서대로 함수가 실행됩니다. 이 때 모든 DOM을 다시 렌더링하지 않고 변경이 발생한 부분만 렌더링합니다.

render()

더 이상의 자세한 설명은 생략한다…

componentDidUpdate()

컴포넌트가 업데이트되었을 때 호출됩니다.

export default class App extends Component {
  componentDidUpdate(prevProps, prevState) {
    console.log('prevProps', prevProps);
    console.log('prevState', prevState);
    if(prevProps.value !== this.props.value){
      // Ajax call and get new data from the server.
    }
  }

  // 생략
}

Unmount

DOM에서 컴포넌트가 제거될 때 componentWillUnmount() 함수가 호출됩니다.

componentWillUnmount()

DOM에서 컴포넌트가 제거되기 직전에 호출됩니다. 컴포넌트를 삭제하기 전 메모리를 차지하는 객체(타이머, 리스너 관련 객체 등)들을 초기화(제거)할 때 주로 사용합니다.

export default class App extends Component {
  componentWillUnmount() {
    console.log('App - Unmount');
  }

  // 생략
}

연습: 영화 목록 Like 버튼 만들기

아래 예제는 영화 정보를 대충 하드코딩으로 때려 박고 그 정보를 테이블 형태로 출력한 후 원하는 영화에 좋아요(Like) 표시를 할 수 있도록 만든 예제입니다.

먼저 Bootstrap이 필요합니다. npm i bootstrap@4.1.1 명령어로 React 프로젝트에 Bootstrap을 설치한 후 index.js 파일에 import 'bootstrap/dist/css/bootstrap.css';를 추가해줍니다.

그 다음 Font Awesome이 필요합니다. npm i font-awesome@4.7.0 명령어로 React 프로젝트에 Font Awesome을 설치한 후 index.js 파일에 import 'font-awesome/css/font-awesome.css';를 추가해줍니다.

/***** ./components/Movies.js (클래스형 컴포넌트) *****/

import React, { Component } from 'react';
import Like from './common/Like';

export default class Movies extends Component {
  state = {
    movies: [
      { id: 0, title: "기생충", release: "2019-05-30", liked: true },
      { id: 1, title: "라이온 킹", release: "2019-07-17" },
      { id: 2, title: "알라딘", release: "2019-05-23" },
      { id: 3, title: "나랏말싸미", release: "2019-07-24" },
      { id: 4, title: "주전장", release: "2019-07-25", liked: true },
      { id: 5, title: "어벤져스: 엔드게임", release: "2019-04-24", liked: true }
    ]
  }

  handleDelete = (movie) => {
    const movies = this.state.movies.filter(m => m.id !== movie.id);
    this.setState({ movies });
  }

  // 좋아요 아이콘 클릭 이벤트 처리기
  handleLike = (movie) => {
    const movies = [...this.state.movies];
    const index = movies.indexOf(movie);
    movies[index] = { ...movies[index] };
    movies[index].liked = !movies[index].liked;
    this.setState({ movies });
  }

  render() {
    const { length: count } = this.state.movies;

    if (count === 0)
      return <p>영화 정보가 없습니다.</p>

    return (
      <React.Fragment>
        <p>{count} 개의 영화 정보가 있습니다.</p>

        <table className="table">
          <thead>
            <tr>
              <th>ID</th>
              <th>제목</th>
              <th>출시일</th>
              <th>좋아요</th>
              <th>삭제</th>
            </tr>
          </thead>
          <tbody>
            {this.state.movies.map(movie =>
              <tr key={movie.id}>
                <td>{movie.id}</td>
                <td>{movie.title}</td>
                <td>{movie.release}</td>
                <td><Like liked={movie.liked} onClick={() => this.handleLike(movie)} /></td>
                <td><button onClick={() => this.handleDelete(movie)}>Delete</button></td>
              </tr>
            )}
          </tbody>
        </table>
      </React.Fragment>
    );
  }
}
/***** ./components/Movies.jsx (함수형 컴포넌트) *****/

import React, { useState } from 'react';
import Like from './common/Like';

const Movies = () => {
  const [movies, setMovies] = useState([
    { id: 0, title: "기생충", release: "2019-05-30", liked: true },
    { id: 1, title: "라이온 킹", release: "2019-07-17" },
    { id: 2, title: "알라딘", release: "2019-05-23" },
    { id: 3, title: "나랏말싸미", release: "2019-07-24" },
    { id: 4, title: "주전장", release: "2019-07-25", liked: true },
    { id: 5, title: "어벤져스: 엔드게임", release: "2019-04-24", liked: true }
  ]);

  const handleDelete = (movie) => {
    const newMovies = movies.filter(m => m.id !== movie.id);
    setMovies(newMovies);
  }

  // 좋아요 아이콘 클릭 이벤트 처리기
  const handleLike = (movie) => {
    const newMovies = [...movies];
    const index = newMovies.indexOf(movie);
    newMovies[index] = { ...movies[index] };
    newMovies[index].liked = !movies[index].liked;
    setMovies(newMovies);
  }

  const { length: count } = movies;

  if (count === 0)
    return <p>영화 정보가 없습니다.</p>

  return (
    <React.Fragment>
      <p>{count} 개의 영화 정보가 있습니다.</p>

      <table className="table">
        <thead>
          <tr>
            <th>ID</th>
            <th>제목</th>
            <th>출시일</th>
            <th>좋아요</th>
            <th>삭제</th>
          </tr>
        </thead>
        <tbody>
          {movies.map(movie =>
            <tr key={movie.id}>
              <td>{movie.id}</td>
              <td>{movie.title}</td>
              <td>{movie.release}</td>
              <td><Like liked={movie.liked} onClick={() => handleLike(movie)} /></td>
              <td><button onClick={() => handleDelete(movie)}>Delete</button></td>
            </tr>
          )}
        </tbody>
      </table>
    </React.Fragment>
  );
}

export default Movies;
/***** ./component/common/Like.js (클래스형 컴포넌트) *****/

import React, { Component } from 'react';

export default class Like extends Component {
  render() {
    let classes = "fa fa-heart"
    if (!this.props.liked) classes += "-o";
    return (
      <i
        onClick={this.props.onClick}
        style={{ cursor: "pointer" }}
        className={classes}
        aria-hidden="true"
  ></i>);
  }
}
/***** ./component/common/Like.js (함수형 컴포넌트) *****/

import React from 'react';

const Like = (props) => {
  let classes = "fa fa-heart"
  if (!props.liked) classes += "-o"; // "fa fa-heart-o"는 Font Awesome에서 사용하기 위한 className
  return (
    <i
      onClick={props.onClick}
      style={{ cursor: "pointer" }}
      className={classes}
      aria-hidden="true"
    ></i>);
}

export default Like;
  • 실행화면

댓글 남기기