(React) 4. 복습 번외편 : Comment 기능 -3-

김동우·2021년 8월 1일
0

wecode

목록 보기
24/32
post-thumbnail

잠깐! 시작하기 전에

이 글은 wecode에서 실제 공부하고(이제 사전 스터디는 아닙니다.), 이해한 내용들을 적는 글입니다. 글의 표현과는 달리 어쩌면 실무와는 전혀 상관이 없는 글일 수 있습니다.

또한 해당 글은 다양한 자료들과 작성자 지식이 합성된 글입니다. 따라서 원문의 포스팅들이 틀린 정보이거나, 해당 개념에 대한 작성자의 이해가 부족할 수 있습니다.

설명하듯 적는게 습관이라 권위자 발톱만큼의 향기가 날 수 있으나, 엄연히 학생입니다. 따라서 하나의 참고자료로 활용하시길 바랍니다.

글의 내용과 다른 정보나 견해의 차이가 있을 수 있습니다.
이럴 때, 해당 부분을 언급하셔서 제가 더 공부할 수 있는 기회를 제공해주시면 감사할 것 같습니다.


구현

2. 이벤트가 발생하는 컴포넌트(input)

대부분의 html 문서에서 무언가를 입력받는 기능은 input 태그가 관리합니다.

또한 받은 입력을 어딘가에 보내야 하니, form 태그도 존재해야 합니다.

먼저 <input />을 끝내고 <form>을 고려해봅시다.

여기까지는 동일한데, 만약 댓글의 길이가 길어지고, 여러줄의 text를 받아야겠다는 생각이 있다면 textarea 태그를 사용해도 된다고 봅니다.

그건 상황에 따라 다른데, 저는 label 태그 내부에 input을 두는 방식을 선호해서 input을 사용해 구현하겠습니다.

그럼 먼저, comment 입력을 받는 컴포넌트를 작성해야 합니다.


import React from "react";

class CommentInput extends React.Component {
  render() {
    return (
      <label>
        <input type="text" placeholder="댓글을 입력해주세요." />
      </label>
    );
  }
}

export default CommentInput;

정말 간단하게 input을 하나 만들어줬습니다.

  1. 이제 input 이벤트 제어를 구현해야 합니다.

  2. 또한 input에서 작성되는 정보를 저는 활용해야 하니, 이벤트 내에서 값을 어딘가에 담아야겠습니다.

그럼 여기서부터는 정말 취향의 문제가 됩니다.

수준이 높지 않기에 이것저것 다 해봐도 된다는 말이죠.

1, 2 에 해당하는 기능을 구현하기 위해서는 보다 더 세세한 것들을 고려할겁니다.

숫자에 따라 고려할 사항을 적어보겠습니다.

사고의 순서는 다음과 같습니다.

  1. input 이벤트 제어
    • input event 종류
    • event 객체가 담고 있는 프로퍼티 선택
    • event.target.value 자료형

이어서,

  1. 이벤트 발생 시, 데이터 저장

    if(컴포넌트 state 당위성)

    • state 내부 정보의 자료형
    • setState -> render

    else

    • 부모 setState -> render

순서대로 고려해봅시다.

1. input 이벤트 제어

input event 종류

input에서는 다양한 이벤트를 관찰할 수 있습니다.

대표적으로 onClick, onChange, onKeyPress 등이 있습니다.

댓글의 경우 click, change, keyPress 모두 발생하게 됩니다.

그렇다면 이제 직관적으로 접근해서, 어떤 이벤트를 통해 무엇을 가져올지를 결정합니다.

  • click 순간에 발생하는 이벤트 onClick 이벤트는 추후 input 내 text 작성에 있어 유의미한 이벤트가 아니니 가장 먼저 제외합니다.

  • keyPress 이벤트의 경우 change와 동일해보이는 기능의 구현이 가능하겠지만, 여기서는 'Enter, BackSpace' 등에 대한 입력 이벤트를 관리할지에 따라 달라집니다.
    keyPress event의 경우 제가 원하는 기능에 무언가 더 더해질 때 유용할 것 같으니, 지금은 보류해두도록 하겠습니다.

  • change event만 남았는데, input element 내부 변화가 있을 때 호출이 되기에 우선은 change event를 사용하겠습니다.

그럼 이제 input 내부에서 변화가 발생할 때, 이벤트 발생에 따라 실행될 메서드를 선언해주어야 합니다.

완성된 상태로 두는 것이 아니고, 기본적인 형태로만 선언해주면 됩니다.

class CommentInput extends React.Component {
  handleInputChange = (event) => {
    console.log(event);
  };

  render() {
    const { handleInputChange } = this;
    return (
      <label>
        <input
          type="text"
          placeholder="댓글을 입력해주세요."
          onChange={handleInputChange}
        />
      </label>
    );
  }
}

그럼 이제 input text가 변경될 때 마다 호출되는 event를 관찰해봅시다.

event 객체 내 사용할 property & 자료형 결정

event를 뜯어보면 이것저것 참 많습니다.

참고자료 : Synthetic Event 아니면 console.log(event)

그 중 지금 무엇을 사용해야 할까? 생각하는 것은 때로는 시간이 오래 걸리는 일이 될지도 모릅니다.

제가 관찰해본 결과, target obj 내부 value property에 접근하면 input의 값을 가져올 수 있을 것 같습니다.

결정했으면 한 번 console.log()로 확인(typeof로 자료형 확인)해보고, 이후 과정으로 넘어가면 됩니다.

2. 이벤트 발생 시, 데이터 저장

컴포넌트 내 state의 당위성

input event에서 event.target.value를 사용하는것까지 결정했습니다.

그렇다면 이제 해당 데이터를 어떻게 처리할지 결정해야 합니다.

먼저, 데이터의 범용성과 render 여부가 state의 당위성을 결정하게 됩니다.

  1. 해당 데이터가 앞으로 다른 컴포넌트(부모, 형제)에서 쓰일 일이 아예 없고, 해당 컴포넌트의 독자적인 값인가?

  2. 이벤트 발생 시 render() 호출 혹은 뭔가 초기화가 필요한가?

  3. setState()-render() 과정이 비교적 자유로운가?(위치, 횟수 등등)

3번은 현재 기능 구현에서는 크게 고려할 이유가 없습니다.

그 이유는 state의 변경이 발생했을 때, render 내부 element tag 변경이 이루어지지 않는다는 점이 가장 큽니다.

이는 React 재조정 문서에서 '동기' 파트를 보면 알 수 있는 내용입니다.

즉, render의 호출이 있더라도 눈에 띄는 성능저하의 원인이 될 일은 없다는 말이 되겠습니다.

그러나 1번, 당위성은 좀 더 고민해야 합니다.

우리의 댓글기능은 작성으로 완료되는 것이 아니고, 분명 어딘가에서 해당 댓글의 내용을 다룰 일이 존재합니다.

또한 댓글정보의 구조만 봐도 input에서의 data(commentText or text)는 범용성이 있는 데이터죠.

이후 2번을 고민하면,

input이 입력될 때, reset이 혹시나 필요한가? 생각해보면 전혀 필요하지 않다는 결론에 도달할겁니다.

그럼 바로 작성을 마칩니다.

작성을 끝낸 input 컴포넌트의 형태는 다음처럼 결정됩니다.

import React from "react";

class CommentInput extends React.Component {
  handleInputChange = (event) => {
    const { value } = event.target;
    // console.log(value);
  };

  render() {
    const { handleInputChange } = this;
    return (
      <label>
        <input
          type="text"
          placeholder="댓글을 입력해주세요."
          onChange={handleInputChange}
        />
      </label>
    );
  }
}

export default CommentInput;

이제 드디어 input을 만들어냈고, 우린 내보낼 준비가 되었습니다.

다음은 form 컴포넌트입니다.

form 컴포넌트에서는 보다 다양한 생각들을 다뤄야합니다.

2. 이벤트가 발생하는 컴포넌트(form)

form 에서 우리가 고려해야 할 사항은 input보다 많아집니다.

우선은 form 컴포넌트를 하나 만들어보고, 해당 컴포넌트를 input의 부모로 정하겠습니다.

html 구조에서 form 태그는 자식으로 label, input 등의 노드를 가집니다. 저는 그런 구조를 컴포넌트로 분할해서 제작하겠습니다.

import React from "react";
import CommentInput from "./commentInput";

class CommentForm extends React.Component {
  render() {
    return (
      <form>
        <CommentInput />
      </form>
    );
  }
}

import-export를 통해 정말 간소하게 만들 수 있습니다.

이제 동일한 사고로

  1. form 이벤트 제어

  2. 이벤트 데이터의 저장방식

을 고려해야 하는데, 마찬가지니 짧고 빠르게 글로 정리한 뒤, 다른 항목을 적겠습니다.

상위 목차를 가져와보면

3. 댓글이 나타나는 컴포넌트

4.이벤트 & 댓글 렌더링 두 컴포넌트의 위치(state 위치)

가 남았음에도 저는 아직 2번을 진행하고 있습니다.

form 이벤트 제어

제가 input과 같은 과정을 통해 알아본 결과, onSubmit 이벤트가 좋겠다는 생각이 들었습니다.

저는 onSubmit 이벤트를 사용하겠습니다.

이벤트 발생, 데이터 저장?

form은 이벤트 객체 내의 데이터를 어딘가에 저장하는것보다, 어떻게 React스럽게 이벤트를 제어할 것인지가 관건입니다.

form 태그 이벤트 객체에 대해서는 크게 고민할 사항이 아직은 없다는 말입니다.

예를 들면, event.preventDefault()가 예시입니다.

event.preventDefault() 를 handleEvent(submit 제어) 메서드 내에서 호출할 경우, 브라우저의 기본 액션을 모두 취소시킵니다.

form 태그의 기본 액션은 제출 후 새로고침이 있는데, 이를 처리하지 않겠다는 의미가 됩니다.

저는 리소스의 낭비를 줄이기 위해 submit 후 새로고침을 막겠습니다.

class CommentForm extends React.Component {
  handleSubmit = (event) => {
    event.preventDefault();
  };

  render() {
    const { handleSubmit } = this;
    return (
      <form onSubmit={handleSubmit}>
        <CommentInput />
      </form>
    );
  }
}

form 태그 이벤트객체의 데이터를 저장할 일은 없어보입니다.

하지만 input의 데이터를 상위 컴포넌트에 넘겨주기로 했던게 남아있습니다.

그렇다면 다시 돌아, form 컴포넌트의 state 당위성을 생각해봅시다.

input의 value를 어떻게 처리할지 상위 컴포넌트인 form에서 고민하는겁니다.

1. 해당 데이터가 범용성이 있는가?

댓글 구조만 보면 음... form 에서 관리할 데이터도 사실 없어보입니다.

상위 컴포넌트에서 props로 넘겨받으면 그만일 것 같습니다.

그런데 문제는 input이 넘겨줄 값이 마음에 걸립니다.

애매할 때에는 다음 사항을 체크하는 것으로 흘러갑시다.

2. 이벤트 발생 시 render() 호출 혹은 뭔가 초기화가 필요한가?

댓글을 제출(게시)했을 때, 달라지는 점을 생각해봅시다.

먼저, 해당 댓글 내용을 입력했던 input은 초기화가 될거고, 제출한 댓글은 댓글 모음에 즉시 추가될겁니다.

여기서 초기화가 존재한다는 것은 무언가 정보의 변형이 일어난다는 것이고, 바뀌면 즉시 render()를 호출해야 한다는 생각으로 이어집니다.

input value를 초기화하고 다시 render().

state로 관리해야 할 정보가 생겼습니다.

그리고 이제 input value를 끌어와야하니, 메서드를 만들어줍니다.

// CommentForm
import React from "react";
import CommentInput from "./commentInput";

class CommentForm extends React.Component {
  constructor() {
    super();
    this.state = {
      commentText: ``,
    };
  }

  // input value 끌어오기 위한 메서드
  setCommentText = (text) => {
    this.setState((prevState) => {
      return { ...prevState, commentText: text };
    });
  };

  // 이벤트 발생 시, input value 초기화
  handleSubmit = (event) => {
    event.preventDefault();
    this.setState((prevState) => {
      return { ...prevState, commentText: `` };
    });
  };

  render() {
    const { handleSubmit, setCommentText } = this;
    const { commentText } = this.state;
    return (
      <form onSubmit={handleSubmit}>
        // props로 메서드와 this.state.commentText 전달
        <CommentInput
          setCommentText={setCommentText}
          commentText={commentText}
        />
      </form>
    );
  }
}

export default CommentForm;


// CommentInput
import React from "react";

class CommentInput extends React.Component {
  handleInputChange = (event) => {
    const { setCommentText } = this.props;
    const { value } = event.target;
    setCommentText(value);
  };

  render() {
    const { handleInputChange } = this;
    const { commentText } = this.props;
    return (
      <label>
        <input
          type="text"
          placeholder="댓글을 입력해주세요."
          onChange={handleInputChange}
          // value는 항상 부모 state.commentText를 바라봄.
          value={commentText}
        />
      </label>
    );
  }
}

export default CommentInput;

이런 코드가 나오겠네요.

댓글 구조상 나머지 데이터는 form에서 state로 관리할 필요는 없어보입니다.

그럼 이제 작성을 마치고 다음 단계로 넘어갑니다.

이어서

다음 글에서는 3. 댓글을 그리는 컴포넌트에 대한 구현을 해보겠습니다.

1. intro
2. 댓글 정보 구조
4. 댓글 구현 컴포넌트
5. 좋아요 기능 추가

0개의 댓글