간단한 카운터 만들기
cra를 통해 프로젝트를 생성하고 App.js를 아래와같이 만들어주겠습니다.
function App() { return ( <div> </div> ); }
그리고 카운터를 만들어 추가해 보겠습니다.
import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0) const countUp =(e)=>{ setCount(count+1) } return( <> <div>{count}</div> <button onClick={countUp}>up!</button> </> ) } function App() { return ( <div> <Counter/> </div> ); } export default App;
useEffect
count가 컴포넌트의 state로 존재하는데 이 state가 변할 때마다 뭔가 다른 효과를 주고 싶습니다.
아래와 같이 수정하여 count가 홀수인지 짝수인지 비교해서 alert을 띄워주도록 합니다.
import React, { useState, useEffect } from 'react';//useEffect추가 function Counter() { const [count, setCount] = useState(0) const countUp =()=>{ setCount(count+1) } //count가 변했을때 동작할 행동을 useEffect를 이용해 구현 useEffect(() => { if(count%2){ alert("홀수입니다") }else{ alert("짝수입니다") } }, [count]) return( <> <div>{count}</div> <button onClick={countUp}>up!</button> </> ) } function App() { return ( <div> <Counter/> </div> ); } export default App;
useEffect라는 것을 사용했습니다. useState와 마찬가지로 useEffect를 사용하기 위해 import합니다. 이 useEffect는 state를 지정하여 해당 스테이트가 변경되었음을 감지하면 함수를 실행시켜줍니다.
자, 여기서 한 번 페이지를 새로고침 해봅시다. 그러면 클릭하지 않았는데도 "짝수입니다!"라는 메시지가 나옵니다. 이유는 함수가 실행되면서 useEffect가 최초한번 실행되기 때문입니다.
즉 한번 실행 후, 의존성 배열에 있는 값이 변할때(또는 렌더링마다) effect가 일어나는 것 입니다..
useEffect를 조금 더 살펴봅시다. 기본적인 구조는 아래와같습니다.
useEffect(()=>{ // state가 변경되어 렌더링 될때 실행하는 부분! // 공부하려고 책 펴는 타이밍! return()=>{ // 다시 렌더링을 하기 이전에 컴포넌트를 지우고 다시 그리겠죠? // 이 과정에서 지우기 전에 실행되는 부분입니다! clean-up이라고도 하죠. // 여러분들 시험 공부 하나를 마치고 다음 시험 공부 전에 책상 정리하는 느낌이랄까요.. } },[/*state값이 들어갑니다.(들어가지 않으면 최초 1번만 실행됩니다.*/)])
퀴즈!
최초로 렌더링 될 때, 상태가 업데이트 됐을 때, 업데이트가 된 내용을 렌더링 할 때 log를 찍도록 해보세요!
실제 적용해보기(useEffect
) ①
useEffect에 작성한 알림이 페이지 들어가자마자 나와 불편합니다.
(Hint : useEffect를 수정해 보세요. useState를 활용하셔도 좋습니다.)
정답
처음 상태가 초기화되고난 이후에만 동작할 수 있도록 check할수있는 상태를 만들어주었습니다.
import React, { useState, useEffect } from 'react'; function Counter() { const [count, setCount] = useState(0) const [checkRender, setCheckRender] = useState(false); const countUp =()=>{ setCount(count+1) } useEffect(() => { if (checkRender) { if(count%2){ alert("홀수입니다") }else{ alert("짝수입니다") } } setCheckRender(true); }, [count]) return( <> <div>{count}</div> <button onClick={countUp}>up!</button> </> ) } function App() { return ( <div> <Counter/> </div> ); } export default App;
실제 적용해보기(useEffect
, cleanup
) ②

Date 객체를 사용하여 위와같은 컴포넌트를 만들어 추가하고,
function Time(props) { const [today, setToday] = useState(new Date()); const [hour, setHour] = useState(today.getHours()); const [min, setMin] = useState(today.getMinutes()); const [sec, setSec] = useState(today.getSeconds()); console.log("렌더링이 됩니다..?")//렌더링이 잘 되는지 확인용! 꼭 넣고 진행해주세요. return ( <div> <h1> 시간 : {hour}시 {min}분 {sec}초 </h1> </div> ); } function App() { return ( <div> <Counter/> <Time/> </div> ); }
시간을 새로고침하지 않아도 실시간으로 바뀌게 해보세요 :)
정답
아래와 같이 작성하셨다면 Time컴포넌트에서 로그를 살펴보겠습니다.
정말 많은 횟수의 로그를 보실 수 있을것입니다.
마찬가지, useEffect를 이용했다 해도 만약 cleanup을 이용하지 않았다면,
마찬가지로 무수한 렌더링이 일어나고있음을 보실 수 있습니다.
function Time(props) { const [today, setToday] = useState(new Date()); const [hour, setHour] = useState(today.getHours()); const [min, setMin] = useState(today.getMinutes()); const [sec, setSec] = useState(today.getSeconds()); console.log("렌더링이 됩니다..?") setInterval(() => { const t = new Date(); setToday(t); setHour(t.getHours()); setMin(t.getMinutes()); setSec(t.getSeconds()); }, 1000); return ( <div> <h1> 시간 : {hour}시 {min}분 {sec}초 </h1> </div> ); } function App() { return ( <div> <Counter/> <Time/> </div> ); }
그래서 우리는 cleanup이란것을 사용해야합니다!
setinterval 을 하게되면 1초에 한번 같은 동작을 반복하게 될텐데요,
이 1초에 하는일을 계속 추가해서 1초에 수번(setinterval한 횟수만큼)의 같은일을 하게됩니다.
즉, setState를 하는 횟수가 계속 추가되는거죠.
그러니 우리는 1초마다(컴포넌트가 사라지기 직전) 반복해주는 동작을 삭제해주는 일을 해주어야합니다.
아래와같이 하면 됩니다!
import { useState, useEffect } from "react"; function Time(props) { const [today, setToday] = useState(new Date()); const [hour, setHour] = useState(today.getHours()); const [min, setMin] = useState(today.getMinutes()); const [sec, setSec] = useState(today.getSeconds()); console.log("렌더링이 됩니다..?") useEffect(() => { let time = setInterval(() => { const t = new Date(); setToday(t); setHour(t.getHours()); setMin(t.getMinutes()); setSec(t.getSeconds()); }, 1000); return () => { //컴포넌트가 사라지기 전에 setinterval을 clearinterval해줍니다 clearInterval(time); }; }, [today]); return ( <div> <h1> 시간 : {hour}시 {min}분 {sec}초 </h1> </div> ); } export default Time;
// 코드 리펙토링 import { useState, useEffect } from "react"; function Time(props) { const [today, setToday] = useState(new Date()); const hour = today.getHours(); const min = today.getMinutes(); const sec = today.getSeconds(); console.log("렌더링이 됩니다..?") useEffect(() => { let time = setInterval(() => { const t = new Date(); setToday(t); }, 1000); return () => { clearInterval(time); }; }, [today]); return ( <div> <h1> 시간 : {hour}시 {min}분 {sec}초 </h1> </div> ); } export default Time;