컴포넌트의 개념과 종류
지난 장에서 우리는 리액트의 UI를 구성하는 핵심 문법인 JSX에 대해 알아보았습니다. 이제 JSX를 활용하여 실제로 UI를 만들고 재사용하는 리액트의 가장 중요한 개념인 컴포넌트(Component) 에 대해 깊이 있게 탐구해 볼 시간입니다. 리액트를 배운다는 것은 곧 '컴포넌트'를 이해하고 활용하는 방법을 익히는 것이라고 할 수 있습니다.
컴포넌트란 무엇인가?
다시 한번, 웹사이트를 레고 블록으로 만든다고 상상해 봅시다. 이때 각각의 레고 블록은 특정 기능을 수행하고, 독립적으로 존재하는 작은 부품들입니다. 이 레고 블록들을 조립하여 하나의 멋진 구조물을 완성할 수 있습니다.
리액트에서 컴포넌트는 이 레고 블록과 같습니다. 웹 애플리케이션의 UI를 구성하는 독립적이고 재사용 가능한 최소 단위입니다. 즉, 웹 페이지를 여러 개의 독립적인 조각으로 나누어 개발하는 방식이며, 각각의 조각이 바로 컴포넌트입니다.
예를 들어, 온라인 쇼핑몰 웹사이트를 생각해 볼까요?
- 헤더(Header) 영역: 로고, 검색창, 로그인 버튼, 장바구니 아이콘
- 상품 목록(ProductList) 영역: 여러 개의 상품 카드
- 상품 카드(ProductCard) 하나: 상품 이미지, 상품 이름, 가격, '장바구니 담기' 버튼
- 푸터(Footer) 영역: 회사 정보, 저작권 표시
이 모든 요소들을 각각 하나의 컴포넌트로 만들 수 있습니다. '상품 카드' 컴포넌트는 여러 번 재사용될 수 있으며, '상품 목록' 컴포넌트 안에는 여러 개의 '상품 카드' 컴포넌트가 포함될 수 있습니다.
컴포넌트 기반 개발의 장점:
- 재사용성(Reusability): 한 번 만든 컴포넌트는 필요한 곳 어디에서든 다시 사용할 수 있어 개발 시간을 단축하고 코드 중복을 줄입니다.
- 유지보수성(Maintainability): 각 컴포넌트가 독립적으로 작동하므로, 특정 컴포넌트에 문제가 발생해도 전체 애플리케이션에 미치는 영향을 최소화하고, 문제 해결이 용이해집니다.
- 모듈성(Modularity): 복잡한 UI를 작은 단위로 분리하여 개발함으로써, 여러 개발자가 동시에 다른 컴포넌트를 개발할 수 있어 협업 효율성을 높입니다.
- 가독성(Readability): UI를 구조적으로 파악하기 쉬워 코드를 이해하고 관리하기 용이합니다.
- 예측 가능성(Predictability): 데이터의 변화에 따라 컴포넌트가 어떻게 렌더링될지 예측하기 쉬워 디버깅에 유리합니다.
컴포넌트의 종류
리액트 컴포넌트는 크게 두 가지 방식으로 정의할 수 있습니다. 과거에는 기능적인 차이가 있었지만, 리액트 16.8 버전에서 Hooks(훅스)가 도입되면서 이제는 기능적인 면에서 거의 동일하게 사용될 수 있습니다. 하지만 코드 작성 방식에서 차이가 있으므로 두 가지 모두 알아두는 것이 좋습니다.
함수형 컴포넌트 (Functional Component)
함수형 컴포넌트는 가장 기본적인 형태의 리액트 컴포넌트입니다. 말 그대로 자바스크립트 함수로 컴포넌트를 정의합니다. 이 함수는 JSX를 반환하며, 외부에서 전달받는 데이터(props
)를 함수의 인자로 받습니다.
import React from 'react'; // JSX를 사용하려면 React를 임포트할 필요가 없었으나,
// 이전 버전과의 호환성이나 특정 도구 설정에 따라 필요할 수 있습니다.
// 최신 React 버전(17 이상)에서는 더 이상 JSX를 사용하기 위해 React를 import할 필요가 없습니다.
function MyFunctionComponent(props) { // (1) 함수 정의: 함수의 첫 글자는 대문자로 시작해야 합니다.
return ( // (2) JSX 반환: 하나의 루트 요소로 감싸져야 합니다.
<div>
<h1>안녕하세요, {props.name}님!</h1>
<p>함수형 컴포넌트입니다.</p>
</div>
);
}
export default MyFunctionComponent; // (3) 컴포넌트 내보내기 (다른 파일에서 사용하기 위함)
특징
- 간결성: 클래스형 컴포넌트보다 코드가 간결하고 읽기 쉽습니다.
- Hooks 사용: 리액트 16.8 버전 이후 Hooks의 등장으로
state(상태)
관리,life cycle(생명 주기)
관리 등 클래스형 컴포넌트에서만 가능했던 기능들을 함수형 컴포넌트에서도 사용할 수 있게 되었습니다. 현대 리액트 개발에서는 함수형 컴포넌트와 Hooks를 조합하여 사용하는 것이 주류입니다. - 성능 이점: 특정 상황에서 클래스형 컴포넌트보다 미세한 성능 이점을 가질 수 있습니다.
예시
src/components/WelcomeMessage.js
파일을 만들고 다음과 같이 작성해 보세요.
import React from 'react'; // 최신 리액트에서는 필수 아님 (CRA 등 빌드도구에 따라 자동 처리)
function WelcomeMessage(props) {
return (
<div>
<h2>{props.title}</h2>
<p>{props.message}</p>
</div>
);
}
export default WelcomeMessage;
그리고 App.js
에서 이 컴포넌트를 불러와 사용합니다.
import React from 'react';
import './App.css'; // App.css가 있다면 그대로 둡니다.
import WelcomeMessage from './components/WelcomeMessage'; // WelcomeMessage 컴포넌트 불러오기
function App() {
return (
<div className="App">
<h1>나 혼자 React!</h1>
<WelcomeMessage title="환영합니다!" message="즐거운 리액트 학습 되세요!" />
<WelcomeMessage title="다시 오셨군요!" message="리액트 컴포넌트 재사용 예시입니다." />
</div>
);
}
export default App;
위 코드를 저장하고 브라우저를 확인하면, WelcomeMessage
컴포넌트가 두 번 렌더링되고 각각 다른 props
값이 적용된 것을 볼 수 있을 것입니다.
클래스형 컴포넌트 (Class Component)
클래스형 컴포넌트는 ES6의 클래스 문법을 사용하여 정의합니다. React.Component
를 상속받아 render()
메서드 안에서 JSX를 반환하는 형태입니다.
import React, { Component } from 'react'; // React에서 Component를 임포트합니다.
class MyClassComponent extends Component { // (1) 클래스 정의: Component를 상속받습니다.
render() { // (2) render() 메서드 안에서 JSX를 반환합니다.
return (
<div>
<h1>안녕하세요, {this.props.name}님!</h1>
<p>클래스형 컴포넌트입니다.</p>
</div>
);
}
}
export default MyClassComponent;
특징
render()
메서드: 반드시render()
메서드를 포함해야 하며, 이 메서드 안에서 JSX를 반환합니다.this.props
사용:props
는this.props
를 통해 접근합니다.state
와 생명주기 메서드: 과거에는state(상태)
관리와life cycle(생명 주기)
메서드를 사용하기 위해서는 클래스형 컴포넌트를 사용해야만 했습니다. (예:componentDidMount
,componentDidUpdate
등) 하지만 Hooks의 등장으로 함수형 컴포넌트에서도 이들을 대체하는 기능을 사용할 수 있게 되었습니다.
현대 리액트 개발에서는 대부분 함수형 컴포넌트와 Hooks를 사용하여 애플리케이션을 구축합니다. 하지만 기존에 만들어진 많은 리액트 프로젝트들이 클래스형 컴포넌트로 되어 있거나, 특정 상황에서는 여전히 클래스형 컴포넌트가 사용될 수 있기 때문에 기본적인 구조는 알아두는 것이 좋습니다. 이 책에서는 함수형 컴포넌트와 Hooks를 중심으로 학습을 진행할 것입니다.
컴포넌트 이름 규칙
리액트 컴포넌트의 이름은 반드시 대문자로 시작해야 합니다. 이는 JSX가 일반 HTML 태그(소문자)와 리액트 컴포넌트(대문자)를 구분하는 방식입니다.
<div>
는 일반 HTML DOM 태그로 인식됩니다.<MyComponent>
는 리액트 컴포넌트로 인식됩니다.
만약 컴포넌트 이름을 소문자로 시작하면, 리액트는 이를 HTML 태그로 인식하여 오류를 발생시키거나 예상치 못한 동작을 할 수 있습니다.
컴포넌트를 언제 사용해야 할까?
어떤 부분을 컴포넌트로 만들어야 할지 고민될 수 있습니다. 다음 경우들을 고려하여 컴포넌트를 분리하면 좋습니다.
- 재사용 가능한 부분: 여러 곳에서 반복적으로 사용되는 UI (버튼, 입력 필드, 카드 등)
- 복잡한 UI의 특정 부분: 하나의 페이지가 너무 복잡하다면, 그 페이지를 논리적으로 분리될 수 있는 작은 단위로 나눕니다.
- 데이터의 변화에 따라 독립적으로 업데이트되어야 하는 부분: 특정 데이터가 변경될 때 해당 부분만 효율적으로 렌더링해야 할 경우
- 독립적인 기능 블록: 예를 들어, 사용자 정보를 표시하는 블록, 댓글 목록을 표시하는 블록 등 명확한 기능을 가진 단위
컴포넌트 분리는 소프트웨어 개발의 '모듈화' 원칙과 일맥상통합니다. 처음부터 완벽하게 분리하기는 어렵지만, 코드를 작성하면서 중복되는 부분이 보이거나, 하나의 파일이 너무 길어진다면 컴포넌트로 분리하는 것을 고려해 보세요.
2장 "컴포넌트의 개념과 종류"는 여기까지입니다. 컴포넌트의 중요성과 함수형/클래스형 컴포넌트의 차이점, 그리고 컴포넌트 이름 규칙까지 자세히 다루었습니다. 특히 현대 리액트 개발의 주류인 함수형 컴포넌트에 집중하여 설명했습니다.
다음 장에서는 컴포넌트에 데이터를 전달하는 핵심 방법인 props
에 대해 더 자세히 알아보겠습니다. 혹시 더 추가하거나 수정하고 싶은 부분이 있다면 말씀해주세요.