container - presenter 구조

Kingmo·2022년 3월 25일
0

프로젝트를 만들 때 폴더 구조는 매우 중요하다.
실무에서는 패턴이라 불리는 여러 방법들이 존재한다.
그 중 리액트에서 사용하는 유명한 패턴으로는
contianer / presentational 패턴과 atomic패턴이 있다.

1. container / presentational 패턴

container, presentational 패턴이란,
소스코드를 기능과 UI로 나누는 방법을 의미한다.
여기서 container은 기능을 담당하고, presenter는 UI를 담당한다.

아래의 React 컴포넌트를 container, presenter 구조로 나누어 보자

아래와 같이 container, presenter 파일을 생성한다.

생성한 다음 container에서 presenter를 다음과 같이 import한다.

import BoardDetailUI from "./BoardDetail.presenter";
export default function BoardRead() {
  const onClickDelete = () => {
    	...
    };
	return <BoardDetailUI onClickDelete={onClickDelete}/>
}

여기서 presenter는 다음과같이 부모로 부터
함수나 변수를 동적 라우팅 할때와 같이
아래코드의 props와 같은 매개변수로 받아와 사용할 수 있다.

export default function BoardDetailUI(props) {
 	return (
    	<button onClick={props.onClickDelete}></div>
    ) 
}

React에서 props는 부모 컴포넌트에서 자식컴포넌트로만 넘겨줄 수 있다.
부모 컴포넌트와 자식컴포넌트가 양방향으로 데이터를 주고 받을 수 있는 프레임워크로는 Angular가있다.
얼핏 보면 양방향이 더 좋아보이지만 코드를 이해하기 위해서는 양쪽의 컴포넌트를 살펴봐야해서
협업 및 오류수정이 단방향보다 훨씬 번거롭다.
단방향은 부모에서 자식 순으로 코드를 읽어내려가며 이해하기 쉽기 때문에 훨씬 협업 및 유지보수가 쉽다.

2. CSS in presenter

emotion패키지를 사용해 아래와 같이 css를 export 했다고 하자

import styled from "@emotion/styled";

export const Button = styled.button`
    color: red
`;

export const Div = styled.div`
    background-color: red
`;

presenter 컴포넌트에서 다음처럼 import해서 사용하면
효율적으로 재사용 및 관리할 수 있다.

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

export default function BoardDetailUI(props) {
 	return (
      <S.Div>
    	<S.Button onClick={props.onClickDelete}></S.Button>
      </S.Div>
    ) 
}

emotion에 props 던지기

export const Test = styled.div`
  color: ${(props) => (props.isTrue ? 'red' : 'blue')}`

emotion에서도 위 코드처럼 props를 받을 수 있다.

3. queries in container

백엔드에 데이터를 요청하는 코드를 따로 queries 컴포넌트를 만들어
import 하여 사용하면 container의 코드가 간결해져 가독성이 훨씬 좋아지고
기능의 재사용및 수정이 간편해진다.

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

export const FETCH_BOARD = gql`
    query fetchBoard($boardId: ID!) {
        fetchBoard(boardId: $boardId) {
            _id
            writer
            title
            contents
            youtubeUrl
            likeCount
            dislikeCount
            images
            boardAddress {
                zipcode
                address
                addressDetail
            }
            createdAt
        }
    }
`;

export const DELETE_BOARD = gql`
    mutation deleteBoard($boardId: ID!) {
        deleteBoard(boardId: $boardId)
    }
`;

쿼리는 props를 보내줄 필요없이 useMutation, useQuery로 부모 컴포넌트에서 바로 실행이 가능하다.

import BoardDetailUI from "./BoardDetail.presenter";
import { useRouter } from "next/router";
import { useQuery, useMutation } from "@apollo/client";
import { FETCH_BOARD, DELETE_BOARD } from "./BoardDetail.queries";

export default function BoardRead() {
  const router = useRouter();
  const [deleteBoard] = useMutation(DELETE_BOARD);
  const { data } = useQuery(FETCH_BOARD, {
    variables: { boardId: router.query.boardId },
  }
	return(<>...</>)
}

4. export vs export default

export한 컴포넌트는 import로 가져 올 수 있는데
여기서 export default와 export는 서로 다른 방법으로 받아 주어야 한다.

  • export로 여러 함수들을 export를 했을 경우
import { useMutation, useQuery } from "@apollo/client"

위 처럼 중괄호로 감싸서 import 해주어야 한다.

  • export default로 함수를 export 했을 경우
import BoardDetailUI from "./BoardDetail.presenter"

위 처럼 중괄호 없이 가져 올 수 있다. export default는 하나의 함수만 보내고 받는 것이 가능하다.

profile
Developer

0개의 댓글