[React] 합성과 상속

문지은·2023년 7월 23일
0

React

목록 보기
12/24
post-thumbnail

합성(Composition)

  • 여러 개의 컴포넌트를 합쳐서 새로운 컴포넌트를 만드는 것

Containment

  • 하위 컴포넌트를 포함하는 형태의 합성 방법
  • 리액트 컴포넌트의 props에 기본적으로 들어 있는 children 속성 사용

예제 1 - children이 한개일 때

  • 자신의 하위 컴포넌트를 포함하여 예쁜 테두리로 감싸주는 컴포넌트
    • props.children을 사용하여 해당 컴포넌트의 하위 컴포넌트가 children 이라는 이름으로 들어오게 됨
import React from 'react';

function FancyBorder(props) {
    return (
        <div className={'FancyBorder FancyBorder-' + props.color}>
            {props.children}
        </div>
    );
}

export default FancyBorder;
  • 위에서 작성한 컴포넌트를 사용하는 컴포넌트
import React from "react";
import FancyBorder from "./FancyBorder";

function WelcomeDialog(props) {
    return (
        <FancyBorder color="blue">
            <h1 className="Dialog-title">
                어서오세요.
            </h1>
            <p className="Dialog-message">
                제 블로그에 방문하신걸 환영합니다.!
            </p>
        </FancyBorder>
    );
}

export default WelcomeDialog;
  • css 파일 작성(src/index.css)
.FancyBorder {
  border: 2px solid #aaa; 
  padding: 10px; 
  border-radius: 5px; 
}

.FancyBorder-blue {
  border-color: blue;
}
  • 실행 결과

예제 2 - 여러 개의 children 집합

  • 별도의 props를 정의해서 각각 원하는 컴포넌트를 넣어주면 됨

예시 코드

function SplitPane(props) {
    return (
        <div className="SplitPane">
            <div className="SplitPane-left">
                {props.left}
            </div>
            <div className="SplitPane-right">
                {props.rigiht}
            </div>
        </div>
    );
}

function App(props) {
    return (
        <SplitPane
            left={
                <Contacts />
            }
            right={
                <Contacts />
            }
        />
    );
}
  • 화면을 왼쪽과 오른쪽으로 분할해서 보여주는 SplitPane 컴포넌트
  • App 컴포넌트에서는 SplitPane 컴포넌트를 사용하여 left, right 두 개의 props를 정의한 후 그 안에 각각 다른 컴포넌트를 넣어주게 됨
  • SplitPaned에서는 이 left, right를 props로 받게되고 각각 화면의 왼쪽과 오른쪽에 분리해서 렌더링

Specialization

  • 범용적인 개념을 구별이되게 구체화하는 것
  • 범용적으로 쓸 수 있는 컴포넌트를 만들어 놓고 이를 구체화 시켜서 컴포넌트를 사용하는 합성 방법

예시 코드

function Dialog(props) {
    return (
        <FancyBorder color="blue">
            <h1 className="Dialog-title">
                {props.title}
            </h1>
            <p className="Dialog-message">
                {props.message}
            </p>
        </FancyBorder>
    );
}

function WelcomeDialog(props) {
    return (
        <Dialog title="어서 오세요"
                message="제 블로그에 오신 것을 환영합니다!"
        />
    );
}
  • Dialog라는 범용적인 의미를 가진 컴포넌트와 Dialog 컴포넌트를 사용하는 WelcomeDialog 컴포넌트
  • Dialog 컴포넌트에서는 title과 message라는 두 가지 props를 가짐
  • 어떤 title과 message를 가지느냐에 따라 다양한 다이얼로그를 만들 수 있음

Containment + Specialization

  • props.children을 통해 하위 컴포넌트를 포함시키고(Containment)
  • 별도의 props를 선언하여 구체화 시키는 합성 방법(Specialization)
function Dialog(props) {
    return (
        <FancyBorder color="blue">
            <h1 className="Dialog-title">
                {props.title}
            </h1>
            <p className="Dialog-message">
                {props.message}
            </p>
            {props.children}
        </FancyBorder>
    );
}

function SignUpDialog(props) {
    const [nickname, setNickname] = useState('');

    const handleChange = (event) => {
        setNickname(event.target.value);
    }

    const handleSignUp = () => {
        alert(`어서오세요, ${nickname} 님!`)
    }

    return (
        <Dialog
            title="화성 탐사 프로그램"
            message="닉네임을 입력해주세요.">
            <input 
                value={nickname}
                onChange={handleChange}/>
            <button onClick={handleSignUp}>
                가입하기
            </button>
        </Dialog>
    )
}
  • Dialog 컴포넌트에 props.children을 추가하여 하위 컴포넌트가 다이얼로그 하단에 렌더링 되도록 작성
  • SignUpDialog 컴포넌트에서는 Specialization을 위해 props인 title과 message 값을 넣어주고 있음
  • 또한, 사용자로부터 닉네임을 입력받고 가입하도록 유도하기 위해 input과 button 태그를 사용
    • 이 두 개의 태그는 props.children으로 전달되어 다이얼로그에 표시됨(Containment)

상속

  • 다른 컴포넌트로부터 상속받아서 새로운 컴포넌트를 만드는 것
  • 상속을 사용하여 컴포넌트를 만드는 것을 추천할 만한 사용 사례를 찾지 못함
  • 리액트에서는 상속이라는 방법을 사용하는 것보다는 합성을 사용하는 것이 좋음

실습 - Card 컴포넌트 만들기

  • 하위 컴포넌트를 감싸서 카드 형태로 보여주는 Card 컴포넌트 작성
  • Containment와 Specialization 두 가지 합성 방법 모두 사용
function Card(props) {
    const { title, backgroundColor, children } = props;

    return (
        <div
            style={{
                margin: 8,
                padding: 8,
                borderRadius: 8,
                boxShadow: "0px 0px 4px grey",
                backgroundColor: backgroundColor || "white",
            }}
        >
            {title && <h1>{title}</h1>}
            {children}
        </div>
    );
}

export default Card;
  • 위에서 작성한 Card 컴포넌트를 사용할 ProfileCard 컴포넌트 작성
import Card from "./Card";

function ProfileCard(props) {
    return (
        <Card title="Inje Lee" backgroundColor="#4ea04e">
            <p>안녕하세요, 문지은입니다.</p>
            <p>저는 리액트를 사용해서 개발하고 있습니다.</p>
        </Card>
    );
}

export default ProfileCard;
  • 실행 결과

실습 전체 코드

References

profile
코드로 꿈을 펼치는 개발자의 이야기, 노력과 열정이 가득한 곳 🌈

0개의 댓글