-
React Hook 기본 - useState, useEffectFrontEnd 2025. 11. 14. 21:37
useState
버튼을 누르면 값을 한개씩 증가하는 컴포넌트를 만들어보겠습니다.
아주 간단한 클릭버튼과 , 버튼 아래에 count를 표시하도록 만들고, 버튼을 클릭(onClick)하면 addCount() 함수가 실행되어 count변수가 +1씩 증가하도록 했습니다.
const Component = ()=>{ let count = 0; const addCount =()=>{ count++; console.log("count:", count); } return( <div className="mt-50 ml-20"> <button className="bg-gray-200 p-3" onClick={()=>addCount()}>click</button> <div>count : {count}</div> </div> ); } export default Component;
아주 예쁜 버튼과 카운트가 생겼어요! 그럼 이제 개발자도구를 켜두고 클릭 버튼을 눌러보겠습니다.
버튼을 한번 누르면, 콘솔에 count : 1로 count가 1이 증가했습니다.

버튼을 한번 더 누르면, 콘솔에 count : 2로 count가 1이 증가했습니다.

버튼을 몇번 더 누르면, count가 정상적으로 증가하고있습니다.

하지만, 이상한게 콘솔에는 count가 잘 찍히는데 버튼 아래의 count는 증가하지 않고있습니다.
버튼 아래의 count값이 바뀐다는 뜻은 count변수를 가져와 화면을 다시 그린다는 것과 같습니다.
하지만 React는 아무때나 화면을 다시 그리지 않습니다.
만약 React가 변수 하나하나의 변화에 다 반응해 화면을 다 그린다면 어떻게될까요?
화면에 보여주기 위한 변수가 아니라 단순 계산하기 위한 변수인데도 해당 변수가 바뀔때마다 화면이 다시 그려질 것입니다.
아주 복잡한 로직이 필요한 화면에서 단순 계산용 변수가 하나가 아니라 여러개라면 React는 화면을 1초마다 다시 그려야할 수도 있습니다.
그래서 React는 화면을 다시 그려야하는 변수에만 반응을합니다.
그렇다면 버튼을 몇번 누르고 새로고침을 누르면 화면이 다시 그려지면서 count가 바뀌어있을까요?
그렇지는 않습니다. 새로고침하면 화면을 다시 그리는 것은 맞지만, 그때마다 count 변수는 0으로 초기화되기 때문입니다.
React는 변경이 있는지 없는지 감지하기 위해 기억해둘 변수가 필요하고 이런 변수를 "state" 즉, "상태"라고 합니다.
그리고 React에게 state 변수임을 알려주는 도구가 useState입니다.
useState를 사용하는 방식으로 코드를 변경해보겠습니다.
먼저 useState를 사용하기 위해 "react"에서 import를 해줍니다.
import {useState} from "react";그리고 state 변수를 선언합니다.
const [count, setCount] = useState(0);count는 변수이름, setCount는 count에 값을 넣어주는 함수, useState의 괄호안의 숫자는 초기값입니다.
즉, count는 초기값이 0이고 setCount를 통해 값을 변경할 수 있습니다.
상태 값을 넣는 함수의 이름은 아무거나 상관없지만, 보통 변수이름 앞에 set을 붙이고 camel모양으로 함수 이름을 지어요 setCount 처럼요.
addCount 함수에서 count++이 아닌 setCount로 count값을 증가시키도록합니다 .
setCount(count+1);그럼 전체 코드가 아래처럼 바뀝니다 .
import {useState} from "react"; const Component = ()=>{ // let count = 0; const [count, setCount] = useState(0); const addCount =()=>{ // count++; setCount(count+1); console.log("count:", count); } return( <div className="mt-50 ml-20"> <button className="bg-gray-200 p-3" onClick={()=>addCount()}>click</button> <div>count : {count}</div> </div> ); } export default Component;그럼 이제 다시 화면에서 개발자 도구를 켜두고 버튼을 클릭해보겠습니다.



이제 버튼을 누를때마다 아래 count 가 잘 변하게 되었습니다.
그런데 좀 이상합니다. 콘솔의 count가 한박자씩 늦게 증가되는 것 같지않나요?
이는 React의 상태 업데이트 방식때문에 일어나는 현상으로 정상적인 현상입니다 .
setCount와 같이 state변경 함수는 값을 바로 바꾸는 것이 아니라, "다음 렌더링 때 이 값으로 바꿔?" 라고 예약해두는 것입니다.
버튼을 클릭하면, 먼저 setCount가 count 값을 1로 "예약"해둡니다.
하지만 화면 아직 리렌더링되지 않습니다. 아직 실행해야할 코드들이 남았으니까요.
그 다음 console.log("count:", count)가 실행됩니다.
count는 아직 변하지 않았기때문에 콘솔에 count:0으로 출력됩니다.
이후 실행할 모든 코드가 끝났으니 화면이 다시 렌더링되면서 count가 예약해둔 1로 변경됩니다.
그럼, count 변경에 맞게 콘솔에 찍고싶다면 어떻게해야할까요?
다양한 방법이 있겠지만, useEffect를 통해 count 변경에 맞게 콘솔을 찍어보겠습니다.
useEffect
useEffect는 렌더링 후에 어떤 효과를 처리하는 전용 훅입니다.
앞서 작성한 코드는 렌더링 전에 console을 찍어서 값이 일치하지 않았으니 렌더링 후에 console을 찍으면 값이 맞아질 것입니다.
먼저 useState와 동일하게 useEffect를 import 해줍니다.
import {useEffect, useState} from "react";그리고 useEffect 훅을 만들어줍니다.
useEffect(실행할 함수, [효과를 발생시킬 값들]) 형태로 작성합니다.
React는 useEffect의 [] 안의 값들을 감시하고있다가, 해당 값이 변경되면 왼쪽에 선언해둔 함수를 실행합니다.
[]이 비어있을 경우, 화면이 렌더링 될 때마다 함수를 실행합니다.
그럼 []안의 값이 바뀌면 무조건 함수가 실행될까요?
그렇지 않습니다.
만약 let count = 0 ;으로 선언한 변수를 넣으면 어떻게될까요?
useEffect는 "렌더링 후"에 어떤 효과를 처리할지를 결정하는 훅입니다. useEffect의 []안에 count를 넣어서 해당 값을 감시한다고 한들, let으로 선언한 count가 변한다고 화면이 리렌더링되지 않기 때문에 아무 일도 일어나지 않습니다.
그래서, useEffect를 통해 함수가 실행되도록 하기위해서는 state변수와 같이 React가 화면을 다시 그려야하는지 아닌지 관리하고있는 값을들 넣어주어야합니다.
말이 어렵지만, 우선 다른 것들을 배우기 전에는 useEffect(,[])의 []안에는 state 변수가 들어간다고 생각해도 좋습니다!
예시 1 ) count 값 일치시키기
우리의 목적은 count(state변수)가 변하면 렌더링 후에 콘솔을 찍어 콘솔의 count 값을 일치시키는 것이였습니다.
그렇다면 []안에 count를 넣어주면, React가 "아, count 값에 의해 화면이 렌더링되면 useEffect에 선언한 함수를 실행하면 되겠구나!" 라고 인식하게됩니다.
useEffect( function effect() { console.log("count:", count); } , [count]);addCount 함수에 있던 콘솔을 없애고, 위와 같이 useEffect 훅을 추가한 후에 다시 버튼을 클릭해보겠습니다.
import {useEffect, useState} from "react"; const Component = ()=>{ const [count, setCount] = useState(0); useEffect( function effect() { console.log("count:", count); } , [count]); const addCount =()=>{ setCount(count+1); // console.log("count:", count); } return( <div className="mt-50 ml-20"> <button className="bg-gray-200 p-3" onClick={()=>addCount()}>click</button> <div>count : {count}</div> </div> ); } export default Component;

이제 버튼을 클릭하면, 콘솔과 count 값이 일치하게 되었습니다 !
예시 3 ) 버튼 클릭 유무 표시하기
useEffect가 얼마나 꼭 필요한 존재인지 확인하기 위해 다른 코드 예시를 들어보겠습니다 .
버튼이 한번도 눌린 적 없으면 버튼에 "클릭된 적 없음" 이라고 적혀있고, 한번이라도 클릭이 됐다면 "클릭됨" 이라고 적혀있어야합니다.
statement라는 state 변수를 만들고, 초기값으로 "클릭된 적 없음"을 넣었습니다 .
그리고 count 변경을 딱 한번만 인지하기 위해 count === 1 일 때 setStatement("클릭됨") 이 실행되도록 했습니다.
import {useState} from "react"; const Component = ()=>{ const [count, setCount] = useState(0); const [statement, setStatement] = useState("클릭된 적 없음"); const addCount =()=>{ setCount(count+1); console.log("count:", count); } if(count === 1){ setStatement("클릭됨"); } return( <div className="mt-50 ml-20"> <button className="bg-gray-200 p-3" onClick={()=>addCount()} > {statement} </button> <div>count : {count}</div> </div> ); } export default Component;이제 버튼을 클릭하면 어떻게될까요?
Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
버튼을 클릭하자마자 화면이 멈추고 위 에러가 났습니다.
에러를 해석해보자면, "너무 많은 리렌더링이 일어났다"라고 합니다.
왜 이런 에러가 생겼을까요?
처음 버튼을 클릭하면, count의 값이 1로 예약되고 React는 "실행해야할 코드가 다 끝나면 화면을 다시 그려야지" 라고 생각합니다.
그리고 아직 count=0이기 때문에 if문을 지나치고 화면이 리렌더링됩니다.
그런데, 화면을 리렌더링 하는 과정에서 이제 count가 1이 되었기 때문에 ifcount===1)이 실행됩니다.
이때, setStatement가 호출되었기 때문에 React는 다시 생각합니다. "statement가 변경 예약되었으니 화면을 다시 그려야겠다".
이때부터 React에게 재앙이 시작됩니다.
count는 계속 1이기 때문에 계속해서 화면이 다 그려지기도 전에 리렌더링 예약이 걸리면서 무한루프에 걸리게됩니다.
무한루프가 안걸리게 하기 위해서는 useEffect를 사용해야합니다.
const Component = ()=>{ const [count, setCount] = useState(0); const [statement, setStatement] = useState("클릭된 적 없음"); const addCount =()=>{ setCount(count+1); } useEffect(() => { if(count === 1){ setStatement("클릭됨"); } }, [count]); // if(count === 1){ // setStatement("클릭됨"); // } return( <div className="mt-50 ml-20"> <button className="bg-gray-200 p-3" onClick={()=>addCount()} > {statement} </button> <div>count : {count}</div> </div> ); } export default Component;버튼을 처음 클릭하면, count 가 1로 예약되면서 화면이 다시 그려집니다.
useEffect는 렌더링 후에 실행되기 때문에, count에 의해 화면이 다 그려지고 나면 if(count===1)이 실행됩니다.
React는 setStatement에 의해 리렌더링을 예약하고, 마지막으로 화면이 다시 그려지고 끝이납니다.
정리하자면, useEffect는 렌더링 과정에서 바로 실행되는 것이 아니라, 화면이 모두 그려진 뒤에 실행되는 “후처리 단계”입니다.
그 덕분에 setStatement처럼 상태를 변경하는 작업을 렌더링 중이 아닌 렌더링 이후에 안전하게 수행할 수 있고,
React가 다시 렌더링을 1번만 더 하고 정상적으로 종료하게 됩니다.
React의 쭈꾸미(추구미)
React 공식 문서에 "React는 컨포넌트가 순수 함수로 동작하기를 기대한다"라고 되어있습니다.
순수 함수처럼 동작한다는 것은
컴포넌트 함수가 호출될 때마다 같은 입력(props)과 같은 내부 상태(state)라면 항상 동일한 결과(JSX)를 반환하며, 함수 내부에서 외부의 값을 변경하거나 외부에 영향을 미치는 행위 즉, side effect가 없는 것을 의미합니다.
예를들어,
1. 함수 외부에 선언한 변수를 함수 내부에서 변경
2. 렌더링 중 api 호출
3. 렌더링 중 dom 수정
4. 렌더링 중 state 변경
다음과 같은 상황들이 모두 순수 함수가 아닌 예시입니다.
React의 렌더링이란, 함수가 호출될때마다 ui를 다시 계산하는 것입니다.
따라서 렌더링중에 부작용이 있을 경우 안정적으로 ui를 갱신할 수 없기 때문에 React는 컴포넌트가 항상 순수 함수처럼 동작하길 기대한다는 것입니다.
그리고, 이때문에 생긴 것이 useEffect입니다.
Side Effect를 따로 처리할 수 있도록 한것입니다.
'FrontEnd' 카테고리의 다른 글
Javascript (1) 2024.08.05