본문 바로가기
웹/24-StudyWithPnP

[React.js] 5주차 - Counter 만들기, useEffect

by 보먀 2024. 11. 4.
728x90
반응형

1. Counter 만들어보기

 

Viewer 컴포넌트는 count 값을 보여주고 있고, Controller 컴포넌트는 count 값을 증가시킬 수 있는 버튼을 가지고 있다. 

두 컴포넌트는 state 변수인 count 를 공유하고 있는데, Viewer 와 Controller 컴포넌트는 병렬적인 관계이기 때문에 props 공유가 불가능하다. 그래서 두 컴포넌트의 부모 컴포넌트인 App 컴포넌트에 state 변수를 두고 자식 컴포넌트인 Viewer, Controller 컴포넌트에 count 변수를 공유해준다.

 

이렇게 공유되는 state 를 계층 구조의 맨 위로 끌어올려 아래 컴포넌트들이 모두 공유할 수 있도록 만드는 것을 State Lifting 이라고 한다. 리액트에서 데이터는 위에서 아래로 단방향으로 흐르기 때문에 파악하기 쉽고 직관적이다. 

 

정리)

1. 리액트에 컴포넌트는 부모-자식 관계를 이루며 계층 구조를 이룸

2. 특정 컴포넌트가 다른 컴포넌트에게 데이터를 전달하려면 반드시 부모-자식 관계여야함 (부모 -> 자식)

3. 하나의 state 를 여러 컴포넌트에서 관리할 경우 가장 최상위 컴포넌트에 state 를 만들어야함

 

이 코드에서 또 하나 중요한 점은 Controller 컴포넌트로 count 와 setCount 함수를 각각 보내주는 것이 아니라 onClickButton 이라는 핸들러 함수를 만들어서 핸들러 함수를 Controller 에 보내주는 것이다. 핸들러 함수를 보내고 Controller 에서는 핸들러 함수를 받아 count 를 증가시킨다. 

// App.jsx

import Viewer from "./component/Viewer";
import Controller from "./component/Controller";
import { useState, useEffect } from "react";
import Even from "./component/even";


function App() {

  const [count, setCount] = useState(0);

  const onClickButton = (value) => {
    setCount(count+value);
  }

  return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        <input value={input}
        onChange={(e) => {
          setInput(e.target.value);
        }} />
      </section>
      <section>
        <Viewer count={count}/>
      </section>
      <section>
        <Controller onClickButton={onClickButton} />
      </section>
    </div>
  )
}

export default App
// Viewer.jsx

import '../App.css';

const Viewer = ({count}) => {

    return (
        <div>
            <div>현재 카운트: </div>
            <h1>{count}</h1>
        </div>
    );
}

export default Viewer
const Controller = ({onClickButton}) => {
    return (
      <div>
        <button onClick={() => {
            onClickButton(-1);
        }}>-1</button>
        <button onClick={() => {
            onClickButton(-10);
        }}>-10</button>
        <button onClick={() => {
            onClickButton(-100);
        }}>-100</button>
        <button onClick={() => {
            onClickButton(1);
        }}>+1</button>
        <button onClick={() => {
            onClickButton(10);
        }}>+10</button>
        <button onClick={() => {
            onClickButton(100);
        }}>+100</button>
      </div>
  
    );
  }

export default Controller

 

 

2. 컴포넌트의 사이프사이클(LifeCycle)

  • Mount: 컴포넌트가 화면에 처음 렌더링 되는 순간
  • Update: 컴포넌트가 다시 렌더링 되는 순간 ( == 리렌더링 될 때)
  • UnMount: 컴포넌트가 화면에서 사라지는 순간 ( == 렌더링에서 제외되는 순간)

 

2.1 useEffect

 

리액트 컴포넌트의 사이드 이펙트를 제어하는 새로운 React Hook 으로 mount, update, unmount 때 특정 코드를 실행시킬 수 있다.

 

- 사이드 이펙트 (Side Effect) ?

리액트에서 사이드 이펙트란 어떠한 동작에 따른 부수적인 또는 파생되는 효과를 말한다. 

컴포넌트는 렌더링 자체가 주된 작업인데, 렌더링하는 것 외에 mount / update / unmount 될 때 추가적으로 수행해야 하는 작업이 존재할 수 있다. 이런 작업들은 컴포넌트의 기본 렌더링 동작과 직접적인 관련이 없는 부수적인 행동(사이드 이펙트) 이기 때문에 사이드 이펙트라고 한다. 

 

useEffect 의 첫번째 인자는 특정 값을 관찰해 렌더링할 때 실행할 콜백함수이고, 두번째 인자는 의존성 배열이다. 

(참고로 의존성 배열은 dependency array 를 줄여 deps 라고 부르며, 의존성 배열에는 값을 넣지 않고 빈 배열로 사용할 수도 있고 값을 여러 개 넣을 수도 있다)

 

useEffect 는 두번째 인자에 들어있는 값(들)을 관찰해서 값이 변화할 때 콜백함수가 실행되도록 제어한다. 

useEffect(() => { }, [변화를 관찰할 값]);

 

 

2.2. mount 를 useEffect 로 제어하기

 

useEffect 는 deps 의 값이 변경되어야만 실행되기 때문에 결국 콜백함수는 컴포넌트가 처음 mount 된 이후에는 실행되지 않는다. 

useEffect(() => { }, []);

 

 

2.3. update 를 useEffect 로 제어하기

 

두번째 인자인 deps 를 생략하면 콜백함수는 mount 될 때 한 번 실행 + 리렌더링될 때마다(== update 가 일어날 때마다) 실행이 된다. 

useEffect(() => { });

 

만약 mount 시점을 제외하고 컴포넌트가 update 되는 순간에만 콜백함수를 실행하고 싶다면 아래와 같이 하면 된다. 

const isMount = useRef(false);

useEffect(() => {
    if (!isMount.current) {
        isMount.current = true;
        return;
    }
    
    // update 될 때마다 실행할 코드
})

 

 

2.4. unmount 를 useEffect 로 제어하기

 

렌더링이 컴포넌트가 화면에 그려지거나 업데이트 되는 과정이라면, unmount 는 컴포넌가 DOM 에서 제거될 때 실행되는 과정이다. 

 

useEffect 는 클린업 함수를 반환할 수 있는데, 클린업 함수는 컴포넌트가 unmount 될 때나 useEffect 가 재실행되기 전에 호출된다.

이 기능을 통해 이벤트 리스너 제거, 타이머 해제 등의 정리 작업을 수행해 컴포넌트가 unmount 된 후에도 리소스가 해제되지 않아 발생하는 메모리 누수나, 불필요한 코드 실행을 막을 수 있다. 

 

 

간단한 예시를 통해 unmount 시 useEffect 동작을 살펴보자.  

function App() {

  // ...

  return (
    <div className="App">
      <h1>Simple Counter</h1>
      <section>
        <input value={input}
        onChange={(e) => {
          setInput(e.target.value);
        }} />
      </section>
      <section>
        <Viewer count={count}/>
        {count % 2 === 0 ? <Even /> : null }
      </section>
      <section>
        <Controller onClickButton={onClickButton} />
      </section>
    </div>
  )
}

export default App
// Even.jsx

import { useEffect } from "react"

const Even = () => {

    useEffect(() => {

        return () => {
            console.log('unmount');
        };
    }, []);

    return <div>짝수입니다</div>
}

export default Even

 

 

Even 컴포넌트는 컴포넌트가 unmount 될 때 클린업 함수 안에서 'unmount' 를 출력해주고,

App 컴포넌트는 count 값이 짝수일 때 <div>짝수입니다</div> 를 화면에 렌더링해준다. 

 

count 의 초기값은 0 이기 때문에 처음에 '짝수입니다' 가 화면에 렌더링되어 있다. +1 버튼을 눌러 count 값을 바꿔주는 순간 count 의 값이 더 이상 짝수가 아니기 때문에 Even 컴포넌트는 unmount 가 된다.

-> 클린업 함수는 unmount 될 때 실행되기 때문에 클린업 함수가 실행되어 콘솔창에 unmount 가 출력이 되는 것을 확인할 수 있다.

 

count = 0 일 때

 

 

 

 

추가 공부 - section vs article vs div

 

나는 보통 div 태그들을 이용해서 개발했었는데, 강의에서는 section 태그를 이용하길래 div 태그와 section 태그 그리고 section 과 비슷한 느낌을 주는 article 태그가 뭐가 다른지, 또 어떻게 써야하는지 궁금해서 찾아보게 되었다. 

 

- <div>

  • 콘텐츠를 묶어주는 역할, 특별한 의미 없음
  • 레이아웃, 스타일링 목적으로 사용됨

 

- <section> 

  • 웹 페이지에서 관련 콘텐츠를 묶는 데 사용되거나 주제별로 콘텐츠를 나누어 문서의 흐름을 구조화할 때 사용
  • 태그 안의 내용은 다른 섹션과 함께 있어야만 맥락이 명확해질 때가 많음

 

- <article>

  • 독립적으로 의미를 가질 수 있는 콘텐츠  -> 태그 안의 내용을 웹페이지 맥락에서 떼어내도 충분히 이해 가능하며 그 자체로 완전한 정보 제공
  • 개별적인 의미를 가질 수 있으며 재사용 가능

 

시맨틱 태그 (Semantic Tag) ?

 

시맨틱(Semantic) 은 '의미의, 의미론적인' 뜻을 가진다. 단어의 뜻처럼 시맨틱 태그는 태그의 내용에 의미를 부여하여 콘텐츠의 구조를 명확히 설명하는 태그로, 태그의 이름이 해당 콘텐츠의 용도와 역할을 나타낸다. 

-> 사람들이 코드를 읽고 파악하기 쉽게 하고, 검색엔진최적화로 성능 향상 가능

-> article, section 은 시맨틱 태그

 

 

예시1. 블로그

 

article 은 블로그에서 포스트 자체라고 생각하면 된다. 각 포스트는 개별적으로 완전한 이야기를 담고 있기 때문에 혼자 떼어내도 독립적으로 이해 가능하다. 

<article>
  <h2>여행 준비 꿀팁</h2>
  <p>꿀팁 어쩌고...</p>
</article>

<article>
  <h2>여름 휴가지 추천</h2>
  <p>올해 여름엔 어쩌고...</p>
</article>

 

section 은 블로그의 한 포스트 내에서 특정 주제를 나누는 역할을 한다.

<section>
  <h2>제주도 동부 갈만한 곳</h2>
  <p>장소 추천 ..</p>
</section>
<section>
  <h2>제주도 동부 맛집</h2>
  <p>맛집 어쩌고...</p>
</section>

 

 

예시2. 뉴스

 

기사 자체를 article 로 나타낼 수 있다. 

<article>
  <h2>기후 변화에 대한 새로운 연구</h2>
  <p>새로운 연구에 따르면, 기후 변화는 예상보다 빠르게 진행되고 있습니다...</p>
</article>

<article>
  <h2>지역 축제 일정 안내</h2>
  <p>다음 주에는 각 지역에서 다양한 축제가 열릴 예정입니다...</p>
</article>

 

뉴스의 각 주제를 section 으로 분류하고, section 안에는 article 로 각 기사를 나타낼 수 있다.

<section>
  <h2>경제</h2>
  <article>
    <h3>경제 뉴스1</h3>
    <p>경제 ..</p>
  </article>
  <article>
    <h3>경제 뉴스2</h3>
    <p>경제 ...</p>
  </article>
</section>

<section>
  <h2>연예</h2>
  <article>
    <h3>연예 뉴스1</h3>
    <p>연예 ..</p>
  </article>
  <article>
    <h3>연예 뉴스2</h3>
    <p>연예 ...</p>
  </article>
</section>

 

 

예시 3. 기술 문서

 

각 항목은 별개의 가이드

<article>
  <h2>컴퓨터 기본 설정 방법</h2>
  <p>컴퓨터를 처음 시작할 때는 다음 단계를 따라야 합니다...</p>
</article>

<article>
  <h2>프린터 설치 가이드</h2>
  <p>프린터를 설치하는 방법은...</p>
</article>

 

관련된 주제를 나누어 그룹화 한 것

<section>
  <h2>기본 설정 가이드</h2>
  <p>다음은 기본 설정에 관한 정보입니다.</p>
</section>
<section>
  <h2>고급 설정 가이드</h2>
  <p>다음은 고급 설정을 위한 안내입니다.</p>
</section>

 

728x90
반응형