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 가 출력이 되는 것을 확인할 수 있다.


※ 추가 공부 - 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>
'웹 > 24-StudyWithPnP' 카테고리의 다른 글
[React.js] 7주차 - 성능 최적화: useMemo, useCallback, useContext, React.memo (1) | 2024.11.26 |
---|---|
[React.js] 6주차 - TodoList 만들기 (0) | 2024.11.18 |
[React.js] 4주차 - state, useState, useRef, React Hooks, 렌더링 (0) | 2024.10.29 |
[React.js] 3주차 - React.js 입문(1) (0) | 2024.10.14 |
[React.js] 1주차 - Node.js, React.js (1) | 2024.10.07 |