[react/js] React 컴포넌트 살펴보기

Date:     Updated:

카테고리:

태그:

리액트 컴포넌트

기존 html 을 작성하게 되면, 화면이 복잡해질 수록 관리하기가 힘들다는 단점이 있었습니다. 리액트에서는 이러한 단점을 보완하고자 component라는 개념을 도입하여 html을 구성하게 됩니다.

component란, 스스로 상태를 관리하는 캡슐화된 코드조각 입니다. 이 코드조각은 html을 구성할 때, 태그처럼 사용할 수 있으며, 특정 기능 또는 특정 스타일이 담긴 요소들을 재사용할 수 있다는 장점이 있습니다.

그리고, React의 component는 본인만의 생명주기를 가지게 됩니다. 스스로의 상태를 반영하는것도 가능합니다. 따라서, React를 할 때는 component단위로 잘게 쪼개어 화면을 구성해야 합니다.

컴포넌트 만들기

컴포넌트를 만들어 봅니다. 예전의 react에서는 class 형식으로 컴포넌트를 작성하여 관리하였습니다. 하지만 클래스로 컴포넌트를 작성하다 보니, 클래스의 생명주기와 컴포넌트의 생명주기가 같이 공존하여 관리가 힘들었다고 합니다. 그래서 요즘은 함수 형태로 컴포넌트를 관리하고 있습니다.

컴포넌트는 src 폴더 하위에 components라는 폴더를 만들어서 관리하는게 관행이라고 합니다.

저는 우선, 리액트 앱의 최상위 루트 컴포넌트인 App.js에서 코드를 작성해 보겠습니다.

App.js

//export하면 다른 파일에서 해당 함수를 사용할 수 있게 됨
export default function App() {
  return (
    <div>
      <h1>Hello,</h1>
      <h2>World</h2>
    </div>
  );
}

컴포넌트 함수는 기본적으로 외부에서 사용해야 하기 때문에, export 라는 키워드를 붙여주어서 다른 파일에서 사용 가능하도록 해줍니다.

그리고 해당 컴포넌트는 jsx를 반환하도록 만들어서 화면에 랜더링을 할 수 있도록 합니다.

컴포넌트를 작성 한 후, index.js에서 사용을 할것입니다. 사용하는 방법은 해당 컴포넌트의 경로를 명시하고, 컴포넌트 함수의 이름을 태그처럼 사용하면 됩니다.

index.js

import ReactDOM from "react-dom/client";
import App from "./App";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);

위 코드를 랜더링 하면 다음과 같이 나타납니다.

컴포넌트 분리

지금 보면, App.js에 있는 jsx를 보면, Hello, 와 World 로 나눠져 있는것을 볼 수 있습니다. 지금은 코드가 간단해서 기본 html 태그로 나타내어도 괜찮지만, 같은 코드가 재사용 되거나, 코드가 길어지면 유지보수에 어려움이 생길 수 있습니다.

따라서 App.js에 있는 컴포넌트를 잘게 쪼개보겠습니다. Hello 라는 컴포넌트와 World 라는 컴포넌트로 쪼개보겠습니다.

Hello.js

export default function Hello(){
    return (<h1>Hello,</h1>);
}

World.js

export default function World() {
    return (<h2>World</h2>);
}

그 다음, App.js에서 마찬가지로, 두 파일을 import하여 사용합니다. (import를 할 때, js 파일명에서 .js 확장자는 생략 가능합니다.)

App.js

import Hello from './components/Hello/Hello'
import World from './components/World/World'

//export하면 다른 파일에서 해당 함수를 사용할 수 있게 됨
export default function App() {
  return (
    <div>
      <Hello />
      <World />
    </div>
  );
}

컴포넌트를 임포트하여 사용하여도 같은 결과가 나오는것을 볼 수 있습니다.

컴포넌트의 Props

컴포넌트에서 외부의 값을 받아서 랜더링을 해야 하는 경우가 있습니다. 만약 재사용 해야하는 컴포넌트라면 더더욱 외부의 값을 받아서 사용하는 것이 좋습니다. 컴포넌트에서 함수의 인자 처럼 외부의 값을 받아서 랜더링을 하는 방법을 Props라고 합니다.

Props를 사용하려면, 컴포넌트 함수에 인자를 넣어주고, 외부에서는 html 태그의 속성값을 넣는것과 같이 작성해주면 됩니다.

위에서 봤던 예제에서, Hello 와 World 컴포넌트로 분리하면, 문자열과 태그가 하드코딩 되어있기 때문에 재사용하기가 불편합니다. 이것들을 Props를 사용하여 Heading 이라는 컴포넌트로 만들고, 재사용하기 좋도록 만들어 봅니다.

Heading.js

export default function Heading(props) {
    if (props.type === 'h1') {
        return (<h1>{props.children}</h1>);
    }
    else if (props.type === "h2") {
        return (<h2>{props.children}</h2>)
    }
}

props를 사용한 Heading 컴포넌트 입니다. 기본적으로 함수의 인자로 props 를 선언해 줍니다. 그리고, props에서 사용할 속성들은 임의로 정해주시면 됩니다. (children은 태그의 Content 영역을 의미함. 이처럼 예약된 속성도 존재함)

그 뒤에, App.js를 Heading 컴포넌트를 사용하여 다음과 같이 변경합니다.

import Heading from './components/Heading/Heading';

//export하면 다른 파일에서 해당 함수를 사용할 수 있게 됨
export default function App() {
  return (
    <div>
      <Heading type='h1'>Hello,</Heading>
      <Heading type='h2'>World</Heading>
    </div>
  );
}

이렇게 하면, props를 사용하여 재사용성이 좋은 컴포넌트를 제작하였습니다.

일반적으로, props를 사용할 때, 인자로 예약어인 props를 넣어주는 방식도 있지만, 이는 가독성이 안좋을 수 있습니다. 따라서 React는 구조분해 할당 방식을 지원합니다. 이는 props라는 예약어 대신에 객체를 인자로 할당하여 사용하는 방식입니다.

export default function Heading({type, children}) {
    if (type === 'h1') {
        return (<h1>{children}</h1>);
    }
    else if (type === "h2") {
        return (<h2>{children}</h2>)
    }
}

위와 같은 방식으로 구조분해 할당 방식을 사용하면 가독성이 더 올라갑니다. 따라서, 해당 방식이 실무 표준으로 작동한다고 합니다.

컴포넌트의 State

컴포넌트에서 동적으로 값이 바뀌는 경우가 존재합니다. 대표적인 예가 바로 Counter예제 입니다. 버튼을 누르면 값이 하나씩 올라가고, 그것을 동적으로 화면에 랜더링 하는 기능입니다.

해당 기능을 하기 위해서는 컴포넌트의 state기능이 필요합니다. 컴포넌트의 state기능은 리액트 프레임워크가 지정한 값을 계속 추적하며, 추적하는 값이 변경될 때 마다 화면을 리랜더링 하게 됩니다. 사용하는 방법은 다음과 같습니다.

간단하게 카운터 앱을 만들어 보았습니다.

App.js

export default function App() {
  let value = 0;
  return (
    <div>
      <h1>value: {value}</h1>
      <button onClick={() => { increaseValue(value); }}>Increase value</button>
      <button>Reset value</button>
    </div>
  )
}

function increaseValue(value) {
  let result = value + 1;
  console.log(result);
  return result;
}

제가 Increase value를 눌렀음에도 화면에는 값이 0으로 고정 됩니다. 이는 첫번째로, 값이 변경됨에 따라 리랜더링이 안되어서 발생하는 문제입니다. 두번째는 App 함수 내부에서 value 의 값이 0으로 고정되어 있기 때문에, IncreaseValue라는 함수가 호출되어도 계속 1이라는 로그만 출력이 됩니다. 이러한 문제를 해결하기 위해 나온것이 state입니다.

state는 value의 값을 추적하며, value의 값이 변경되면 자동으로 컴포넌트를 리랜더링 하고, value의 값을 저장하게 됩니다. 다음은 사용하는 예제 입니다.

App.js

import { useState } from 'react';

export default function App() {
  const [value, setValue] = useState(0);
  return (
    <div>
      <h1>value: {value}</h1>
      <button onClick={() => { setValue(value + 1); }}>Increase value</button>
      <button onClick={() => { setValue(0); }}>Reset value</button>
    </div>
  )
}

우선, useState를 사용하려면 다음과 같은 import 구문이 필요합니다.

import {useState} from 'react';

그리고, 컴포넌트 내부에서 useState()를 사용하는 방법은 다음과 같습니다.

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

useState()는 배열을 반환하게 되는데, 첫번째 요소로는 값, 두번쨰 요소로는 값을 세팅하는 setter 입니다. 앞으로, setter를 통해서 값을 변경하게 됩니다. useState() 안에 인자로 들어가는 숫자는 초기값을 뜻합니다. (반드시 초기값을 설정해줘야 함)

위 처럼 코드를 작성하고, 다시 카운터 예제를 돌려보면 잘 작동하는것을 볼 수 있습니다.

React 카테고리 내 다른 글 보러가기

댓글 남기기