[React] Westagram Refactoring - Main 기능 구현

hangkemiii·2022년 5월 17일
0

wecode

목록 보기
21/26
post-thumbnail
post-custom-banner

Mission 3) Main | 댓글 기능

  • 사용자가 댓글 입력 후 enter 를 누르거나 왼쪽의 버튼 클릭 시 댓글이 추가되도록 구현해주세요.
  • 댓글 기능을 구현하기 위해서는 배열 데이터 타입을 활용해야 합니다.
  • Array.map 참고해서 시도해주세요.

Mission 4) Main | 댓글 컴포넌트화 + props로 데이터 전달

  • map 함수를 활용해 댓글 목록을 구현해주세요.
  • 댓글 하나를 컴포넌트화 시켜주세요.
  • 부모의 state 에 저장된 댓글 데이터에 Array.map() 메소드를 적용해 댓글의 개수만큼 댓글 컴포넌트가 나타나게 해주세요.
  • 필요한 데이터를 props 로 넘겨주세요.
  • 기존에 보였던 대로 댓글이 화면에 나타나면 과제 완료입니다.

정말 이번 리팩토링의 최대 난관인 미션이었다. 리액트를 다루는 기술 책으로 To-Do List를 만들어봤음에도 props와 state의 동작 과정이라던지, 경로가 잘 이해가 되지 않았어서 많은 어려움을 겪었다. 그러나 이번 Main.js의 댓글 추가, 삭제, 좋아요 버튼 수정 등의 기능을 구현하면서 해당 기능들을 보다 잘 이해할 수 있는 계기가 되었다.

우선, 댓글을 props로 전달할 key와 값들로 구성된 객체들의 배열로 설정하기 위해, useState로 기본값을 설정해 두었다.

const [commentList, setCommentList] = useState([
    { id: 1, name: 'iron_man', text: '너무 귀엽다~' },
    { id: 2, name: 'mighty_thor', text: '나도 강아지 키우고싶다!' },
    { id: 3, name: 'iamgroot', text: '아이엠그루트' },
  ]);

그리고 댓글 컴포넌트들이 올라가게 될 공간인 FeedCommentList 컴포넌트에 이 댓글들의 배열을 props로 전달해 주었다.

<FeedCommentList commentList={commentList} onRemove={onRemove} />

FeedCommentList 컴포넌트에서는 props로 받은 이 commentList를 map() 함수를 통해 여러 컴포넌트들의 집합으로 변경해 주는데, 이 과정에서 다시 한번 댓글 컴포넌트인 FeedComment에 props를 전달해 주게 된다. 여기서 map 함수를 사용하기 때문에, 리액트가 이를 식별할 수 있게 하려면 key값으로 props로 전달된 id를 지정해 주면 된다.

import React from 'react';
import FeedComment from './FeedComment';

const FeedCommentList = ({ commentList }) => {
  return (
    <div className="feedBottomComment">
      {commentList.map(comment => {
        return (
          <FeedComment comment={comment} key={comment.id} />
        );
      })}
    </div>
  );
};

export default FeedCommentList;

마지막으로 댓글의 모습을 JSX 문법으로 보여줄 FeedComment 컴포넌트에서는, FeedCommentList 컴포넌트에서 props로 보내준 인자 값들을 각 div, p 태그 안에 넣어서 보여준다.

import React, { useState } from 'react';
import './FeedComment.scss';

const FeedComment = ({ comment, onRemove }) => {
  const { id, text, name } = comment;

  return (
    <div className="commentDesc">
      <p className="descNickname">{name}</p>
      <div className="commentTxt">{text}</div>
      <div className="commentBtn">
        <i
          id="commentLike"
          className="fa-regular fa-heart"
        />
        <i class="fa-regular fa-trash-can" />
      </div>
    </div>
  );
};

export default FeedComment;

이렇게 하면, Feed 컴포넌트에서 기본값으로 설정해 두었던 객체의 배열들이, FeedComment 컴포넌트의 형태로 FeedCommentList 위에 나타나게 된다.

그러면, 댓글 추가 기능을 구현해보자.

우선, 추가되는 댓글에는 이전 댓글들과 id값이 달라야 하기 때문에, useRef로 nextId 값에 4를 할당한 뒤, .current값을 불러온다.

const nextId = useRef(4);

그런 다음, 추가할 comment를 객체형식으로 정의한 후, concat() 함수를 활용하여 onInsert 함수가 실행될 경우, 기존의 commentList 배열에 새로운 인자로 합쳐진 후, setCommentList 함수를 통해 commentList 값을 업데이트 해준다. 이때, nextId.current += 1 구문을 통해 comment의 id 값은 추가될때마다 1씩 증가하게 된다. 이 onInsert 함수는 InputComment Component에 props로 전달되게 된다.

const onInsert = text => {
    const comment = {
      id: nextId.current,
      name: 'hang_ke_mi',
      text,
    };
    setCommentList(commentList.concat(comment));
    nextId.current += 1;
  };

<InputComment onInsert={onInsert} />

InputComment로 전달된 onInsert props를 인자로 받아오고, 여기서는 다시 useState를 통해 input의 value를 빈 문자열로, setValue 함수로 value 값을 업데이트 하게 된다. 그리고 그 업데이트 된 value 값을, onSubmit 함수안에 onInsert 함수의 인자값으로 전달하고, 다시 setValue('') 함수를 선언해 submit이 이루어지면 input을 빈칸으로 만들어주는 작업을 한다. 이때 e.preventDefault();를 선언하는 이유는 submit이 일어났을 때, 브라우저가 초기화되는 것을 방지하기 위함이다.

import React, { useState } from 'react';
import './InputComment.scss';

const InputComment = ({ onInsert }) => {
  const [value, setValue] = useState('');

  const onChange = e => {
    setValue(e.target.value);
  };

  const onSubmit = e => {
    onInsert(value);
    setValue('');

    e.preventDefault();
  };

  return (
    <form className="feedComment" onSubmit={onSubmit}>
      <input
        className="inputComment"
        type="text"
        placeholder="댓글 달기..."
        value={value}
        onChange={onChange}
      />
      <button type="submit" className="addComment">
        게시
      </button>
    </form>
  );
};

export default InputComment;

이 루프를 돌고 나면, onSubmit으로 업데이트 된 commentList는 다시 FeedCommentList 컴포넌트의 props로 전달되고, 아까와 같이 댓글을 보여주는 동작 과정을 다시 한번 거쳐 업데이트 된 댓글이 나타나게 되는 것이다.

댓글 삭제도 이와 비슷한 과정으로 이루어지지만, 댓글 삭제는 filter() 함수를 통해 event가 발생한 컴포넌트와 id가 같지 않은 요소들만 배열로 반환하여 나타낸다는 차이점이 있다.

const onRemove = id =>
    setCommentList(commentList.filter(comment => comment.id !== id));

<FeedCommentList commentList={commentList} onRemove={onRemove} />

댓글의 좋아요 버튼은 FeedComment Component 안에서 useState로 기본값과 setter 함수를 선언한 뒤, 클릭 이벤트가 발생했을 경우 setter 함수로 기본값(현재값)의 반대값으로 업데이트하게 설정하였다.

const [likedBtn, setlikedBtn] = useState(false);

const likeButton = e => {
    e.preventDefault();
    e.target.style.color = likedBtn ? 'red' : 'black';
    e.target.className = likedBtn ? 'fa-solid fa-heart' : 'fa-regular fa-heart';
    setlikedBtn(!likedBtn);
  };

<i
   id="commentLike"
   className="fa-regular fa-heart"
   onClick={likeButton}
/>

완성!

profile
Front-End Developer
post-custom-banner

0개의 댓글