먼저 우리가 익숙한 방식으로 1초마다 증가하는 타이머를 만들어봅시다.
<!DOCTYPE html>
<html>
<body>
<div id="display">0</div>
<script>
let count = 0;
const display = document.querySelector("#display");
// 1초마다 실행
setInterval(() => {
count++;
display.textContent = count;
}, 1000);
</script>
</body>
</html>
✅ 이 방식은 잘 작동합니다! Session 4까지는 이렇게 했었죠.
같은 코드를 React로 옮기면... 문제가 생깁니다!
"use client";
import { useState } from "react";
export default function Timer() {
const [count, setCount] = useState(0);
// ❌ 이렇게 하면 안 됨!
setInterval(() => {
setCount(count + 1);
}, 1000);
return <div>{count}</div>;
}
setInterval이 실행됨 (타이머 시작)setInterval 실행 (타이머가 또 시작!)
타이머는 useEffect 안에 넣어야 합니다.
"use client";
import { useState, useEffect } from "react";
export default function Timer() {
const [count, setCount] = useState(0);
// ✅ useEffect 안에 넣기!
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
// 정리: 컴포넌트 사라질 때 타이머 멈추기
return () => clearInterval(timer);
}, []); // ← 빈 배열: 처음 한 번만 실행
return (
<div className="text-5xl font-bold text-center p-8">
{count}
</div>
);
}
useEffect(() => {
// 실행할 코드
return () => {
// 정리 코드 (선택사항)
};
}, []); // ← 빈 배열: 처음 한 번만 실행
[] 붙이기 → 처음 한 번만 실행
return () => {} → 정리 작업 (타이머 멈추기 등)
빈 배열 []은 "이 코드를 처음 한 번만 실행하세요"라는 의미입니다.
만약 없으면 매번 렌더링할 때마다 실행되어 문제가 생깁니다.
<script>
let count = 0;
setInterval(() => {
count++;
display.textContent = count;
}, 1000);
</script>
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
return () => clearInterval(timer);
}, []);
useEffect 안에
[] 붙이기
return () => clearInterval()로 정리
[] 빠뜨리면 무한 렌더링!