왜 내 API가 두 번 호출될까? 왜 내 API가 두 번 호출될까?

송연지·5일 전
0

트러블슈팅

목록 보기
32/32
post-thumbnail

React로 개발하다 보면, 분명히 useEffect에 한 번만 API를 넣었는데…

API 요청 로그가 두 번씩 찍히는 현상을 경험하신 적 있으신가요?

저도 Electron + React 기반 사내 프로젝트를 하다가,

“어? 내 코드가 잘못된 건가? 서버가 중복 응답을 주는 건가?” 하고 한참을 헤맸습니다.

결론부터 말씀드리면, 이건 React StrictMode 때문입니다.

제 삽질 경험을 토대로, 왜 이런 일이 일어나는지, 배포에서는 어떤지,

그리고 제가 얻은 교훈까지 정리해보겠습니다.


1. StrictMode란 무엇인가요?

React의 StrictMode개발 모드에서만 실행되는 검증 도구입니다.

  • 코드에서 안전하지 않은 패턴을 찾고,
  • 메모리 누수 가능성이 있는 코드를 조기에 발견하고,
  • 사이드 이펙트(side effect) 실행 과정을 검증하기 위해

React가 일부러 컴포넌트를 더 빡세게 실행합니다.

왜 API 요청도 두 번 갈까?

StrictMode는 단순히 렌더링만 두 번 하는 게 아니라,

useEffect 같은 부작용(side effect) 코드도 마운트 → 클린업 → 다시 마운트 흐름으로 반복 실행합니다.

즉, 코드가 이렇게 생겼다면:

useEffect(() => {
  fetchApi(); // API 호출
}, []);

실행 순서는 이렇습니다:

  1. 첫 번째 마운트 → fetchApi() 실행 → API 요청 1회
  2. 언마운트 & 클린업
  3. 두 번째 마운트 → 다시 fetchApi() 실행 → API 요청 1회

👉 결과적으로 API 요청이 2번 전송됩니다.

즉, 내 코드에는 문제가 없고, React가 일부러 그렇게 하는 겁니다.

개발 모드 vs 배포 모드

  • 개발 모드 (npm run start / vite dev 등) StrictMode가 활성화되어 API 요청이 두 번 갑니다.
  • 배포 모드 (npm run build 후 실행, electron-builder 배포 등) StrictMode 자체가 제거됩니다. 따라서 API는 정상적으로 한 번만 호출됩니다.

저도 electron-builder로 패키징해서 실행했을 때는,

로그에 요청이 한 번만 찍히는 걸 확인했습니다.

개발할 땐 두 번, 배포하면 한 번 → 이게 정상입니다.


2. 중복 호출을 막는 방법

개발 모드에서 로그가 두 번 찍히는 게 불편하다면,

간단히 useRef를 써서 가드를 걸 수 있습니다.

const hasFetchedRef = useRef(false);

useEffect(() => {
  if (hasFetchedRef.current) return;
  hasFetchedRef.current = true;

  fetchApi();
}, []);

이렇게 하면 개발 모드에서도 API는 한 번만 호출됩니다.

저는 보통, 조회 API는 두 번 호출돼도 상관없으니 그냥 두고,

중복 호출이 문제가 되는 등록/수정/승인 API만 이런 방어 로직을 넣었습니다.

페이지 클릭 시 조회 API 호출

Electron 프로젝트에서는 페이지 이동(라우터 클릭)마다 조회 API가 다시 호출되는데,

여기에 StrictMode까지 더해지니 “응답이 두 개씩 오는 것처럼” 보여서 헷갈리더군요.

실제로 로그는 이렇게 찍힙니다:

[API ▶ getCpnMember 요청] { cpnId: 2, userId: 2 }
[API ◀ getCpnMember 응답] { result: { pscd: 'OK', data: [], pcsRsltMsg: '응답을 보냈습니다.' } }
[API ◀ getCpnMember 응답] { result: { pscd: 'OK', data: [], pcsRsltMsg: '응답을 보냈습니다.' } }

처음엔 “응답이 두 번 오네?”라고 생각했지만, 사실은 요청을 두 번 보낸 거였어요.

이것도 StrictMode의 영향으로, 배포 모드에선 정상적으로 한 번만 호출됩니다.

다만 페이지 이동 시마다 조회 API가 자동으로 불리는 구조라면,

캐시나 상태 관리 도구를 붙여서 불필요한 호출을 줄이는 게 좋습니다.


3. React Query / SWR 같은 라이브러리 쓰기

이런 문제를 더 깔끔하게 해결하고 싶다면,

React QuerySWR 같은 데이터 관리 라이브러리를 추천드립니다.

  • 같은 API를 여러 페이지에서 불러도 캐시에서 가져오기
  • 새로고침 버튼을 눌렀을 때만 refetch 실행
  • 상태/에러/로딩을 한 줄로 관리

저는 단순 조회 API는 React Query로 관리하고,

트랜잭션성(등록/삭제/승인) API만 직접 호출하도록 나누니 훨씬 편했습니다.

React Query / SWR이 해결해주는 방식

React Query와 SWR은 단순히 API 호출 라이브러리가 아니라, 데이터 캐싱 계층이에요.

이 캐싱 계층이 있으면 “같은 키(key)”로 요청이 들어올 때 다음과 같은 일이 일어납니다:

  1. 디듀플리케이션(Deduplication)
    • 동일한 API 요청이 짧은 시간 안에 여러 번 발생하더라도, 내부에서 “아 이건 같은 요청이네”라고 판단하고 한 번만 실제로 요청을 보냅니다.
    • 나머지는 캐시된 결과나 동일 Promise를 공유.
  2. 캐싱(Cache)
    • 한 번 받아온 데이터를 캐시에 저장하고, 동일한 키로 다시 조회하면 네트워크 요청 없이 즉시 캐시 데이터 반환.
    • refetch() 같은 명령을 내리면 그때만 새로 요청.
  3. 상태 관리 자동화
    • 로딩(isLoading), 성공(data), 에러(error) 상태를 자동으로 관리해 줘서, 개발자가 직접 loading state 만들고, try/catch 돌릴 필요가 없어져요.

구체적인 예시

// React Query 예시
const { data, isLoading, error } = useQuery(
  ['member', cpnId, userId], // 키
  () => getCpnMember({ cpnId, userId }) // API 호출
);

위 코드를 StrictMode 환경에서 두 번 실행한다고 가정해봅시다:

  • 일반 fetch 사용: 요청 2번 → 서버에도 2번 요청
  • React Query 사용: 요청은 2번 트리거되더라도, 내부에서 동일 키(['member', 2, 2])로 관리 → 실제 네트워크는 1번만 발생 두 번째 호출은 첫 번째 호출의 캐시/Promise를 재활용합니다.

SWR은 어떻게 다르냐?

SWR도 원리는 비슷한데, 차이점은 “자동 새로고침”과 “Stale-While-Revalidate” 전략이 강점이에요.

  • React Query: 좀 더 완전한 데이터 관리 플랫폼 (mutate, optimistic update 등 풍부)
  • SWR: 단순 조회 중심, 사용법이 직관적이고 lightweight

그래서 왜 좋은가?

개발 모드에서 StrictMode 때문에 네트워크 요청이 2번씩 찍히는 상황에서도:

  • 일반 fetch → 서버에 실제로 2번 요청 감
  • React Query / SWR → 서버엔 한 번만 요청 감 (개발자 입장에서는 로그만 2번 찍히고, 실제 요청은 캐시가 막아줌)

게다가 캐시·상태 관리까지 자동이니,

“개발 모드라 로그가 번잡해도 실제 서버 트래픽은 안정적으로 1회”라는 안심을 줍니다.

👉 React Query / SWR은 StrictMode 환경에서 API 중복 호출을 막아주는 안전장치 + 상태 관리 자동화 기능을 제공하기 때문에, “로그가 두 번 찍히는 게 헷갈려요”라는 문제와 “불필요한 서버 트래픽” 문제를 동시에 해결해줍니다.


제가 얻은 교훈

  1. 개발 모드에서 API가 두 번 호출되는 건 StrictMode의 정상 동작이다.
  2. 배포 모드에서는 정상적으로 한 번만 호출되니 걱정할 필요 없다.
  3. 불편하다면 useRef 가드로 중복 호출을 막을 수 있다.
  4. 페이지 이동 시마다 조회 API를 다시 호출하는 구조는 필요에 따라 최적화해야 한다.
  5. React Query / SWR 같은 라이브러리를 쓰면 중복 호출 문제를 훨씬 깔끔하게 관리할 수 있다.

저는 이 삽질을 통해, 이제는 API 로그가 두 번 찍혀도 당황하지 않습니다.

“아, StrictMode 때문이지~” 하고 바로 넘길 수 있게 되었죠. 😎

혹시 여러분도 비슷한 경험 있으신가요?

만약 API 로그가 두 번 찍힌다면, 이제 안심하셔도 됩니다.

여러분의 코드가 잘못된 게 아니라, React가 일부러 그런 거니까요!

profile
프론트엔드 개발쟈!!

0개의 댓글