react useeffect 두번 실행 문제 원인과 실무 해결 가이드
react useeffect 두번 실행 이유와 실무 해결법을 정리합니다. 개발환경과 StrictMode 영향, 의존성 관리, useRef 가드 패턴, 예제 코드와 체크리스트로 바로 적용하세요.
react useeffect 두번 실행이 필요한 상황
react useeffect 두번 실행 문제의 원인과 실무 적용 방법을 정리합니다. 이 글을 통해 개발자는 StrictMode에 따른 개발환경 이중 실행, 의존성 배열 관리, 부작용 중복 방지 패턴(useRef 등), 그리고 배포 환경에서의 차이를 이해하고 실제 코드에 바로 적용할 수 있습니다.
react useeffect 두번 실행 핵심 개념
React의 useEffect는 컴포넌트 렌더링 후 부작용을 수행하기 위한 훅입니다. React 18부터 개발 모드에서 StrictMode가 활성화된 경우 의도적으로 mount/unmount를 반복해 사이드 이펙트가 순수한지 확인합니다. 이로 인해 useEffect 내부 로직이 개발 환경에서 두 번 실행되는 것처럼 보일 수 있습니다. 중요한 포인트:
- 프로덕션(배포) 빌드에서는 StrictMode의 이중 실행이 발생하지 않습니다.
- 의존성 배열([] 포함)에 따라 effect가 언제 다시 실행되는지 결정됩니다.
- cleanup 함수를 적절히 작성해야 메모리 누수나 중복 호출을 방지할 수 있습니다.
react useeffect 두번 실행 실무 예제
아래 예제는 개발 환경에서 useEffect가 두 번 실행되는 상황과 이를 방지하는 대표적 패턴을 보여줍니다.
// 예제: useEffect가 두 번 실행되는 상황과 useRef 가드 적용 import React, { useEffect, useRef } from 'react'; function FetchOnceComponent() { const ranRef = useRef(false); useEffect(() => { if (ranRef.current) return; // 이미 실행되었으면 중복 방지 ranRef.current = true; const controller = new AbortController(); async function fetchData() { try { const res = await fetch('/api/data', { signal: controller.signal }); const json = await res.json(); console.log('data', json); } catch (e) { if (e.name !== 'AbortError') console.error(e); } } fetchData(); return () => { controller.abort(); }; }, []); return <div>FetchOnceComponent</div>; } export default FetchOnceComponent;
위 코드는 useRef 가드로 개발환경에서 발생하는 이중 실행을 방지합니다. 또한 fetch에 AbortController를 사용해 cleanup을 명확히 하여 네트워크 요청이 남지 않도록 했습니다.
언제 쓰면 좋고 언제 피해야 할까
언제 쓰면 좋은가
- 개발 모드에서 StrictMode로 인해 effect가 두 번 실행되지만 실제 요청을 한 번만 보내고 싶을 때
- 외부 API 호출이나 초기화 작업을 중복으로 방지해야 할 때
- 상태 초기화가 부작용 없이 한 번만 실행되어야 하는 경우
언제 피해야 하는가
- effect가 의도적으로 매 렌더마다 실행되어야 하는 경우(예: 렌더 카운트 측정)
- 상태 동기화가 의존성 변경에 따라 반복되어야 하는 경우
- 로직이 idempotent하지 않거나 재시도/복구 로직이 필요할 때 (단순 가드로 문제를 숨기면 안 됨)
비교표: 해결 패턴 판단 기준
| 구분 | 항목1 | 항목2 | 항목3 |
|---|---|---|---|
| 내용 | 가드 패턴(useRef) | cleanup + AbortController | 의존성 정확히 지정 |
| 내용 | 개발 환경 이중 실행 방지에 효과적 | 네트워크/비동기 작업 안전 종료 | 불필요한 재실행 방지 |
| 내용 | 간단하고 빠르게 적용 가능 | 리소스 누수를 막음 | 로직 정확성 향상 |
실무 체크리스트
- 개발 모드에서 React.StrictMode 사용 여부 확인
- useEffect 의존성 배열이 정확한지 검토 (누락된 값이 없는가?)
- 비동기 작업에 AbortController 또는 cleanup 함수 적용
- useRef 또는 플래그로 중복 실행 제어가 필요한지 판단
- 프로덕션(번들) 빌드에서 동작 차이 확인
- idempotent하지 않은 부작용은 별도 로직으로 처리
성능·보안·운영 주의점
성능
- 중복 API 호출은 네트워크 비용과 서버 부하를 증가시킵니다. 개발 중에만 이중 호출이 발생하는지, 프로덕션에서도 발생하는지 확인하세요.
- useRef 가드 등으로 중복 호출을 막아도 렌더 수가 급증하는 컴포넌트 구조는 리렌더 최적화가 필요합니다.
보안
- 민감한 초기화(토큰 설정, 로그아웃 등)를 useEffect에서 다룰 때 중복 실행으로 인한 상태 불일치가 생기지 않도록 주의하세요.
- 네트워크 요청에 대한 에러 처리를 철저히 해 중간자 공격 등을 통한 예외 상태를 남기지 마세요.
운영
- 프로덕션과 개발 환경에서 동작 차이를 배포 전 테스트하세요. StrictMode는 개발 전용이며 배포 빌드에서는 이중 실행이 일어나지 않습니다.
- 로그가 중복으로 쌓이는 경우 모니터링과 비용에 영향을 줍니다. 로그 레벨 설정을 고려하세요.
실무 예제: 의존성 배열 실수로 인한 재실행 케이스
// 잘못된 예: 객체를 매번 새로 생성하면 의존성이 변한 것으로 인식 useEffect(() => { // some effect }, [{ key: 'value' }]); // 매 렌더마다 새 객체 -> effect 재실행 // 개선: 상위에서 객체를 memoize하거나 primitive 값 사용 const deps = useMemo(() => ({ key: 'value' }), []); useEffect(() => { // some effect }, [deps]);
자주 묻는 질문
Q: 개발 환경에서만 useEffect가 두 번 실행되는데 배포 안전한가요?
A: 네. React 18의 StrictMode 이중 실행은 개발 모드에서만 발생합니다. 프로덕션 빌드에서는 이 행동이 없습니다. 다만 개발 중에 중복 호출로 인해 로직 오류를 발견할 수 있으니 가드 패턴을 적용하거나 의존성을 점검하세요.
Q: useRef 가드 대신 상태(state)를 써도 되나요?
A: 상태를 사용하면 리렌더를 발생시킬 수 있어 가드 목적에는 적합하지 않습니다. useRef는 렌더링에 영향을 주지 않으므로 중복 실행 방지용으로 더 적합합니다.
Q: cleanup 함수는 항상 필요한가요?
A: 비동기 작업, 타이머, 이벤트 리스너 등 리소스를 사용하는 effect에는 반드시 cleanup을 작성해 메모리 누수와 중복 동작을 방지해야 합니다.
Q: StrictMode가 문제라면 비활성화해도 될까요?
A: 권장하지 않습니다. StrictMode는 잠재적 문제를 조기에 발견하게 해줍니다. 대신 부작용을 idempotent하게 만들고 테스트를 통해 해결하는 것이 좋습니다.
정리
- React 개발 모드의 StrictMode 때문에 useEffect가 두 번 실행되는 현상을 이해하자.
- 의존성 배열을 정확히 관리하고 cleanup을 항상 구현하자.
- 중복 실행이 문제라면 useRef 가드나 idempotent 설계로 해결하자.
- 프로덕션 환경과 개발 환경의 동작 차이를 항상 검증하자.
- 성능·보안·운영 관점에서 네트워크 요청과 로그 중복을 주의하자.