컴포넌트 이해하기

Created
May 11, 2024
Tags
React

리액트 프레임워크가 제공하는 리액트 컴포넌트와 사용자가 구현하는 사용자 컴포넌트라는 2가지 의미를 포함한다.

리액트 컴포넌트의 이름은 div, h1처럼 첫 글자를 소문자로 시작하는 반면, 사용자 컴포넌트의 이름은 MyComponent 같이 첫 글자는 대문자로 시작하는 카멜 표기법을 따른다.

사용자 컴포넌트를 만드는 이유는 결국 React.createElement 호출이나 JSX 문으로 생성하는 가상 DOM 생성 코드를 사용자 컴포넌트 쪽으로 이동하여 코드를 간결하게 하려는 데 목적이 있다.

클래스 컴포넌트

리액트에서 클래스 컴포넌트는 반드시 react 패키지가 제공하는 Component 클래스를 상속해야 한다. 그리고 Component를 상속한 클래스 컴포넌트는 render라는 이름의 메서드를 포함해야 하며, render 메서드는 null이나 React.createElement 호출로 얻는 반환값, 또는 JSX 문 등으로 가상 DOM 객체를 반환해야 한다.

import React, { Component } from 'react';

export default class MyComponent extends Component {
	render() { 
		return null 
	};
};

Props

리액트 프레임워크에서 속성(properties)은 부모 컴포넌트가 자식 컴포넌트 쪽에 정보를 전달하는 목적으로 사용된다. 속성 값이 변하면 리액트는 해당 컴포넌트를 다시 렌더링하여 수정된 속성 값을 다시 화면에 반영하는 기능도 한다.

function createElement<P extends {}>(
	type: FunctionComponent<P> | ComponentClass<P> | string,
	props?: Attributes & P | null,
	...children: ReactNode[]): ReactElement<P>;

속성 P{}를 확장한다(P extends {})는 타입 제약이 걸려 있는데, 이는 리액트 속성은 객체여야 함을 의미한다. 또한, props 뒤에 ? 기호가 붙었으므로 선택(optional) 속성이다.

예를 들어, App에서 전달한 속성을 MyComponent에서 어떻게 사용할까? 리액트 관점에서는 hreftext가 유효한 속성 이름인지 알 수 있는 방법이 없다.

// App.tsx
<ul>
	<MyComponent href="http://www.google.com" text="go to Google" />
</ul>

그래서 리액트는 다음처럼 Component 타입에 속성 이름과 타입을 기입한 Props와 같은 속성 타입을 새로 만들어 넘겨줄 것을 요구한다. Props는 TypeScript에서 객체 타입을 만들 때 사용하는 type 키워드로 Props라는 이름의 객체 타입을 만들었으므로 이 조건에 만족한다.

이제 MyComponent 내부에서는 this.props 형태로 외부에서 넘어온 속성을 사용할 수 있다. 참고로 모든 클래스 컴포넌트의 부모 타입인 Componentprops라는 이름의 속성을 제공한다.

export type MyComponentProps = {
	href: string,
	text: string
};

export default class MyComponent extends Component<MyComponentProps> {
	render() { 
		const { href, text } = this.props;
		return (
			<li>
				<a href={href}>
					<p>{text}</p>
				</a>
			</li>
		);
 };
};

함수 컴포넌트

클래스 방식의 App 컴포넌트이다. 그런데 이 코드는 사실 render 메서드만 의미가 있고 나머지 코드는 render 메서드를 구현할 수 있게 하는 프로그래밍 언어의 문법을 갖추고 있는 코드일 뿐이다.

export default class App extends Component {
	render() {
		return <h1>Class component</h1>
	};
};

리액트 팀은 이것 또한 불편했나보다. render 메서드 부분을 간단히 함수로 만들 수 있게 했고, 이를 클래스 컴포넌트와 구분하고자 함수 컴포넌트라고 이름을 붙였다. TypeScript에서는 함수 컴포넌트를 만드는 방식이 또 2가지로 나뉘는데, 하나는 function 키워드로 만드는 방법이고 나머지 하나는 화살표(arrow) 기호를 사용해서 만드는 방법이다.

// function 키워드 사용
export default function App() {
	return <h1>Function component</h1>
};

// 화살표 기호 => 사용
const App = () => {
	return <h1>Function component</h1>
};
export default App;