프로그래밍⚡️/React

container/presentational 패턴 사용 후기

Kwangkki 2023. 1. 23. 20:13

리액트를 활용할 때 효율적이고 빠른 유지보수를 위한 폴더구조를 나누는 패턴이 존재한다. 

가장 흔하게 쓰이는 패턴으로는 container/presentational 패턴, hooks 패턴, atomic 패턴이 있다. 현재는 hooks 패턴이 많이 쓰이지만, 여전히 container / presentational 패턴을 사용하는 사람들이 많기 때문에 알아는 둬야한다고 한다. 그래서 직접 써보며 알아보기로 했다. 

 

 

우선 폴더 구조를 나눠봤다. 기존에 한 파일에서 작업했을 때는 다음과 같았다.

const FETCH_BOARD = gql`
  query fetchBoard($number: Int) {
    fetchBoard(number: $number) {
      number
      writer
      title
      contents
    }
  }
`;

export default function StaticRoutingMovedPage() {
  const router = useRouter();
  console.log(router);

  const { data } = useQuery(FETCH_BOARD, {
    variables: { number: Number(router.query.number) },
  });

  const onClickMove = () => {
    router.push(`/section09/09-04-boards/${router.query.number}/edit`);
  };

  return (
    <div>
      <div>{router.query.number}번 게시글 페이이지</div>
      <div>작성자 : {data?.fetchBoard?.writer}</div>
      <div>제목 : {data?.fetchBoard?.title}</div>
      <div>내용 : {data?.fetchBoard?.contents}</div>
      <button onClick={onClickMove}>수정하기</button>
    </div>
  );
}

 

 이것을 무려 4개의 파일로 쪼갰다. import를 통해 부모자식 관계도 설정했는데, index -> container -> presenter/querise -> styles 구조로 만들었다. index는 화면이 보여지는 pages 폴더에 있는 가장 상위의 파일이다. 

index 파일안에 container 파일을 import하고

container 파일에는 presenter과 querise를 import했다.

마지막으로 presenter에는 styles를 import 함으로써 복잡다난한 구조를 만들었다!

 

각 파일에는 독립된 기능을 부여했는데, 

container는 자바스크립트 기능을 구현했고,

presenter에는 HTML을 보여주고,

queries에는 graphQL 변수를 선언했다.

그리고 styles에는 emotion을 사용한 css를 적용했다.

 

Index 파일

import BoardWriteContainer from "../../../src/component/units/boards/write/BoardWrite.container";

export default function BoardWritePage() {
  return <BoardWriteContainer IsEdit={false} />;
}

container 파일

import { CREATE_BOARD, UPDATE_BOARD, FETCH_BOARD } from "./BoardWrite.queries";
import BoardWritePresenter from "./BoardWrite.presenter";

export default function BoardWriteContainer() {
    const onClickEditBtn = async () => {
        const myVariables: ImyVariables = {
          boardId: router.query.boardNumber,
        };
        if (title) myVariables.title = title;
        if (contents) myVariables.contents = contents;
        if (youtube) myVariables.youtubeUrl = youtube;
    }
	
    return <BoardWritePresenter>;
}

presenter 파일

import * as S from "./BoardWrite.styles";

export default function BoardWritePresenter() {
  return (
    <div>
      <S.MainBox>
        <S.ListBox>
          <S.TitleRow>
            <span>번호</span>
            <span>제목</span>
            <span>작성자</span>
            <span>날짜</span>
          </S.TitleRow>
        </S.ListBox>
      </S.MainBox>
    </div>

querise 파일

import { gql } from "@apollo/client";

export const CREATE_BOARD = gql`
  mutation createBoard($createBoardInput: CreateBoardInput!) {
    createBoard(createBoardInput: $createBoardInput) {
      _id
      writer
      title
      contents
      createdAt
      boardAddress {
        zipcode
        address
        addressDetail
      }
      youtubeUrl
      images
    }
  }
`;

styles 파일

import styled from "@emotion/styled";

export const MainBox = styled.div`
  display: flex;
  width: 1920px;
  flex-direction: column;
`;

export const ListBox = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  border-top: 1px solid black;
  border-bottom: 1px solid black;
`;

모든 코드를 옮긴 것은 아니며 대략 이런 모습이다.

 

처음에 이 패턴을 사용할 때 정말 유지보수에 유리한 게 맞아? 의문이 들었다.

왜냐하면 페이지가 늘 때마다 폴더가 기하급수적으로 늘었으며 어차피 기능을 수정하기 위해서는 container, presenter, queries 파일을 열수 밖에 없었다. 결국 창을 4분할 5분할 해가며 작업했다. 

 

엄청나게 늘어난 폴더와 파일
정말 많은 파일을 오가야 한다..

알고 있어야하는 개념이기 때문에 해당 패턴으로 프로젝트를 진행하고 있지만 그리 효율적이다고 생각할 수는 없었다.

 

찾아보니 이미 container/presentational 패턴을 고안해낸 사람도 새로운 패턴의 등장으로 해당 패턴의 사용을 추천하지 않는다고 한다. 물론, 해당 패턴의 필요성이 있는 프로젝트가 있을 것이다. 다만, 강제적으로 혹은 가장 흔히 쓰이는 패턴이니 이 패턴을 사용한다는 것은 금물이란 것이다. 그렇기 때문에 다양한 패턴을 접해보고 필요와 상황에 따라 적절하게 패턴을 사용하면 될 것 같다.