리액트의 Component

thumb_hyeok·2022년 4월 30일
0

⚛️ React

목록 보기
3/5
post-thumbnail

🔩 컴포넌트(Component)란?

리액트에서 컴포넌트란 마크업, CSS 및 JavaScript를 앱에서 재사용 가능한 UI 요소로 결합한 것을 말한다. 리액트는 컴포넌트를 통해 UI를 재사용 가능한 개별적인 여러 조각으로 나누고, 각 조각을 개별적으로 살펴볼 수 있다. 개념적으로 컴포넌트는 JavaScript 함수와 유사하다. “props”라고 하는 임의의 입력을 받은 후, 화면에 어떻게 표시되는지를 기술하는 React elements를 반환한다.


⚙️ 컴포넌트와 엘리먼트

엘리먼트는 컴포넌트의 구성 요소이다. React 엘리먼트는 불변객체이며, 엘리먼트를 생성한 이후에는 해당 엘리먼트의 자식이나 속성을 변경할 수 없다. 엘리먼트는 하나의 프레임과 같이 특정 시점의 UI를 보여준다.


⚖️ 함수 컴포넌트와 클래스 컴포넌트

리액트에는 함수 컴포넌트와 클래스 컴포넌트가 존재한다.
이 둘은 각각 함수 컴포넌트와 클래스 컴포넌트를 직접 다룰 때 자세히 다루도록하고 이 글에서는 함수 컴포넌트를 사용해서 컴포넌트에 대해서 설명하도록 하겠다.

// 함수 컴포넌트
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 클래스 컴포넌트
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

React의 관점에서 볼 때 위 두 가지 유형의 컴포넌트는 동일하다.


📦 컴포넌트와 Props

React 컴포넌트는 “props”를 사용하여 서로 통신한다. 상위 컴포넌트는 props를 제공하여 하위 컴포넌트에 일부 정보를 전달할 수 있다. props는 “HTML attribute”처럼 생겼지만 모든 JavaScript 값을 전달할 수 있다. 어떻게 props를 사용할 수 있는지 알아보자.

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

const element = <Welcome name="Harry" />;
ReactDOM.render(element, document.getElementById('root');

위 코드는 페이지에 “Hello, Harry”를 렌더링하는 예시 코드이다.
이 코드를 실행하면 어떤 일이 일어나는지 알아보자.

  1. <Welcome name="Harry" /> 엘리먼트로 ReactDOM.render() 를 호출한다.
  2. React는 {name: 'Harry'} 를 props로 하여 Welcome 컴포넌트를 호출한다.
  3. Welcome 컴포넌트는 결과적으로 <h1>Hello, Harry</h1> 엘리먼트를 반환한다.
  4. React DOM은 <h1>Hello, Harry</h1> 엘리먼트와 일치하도록 DOM을 효율적으로 업데이트한다

React는 작성하는 모든 컴포넌트는 순수 함수라고 가정하고 설계되었다. 그렇기 때문에 React의 컴포넌트는 동일한 입력에 대해 동일한 출력을 반환해야하며, 외부에 영향을 줘서는 안 된다.
그렇기 때문에 “props”를 수정해서는 안 된다.


🧰 컴포넌트의 이벤트

React 엘리먼트에서 이벤트를 처리하는 방식은 DOM 엘리먼트에서 이벤트를 처리하는 방식과 매우 유사하다. 몇 가지 문법 차이는 다음과 같다. React의 이벤트는 소문자 대신 캐멀 케이스(camelCase)를 사용하 JSX를 사용하여 문자열이 아닌 함수로 이벤트 핸들러를 전달한다.

// HTML
<button onclick="activateLasers()">
  Activate Lasers
</button>

// React
<button onClick={activateLasers}>
  Activate Lasers
</button>

일반적으로 이벤트 핸들러 함수는 컴포넌트 내부에 정의되고, “handle”로 시작하는 이름 뒤에 발생할 이벤트 이름이 붙는다. 예를 들어, onClick={handleClick} 와 같다. 이 때 함수는 DOM 엘리먼트와 달리 함수 호출이 아닌 함수 자체를 이벤트 핸들러 어트리뷰트에 바인딩해야한다.


⚗️ 컴포넌트와 State

위에서 컴포넌트는 순수 함수이기때문에 “props”를 수정해서는 안 된다고 했다. 그러나 실제 웹 애플리케이션의 UI는 동적이며 상호 작용의 결과로 화면에 표시되는 내용을 변경해야 하는 경우가 많다.

function Component(props) {
  let index = 0;
  
  function handleClick() {
    index = index + 1;
  }
  
  return (<button onClick={handleClick}>{index}</button>);
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Component />);

위의 코드는 props를 수정하지 않고, 내부 지역 변수 index를 활용해서 동작한다. 나는 button을 클릭하면 index가 증가되어 렌더링되는 것을 원했지만 위 코드는 그렇게 동작하지 않는다.

여기에는 두 가지 이유가 있다.

  1. 지역 변수는 렌더링 간에 지속되지 않는다.
  2. 지역 변수를 변경해도 React는 새 데이터로 컴포넌트를 다시 렌더링해야 한다는 것을 인식하지 못한다.

즉, 새 데이터로 컴포넌트를 업데이트하려면 렌더 간의 데이터를 유지하면서 React가 새 데이터로 컴포넌트를 다시 렌더링하도록 인식하도록 해야한다.

리액트에서 이를 위해서 존재하는 컴포넌트 별 메모리인 “state”가 있다.
앞의 예제 코드를 state를 활용한 코드로 바꿔보도록 하겠다.

const { useState } = React;
// useState는 함수형 컴포넌트에서 사용할 수 있는 Hooks이다.
// 함수형 컴포넌트에서 useState를 통해 state 변수를 만들 수 있다는 사실만 받아들이자.

function Component(props) {
  const [index, setIndex] = useState(0);
  // React는 초기 상태를 한 번 저장하고 다음 렌더링에서 이를 무시한다.
  
  function handleClick() {
    setIndex(index + 1);
    // state를 직접 수정하지 말아야 한다.
	// state를 직접 수정하면 컴포넌트를 다시 렌더링하지 않는다.
  }
  
  return (<button onClick={handleClick}>{index}</button>);
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Component />);

이제 함수 컴포넌트에서 state를 활용해서 상호작용에 대응할 수 있게 되었다. 리액트에서 state의 비교는 전체 state를 비교하는 것이 아닌 이전 state와의 레퍼런스로 비교를 통해 변화를 감지하기 때문에 불변성을 지켜주어야한다.


🚦 컴포넌트의 생명주기(Lifecycle)

리액트는 컴포넌트 기반의 View를 중심으로 한 라이브러리이다. 각각의 컴포넌트에는 생명주기(lifecycle)이 존재한다. 생명주기(lifecycle)란 보통 컴포넌트가 페이지에서 렌더링되기 전인 생성시기인 Mount, props나 state가 변경되는 Update, 마지막으로 페이지에서 제거되는 Unmount 로 이루어진다.
클래스 컴포넌트와 함수 컴포넌트의 lifecycle event들은 완전히 다르다. 아까 설명한 것처럼 각각을 다루면서 더 자세히 알아보도록하자.


➕ 컴포넌트 합성

리액트는 강력한 합성 모델을 가지고 있으며, 컴포넌트는 합성을 이용해 코드를 재사용할 수 있다. 만약 내가 Button 컴포넌트를 가지고 있다면 이를 다른 컴포넌트에서 참조하여 사용할 수 있다.
아래 코드를 살펴보자.

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

function App() {
	return (
		<>
			<Button value="Y"/>
			<Button value="N"/>
		</>
	)
}

이러한 방식을 통해 컴포넌트의 합성을 할 수 있고, 이러한 합성을 활용하기 위해 컴포넌트를 작은 단위로 추출해 작은 컴포넌트로 만들고, 이를 다른 컴포넌트에 합성하면 코드를 재사용할 수 있다.


🧐 정리

  • 컴포넌트란 마크업, CSS 및 JavaScript를 앱에서 재사용 가능한 UI 요소로 결합한 것을 말한다.
  • 컴포넌트는 순수 함수로 동작해야하며, props, state를 직접 수정해서는 안 된다.
  • 컴포넌트는 합성을 통해 재사용할 수 있다.

📖 참고자료

profile
우아한테크코스 4기 웹 프론트엔드

0개의 댓글