프로젝트

[React] 재사용 가능한 컴포넌트 만들어 사용하기

기절초뿡 2022. 10. 28. 12:34

 

 코딩을 하면서 놓치고 갈 수 없는 부분 중 하나가 바로 '재사용성'이다. 재사용성을 높이면 반복되는 코드를 줄일 수 있을 뿐만 아니라 확장성까지 높일 수 있어 추후에 작업을 해나갈 때 매우 편리하다. 이번 프로젝트를 하면서 재사용 가능한 컴포넌트 단위를 만들어서 사용 해보는데, 한 번은 그대로 재사용하였고, 한 번은 컴포넌트를 확장하여 사용하였다. 각각 어떻게 사용했는지 이 글에 적어 놓고자 한다. 여기서는 React, TypeScript, Styled-Components를 사용하였다. 

 

✅ 1. 컴포넌트 재사용하기

 위의 네 가지 버튼을 언뜻 보면 배경색이나 내부 컨텐츠가 매우 다르게 생겼다. 배경 색도 제각각이고, 내부 컨텐츠에도 어떤 것은 text만 들어있고, 어떤 것은 text와 image 도 함께 들어있고 심지어 어떤 것은 input이 들어있기도 하다.

 하지만 동일한 외형과 같은 크기의 padding을 가지고 있다. 그리고 click했을 때 특정한 event를 발생시킬 수도 있다. 내부 content는 결국 버튼의 children에 무엇을 넣느냐에 따라 다를 뿐이다.

 한 번 재사용 가능한 컴포넌트를 만들어 두면 쉽게 import 하여 사용할 수 있고, 공통적인 수정사항이 생겼을 때 빠르게 적용할 수도 있으므로 이 편이 더 편리하다고 할 수 있다. 

 

 

🔥 CommomButton 

import React from 'react';

import { ReactNode } from 'react';
import styled from 'styled-components';

type ButtonProps = {
  className: string;
  onClick?: () => void;
  children?: ReactNode;
};

const CommonButton = ({ className, onClick, children }: ButtonProps) => {
  return (
    <ButtonWrapper className={className} onClick={onClick}>
      {children}
    </ButtonWrapper>
  );
};

export default CommonButton;

const ButtonWrapper = styled.button`
  display: flex;
  outline: none;
  align-items: center;
  border-radius: 20px;
`;

공통 속성을 공유하는 컴포넌트를 CommonButton이라는 이름으로 만들었다. onClick 이벤트와 Children을 props로 받는다. className은 styled-components에서 'styled()' 문법을 사용하여 확장할 때 className이 있어야 하기 때문에 넣어주어야 한다. 그리고 공통적인 styling을 해주었다. 

 

 

🔥 CommonButtom을 재사용하여 만든 더보기 버튼(ToggleIntroButton)

import React, { useState } from 'react';
import styled from 'styled-components';

type MoreIntro = {
  moreIntro: boolean;
};

const BookInfo = () => {
  const [moreIntro, setMoreIntro] = useState(false);

  const toggleIntro = () => {
    moreIntro ? setMoreIntro(false) : setMoreIntro(true);
  };
  
  return (
    <BookInfoWrapper>
      <ToggleIntroButton className="moreIntroButton" onClick={toggleIntro}>
        {moreIntro ? '숨기기' : '더보기'}{' '}
      </ToggleIntroButton>
    </BookInfoWrapper>
  );
};

export default BookInfo;

const BookInfoWrapper = styled.section`
  padding-bottom: 40px;
  border-bottom: 1px solid ${(props) => props.theme.colors.grey4};
`;

const ToggleIntroButton = styled(CommonButton)`
  display: block;
  box-sizing: border-box;
  width: 112px;
  height: 44px;
  margin: 12px auto 0;
  padding: 12px 16px;
  color: ${(props) => props.theme.colors.black};
  font-size: ${(props) => props.theme.fontSize.body02};
  font-weight: ${(props) => props.theme.fontWeight.regular};
  line-height: ${(props) => props.theme.lineHeight.lh20};
  background-color: ${(props) => props.theme.colors.white};
  border: 1px solid ${(props) => props.theme.colors.grey3};
  font-family: inherit;
  border-radius: 24px;
  outline: none;
`;

 

 

 

✅ 2. 컴포넌트 확장하여 재사용하기

위: 대댓글, 아래: 댓글

 댓글과 대댓글을 작성할 수 있는 컴포넌트이다. 둘은 공통적으로 댓글 입력을 받는 부분과 등록 버튼이 존재한다. 하지만 댓글을 작성하는 input 컴포넌트에는 페이지를 입력할 수 있는 다른 input 요소가 존재한다는 점이 다르다. 그래서 대댓글 컴포넌트를 재사용 가능한 단위로 만들고, 댓글 컴포넌트에서는 이를 확장하여 사용해 보았다. 

 

 

🔥 대댓글 컴포넌트(InputComment)

import React, { ReactNode } from 'react';
import styled from 'styled-components';

import CommonButton from '../../../components/CommonButton';

type InputProps = {
  className: string;
  placeholder: string;
  onClick: () => void;
  children?: ReactNode;
};

const InputComment = ({ className, placeholder, onClick, children }: InputProps) => {
  return (
    <InputWrapper className={className}>
      {children}
      <InputArea placeholder={placeholder} />
      <InputButtonWrapper>
        <InputButton className={className} onClick={onClick}>
          댓글등록
        </InputButton>
      </InputButtonWrapper>
    </InputWrapper>
  );
};

export default InputComment;

const InputWrapper = styled.div`
  display: flex;
  width: 100%;
  min-height: 110px;
  padding: 20px;
  border: 1px solid ${(props) => props.theme.colors.grey4};
  border-radius: 8px;
  position: relative;
  box-sizing: border-box;
`;

const InputArea = styled.textarea`
  flex: 1;
  display: block;
  min-height: 70px;
  resize: none;
  outline: none;
  border: none;
  overflow-y: visible;
`;

const InputButtonWrapper = styled.div`
  width: 100px;
`;

const InputButton = styled(CommonButton)`
  position: absolute;
  right: 20px;
  bottom: 20px;
  padding: 12px 16px;
  background-color: ${(props) => props.theme.colors.primary};
  border: none;
  font-size: ${(props) => props.theme.fontSize.body02};
  font-weight: ${(props) => props.theme.fontWeight.bold};
`;

textarea로 여러줄 입력(글 입력)을 받을 것인데, 이 textarea 좌측에 children이 올 수 있도록 하였다.

InputButton은 위에서 만든 CommonButton을 재사용하고, styling을 확장하여 사용하였다. 

 

 

🔥 댓글 컴포넌트 (InputCommentWithPag)

import React from 'react';
import styled from 'styled-components';

import InputComment from './InputComment';
import CommonButton from '../../../components/CommonButton';

type InputCommentProps = {
  className: string;
  onClick: () => void;
  placeholder: string;
};
const InputCommentWithPage = ({ className, onClick, placeholder }: InputCommentProps) => {
  return (
    <InputCommentWrapper className={className} placeholder={placeholder} onClick={onClick}>
      <InputPageWrapper>
        <span>책 페이지</span>
        <InputPage className="pageInput">
          <input placeholder="숫자 입력" />
          p.
        </InputPage>
      </InputPageWrapper>
    </InputCommentWrapper>
  );
};

export default InputCommentWithPage;

const InputCommentWrapper = styled(InputComment)`
  display: flex;
`;

const InputPageWrapper = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding-right: 20px;
  margin-right: 20px;
  border-right: 1px solid ${(props) => props.theme.colors.grey4};
  > span {
    font-size: ${(props) => props.theme.fontSize.body02};
    font-weight: ${(props) => props.theme.fontWeight.bold};
  }
`;
const InputPage = styled(CommonButton)`
  padding: 12px 16px;
  border: 1px solid ${(props) => props.theme.colors.grey4};
  background-color: ${(props) => props.theme.colors.white};
  color: ${(props) => props.theme.colors.grey1};
  > input {
    width: 66px;
    border: none;
    outline: none;
    &::placeholder {
      font-family: inherit;
      font-size: ${(props) => props.theme.fontSize.badge01};
      font-weight: ${(props) => props.theme.fontWeight.bold};
      color: ${(props) => props.theme.colors.grey1};
    }
  }
`;

위에서 만든 InputComment(여기서는 추가적인 styling을 하기 위해서 InputCommnetWrapper라고 사용하였다.) 안에 추가할 요소(InputPageWrapper)만 넣어주면 위와 같은 댓글 컴포넌트를 만들 수 있다. 반복하여 작성해야 했던 코드를 매우 줄일 수 있다. 

 


💡 예전엔 React, TypeScript, Styled-Components 문법에 익숙하지 않아 재사용 컴포넌트를 만들어 사용하고 싶어도 그러지 못했다. 사용에 조금 익숙해진 지금, 어떻게 하면 재사용 컴포넌트를 만들어서 사용할 수 있을까 고민하고 도전하여 만들어 보았다. 그래서 아직 미숙한 점이 있을 수 있다.

댓글 및 피드백은 언제나 감사합니다.