컴포넌트
고친 사람 github-actions[bot]
컴포넌트는 화면 한 조각을 맡아 그려 내는 부품입니다. 쇼핑몰 화면의 상품 카드 하나가 컴포넌트 하나입니다. 작은 컴포넌트를 모아 큰 화면을 짓습니다. 소프트웨어 설계에서는 시스템을 나눈 덩어리 하나도 컴포넌트라고 부릅니다.
쉽고 빠른 이해
컴포넌트는 화면 조각 하나를 맡는 부품입니다. 상품 카드 컴포넌트를 하나 만들어 두면 상품이 스무 개여도 같은 카드를 스무 번 씁니다.
조각으로 나누지 않으면 같은 모양을 화면 곳곳에 복사하게 됩니다. 카드 모양을 고칠 때 복사본을 다 찾아 고쳐야 합니다. 하나라도 빠뜨리면 화면마다 모양이 달라집니다.
- 화면을 버튼 · 카드 · 목록 같은 조각으로 나눕니다
- 조각마다 「이 값을 받으면 이렇게 생긴다」를 함수로 적습니다
- 작은 조각을 큰 조각 안에 넣어 화면 전체를 쌓습니다
여러 곳에서 되풀이되는 조각은 컴포넌트로 떼고, 한 번만 쓰는 조각은 떼지 않습니다. 다 떼면 파일만 늘어납니다.
대가는 조각 사이의 연락입니다. 멀리 떨어진 두 조각이 같은 값을 써야 하면 그 값을 여러 단계에 걸쳐 건네야 합니다.
상세
레고로 집을 지을 때 벽돌 하나하나를 깎지 않습니다. 이미 만들어진 블록을 골라 끼웁니다. 창문 블록이 마음에 안 들면 그 블록만 빼고 다른 것을 끼웁니다.
이 절은 컴포넌트가 무엇이고 왜 화면을 이렇게 나누는지를 다룹니다. 예는 쇼핑몰의 상품 목록 화면 하나로 끝까지 갑니다.
컴포넌트라는 말의 두 쓰임
컴포넌트는 「구성 요소」라는 뜻의 영어 낱말입니다. 소프트웨어에서는 크게 두 갈래로 씁니다. 둘 다 「따로 만들어 갈아 끼울 수 있는 부품」이라는 뜻을 공유합니다.
첫째는 소프트웨어 설계의 쓰임입니다. 시스템을 나눈 덩어리 하나를 컴포넌트라고 부릅니다. 주문 서비스 안의 결제 모듈처럼 정해진 인터페이스로만 바깥과 말하는 덩어리입니다. 안을 고쳐도 인터페이스가 같으면 바깥은 모릅니다.
둘째는 프론트엔드의 쓰임입니다. 화면을 나눈 조각 하나를 컴포넌트라고 부릅니다. 이 글은 이 둘째 뜻을 다룹니다. 화면 컴포넌트를 UI(User Interface, 사용자 인터페이스) 컴포넌트라고도 부릅니다.
화면을 조각으로 나누는 까닭
웹 화면은 HTML(HyperText Markup Language)로 적습니다. 상품 목록 화면을 HTML 파일 하나에 다 적는다고 해 봅시다. 상품 카드 스무 장의 모양이 파일 안에 스무 번 들어갑니다.
이 상태에서 카드에 「품절」 표시를 하나 더하려면 스무 곳을 고쳐야 합니다. 상품 상세 화면에도 같은 카드가 있다면 그 파일도 고쳐야 합니다. 한 곳만 빠뜨려도 화면마다 카드 모양이 달라집니다.
컴포넌트는 이 복사를 없앱니다. 카드 모양을 한 곳에만 적습니다. 화면은 그 카드를 필요한 만큼 불러 씁니다. 모양을 고치면 카드를 쓰는 모든 화면이 함께 바뀝니다.
백엔드 코드에 견주면 중복된 코드를 함수 하나로 뽑아내는 것과 같은 일입니다. 컴포넌트는 화면을 그리는 코드를 함수처럼 다룹니다.
값을 받아 화면을 돌려주는 함수
화면 컴포넌트는 대개 함수 하나로 적습니다. 입력은 바깥에서 건네주는 값입니다. 출력은 「화면이 이렇게 생겨야 한다」는 설명입니다.
바깥에서 건네주는 값을 props(properties, 속성)라고 부릅니다. 함수의 인자와 같은 것입니다. 같은 컴포넌트라도 props 가 다르면 다른 모습을 그립니다.
아래는 글자를 받아 꼬리표를 그리는 컴포넌트입니다. 이해를 돕도록 화면 설명 대신 문자열을 돌려주게 줄였습니다.
function Badge(props) {
return '[' + props.text + ']';
}
Badge({ text: '새 글' }); // '[새 글]'
Badge({ text: '품절' }); // '[품절]'
함수는 하나입니다. props 만 바꿔 두 번 불렀더니 결과가 둘로 갈렸습니다. 컴포넌트를 다시 쓴다는 말은 이렇게 같은 함수를 다른 값으로 부른다는 뜻입니다.
화면 라이브러리 React 의 컴포넌트는 문자열 대신 화면 요소를 설명하는 객체를 돌려줍니다. 그 객체를 받아 브라우저 화면에 옮기는 일은 라이브러리가 맡습니다. 브라우저가 들고 있는 화면 구조를 DOM(Document Object Model, 문서 객체 모델)이라고 합니다.
컴포넌트가 스스로 기억하는 값
props 만으로는 모자랄 때가 있습니다. 상품 카드의 「자세히 보기」를 누르면 설명이 펼쳐진다고 해 봅시다. 지금 펼쳐져 있는지 접혀 있는지는 바깥이 아니라 카드 자신이 알아야 합니다.
이렇게 컴포넌트가 안에 들고 있는 값을 상태라고 부릅니다. props 는 바깥이 주는 값이라 컴포넌트가 고치지 않습니다. 상태는 컴포넌트 자신의 값이라 스스로 바꿉니다.
상태가 바뀌면 React 는 그 컴포넌트 함수를 다시 부릅니다. 함수는 새 상태에 맞는 화면 설명을 돌려줍니다. React 는 바로 전 설명과 견줘 달라진 곳만 화면에서 고칩니다.
견주려면 바로 전 설명을 어딘가에 들고 있어야 합니다. 화면 설명을 메모리에 들고 있다가 바뀌기 전후를 견주는 이 방식이 가상 DOM입니다. 덕분에 브라우저 화면은 달라진 곳만 손대면 됩니다.
컴포넌트를 쌓아 만든 화면
컴포넌트는 다른 컴포넌트를 품을 수 있습니다. 품는 쪽을 부모, 들어가는 쪽을 자식이라고 부릅니다. 작은 부품을 모아 큰 부품을 만드는 이런 방식을 컴포지션(composition, 합성)이라고 합니다.
상품 목록 화면을 나눠 보면 아래처럼 네 겹이 됩니다. 화면이 목록을 품고, 목록이 카드를, 카드가 버튼을 품습니다.
flowchart TD
P["상품 목록 화면"] --> H["머리글"]
P --> L["상품 목록"]
L --> C1["상품 카드"]
L --> C2["상품 카드"]
C1 --> T["가격표"]
C1 --> B["담기 버튼"]
C2 --> T2["가격표"]
C2 --> B2["담기 버튼"]
이렇게 가지를 치며 이어진 모양을 트리라고 합니다. 화면 전체가 컴포넌트 트리 하나입니다. 가격표를 고치면 모든 상품 카드의 가격표가 바뀝니다.
위에서 아래로 흐르는 값
트리 안에서 값이 흐르는 방향은 대개 정해져 있습니다. 부모가 자식에게 props 로 값을 내려 줍니다. 자식은 받은 값을 고치지 않고 그리기만 합니다. 이 규칙이 단방향 데이터 흐름입니다.
그러면 자식이 부모에게 알릴 일은 어떻게 할까요. 부모가 함수 하나를 props 에 넣어 내려 줍니다. 담기 버튼이 눌리면 버튼은 그 함수를 부릅니다. 이렇게 넘겨받아 나중에 부르는 함수를 콜백이라고 합니다.
콜백이 불리면 부모는 자기 상태를 바꿉니다. 상품 카드라면 「담긴 수량」 같은 값입니다. 상태가 바뀌었으니 부모는 다시 그려지고, 바뀐 값을 새 props 로 자식에게 내려 줍니다. 아래 그림이 이 한 바퀴입니다.
sequenceDiagram
participant 부모 as 상품 카드
participant 자식 as 담기 버튼
부모->>자식: props 로 상품 이름과 콜백을 내려 줌
Note over 자식: 사용자가 버튼을 누름
자식->>부모: 받은 콜백을 부름
Note over 부모: 상태를 바꾸고 다시 그림
부모->>자식: 새 props 를 내려 줌
값을 바꾸는 쪽이 늘 그 값의 주인이라 흐름을 따라가기 쉽습니다. 화면이 이상하면 그 값을 가진 부모부터 보면 됩니다.
조각을 나누는 크기
여러 곳에서 되풀이되는 조각은 컴포넌트로 떼면 이득입니다. 상품 카드나 버튼이 그렇습니다. 홀로 떼어 시험하기도 쉬워집니다.
한 번만 쓰이는 조각까지 떼면 거꾸로 됩니다. 파일만 늘어납니다. 화면 하나를 읽으려면 파일 열 개를 오가야 합니다.
너무 잘게 나눠도 값을 건네는 일이 늘어납니다. 트리 맨 위의 값이 맨 아래 자식에게 필요하면 사이의 컴포넌트가 전부 그 값을 받아 넘겨야 합니다. 쓰지도 않는 값을 넘기기만 하는 이 문제를 prop drilling이라고 부릅니다.
그래서 React 의 컨텍스트처럼 값을 여러 단계 건너뛰어 건네는 장치를 라이브러리마다 둡니다. 위쪽 컴포넌트가 값을 놓아 두면 아래 자식이 사이를 거치지 않고 바로 꺼내 씁니다.
다른 길도 있습니다. 화면 전체가 함께 쓰는 값을 컴포넌트 밖 한 곳에 모아 두고, 필요한 컴포넌트가 거기서 직접 읽습니다. 이렇게 값을 트리 밖에 모아 다루는 일을 상태 관리라고 부릅니다.
컴포넌트로 화면을 짓는 라이브러리
React 는 컴포넌트를 함수로 적습니다. 그 함수가 돌려준 화면 설명을 받아 바뀐 곳만 화면에 옮깁니다. React Native 는 같은 방식의 컴포넌트를 폰 화면에 그립니다. 브라우저의 DOM 대신 폰 운영체제의 화면 부품으로 옮깁니다.
Vue.js 와 Svelte 도 화면을 컴포넌트로 나눕니다. 컴포넌트를 적는 문법과 화면을 고치는 방법은 라이브러리마다 다릅니다.
브라우저 자체에도 컴포넌트를 만드는 표준 기능이 있습니다. 새 HTML 태그를 직접 정의해 씁니다. 이 방식을 웹 컴포넌트라고 부릅니다. 라이브러리 없이 돌아갑니다.
컴포넌트가 꼭 브라우저나 폰에서만 도는 것은 아닙니다. 서버에서 미리 돌려 화면을 만들어 보내는 서버 컴포넌트도 있습니다.
관련 항목
컴포넌트가 값을 주고받는 수단
props · UI 상태 · 콜백 · 이벤트 · 컨텍스트 · 이벤트 핸들러
컴포넌트를 짜 맞추는 원칙
컴포지션 · 단방향 데이터 흐름 · prop drilling · 상태 끌어올리기 · 관심사 분리 · 캡슐화
컴포넌트가 화면을 고치는 처리 단계
렌더링 · 렌더 함수 · 가상 DOM · 재조정 · 마운트 · 생명주기
컴포넌트를 적는 문법과 형태
JSX · 함수형 컴포넌트 · 클래스형 컴포넌트 · 훅 · 순수 컴포넌트 · 고차 컴포넌트 · 싱글 파일 컴포넌트
컴포넌트로 화면을 짓는 라이브러리
React · React Native · Vue.js · Angular · Svelte · SolidJS
컴포넌트가 도는 렌더링 환경
클라이언트 사이드 렌더링 · 서버 사이드 렌더링 · 서버 컴포넌트 · 하이드레이션
브라우저 표준으로 만드는 컴포넌트 기술
웹 컴포넌트 · 커스텀 엘리먼트 · 섀도 DOM · HTML 템플릿
컴포넌트를 모으고 검사하는 도구
디자인 시스템 · 컴포넌트 라이브러리 · 스토리북 · 컴포넌트 테스트
소프트웨어 설계에서 컴포넌트와 짝을 이루는 개념
아키텍처 양식 · 커넥터 · 인터페이스 · 모듈화 · 컴포넌트 기반 개발 · 앱 컴포넌트 · 스프링 빈
컴포넌트가 속하는 상위 분류
다른 이름: component · components · UI 컴포넌트 · 화면 컴포넌트