블록체인 sw개발자

클래스형 컴포넌트( comment 기능)

sang969 2023. 10. 16. 14:48

클래스형 컴포넌트는 React 16.8 이전에 사용했던 React 컴포넌트의 종류입니다. 클래스형 컴포넌트는 JavaScript 클래스를 사용하여 정의됩니다. 클래스형 컴포넌트는 다음과 같은 메서드와 속성을 제공합니다.

  • constructor(): 컴포넌트가 생성될 때 호출됩니다.
  • render(): 컴포넌트를 DOM에 렌더링합니다.
  • setState(): 컴포넌트의 상태를 업데이트합니다.
  • componentDidMount(): 컴포넌트가 DOM에 렌더링된 후 호출됩니다.
  • componentDidUpdate(): 컴포넌트의 상태가 업데이트된 후 호출됩니다.
  • componentWillUnmount(): 컴포넌트가 DOM에서 제거되기 전에 호출됩니다.

클래스형 컴포넌트는 React 16.8에서 도입된 함수형 컴포넌트로 대체되었습니다. 함수형 컴포넌트는 더 간단하고 이해하기 쉽고 성능이 더 뛰어납니다. 그러나 클래스형 컴포넌트는 여전히 유효한 컴포넌트 형식이며, 많은 React 프로젝트에서 계속 사용되고 있습니다.

클래스형 컴포넌트의 장점은 다음과 같습니다.

  • 상태 관리가 쉽습니다.
  • 이벤트 처리가 쉽습니다.
  • 라이프사이클 메서드를 사용하여 컴포넌트의 상태를 추적하고 관리할 수 있습니다.

클래스형 컴포넌트의 단점은 다음과 같습니다.

  • 코드가 길고 복잡합니다.
  • 함수형 컴포넌트에 비해 성능이 떨어집니다.

클래스형 컴포넌트는 다음과 같은 상황에서 사용하기 좋습니다.

  • 상태 관리가 복잡한 컴포넌트
  • 이벤트 처리가 복잡한 컴포넌트
  • 라이프사이클 메서드를 사용하여 컴포넌트의 상태를 추적하고 관리해야 하는 컴포넌트

React에서 component는 UI의 일부를 제어하는 재사용 가능한 코드입니다. 컴포넌트는 JavaScript에서 함수의 역할을 React에게서 담당하는 것과 같습니다. 애플리케이션을 논리적이고 재사용 가능한 부분으로 구성하는 데 도움을 줍니다.

React 컴포넌트는 다음과 같은 특징을 가지고 있습니다.

  • 재사용 가능합니다. 한 번 만든 컴포넌트를 다른 곳에서 다시 사용할 수 있습니다.
  • 상태를 가질 수 있습니다. 컴포넌트의 상태는 시간이 지남에 따라 변경될 수 있습니다.
  • props를 받을 수 있습니다. props는 컴포넌트에게 전달되는 외부 데이터입니다.

React 컴포넌트를 사용하는 방법은 다음과 같습니다.

  1. React.Component 클래스를 상속받습니다.
  2. render() 메서드를 구현합니다. 이 메서드는 컴포넌트의 출력을 반환합니다.
  3. props 속성을 사용하여 외부 데이터를 받습니다.

다음은 간단한 React 컴포넌트의 예입니다.

ex.1

class Hello extends React.Component {
  render() {
    return (
      <div>
        Hello, world!
      </div>
    );
  }
}

이 컴포넌트는 Hello, world!라는 텍스트를 표시합니다.

React 컴포넌트를 사용하면 애플리케이션을 더 쉽게 유지 관리하고 확장할 수 있습니다. 컴포넌트는 애플리케이션을 논리적이고 재사용 가능한 부분으로 구성하는 데 도움이 되며, 상태와 props를 사용하여 컴포넌트의 동작을 제어할 수 있습니다.

 

 

ex.2 기초적인 comment 기능

 // index.html
 <script src="./js/index.js" type="module"></script>
 
<div id="app"></div>
// index.js
import App from "./app.js";

const root = document.querySelector("#app");
new App(root);

import App from "./app.js";은 App 컴포넌트를 가져오는 코드입니다.

const root = document.querySelector("#app");은 App 컴포넌트가 렌더링될 DOM 노드를 가져오는 코드입니다.

new App(root);은 App 컴포넌트를 생성하고 렌더링하는 코드입니다.

 

 

// app.js

import Component from "./core/component.js";
import Comment from "./src/components/comments.js";

class App extends Component {
  //   constructor(target) {
  //     super(target);
  //   ...
  //   }
  //없어도 실행된다
  setup() {}
  template() {
    return `
    <div>header</div>
    <div>
      <div data-component='comment'></div>
      
    </div>
    <div>footer</div>
    `;
  }
  mounted() {
    const commentTarget = document.querySelector(`[data-component="comment"]`);
    new Comment(commentTarget);
  }
  setEvent() {}
}

export default App;

import Component from "./core/component.js";은 Component 클래스를 가져오는 코드이고

import Comment from "./src/components/comments.js";은 Comment 컴포넌트를 가져오는 코드입니다.

class App extends Component 은 App 컴포넌트가 Component 클래스를 상속받는다는 것을 나타냅니다.

template() 메서드는 App 컴포넌트의 렌더링된 HTML을 반환합니다.

mounted() 메서드는 App 컴포넌트가 렌더링된 후 호출됩니다. 이 메서드에서는 Comment 컴포넌트를 생성하고 렌더링합니다.

setEvent() 메서드는 App 컴포넌트의 이벤트 핸들러를 설정합니다.

export default App;은 App 컴포넌트를 내보냅니다.

해석을 자세하게 설명하자면,

  • import App from "./app.js";은 App 컴포넌트를 가져오는 코드입니다. 이 코드는 App 컴포넌트의 코드를 브라우저로 가져옵니다.
  • const root = document.querySelector("#app");은 App 컴포넌트가 렌더링될 DOM 노드를 가져오는 코드입니다. 이 코드는 #app이라는 ID를 가진 DOM 노드를 가져옵니다.
  • new App(root);은 App 컴포넌트를 생성하고 렌더링하는 코드입니다. 이 코드는 App 컴포넌트를 생성하고 root DOM 노드에 렌더링합니다.
  • class App extends Component {은 App 컴포넌트가 Component 클래스를 상속받는다는 것을 나타냅니다. Component 클래스는 React 컴포넌트의 기본 클래스입니다.
  • template() 메서드는 App 컴포넌트의 렌더링된 HTML을 반환합니다. 이 메서드는 <div>header</div>, <div><div data-component="comment"></div></div>, <div>footer</div>라는 HTML을 반환합니다.
  • mounted() 메서드는 App 컴포넌트가 렌더링된 후 호출됩니다. 이 메서드에서는 Comment 컴포넌트를 생성하고 렌더링합니다.
  • setEvent() 메서드는 App 컴포넌트의 이벤트 핸들러를 설정합니다. 이 메서드는 현재로서는 아무런 작업을 수행하지 않습니다.
  • export default App;은 App 컴포넌트를 내보냅니다. 이 코드는 App 컴포넌트를 다른 파일에서 사용할 수 있도록 합니다.

 

 

// component.js

class Component {
  target;
  props;
  state = {};
  constructor(target, props) {
    this.target = target;
    this.props = props;
    this.setup();
    this.setEvent();
    this.render();
  }

  setup() {}
  template() {
    return "";
  }
  setState(newState) {
    this.state = { ...this.state, ...newState };
    this.render();
  }

  mounted() {}

  render() {
    this.target.innerHTML = this.template();
    this.mounted();
  }
  setEvent() {}
  addEvent(eventType, selector, callback) {
    this.target.addEventListener(eventType, (e) => {
      if (!e.target.closest(selector)) return false;
      callback(e);
    });
  }
}

export default Component;

이 클래스는 다음과 같은 속성과 메서드를 가지고 있습니다.

      • target: 컴포넌트가 렌더링될 DOM 노드.
      • props: 컴포넌트에 전달되는 데이터.
      • state: 컴포넌트의 상태.
      • constructor(): 컴포넌트가 생성될 때 호출되는 메서드.
      • setup(): 컴포넌트가 생성된 후 호출되는 메서드.
      • template(): 컴포넌트의 렌더링된 HTML을 반환하는 메서드.
      • setState(): 컴포넌트의 상태를 업데이트하는 메서드.
      • mounted(): 컴포넌트가 렌더링된 후 호출되는 메서드.
      • render(): 컴포넌트를 렌더링하는 메서드.
      • setEvent(): 컴포넌트의 이벤트 핸들러를 설정하는 메서드.
      • addEvent(): 컴포넌트의 DOM 노드에 이벤트 핸들러를 추가하는 메서드.

React 컴포넌트는 Component 클래스를 상속받음으로써 이러한 속성과 메서드를 사용할 수 있습니다.

 

// form.js

import Component from "../../core/component.js";

class CommentForm extends Component {
  setup() {
    console.log(this.props.insertItem);
  }
  template() {
    return `
          <h4>
            댓글쓰기
            <span>${this.props.length}</span>
          </h4>
          <form action="" id="commentFrm" class="commentFrm">
            <span class="ps_box">
              <input type="text" placeholder="댓글 내용을 입력해주새요" />
            </span>
            <button type="submit" class="btn">등록</button>
          </form>
    `;
  }
  mounted() {}

  handleSubmit(e) {
    e.preventDefault();
    console.log("submit 발동");
    this.props.insertItem();
  }

  setEvent() {
    const handleSubmit = this.handleSubmit.bind(this);
    this.addEvent("submit", "#commentFrm", handleSubmit);
  }
}

export default CommentForm;

위 코드는 다음과 같은 속성과 메서드를 가지고 있습니다.

  • props: 컴포넌트에 전달되는 데이터
  • insertItem: 댓글을 등록하는 함수
  • length: 댓글의 개수
  • handleSubmit(): 컴포넌트의 이벤트 핸들러를 설정하는 메서드

 

 

// item.js

import Component from "../../core/component.js";

class CommentItem extends Component {
  setup() {}
  template() {
    const items = this.props.comments.map(
      (item) => `
    <ul class="comment-row" data-index="${item.id}">
    <li>${item.userid}</li>
      <li>
          <span>${item.content}</span>
          <span>❌</span>
     </li>
          <li>${item.date}</li>
  </ul>`
    );

    return items.join("");
  }
  mounted() {}
  setEvent() {}
}

export default CommentItem;

comments 속성으로 받은 댓글 목록을 map() 메서드를 사용하여 반복합니다. map() 메서드는 댓글 목록의 각 항목에 대해 template() 메서드를 호출하고, 그 결과를 배열로 반환합니다.

template() 메서드에서 반환한 배열의 join() 메서드를 사용하여 문자열로 변환합니다. join() 메서드는 배열의 각 항목 사이에 구분자를 삽입하고, 문자열을 반환합니다.

template() 메서드에서 반환한 문자열을 반환합니다.

mounted() 메서드는 컴포넌트가 렌더링된 후 호출됩니다. 이 메서드에서는 아무런 작업을 수행하지 않습니다.

setEvent() 메서드는 컴포넌트의 이벤트 핸들러를 설정합니다. 이 메서드 또한 아무런 작업을 수행하지 않습니다.

CommentItem 컴포넌트는 댓글 목록을 표시하는 컴포넌트입니다. CommentItem 컴포넌트는 comments 속성으로 받은 댓글 목록을 목록으로 표시합니다.

// comment.js

import Component from "../../core/component.js";
import CommentForm from "../contents/form.js";
import CommentItem from "../contents/items.js";

class Comment extends Component {
  setup() {
    this.state = {
      comments: [
        { id: 1, userid: "sang969", content: "안녕하새요", date: "2023-10-7" },
        { id: 2, userid: "sang969", content: "안녕하새요", date: "2023-10-7" },
        { id: 3, userid: "sang969", content: "안녕하새요", date: "2023-10-7" },
      ],
    };
  }
  template() {
    return `
    <ul class="comment">
     <li class="comment-form">
     </li>
     <li class="comment-list">
     </li>
    </ul>
    `;
  }

  insertItem() {
    const newState = this.state.comments.push({
      id: 4,
      useid: "sang969",
      content: "content",
      date: "2023-10-16",
    });
    this.setState({ ...this.state, ...newState });
  }

  mounted() {
    const { comments } = this.state;
    const formTarget = document.querySelector(".comment-form");
    const insertItem = this.insertItem.bind(this);
    new CommentForm(formTarget, {
      length: comments.length,
      insertItem,
    });

    const itemTarget = document.querySelector(".comment-list");
    new CommentItem(itemTarget, { comments });
  }
  setEvent() {}
}

export default Comment;

Comment 컴포넌트는 mounted() 메서드에서 CommentForm 컴포넌트와 CommentItem 컴포넌트를 생성하고 렌더링합니다.

  • CommentForm 컴포넌트는 댓글 등록 폼을 제공하는 컴포넌트입니다.
  • CommentItem 컴포넌트는 댓글 목록을 표시하는 컴포넌트입니다.

Comment 컴포넌트는 state의 comments 속성이 변경되면 CommentItem 컴포넌트를 다시 렌더링합니다.

라이브서버 결과

 

 

코드 실행 과정

  1. 브라우저가 HTML 코드를 읽고 DOM 트리를 만듭니다.
  2. 브라우저가 JavaScript 코드를 읽고 실행합니다.
  3. App 클래스의 인스턴스가 생성되고 #app 요소에 렌더링됩니다.
  4. App 클래스의 template() 메서드가 호출되고 댓글 목록이 반환됩니다.
  5. App 클래스의 render() 메서드가 호출되고 댓글 목록이 #app 요소에 렌더링됩니다.
  6. #app_btn 버튼이 클릭될 때 App 클래스의 setEvent() 메서드가 호출되고 댓글 목록에 새로운 댓글이 추가됩니다.

 

 

App 클래스는 Component 클래스를 상속받습니다. App 클래스는 댓글 목록을 상태로 관리하고 있습니다. template() 메서드는 댓글 목록을 반환하고, setEvent() 메서드는 #app_btn 버튼이 클릭될 때 댓글 목록에 새로운 댓글을 추가하는 이벤트 리스너를 설정합니다.

const app = new App(document.querySelector("#app")); 코드는 App 클래스의 인스턴스를 생성하고 #app 요소에 렌더링합니다.

  • Component 클래스의 setup() 메서드는 컴포넌트의 상태를 초기화합니다. App 클래스의 경우, setup() 메서드는 댓글 목록을 초기화합니다.
  • Component 클래스의 template() 메서드는 컴포넌트의 HTML을 반환합니다. App 클래스의 경우, template() 메서드는 댓글 목록을 반환합니다.
  • Component 클래스의 setState() 메서드는 컴포넌트의 상태를 업데이트합니다. App 클래스의 경우, setState() 메서드는 댓글 목록에 새로운 댓글을 추가합니다.
  • Component 클래스의 render() 메서드는 컴포넌트를 DOM에 렌더링합니다. App 클래스의 경우, render() 메서드는 댓글 목록을 #app 요소에 렌더링합니다.