본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 10. · 18 Views
setCount() 기본 사용법 완벽 가이드
React의 useState 훅에서 제공하는 setCount() 함수의 기본 사용법을 초급 개발자를 위해 이북 스타일로 쉽게 설명합니다. 실무 상황 스토리와 비유를 통해 상태 업데이트의 핵심 원리를 배워봅니다.
목차
1. setCount란 무엇인가?
어느 날 김개발 씨가 처음으로 React 프로젝트를 시작했습니다. 버튼을 클릭하면 숫자가 올라가는 간단한 카운터를 만들고 있었는데, 선배가 "setCount를 사용하면 돼요"라고 말했습니다.
setCount? 이게 대체 무엇일까요?
setCount는 React의 useState 훅에서 반환하는 상태 업데이트 함수입니다. 마치 리모컨의 버튼을 누르면 TV 채널이 바뀌는 것처럼, setCount를 호출하면 컴포넌트의 상태가 업데이트됩니다.
이 함수를 통해 화면에 표시되는 데이터를 동적으로 변경할 수 있습니다.
다음 코드를 살펴봅시다.
import React, { useState } from 'react';
function Counter() {
// useState가 count와 setCount를 반환합니다
const [count, setCount] = useState(0);
// 버튼 클릭 시 setCount로 상태를 업데이트
const handleClick = () => {
setCount(1); // count를 1로 변경
};
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={handleClick}>클릭</button>
</div>
);
}
김개발 씨는 입사 1주일 차 주니어 개발자입니다. 오늘 처음으로 React로 간단한 웹 애플리케이션을 만들어보라는 과제를 받았습니다.
버튼을 누르면 숫자가 올라가는 카운터 앱이었습니다. 김개발 씨는 JavaScript로 변수를 선언하고 값을 바꾸는 방법은 알고 있었습니다.
하지만 React에서는 그냥 변수에 값을 할당한다고 해서 화면이 바뀌지 않았습니다. 당황한 김개발 씨는 옆자리 선배 박시니어 씨에게 물었습니다.
"선배님, 왜 count 변수를 바꿔도 화면이 안 바뀌죠?" 박시니어 씨가 모니터를 보더니 웃으며 말했습니다. "아, React에서는 useState의 setCount를 사용해야 화면이 다시 그려져요." setCount란 무엇일까요? 쉽게 비유하자면, setCount는 마치 백화점 전광판의 리모컨과 같습니다.
백화점 직원이 전광판에 표시될 내용을 바꾸려면 직접 전광판에 올라가서 바꾸는 게 아니라 리모컨으로 신호를 보냅니다. 그러면 전광판이 자동으로 내용을 업데이트합니다.
React에서도 마찬가지입니다. 우리가 화면에 표시될 데이터를 바꾸려면 직접 변수를 수정하는 게 아니라 setCount라는 전용 함수를 통해 "이 값을 바꿔주세요"라고 React에게 요청해야 합니다.
왜 그냥 변수를 바꾸면 안 될까요? 초보 시절 누구나 한 번쯤 이런 코드를 작성해본 적이 있을 겁니다. javascript let count = 0; count = count + 1; // 값은 바뀌지만 화면은 그대로 일반 변수는 값이 바뀌어도 React가 이를 감지하지 못합니다.
React는 "아, 이 값이 바뀌었구나. 화면을 다시 그려야겠네!"라고 판단할 방법이 없습니다.
그래서 화면은 여전히 옛날 값인 0을 보여주고 있는 것입니다. 더 큰 문제는 여러 컴포넌트가 같은 데이터를 공유할 때입니다.
한 곳에서 값을 바꿨는데 다른 곳은 여전히 옛날 값을 보여주는 일이 생깁니다. 프로젝트가 커질수록 이런 문제는 감당하기 어려워집니다.
setCount의 등장 바로 이런 문제를 해결하기 위해 React는 useState 훅을 제공합니다. useState를 호출하면 두 개의 값을 배열로 반환합니다.
첫 번째는 현재 상태 값(count), 두 번째는 상태를 업데이트하는 함수(setCount)입니다. 이 setCount 함수를 사용하면 React가 "아, 상태가 바뀌었네.
화면을 다시 그려야지!"라고 즉시 인식합니다. 코드 살펴보기 위의 코드를 한 줄씩 분석해봅시다.
먼저 const [count, setCount] = useState(0); 부분입니다. 이것을 배열 구조 분해라고 합니다.
useState(0)은 초기값이 0인 상태를 만들고, count와 setCount 두 개를 반환합니다. count는 현재 값, setCount는 값을 바꾸는 함수입니다.
다음으로 setCount(1); 부분을 보겠습니다. 이 코드가 실행되면 count 값이 0에서 1로 바뀝니다.
동시에 React는 이 컴포넌트를 다시 렌더링해서 화면에 새로운 값을 표시합니다. 마지막으로 <p>현재 카운트: {count}</p> 부분에서 업데이트된 count 값이 화면에 자동으로 나타납니다.
실무에서는 어떻게 사용할까요? 실제 서비스를 개발할 때도 이 패턴은 매우 자주 사용됩니다. 예를 들어 쇼핑몰 장바구니를 생각해봅시다.
사용자가 "수량 증가" 버튼을 클릭하면 상품 개수가 1개씩 늘어나야 합니다. 이때 setCount를 사용해서 장바구니 수량을 업데이트합니다.
그러면 화면의 숫자가 즉시 바뀌고, 총 금액도 자동으로 다시 계산됩니다. 소셜 미디어의 "좋아요" 버튼도 마찬가지입니다.
사용자가 하트 아이콘을 누르면 setLikeCount를 호출해서 좋아요 숫자를 1 증가시킵니다. 처음 배울 때 주의할 점 초보 개발자들이 흔히 하는 실수가 있습니다.
"그냥 count = 1 하면 되지 않나요?"라고 생각하는 것입니다. 하지만 이렇게 하면 React가 변경 사항을 감지하지 못합니다.
값은 바뀌지만 화면은 그대로입니다. 반드시 setCount를 통해서만 상태를 업데이트해야 합니다.
또 다른 주의점은 setCount를 너무 많이 연속으로 호출하면 성능 문제가 생길 수 있다는 점입니다. React는 상태가 바뀔 때마다 화면을 다시 그리기 때문입니다.
이런 경우에는 함수형 업데이트를 사용하는 것이 좋습니다(이건 다음 카드에서 배우게 됩니다). 다시 김개발 씨의 이야기로 박시니어 씨의 설명을 듣고 김개발 씨는 코드를 수정했습니다.
이번에는 버튼을 클릭하자 화면의 숫자가 정확히 바뀌었습니다. "오!
되네요!" setCount의 기본 원리를 이해하면 React로 동적인 웹 애플리케이션을 만들 수 있는 첫걸음을 뗀 것입니다. 이것이 React 상태 관리의 가장 기본이 되는 개념입니다.
실전 팁
💡 - setCount는 반드시 useState가 반환한 함수를 사용해야 합니다
- 직접 변수를 수정하지 말고 항상 setState 함수를 통해 상태를 업데이트하세요
- setCount를 호출하면 컴포넌트가 다시 렌더링된다는 점을 기억하세요
2. 값을 직접 전달하는 방법
김개발 씨가 카운터를 만드는 데 성공했습니다. 이제 선배가 "버튼마다 다른 숫자로 바꿔보세요"라고 했습니다.
0으로 리셋하는 버튼, 10으로 만드는 버튼, 100으로 만드는 버튼이 필요했습니다. 어떻게 해야 할까요?
setCount에 원하는 값을 직접 전달하는 방법입니다. setCount(10)처럼 호출하면 현재 값이 무엇이든 상관없이 무조건 10으로 바뀝니다.
마치 온도 조절기를 특정 온도로 직접 설정하는 것과 같습니다.
다음 코드를 살펴봅시다.
import React, { useState } from 'react';
function CounterWithButtons() {
const [count, setCount] = useState(0);
// 각 버튼이 다른 값으로 설정합니다
const resetToZero = () => {
setCount(0); // 0으로 리셋
};
const setToTen = () => {
setCount(10); // 10으로 설정
};
const setToHundred = () => {
setCount(100); // 100으로 설정
};
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={resetToZero}>리셋</button>
<button onClick={setToTen}>10으로</button>
<button onClick={setToHundred}>100으로</button>
</div>
);
}
김개발 씨는 첫 번째 카운터 앱을 완성한 뒤 자신감이 생겼습니다. 이제 좀 더 복잡한 기능을 추가해보고 싶었습니다.
팀장님이 다가와서 새로운 요구사항을 전달했습니다. "게임 점수판을 만들어주세요.
리셋 버튼, 10점으로 만드는 버튼, 100점으로 만드는 버튼이 필요합니다." 김개발 씨는 고민했습니다. 지난번에는 setCount(1)로 1로만 바꿨는데, 이번에는 버튼마다 다른 숫자로 바꿔야 했습니다.
값을 직접 전달한다는 의미 setCount에 값을 직접 전달하는 방식은 매우 직관적입니다. 온도 조절기를 생각해봅시다.
에어컨 리모컨에서 "25도"를 누르면 현재 온도가 30도든 20도든 상관없이 무조건 25도로 설정됩니다. setCount도 똑같이 동작합니다.
**setCount(10)**을 호출하면 현재 count가 0이든 99든 상관없이 무조건 10이 됩니다. 이것을 절대값 설정 방식이라고 부를 수 있습니다.
"현재 값에 무언가를 더하거나 빼라"가 아니라 "무조건 이 값으로 만들어라"는 명령입니다. 언제 이 방법을 사용할까요? 실무에서 값을 직접 전달하는 경우는 생각보다 많습니다.
첫 번째는 초기화할 때입니다. 사용자가 "처음부터 다시 시작" 버튼을 누르면 모든 값을 0이나 초기 상태로 돌려야 합니다.
이때 setCount(0)처럼 직접 0을 전달합니다. 두 번째는 특정 상태로 점프할 때입니다.
게임에서 특정 스테이지로 바로 이동하거나, 설정 화면에서 미리 정의된 프리셋을 선택할 때 이 방식을 사용합니다. 세 번째는 서버에서 받은 데이터를 그대로 반영할 때입니다.
API를 호출해서 받은 숫자를 화면에 표시해야 한다면, 그 값을 setCount에 그대로 전달하면 됩니다. 코드 뜯어보기 위 코드를 자세히 살펴봅시다.
setCount(0), setCount(10), setCount(100) 세 가지 호출이 있습니다. 각각의 버튼을 클릭하면 해당 함수가 실행되고, count 값이 그 숫자로 바뀝니다.
여기서 중요한 점은 현재 count 값을 전혀 신경 쓰지 않는다는 것입니다. count가 5든 77이든, setCount(10)을 호출하는 순간 무조건 10이 됩니다.
이런 특징 덕분에 코드가 매우 단순하고 예측 가능합니다. "이 버튼을 누르면 무조건 이 값이 된다"는 것을 명확히 알 수 있습니다.
실전 예제: 타이머 설정 실무에서 이 패턴이 유용한 예를 하나 더 들어보겠습니다. 요리 앱에서 타이머 기능을 만든다고 가정해봅시다.
사용자가 "3분", "5분", "10분" 중 하나를 선택할 수 있습니다. 이때 각 버튼은 setSeconds(180), setSeconds(300), setSeconds(600)을 호출합니다.
현재 타이머가 몇 초에 있든 상관없이 사용자가 선택한 시간으로 즉시 바뀝니다. 많은 앱의 "빠른 설정" 기능이 이런 방식으로 구현됩니다.
사용자가 프리셋을 선택하면 여러 값이 한 번에 특정 상태로 설정되는 것입니다. 주의해야 할 함정 초보 개발자들이 가끔 이런 실수를 합니다.
"버튼을 연속으로 여러 번 눌렀는데 마지막 값만 적용돼요!" 이것은 당연한 동작입니다. setCount(5)를 아무리 여러 번 호출해도 결과는 5입니다.
누적되지 않습니다. 만약 값을 누적시키고 싶다면(예: 1씩 증가) 다음 카드에서 배울 함수형 업데이트를 사용해야 합니다.
하지만 특정 값으로 설정하고 싶을 때는 지금 배운 직접 전달 방식이 정답입니다. 또 하나 주의할 점은, setCount는 비동기로 동작한다는 것입니다.
setCount(10)을 호출한 직후에 console.log(count)를 찍으면 아직 옛날 값이 나올 수 있습니다. 실제 업데이트는 React가 다음 렌더링 때 처리하기 때문입니다.
김개발 씨의 성공 김개발 씨는 세 개의 버튼을 만들고 테스트해봤습니다. 리셋 버튼을 누르니 0이 되고, 10으로 버튼을 누르니 10이 됐습니다.
완벽합니다! 팀장님이 결과를 보고 만족스러워했습니다.
"잘했어요. 이제 사용자가 원하는 점수로 바로 이동할 수 있겠네요." 값을 직접 전달하는 방식은 단순하지만 매우 강력합니다.
상태를 특정 값으로 확실하게 설정해야 할 때 이 방법을 사용하세요.
실전 팁
💡 - 초기화하거나 특정 값으로 점프할 때는 값을 직접 전달하세요
- setCount(n)은 현재 값과 무관하게 무조건 n으로 설정됩니다
- 연속 호출해도 누적되지 않고 마지막 값만 적용됩니다
3. 이전 값을 기반으로 업데이트하기
김개발 씨가 이번에는 "+1" 버튼을 만들고 있었습니다. 사용자가 버튼을 누를 때마다 현재 숫자에 1을 더해야 했습니다.
그런데 setCount(count + 1)로 작성했더니 빠르게 연속으로 클릭하면 이상하게 동작했습니다. 선배가 "함수를 전달해야 해요"라고 했습니다.
이전 값을 기반으로 상태를 업데이트할 때는 함수를 전달해야 합니다. setCount(prevCount => prevCount + 1)처럼 작성하면 React가 항상 최신 값을 기준으로 계산합니다.
이것을 함수형 업데이트라고 부르며, 연속 업데이트가 정확히 동작하게 만듭니다.
다음 코드를 살펴봅시다.
import React, { useState } from 'react';
function CounterWithIncrement() {
const [count, setCount] = useState(0);
// 잘못된 방법: 빠르게 클릭하면 값이 제대로 안 올라갈 수 있음
const incrementWrong = () => {
setCount(count + 1);
};
// 올바른 방법: 함수를 전달하면 항상 최신 값 기준으로 계산
const incrementCorrect = () => {
setCount(prevCount => prevCount + 1);
};
// 여러 번 증가시키기
const incrementByFive = () => {
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
};
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={incrementCorrect}>+1</button>
<button onClick={incrementByFive}>+5</button>
</div>
);
}
김개발 씨는 어느덧 React의 기본을 이해하기 시작했습니다. 이번 과제는 사용자가 버튼을 누를 때마다 숫자가 1씩 증가하는 카운터였습니다.
처음에는 간단해 보였습니다. setCount(count + 1)이라고 작성하면 될 것 같았습니다.
실제로 천천히 버튼을 누르면 잘 동작했습니다. 하지만 QA 팀에서 버그 리포트가 올라왔습니다.
"버튼을 빠르게 연속으로 클릭하면 숫자가 제대로 안 올라가요!" 김개발 씨는 당황했습니다. 분명히 맞게 작성한 것 같은데?
왜 count + 1은 문제가 될까요? 이 문제를 이해하려면 React의 상태 업데이트 메커니즘을 알아야 합니다. setCount를 호출한다고 해서 count 값이 즉시 바뀌는 것은 아닙니다.
React는 성능을 위해 여러 개의 상태 업데이트를 모아서 한 번에 처리합니다. 이것을 배치 업데이트라고 부릅니다.
마치 택배 회사가 소포를 하나씩 보내지 않고 모아서 한 번에 보내는 것과 비슷합니다. 효율적이지만, 이 때문에 예상치 못한 문제가 생길 수 있습니다.
예를 들어봅시다. count가 0일 때 사용자가 버튼을 빠르게 세 번 클릭했습니다.
코드는 setCount(count + 1)을 세 번 호출합니다. 하지만 세 번 모두 count는 여전히 0입니다.
그래서 setCount(0 + 1), setCount(0 + 1), setCount(0 + 1)이 됩니다. 결과는?
1입니다. 3이 아니라!
함수형 업데이트라는 해결책 바로 이 문제를 해결하는 방법이 함수를 전달하는 것입니다. setCount에 숫자 대신 함수를 전달하면 React는 특별하게 처리합니다.
"아, 이건 이전 값에 기반한 업데이트구나"라고 인식하고, 항상 가장 최신의 값을 함수의 인자로 넘겨줍니다. setCount(prevCount => prevCount + 1)이라고 작성하면, prevCount는 항상 현재의 최신 값입니다.
첫 번째 호출에서는 0, 두 번째 호출에서는 1, 세 번째 호출에서는 2가 됩니다. 따라서 최종 결과는 정확히 3입니다.
화살표 함수가 낯설다면 함수형 업데이트는 화살표 함수 문법을 사용합니다. 처음 보면 낯설 수 있습니다.
prevCount => prevCount + 1은 "prevCount를 받아서 prevCount + 1을 반환하는 함수"라는 뜻입니다. 일반 function 문법으로 쓰면 이렇습니다: javascript setCount(function(prevCount) { return prevCount + 1; }); 화살표 함수가 더 간결해서 많이 사용됩니다.
prevCount라는 이름은 관습적으로 많이 쓰이지만, 원하는 이름을 사용해도 됩니다. prev, current, old 등 무엇이든 괜찮습니다.
코드 분석: 5씩 증가시키기 위 코드의 incrementByFive 함수를 보겠습니다. setCount를 다섯 번 연속으로 호출하고 있습니다.
만약 setCount(count + 1)을 다섯 번 호출했다면 결과는 1이었을 겁니다. 하지만 setCount(prev => prev + 1)을 사용했기 때문에, 첫 번째 호출은 0에서 1로, 두 번째는 1에서 2로, 이런 식으로 정확히 5가 증가합니다.
이 패턴은 한 번에 여러 단계의 업데이트를 해야 할 때 매우 유용합니다. 실무 활용: 장바구니 수량 증가 실제 쇼핑몰 프로젝트를 개발할 때 이 패턴이 필수적입니다.
사용자가 "+" 버튼을 빠르게 여러 번 누를 수 있습니다. 만약 함수형 업데이트를 사용하지 않으면 버튼을 다섯 번 눌렀는데 수량은 1만 증가하는 황당한 상황이 벌어집니다.
소셜 미디어의 "좋아요" 버튼도 마찬가지입니다. 사용자가 실수로 버튼을 두 번 연속 누르면?
함수형 업데이트를 사용해야 정확히 두 번 카운트됩니다. 게임 점수 시스템도 똑같습니다.
짧은 시간에 여러 이벤트가 발생해서 점수를 업데이트해야 한다면, 반드시 함수형 업데이트를 사용해야 정확한 점수가 계산됩니다. 언제 함수형 업데이트를 써야 할까요? 간단한 규칙이 있습니다.
"새 값이 이전 값에 의존하는가?" 이 질문에 YES라면 함수형 업데이트를 사용하세요. count + 1, count - 1, count * 2처럼 현재 값을 읽어서 계산한다면 함수형 업데이트입니다.
반대로 setCount(0), setCount(10)처럼 무조건 특정 값으로 설정하는 경우에는 직접 전달 방식을 사용하면 됩니다. 김개발 씨의 깨달음 박시니어 씨가 코드를 보더니 고개를 끄덕였습니다.
"함수를 전달하면 React가 알아서 최신 값을 넘겨줘요. 이제 아무리 빠르게 클릭해도 정확하게 동작할 거예요." 김개발 씨는 코드를 수정하고 다시 테스트했습니다.
이번에는 버튼을 초고속으로 연타해도 숫자가 정확히 올라갔습니다. "오!
완벽하네요!" 함수형 업데이트는 React 개발에서 매우 중요한 패턴입니다. 이전 상태에 기반해 업데이트할 때는 반드시 이 방법을 사용하세요.
실전 팁
💡 - 이전 값에 기반해 계산할 때는 항상 함수형 업데이트를 사용하세요
- setCount(prev => prev + 1)처럼 화살표 함수로 작성합니다
- 연속으로 여러 번 업데이트해야 할 때 특히 중요합니다
4. setCount의 비동기 특성 이해하기
김개발 씨가 이번에는 이상한 버그를 발견했습니다. setCount(10)을 호출한 직후에 console.log(count)를 찍었는데, 여전히 옛날 값이 출력됐습니다.
"분명히 바꿨는데 왜 안 바뀌죠?" 선배가 "setState는 비동기로 동작해요"라고 설명했습니다.
setCount는 비동기로 동작합니다. 호출 즉시 값이 바뀌는 것이 아니라 React가 다음 렌더링 사이클에 업데이트를 처리합니다.
마치 편지를 우체통에 넣으면 바로 도착하지 않고 배달되기까지 시간이 걸리는 것과 같습니다. 따라서 setCount 직후에 count를 읽으면 아직 옛날 값입니다.
다음 코드를 살펴봅시다.
import React, { useState, useEffect } from 'react';
function AsyncStateExample() {
const [count, setCount] = useState(0);
// 잘못된 패턴: 업데이트 직후 값을 읽으려고 함
const handleClickWrong = () => {
setCount(10);
console.log(count); // 0이 출력됨 (아직 업데이트 전)
alert(`카운트: ${count}`); // 옛날 값 표시됨
};
// 올바른 패턴 1: 변수에 담아서 사용
const handleClickCorrect = () => {
const newValue = 10;
setCount(newValue);
console.log(newValue); // 10이 출력됨
alert(`카운트: ${newValue}`);
};
// 올바른 패턴 2: useEffect로 변경 감지
useEffect(() => {
console.log(`count가 변경됨: ${count}`);
}, [count]);
return (
<div>
<p>현재 카운트: {count}</p>
<button onClick={handleClickCorrect}>업데이트</button>
</div>
);
}
김개발 씨는 점점 자신감이 붙었습니다. 이제 setCount를 자유자재로 사용할 수 있을 것 같았습니다.
그런데 디버깅을 하다가 이상한 현상을 발견했습니다. 버튼을 클릭하면 count를 10으로 바꾸고, 그 값을 alert로 띄우는 간단한 코드였습니다.
그런데 alert에는 0이 표시됐습니다. 분명히 setCount(10)을 호출했는데!
김개발 씨는 혼란스러웠습니다. "내가 뭘 잘못한 거지?" 코드를 여러 번 확인해봐도 문제를 찾을 수 없었습니다.
비동기라는 개념 박시니어 씨가 설명했습니다. "setCount는 비동기로 동작해요.
즉시 값을 바꾸는 게 아니라 예약을 해두는 거죠." 비동기라는 단어가 낯설게 느껴질 수 있습니다. 쉽게 비유해봅시다.
여러분이 온라인 쇼핑몰에서 물건을 주문했다고 생각해보세요. 주문 버튼을 누르는 순간 물건이 즉시 집에 도착하나요?
아닙니다. 주문이 접수되고, 포장되고, 배송되는 과정을 거쳐야 합니다.
setCount도 마찬가지입니다. 호출하는 순간 "값을 바꿔주세요"라고 요청하는 것이지, 그 자리에서 즉시 바뀌는 것이 아닙니다.
React는 성능 최적화를 위해 여러 상태 업데이트를 모아서 한 번에 처리합니다. 이것을 배치 처리라고 합니다.
마치 우체국이 편지를 하나씩 배달하지 않고 모아서 한 번에 배달하는 것과 같습니다. 왜 이렇게 설계했을까요? 초보 개발자들은 "그냥 즉시 바꾸면 안 돼?"라고 생각할 수 있습니다.
하지만 즉시 바꾸면 심각한 성능 문제가 생깁니다. 만약 한 함수 안에서 setState를 열 번 호출한다면?
화면을 열 번 다시 그려야 합니다. 이것은 엄청난 낭비입니다.
React는 이런 업데이트를 모아서 한 번만 화면을 다시 그립니다. 사용자 입장에서는 훨씬 부드럽고 빠른 경험을 하게 됩니다.
개발자 입장에서는 비동기 특성을 이해하고 올바르게 사용해야 합니다. 코드 분석: 잘못된 패턴 위 코드의 handleClickWrong 함수를 보겠습니다.
setCount(10)을 호출한 직후 console.log(count)를 실행합니다. 이 시점에 count는 무엇일까요?
답은 "아직 옛날 값"입니다. setCount는 값 변경을 예약만 했을 뿐, 아직 실제로 바뀌지 않았기 때문입니다.
이것은 초보 개발자들이 가장 흔히 하는 실수 중 하나입니다. "분명히 바꿨는데 왜 안 바뀌죠?"라고 질문하는 경우 대부분 이 문제입니다.
올바른 해결 방법 1: 변수 활용 가장 간단한 해결책은 새로운 값을 변수에 담는 것입니다. handleClickCorrect 함수를 보면, newValue라는 변수에 10을 저장하고, setCount와 console.log 모두에서 이 변수를 사용합니다.
이렇게 하면 문제가 해결됩니다. 새 값을 이미 알고 있다면 count를 읽을 필요가 없습니다.
그냥 그 값을 직접 사용하면 됩니다. 올바른 해결 방법 2: useEffect 활용 만약 "count가 바뀔 때마다 뭔가를 해야 한다"면 어떻게 할까요?
이럴 때는 useEffect 훅을 사용합니다. useEffect의 의존성 배열에 count를 넣으면, count가 변경될 때마다 useEffect 안의 코드가 실행됩니다.
이때는 이미 업데이트가 완료된 최신 값을 읽을 수 있습니다. useEffect는 "렌더링이 완료된 후에 실행되는 함수"라고 이해하면 됩니다.
따라서 상태 변경 후의 작업을 처리하기에 적합합니다. 실무 함정: API 호출 타이밍 실무에서 이 비동기 특성 때문에 버그가 생기는 경우가 많습니다.
예를 들어 사용자가 설정을 변경하면 서버에 저장하는 기능을 만든다고 합시다. 이런 코드를 작성하면 문제가 생깁니다: ```javascript setSetting(newValue); api.saveSetting(setting); // 아직 옛날 값!
올바른 방법은 newValue를 직접 API에 전달하거나, useEffect를 사용하는 것입니다. **또 다른 함정: 조건문 처리** 이런 코드도 위험합니다: ```javascript setCount(count + 1); if (count > 10) { alert('10을 넘었습니다!'); } ``` setCount를 호출했지만 count는 아직 옛날 값입니다.
따라서 조건문은 새 값이 아닌 옛날 값으로 판단합니다. 이것도 newValue 변수를 활용하거나 useEffect를 사용해야 합니다.
**김개발 씨의 이해** 박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, 그래서 alert에 옛날 값이 나온 거군요.
setState는 즉시 실행이 아니라 예약이었네요." "맞아요. 이것을 이해하지 못하면 정말 이상한 버그를 만나게 돼요.
항상 비동기라는 점을 기억하세요." 김개발 씨는 코드를 수정했습니다. 이번에는 newValue 변수를 사용해서 문제가 깔끔하게 해결됐습니다.
setState의 비동기 특성은 처음에는 혼란스럽지만, 이해하고 나면 React의 성능 최적화를 위한 현명한 설계라는 것을 알게 됩니다.
**실전 팁**
💡 - setCount 직후에 count를 읽으면 아직 옛날 값입니다
- 새 값이 필요하면 변수에 담아서 사용하세요
- 상태 변경 후 작업은 useEffect를 활용하세요
---
## 5. 자주 하는 실수와 해결법
김개발 씨가 한 달 동안 React로 프로젝트를 진행했습니다. 그동안 setCount와 관련해서 여러 실수를 했고, 그때마다 선배에게 도움을 받았습니다.
이제 그 실수들을 정리해보기로 했습니다. 다른 초보 개발자들도 같은 실수를 하지 않기를 바라면서요.
useState와 setCount를 사용할 때 초보 개발자들이 흔히 하는 **대표적인 실수 패턴**들을 정리합니다. 직접 변수 수정, 객체/배열 직접 변경, 무한 루프, 렌더링 중 호출 등 실무에서 자주 마주치는 함정들과 올바른 해결 방법을 배웁니다.
다음 코드를 살펴봅시다.
```javascript
import React, { useState } from 'react';
function CommonMistakes() {
const [count, setCount] = useState(0);
const [user, setUser] = useState({ name: '김개발', age: 25 });
// 실수 1: 직접 변수 수정 (잘못됨)
const wrongWay = () => {
count = count + 1; // 화면이 안 바뀜!
};
// 실수 2: 객체 직접 수정 (잘못됨)
const wrongObjectUpdate = () => {
user.age = 26; // React가 변경을 감지 못함
setUser(user);
};
// 올바른 방법: 새 객체 생성
const correctObjectUpdate = () => {
setUser({ ...user, age: 26 }); // 스프레드 연산자로 새 객체 생성
};
// 실수 3: 렌더링 중 호출 (무한 루프 발생!)
// setCount(count + 1); // 이렇게 작성하면 안 됨!
return (
<div>
<p>카운트: {count}</p>
<p>이름: {user.name}, 나이: {user.age}</p>
<button onClick={correctObjectUpdate}>나이 증가</button>
</div>
);
}
한 달 동안 김개발 씨는 정말 다양한 실수를 했습니다. 어떤 실수는 에러 메시지가 나와서 금방 알아챘지만, 어떤 실수는 조용히 버그를 만들어냈습니다.
코드 리뷰 시간에 박시니어 씨가 말했습니다. "김개발 씨, 이런 실수들은 누구나 하는 거예요.
중요한 건 같은 실수를 반복하지 않는 거죠. 정리해봅시다." 실수 1: 직접 변수를 수정하는 것 가장 흔한 실수는 count를 직접 수정하려는 시도입니다.
count = count + 1 또는 count++ 같은 코드를 작성하는 것입니다. JavaScript에서 일반 변수는 이렇게 값을 바꾸니까 당연해 보입니다.
하지만 useState로 만든 상태는 다릅니다. count는 읽기 전용입니다.
직접 수정하면 에러가 발생하거나(const로 선언됐기 때문에), 에러가 나지 않더라도 React가 변경을 감지하지 못해서 화면이 업데이트되지 않습니다. 반드시 setCount를 통해서만 값을 변경해야 합니다.
이것은 React의 철칙입니다. useState의 첫 번째 반환값은 절대 직접 수정하지 마세요.
실수 2: 객체나 배열을 직접 수정하는 것 count처럼 숫자나 문자열은 실수하기 어렵습니다. 어차피 직접 수정이 안 되니까요.
하지만 객체나 배열은 함정이 있습니다. user.age = 26 같은 코드는 문법적으로 가능합니다.
실제로 user 객체의 age 값이 바뀝니다. 그런데 화면은?
바뀌지 않습니다! React는 참조 비교로 변경을 감지합니다.
즉, 객체나 배열이 "다른 객체인지"를 확인합니다. user 객체 내부의 값만 바꾼다고 해서 React는 "아, 바뀌었구나"라고 인식하지 못합니다.
여전히 같은 객체니까요. 올바른 방법은 새로운 객체를 만드는 것입니다.
스프레드 연산자 ...를 사용하면 쉽게 복사할 수 있습니다. { ...user, age: 26 }은 "user의 모든 속성을 복사하되, age만 26으로 바꾼 새 객체"를 만듭니다.
실수 3: 렌더링 도중 setState 호출 이것은 정말 위험한 실수입니다. 무한 루프를 만들어냅니다.
컴포넌트 함수 본문에 직접 setCount를 호출하면 어떻게 될까요? 컴포넌트가 렌더링되면서 setCount가 실행되고, setCount가 실행되면 다시 렌더링이 되고, 다시 setCount가 실행되고...
끝없이 반복됩니다. 브라우저가 멈추고, 콘솔에는 "Maximum update depth exceeded" 같은 에러 메시지가 나타납니다.
컴퓨터가 뜨거워지고 팬이 빙빙 돌기 시작합니다. setState는 이벤트 핸들러 안에서 호출하거나, useEffect 같은 훅 안에서 조건부로 호출해야 합니다.
절대 컴포넌트 본문에 직접 작성하면 안 됩니다. 실수 4: 연속 호출을 잘못 사용하는 것 앞에서 배운 내용이지만 다시 한번 강조합니다.
javascript setCount(count + 1); setCount(count + 1); setCount(count + 1); 이 코드는 count를 3 증가시키지 않습니다. 1만 증가시킵니다.
세 번 모두 같은 count 값을 읽어서 계산하기 때문입니다. 올바른 방법은 함수형 업데이트입니다: javascript setCount(prev => prev + 1); setCount(prev => prev + 1); setCount(prev => prev + 1); 이제는 정확히 3이 증가합니다.
실수 5: setState를 동기적이라고 착각하는 것 앞 카드에서 배운 내용입니다만, 너무 중요해서 다시 언급합니다. javascript setCount(10); if (count === 10) { console.log('10입니다!'); } 이 코드에서 if문은 실행되지 않습니다.
setCount가 비동기로 동작하기 때문입니다. 실무에서 겪은 김개발 씨의 사례 김개발 씨는 이커머스 프로젝트에서 장바구니 기능을 만들다가 이런 버그를 만들었습니다.
javascript cart.push(newItem); // 배열에 직접 추가 setCart(cart); // 화면이 안 바뀜! 한 시간 동안 머리를 쥐어뜯다가 결국 선배에게 도움을 요청했습니다.
선배는 한눈에 문제를 알아챘습니다. "배열을 직접 수정하면 안 돼요.
새 배열을 만들어야죠." 올바른 코드는 이렇습니다: javascript setCart([...cart, newItem]); // 새 배열 생성 또 다른 사례: 폼 입력 처리 사용자 정보 폼을 만들 때도 실수가 있었습니다. javascript userInfo.name = e.target.value; setUserInfo(userInfo); 이것도 객체를 직접 수정한 경우입니다.
올바른 방법은: javascript setUserInfo({ ...userInfo, name: e.target.value }); 정리하는 시간 박시니어 씨가 정리해줬습니다. "이런 실수들은 React를 처음 배울 때 누구나 합니다.
중요한 원칙을 기억하세요." 첫째, 상태는 항상 setState 함수로만 변경합니다. 직접 수정 금지.
둘째, 객체와 배열은 새로 만들어서 전달합니다. 내부만 수정하면 React가 감지 못함.
셋째, 렌더링 도중 setState 호출 금지. 무한 루프 주의.
넷째, 연속 업데이트는 함수형으로. 이전 값 기반 계산.
다섯째, setState는 비동기. 직후에 값을 읽지 말 것.
김개발 씨는 이 원칙들을 노트에 적어두었습니다. 앞으로는 같은 실수를 하지 않을 거라고 다짐했습니다.
실수를 통해 배우는 것이 가장 확실한 학습 방법입니다. 여러분도 이런 실수를 하게 되더라도 좌절하지 마세요.
그것이 성장하는 과정입니다.
실전 팁
💡 - 상태는 절대 직접 수정하지 말고 항상 setState를 사용하세요
- 객체/배열은 스프레드 연산자로 새로 만들어서 전달하세요
- 렌더링 중 setState를 호출하면 무한 루프가 발생합니다
6. 실전 활용 팁
김개발 씨가 이제 setCount의 기본은 마스터했습니다. 선배가 "이제 실무에서 유용한 고급 팁들을 알려드릴게요"라고 했습니다.
이 팁들은 코드를 더 깔끔하고 효율적으로 만들어줄 것입니다.
setCount를 실무에서 효과적으로 활용하는 고급 패턴들을 배웁니다. 여러 상태를 동시에 관리하는 방법, 복잡한 상태 로직을 단순화하는 기법, 성능 최적화 전략 등 실전에서 바로 써먹을 수 있는 실용적인 팁들을 소개합니다.
다음 코드를 살펴봅시다.
import React, { useState, useCallback } from 'react';
function AdvancedTips() {
const [count, setCount] = useState(0);
// 팁 1: 초기값을 함수로 전달 (무거운 계산일 때)
const [expensiveState, setExpensiveState] = useState(() => {
console.log('초기값 계산 - 최초 한 번만 실행됨');
return Array(1000).fill(0).reduce((a, b) => a + b, 0);
});
// 팁 2: 여러 상태를 한 번에 업데이트
const updateMultiple = () => {
setCount(prev => prev + 1); // 배치 처리됨
// 다른 setState들도 함께 처리
};
// 팁 3: useCallback으로 핸들러 최적화
const increment = useCallback(() => {
setCount(prev => prev + 1);
}, []); // 의존성이 없으므로 함수가 재생성 안 됨
// 팁 4: 조건부 업데이트
const incrementIfEven = () => {
setCount(prev => {
if (prev % 2 === 0) {
return prev + 1;
}
return prev; // 조건 불만족 시 현재 값 반환
});
};
return (
<div>
<p>카운트: {count}</p>
<button onClick={increment}>증가</button>
<button onClick={incrementIfEven}>짝수일 때만 증가</button>
</div>
);
}
김개발 씨는 이제 setCount의 기본을 완전히 이해했습니다. 하지만 실무 프로젝트를 진행하면서 "좀 더 효율적으로 할 방법은 없을까?"라는 생각이 들었습니다.
박시니어 씨가 옆에 앉더니 말했습니다. "기본은 마스터했으니 이제 실전 팁들을 알려드릴게요.
이걸 알면 코드가 훨씬 프로페셔널해집니다." 팁 1: 초기값을 함수로 전달하기 useState의 초기값으로 무거운 계산이 필요한 경우가 있습니다. 예를 들어 로컬스토리지에서 데이터를 읽어오거나, 복잡한 계산을 해야 한다고 가정해봅시다.
useState(localStorage.getItem('key'))처럼 작성하면 매번 렌더링할 때마다 이 계산이 실행됩니다. 비효율적입니다.
해결책은 함수를 전달하는 것입니다. useState(() => localStorage.getItem('key')) 이렇게 작성하면 함수는 최초 한 번만 실행됩니다.
이후 렌더링에서는 실행되지 않습니다. 이것을 게으른 초기화라고 부릅니다.
무거운 작업은 꼭 필요할 때만 한 번만 하는 것이 효율적입니다. 팁 2: 여러 상태를 동시에 업데이트하기 실무에서는 한 이벤트로 여러 상태를 업데이트해야 하는 경우가 많습니다.
다행히 React 18부터는 이벤트 핸들러 안에서 여러 setState를 호출해도 자동으로 배치 처리됩니다. 즉, 한 번만 렌더링이 발생합니다.
javascript const handleSubmit = () => { setName('김개발'); setAge(25); setEmail('kim@example.com'); // 세 개가 모두 처리된 후 한 번만 렌더링 }; 이전 버전의 React에서는 이게 보장되지 않았지만, 이제는 걱정할 필요가 없습니다. 성능을 위해 React가 알아서 최적화해줍니다.
팁 3: useCallback으로 핸들러 최적화 자식 컴포넌트에 이벤트 핸들러를 전달할 때 성능 이슈가 생길 수 있습니다. 매번 렌더링할 때마다 새로운 함수가 만들어지면, 자식 컴포넌트는 "props가 바뀌었네!"라고 인식해서 불필요하게 다시 렌더링됩니다.
useCallback 훅을 사용하면 함수를 메모이제이션할 수 있습니다. 의존성이 바뀌지 않는 한 같은 함수 인스턴스를 재사용합니다.
위 코드의 increment 함수는 의존성 배열이 비어있습니다. 따라서 컴포넌트가 아무리 재렌더링되어도 항상 같은 함수입니다.
자식 컴포넌트에 전달해도 불필요한 재렌더링이 발생하지 않습니다. 팁 4: 조건부 업데이트 때로는 특정 조건에서만 상태를 업데이트하고 싶을 때가 있습니다.
incrementIfEven 함수를 보세요. 함수형 업데이트 안에서 if문을 사용합니다.
조건을 만족하면 새 값을 반환하고, 아니면 현재 값을 그대로 반환합니다. 현재 값을 반환하면 React는 "변화가 없네?"라고 판단해서 렌더링을 스킵합니다.
이것도 성능 최적화의 한 방법입니다. 팁 5: 상태 업데이트 로직 분리하기 복잡한 상태 업데이트 로직은 별도 함수로 분리하면 코드가 깔끔해집니다.
javascript const calculateNextCount = (prev) => { if (prev < 0) return 0; if (prev > 100) return 100; return prev + 1; }; setCount(calculateNextCount); 이렇게 하면 비즈니스 로직을 테스트하기도 쉽고, 재사용도 가능합니다. 실무 사례: 폼 유효성 검사 회원가입 폼을 만든다고 가정해봅시다.
사용자가 입력할 때마다 유효성을 검사해야 합니다. javascript const [email, setEmail] = useState(''); const [isValid, setIsValid] = useState(false); const handleEmailChange = (e) => { const newEmail = e.target.value; setEmail(newEmail); setIsValid(newEmail.includes('@')); // 간단한 검사 }; 이 코드는 두 개의 상태를 동시에 업데이트합니다.
React가 자동으로 배치 처리해서 한 번만 렌더링됩니다. 실무 사례: 장바구니 총액 계산 장바구니에서 수량을 바꾸면 총액도 자동으로 계산되어야 합니다.
javascript const [quantity, setQuantity] = useState(1); const [total, setTotal] = useState(0); const handleQuantityChange = (newQty) => { setQuantity(newQty); setTotal(newQty * price); // 동시에 총액도 업데이트 }; 하지만 사실 이런 경우에는 total을 상태로 관리할 필요가 없습니다. quantity에서 계산하면 되니까요.
이것을 파생 상태라고 합니다. 더 나은 방법: javascript const [quantity, setQuantity] = useState(1); const total = quantity * price; // 계산된 값, 상태 아님 필요없는 상태를 만들지 마세요.
계산할 수 있으면 계산하는 게 낫습니다. 팁 6: 디버깅 팁 개발하다가 "왜 화면이 안 바뀌지?"라고 고민될 때가 있습니다.
useEffect를 활용해서 상태 변경을 로깅하면 디버깅이 쉬워집니다. javascript useEffect(() => { console.log('count 변경됨:', count); }, [count]); 이렇게 하면 count가 정말 바뀌는지, 언제 바뀌는지 명확히 알 수 있습니다.
김개발 씨의 성장 박시니어 씨의 팁들을 듣고 김개발 씨는 감탄했습니다. "와, 이런 것까지 신경 쓸 수 있구나!" "맞아요.
기본을 이해한 후에 이런 최적화 기법들을 하나씩 적용하면 코드 품질이 확 올라갑니다. 처음부터 다 완벽하게 할 필요는 없어요.
필요할 때 하나씩 배워가면 됩니다." 김개발 씨는 이제 자신감이 생겼습니다. setCount를 사용하는 것은 물론이고, 성능까지 고려한 코드를 작성할 수 있게 됐습니다.
여러분도 이 팁들을 실제 프로젝트에 적용해보세요. 처음에는 기본만 사용하다가, 프로젝트가 커지면서 필요에 따라 고급 기법들을 도입하면 됩니다.
실전 팁
💡 - 무거운 초기값 계산은 함수로 전달해서 한 번만 실행되게 하세요
- 여러 setState는 자동으로 배치 처리되니 걱정하지 마세요
- 자식 컴포넌트에 전달하는 핸들러는 useCallback으로 최적화하세요
- 파생 상태는 별도로 관리하지 말고 계산해서 사용하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
서비스 메시 완벽 가이드
마이크로서비스 간 통신을 안전하고 효율적으로 관리하는 서비스 메시의 핵심 개념부터 실전 도입까지, 초급 개발자를 위한 완벽한 입문서입니다. Istio와 Linkerd 비교, 사이드카 패턴, 실무 적용 노하우를 담았습니다.
EFK 스택 로깅 완벽 가이드
마이크로서비스 환경에서 로그를 효과적으로 수집하고 분석하는 EFK 스택(Elasticsearch, Fluentd, Kibana)의 핵심 개념과 실전 활용법을 초급 개발자도 쉽게 이해할 수 있도록 정리한 가이드입니다.
Grafana 대시보드 완벽 가이드
실시간 모니터링의 핵심, Grafana 대시보드를 처음부터 끝까지 배워봅니다. Prometheus 연동부터 알람 설정까지, 초급 개발자도 쉽게 따라할 수 있는 실전 가이드입니다.
분산 추적 완벽 가이드
마이크로서비스 환경에서 요청의 전체 흐름을 추적하는 분산 추적 시스템의 핵심 개념을 배웁니다. Trace, Span, Trace ID 전파, 샘플링 전략까지 실무에 필요한 모든 것을 다룹니다.
CloudFront CDN 완벽 가이드
AWS CloudFront를 활용한 콘텐츠 배포 최적화 방법을 실무 관점에서 다룹니다. 배포 생성부터 캐시 설정, HTTPS 적용까지 단계별로 알아봅니다.