[React] 컴포넌트 재사용, props.children

Joah·2022년 6월 25일
1

React

목록 보기
21/31
post-thumbnail

컴포넌트가 무엇인지 궁금하다면 아래 링크 클릭하기!
컴포넌트 What is Component?

간단하게 컴포넌트는 재사용 가능한 UI 단위라고 할 수 있다.


컴포넌트를 언제 사용하면 좋을까?

UI 일부가 여러 번 사용되거나, UI 일부가 자체적으로 복잡한 경우에는 별도의 컴포넌트로 만드는게 좋습니다. -React 공식문서-



🤷만약 대부분의 UI 가 같지만 색상, 텍스트 등 조금씩 뭔가 다르다면 어떻게 할까?

두 레이아웃에서 반복적으로 사용되는 요소와 변화되는 요소들을 찾을 수 있다.

변화하는 부분들이 있다고 하더라도 매번 다른 코드로 처리할 필요 없이 컴포넌트화 하여 사용할 수 있다.

예제

웹 사이트의 로그인과 회원가입 Modal창을 구성하는 컴포넌트는 다음과 같다. 첫 번째 이미지에서 로그인 창과 회원가입 창에서

공통적으로 적용되는 부분은

  • 로고
  • email 과 password를 입력하는 input 창
  • 버튼(텍스트 제와)

다른 부분

  • 모달 창의 제목(로그인 or 회원가입)
  • 회원가입 창에서 이름 input 추가
  • 버튼 텍스트
  • 하단의 텍스트(계정이 없으신가요? / 이미 가입하셨나요)

Modal.js

import React from "react";
import Form from "./Components/Form";
import "./Modal.scss";

export default function Modal() {
  return (
    <div className="modal">
      <Form type="signIn" title="로그인" />
      <Form type="signUp" title="회원가입" />
    </div>
  );
}
  • 우선 두 가지의 Modal창이 필요하니 동일한 컴포넌트를 사용해서 바뀔 부분만 수정한다.

  • 상수 데이터를 사용하여 각 Modal창에서 사용할 정적인 데이터를 넘겨준다.


상수 데이터로 각각의 모달 창에 데이터 부여하기

import React from "react";
import Form from "./Components/Form";
import "./Modal.scss";

export default function Modal() {
  return (
    <div className="modal">
      <Form type="signIn" title="로그인" inputData={SIGNIN_DATA} />
      <Form type="signUp" title="회원가입" inputData={SIGNUP_DATA} />
    </div>
  );
}

//상수 데이터 (정적인 데이터, 변하지 않는 데이터)

//로그인에 해당하는 데이터
const SIGNIN_DATA = [
  {
    type: "email",
    text: "이메일",
  },
  {
    type: "password",
    text: "비밀번호",
  },
];

//회원가입에 해당하는 데이터
const SIGNUP_DATA = [
  {
    type: "name",
    text: "이름",
  },
  {
    type: "email",
    text: "이메일",
  },
  {
    type: "password",
    text: "비밀번호",
  },
];
  • Modal에서 자식인 <Form/> 컴포넌트에 넘겨주는 데이터는
    • type이라는 이름으로 "signIn", "signUp" 이라는 문자열
    • title이라는 이름으로 "로그인", "회원가입" 이라는 문자열
    • inputData 라는 이름으로 {SIGNIN_DATA}{SIGNUP_DATA} 상수 데이터

Form.js

export default function Form({ type, title, inputData }) {
  return (
    <FormLayout>
      <div className="form">
        <h2>{title}</h2>
        <div>
          {inputData.map((input, idx) => (
            <Input key={idx} type={input.type} text={input.text} />
          ))}
        </div>
        <Button value={title} />
        {type === "signUp" && (
          <p className="isAlreadyLogin">
            이미 가입하셨나요? <span className="linkBtn">로그인</span>
          </p>
        )}
      </div>
    </FormLayout>
  );
}

map 함수로 input창을 속성과 함께 생성하기

  • 위에서 <Form/>컴포넌트에 넘겨준 propsprops안에 있는 객체를 구조분해할당하여 {type, title, inputData}로 받는다.

  • Form.js 안에는 <FormLayOut/> 컴포넌트가 있다. 이는 Modal창 틀을 구성하고 있다.

  • Modal 창의 제목을 부여하기 위해서 <h2>{title}</h2>를 작성한다.

    • 현재 "로그인", "회원가입" 창 모두 출력되기 때문에 각각의 Modal 창에서 해당하는 문구가 출력된다.
  • inputData는 배열이기 때문에 map 함수를 통해 <Input/> 컴포넌트에 props로 넘겨준다.

    • SIGNIN_DATA에는 2개의 객체, SIGNUP_DATA 에는 3개의 객체
    • 각각의 데이터의 갯수만큼 input창이 생성된다.
    • key는 값을 부여하는 역할이며 map 함수의 가장 상위 태그에 필수로 작성한다.
    • type으로는 상수데이터에 명시했던 {type}을 받아온다.
    • text에 또한 상수데이터에 명시했던 {text}를 받아온다.
  • 따라서 로그인 창에는 2개의 input 창이 생성되고, 회원가입 창에는 3개의 input 창이 생성된다.

  • <Button/> 컴포넌트에는 로그인 창과 회원가입 창마다 명시된 text가 다르기 때문에 value라는 이름으로 각각의 {title}을 넘겨준다.

  • Modal 창의 가장 하단에 적혀있는 text를 구현하기 위해서 조건부 렌더링(&&)을 사용하여 type이 "signup"이라면 아래의 태그를 출력하라는 조건을 추가한다.

  • 처음 Modal.js에서 type이라는 이름으로 "signUp", "signIn"을 각각 넘겨주었다. 아래의 텍스트는 회원가입 즉, "signup"을 할 때만 필요한 text이므로 type이 "signUp"일 때만 아래의 텍스트가 나타나게 한다.


input.js

import React from "react";
import "./Input.scss";

export default function Input({ type, text }) {
  return (
    <div className="input">
      <div className="inputWrapper">
        <input name={type} type={type} placeholder={text} />
      </div>
    </div>
  );
}
  • Form.js로부터 넘겨 받은 props를 구조분해할당하여 input.js에서 {type, text}로 받는다.

  • <input>태그 안에 필요한 속성을 작성한다.

    • name 속성은 추후 데이터가 서버로 전송될 때 해당 input의 이름이 무엇이었는지 명시해주기 위해 작성한다.
    • type 이 password라면 ••••• 표시로 정보 유출을 방지할 수 있다.
    • placeholdertext 데이터가 부여되면서 input 창에 작성하기 전 사용자에게 어떤 정보를 작성해야 하는지 힌트를 준다.

Button.js

import React from "react";
import "./Button.scss";

export default function Button(props) {
  return <div className="button">{props.value}</div>;
}
  • Form.js에서 <Button/> 컴포넌트에 value 이름으로 {title}을 넘겨주었다.

  • 하나의 props를 넘겨주었기 때문에 굳이 구조분해할당을 할 필요는 없다. 따라서 props라고 작성하여 부여할 때 value 객체에 접근하면 된다.

물론 이렇게 작성해도 동일하게 출력된다.

export default function Button({ value }) {
  return <div className="button">{value}</div>;
}

FormLayOut.js

import React from "react";
import "./FormLayout.scss";

export default function FormLayout(props) {
  return (
    <div className="formLayout">
      <header>
        <div className="logo" />
      </header>
      {props.children}
    </div>
  );
}

Modal창의 전체적인 틀을 구성하고 있는 <FormLayOut/> 컴포넌트에는 Form.js에서 작성된 모든 요소들을 감싸고 있었다.

따라서 우리가 작성한 모든 컴포넌트와 요소들이 <FormLayOut/> 컴포넌트에 자식으로 들어가 있다.

하지만

위의 이미지에서 로고는 두 Modal 창 모두 가지고 있어야 하며 변하지 않는 요소이다.
따라서 로고는 정적인 데이터를 따로 분리하기 위해서 <FormLayOut/>컴포넌트를 따로 만든 것이다.


{props.children}은 무엇이며 어떻게 사용할까?

원래 props는 부모에서 자식에게 넘겨주는 데이터라고 알고있다.
하지만 Form.js에서 FormLayOut 컴포넌트에 아무런 props를 넘겨주지 않았는데 왜 이렇게 작성할까?

export default function FormLayout(props){};

props를 console에 찍어보면 Form.js에서 <FormLayout><FormLayout/>안에 작성했던 데이터들이 담겨있다.

props는 결국 <FormLayout><FormLayout/>안에 있는 데이터를 가지고 있다.

보통 컴포넌트를 생성할 때 <FormLayout/> self-closing을 하지만 열고 닫는 태그 안에 요소들을 넣어주면 props.children으로 값들을 불러올 수 있다.

왜 쓰지?

정적인 데이터와 동적인 데이터를 분리해서 관리할 수 있기 때문이다. 유지보수 용이

위에서 언급했듯, <FormLayout><FormLayout/> 안에 있는 요소들을 동적인 데이터이며 동적인 데이터를 수정할 때는 안에서 수정하면 된다.

만약 로고처럼 동적인 데이터를 수정할 때는 FormLayout.js에 와서 정적인 데이터만 수정하면 된다.

profile
Front-end Developer

0개의 댓글