관리 메뉴

거니의 velog

(19) 프론트엔드 프로젝트 : 수정/삭제 기능 구현 및 마무리 1 본문

React_프론트엔드 프로젝트

(19) 프론트엔드 프로젝트 : 수정/삭제 기능 구현 및 마무리 1

Unlimited00 2024. 2. 23. 20:28

* 드디어 블로그 프로젝트의 마지막 장이다. 이 장에서는 포스트를 수정하는 기능과 포스트를 삭제하는 기능을 구현하고, 프로젝트를 마무리한다.

* 이번 실습은 다음 흐름으로 진행된다.


1. 포스트 수정

* 먼저 포스트 수정 기능을 구현해 보자.

(1) PostActionButtons 컴포넌트 만들기

* 포스트를 읽는 화면에서 포스트 작성자에게만 포스트 상단에 수정 버튼과 삭제 버튼이 나타나도록 렌더링해 보겠다. 이번에 만들 버튼은 기존에 만들어서 사용하던 Button과 스타일이 다르므로, 기존의 Button 컴포넌트를 재사용하지는 않을 것이다.

* components/post 디렉터리에 PostActionButtons 라는 컴포넌트를 생성하자.

[components/post/PostActionButtons.js]

import React from 'react';
import styled from 'styled-components';
import palette from '../../lib/styles/palette';

const PostActionButtonsBlock = styled.div`
  display: flex;
  justify-content: flex-end;
  margin-bottom: 2rem;
  margin-top: -1.5rem;
`;

const ActionButton = styled.button`
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
  color: ${palette.gray[6]};
  font-weight: bold;
  border: none;
  outline: none;
  font-size: 0.875rem;
  cursor: pointer;
  &:hover {
    background: ${palette.gray[1]};
    color: ${palette.cyan[7]};
  }
  & + & {
    margin-left: 0.25rem;
  }
`;

const PostActionButtons = () => {
  return (
    <>
      <PostActionButtonsBlock>
        <ActionButton>수정</ActionButton>
        <ActionButton>삭제</ActionButton>
      </PostActionButtonsBlock>
    </>
  );
};

export default PostActionButtons;

* 이제 PostActionButtons 컴포넌트를 PostViewer의 PostHead 하단에서 보여 주어야 한다. 그런데 이 컴포넌트를 PostViewer에서 직접 렌더링하면, 나중에 PostActionButtons에 onEdit, onRemove 등의 props를 전달할 때 무조건 PostViewer 를 거쳐서 전달해야 한다. 정작 PostViewer 내부에서는 사용하지 않지만 내부의 컴포넌트에서 필요하기 때문에 한 번 거쳐 전달하는 것은 조금 불편하다.

// PostViewerContainer에서 렌더링할 때 : 
<PostViewer (...) onEdit={onEdit} onRemove={onRemove} />

// PostViewer에서 렌더링할 때 : 
<PostActionButtons onEdit={onEdit} onRemove={onRemove} />

* 이 방법이 틀린 것은 아니지만, 자칫하면 컴포넌트가 받아 오는 props가 너무 많아져서 관리하기가 어려워질 수 있다.

* 이렇게 컴포넌트를 거쳐서 props를 전달하는 것이 싫다면, 그 대신 두 가지 방법을 고려할 수 있다. 첫 번째 방법은 PostActionButtons의 컨테이너 컴포넌트를 만들고 PostViewer 내부에서 바로 렌더링하는 것이다. 두 번째 방법은 props를 JSX 형태로 받아 와서 렌더링하는 것이다. 다음과 같이 말이다.

<PostViewer 
    post={post}
    loading={loading}
    error={error}
    actionButtons={<PostActionButtons onEdit={onEdit} onRemove={onRemove} />}
/>

* 우리는 두 번째 방법으로 구현하겠다. 두 번째 방법은 굳이 컨테이너 컴포넌트를 새로 만들 필요 없이 기존 PostViewContainer에서 필요한 로직을 작성하면 되기 때문이다.

* PostViewer 컴포넌트를 다음과 같이 수정하자.

[components/post/PostViewer.js]

(...)

const PostViewer = ({ post, error, loading, actionButtons }) => {

  (...)

  return (
    <PostViewerBlock>
      <PostHead>
        <h1>{title}</h1>
        <SubInfo
          username={user.username}
          publishedDate={publishedDate}
          hasMarginTop
        />
        <Tags tags={tags} />
      </PostHead>
      {actionButtons}
      <PostContent dangerouslySetInnerHTML={{ __html: body }} />
    </PostViewerBlock>
  );
};

export default PostViewer;

* 그리고 PostViewerContainer 에서 PostActionButtons 를 불러온 후 PostViewer 의 actionButtons props 를 통해 렌더링해 보자.

[containers/post/PostViewerContainer.js]

import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { readPost, unloadPost } from '../../modules/post';
import PostViewer from '../../components/post/PostViewer';
import PostActionButtons from '../../components/post/PostActionButtons';

const PostViewerContainer = ({ match }) => {
  // 처음 마운트될 때 포스트 읽기 API 요청
  const { postId } = match.params;
  const dispatch = useDispatch();
  const { post, error, loading } = useSelector(({ post, loading }) => ({
    post: post.post,
    error: post.error,
    loading: loading['post/READ_POST'],
  }));

  useEffect(() => {
    dispatch(readPost(postId));
    // 언마운트될 때 리덕스에서 포스트 데이터 없애기
    return () => {
      dispatch(unloadPost());
    };
  }, [dispatch, postId]);

  return (
    <PostViewer
      post={post}
      loading={loading}
      error={error}
      actionButtons={<PostActionButtons />}
    />
  );
};

export default withRouter(PostViewerContainer);

* 이제 포스트 페이지를 열어 보자. 수정, 삭제 버튼이 나타날 것이다.

수정 및 삭제 버튼

* 지금은 로그인된 사용자가 아닌 다른 사용자의 포스트를 볼 때도 이 버튼이 나타날 텐데, 조건에 따라 버튼을 숨기는 작업은 나중에 구현할 것이다.