Front-End

Safari에서 window.open이 차단되는 이유

나른한 노치 2026. 3. 5. 02:20

 

아래 코드 블럭은 이해를 돕기 위해 AI로 만든 예시 코드입니다.

 

최근 진행한 쿠폰 프로젝트에서 Safari 브라우저의 팝업 차단 이슈를 경험했습니다.

크롬에서는 정상적으로 동작하던 기능이었기 때문에 QA 과정에서 발견되었고, 원인을 파악해보니 브라우저의 사용자 이벤트 정책과 관련된 문제였습니다.


🧩 문제 상황

쿠폰 발급 여부를 확인하는 API를 호출한 뒤 조건이 충족되면 외부 웹사이트로 이동시키는 로직이 있었습니다.

초기 구현 코드는 다음과 같았습니다.

const handleClick = async () => {
  const isEligible = await checkCouponEligibility(); // 비동기 호출
  if (isEligible) {
    window.open('https://other-website-apply.com'); // 팝업
  }
};

이 코드는 다음 브라우저에서는 정상 동작했습니다.

  • Chrome
  • Edge
  • 대부분의 Chromium 기반 브라우저

하지만 Safari에서는 아무 반응 없이 차단되었습니다.

심지어 설정에 따라 팝업 차단 알림도 없이 동작 자체가 무시되는 경우도 있었습니다.


🔍 원인 분석

Safari는 보안 정책상 사용자 이벤트로 인식되지 않는 window.open 호출을 차단합니다.

즉 다음과 같은 흐름이 문제였습니다.

사용자 클릭
→ API 비동기 호출
→ window.open 실행

Safari는 이 과정을 다음처럼 해석합니다.

사용자 클릭
→ 비동기 처리 발생
→ 사용자 이벤트 컨텍스트 종료
→ window.open (사용자 이벤트 아님)

결과적으로 Safari는 이를 사용자 인터랙션이 아닌 팝업 호출로 판단하고 차단하게 됩니다.


🛠 해결 방법: React Query로 사전 조회

이 문제를 해결하기 위해 쿠폰 발급 여부를 페이지 진입 시점에 미리 조회하도록 구조를 변경했습니다.

React Query를 사용하여 데이터를 미리 가져오고, 클릭 시점에는 동기적으로 window.open이 실행되도록 수정했습니다.

import { useQuery } from '@tanstack/react-query';

const { data: isEligible } = useQuery({
  queryKey: ['coupon', userId],
  queryFn: () => checkCouponEligibility(userId),
});

const handleClick = () => {
  if (isEligible) {
    window.open('https://other-website-apply.com');
  }
};

이렇게 변경하면 흐름이 다음과 같이 바뀝니다.

페이지 진입
→ 쿠폰 여부 미리 조회

사용자 클릭
→ 동기적으로 window.open 실행

따라서 Safari에서도 정상적으로 팝업이 실행됩니다.


👍 이 방식의 장점

구조적으로도 몇 가지 장점이 있었습니다.

1️⃣ 여러 컴포넌트에서 재사용 가능

React Query 캐시를 활용하면

props drilling 없이

여러 컴포넌트에서 쿠폰 발급 여부를 사용할 수 있습니다.


2️⃣ Safari 팝업 차단 회피

핵심은 다음입니다.

window.open이 사용자 클릭 이벤트 안에서
동기적으로 실행됨

이 조건을 만족하면 Safari에서도 팝업이 차단되지 않습니다.


😕 아쉬운 점

React Query는 캐시 기반으로 동작합니다.

즉 데이터가 항상 최신 상태라는 보장은 없습니다.

쿠폰 발급 여부처럼 상태가 바뀔 수 있는 데이터라면 문제가 될 수 있습니다.

그래서 협의를 통해 다음과 같이 설정했습니다.

staleTime: 1000

1초 동안만 캐시를 유지하도록 설정했습니다.

다행히 쿠폰 소개 페이지에서는 발급 여부가 초 단위로 변하는 데이터가 아니었기 때문에 큰 문제는 없었습니다.


🧭 정리

이번 이슈를 통해 얻은 교훈은 다음과 같습니다.

  • window.open()은 사용자 클릭 이벤트 내부에서 동기적으로 실행되어야 안전하다
  • Safari는 Chrome보다 팝업 정책이 훨씬 엄격하다
  • React Query는 구조적으로 좋은 해결책이지만 캐시 특성을 고려해야 한다
  • 팝업 동작은 브라우저마다 다르기 때문에 크로스 브라우저 테스트가 중요하다

🧠 마무리하며

Safari의 보안 정책은 Chrome보다 훨씬 엄격합니다.

이번 경험을 통해 비동기 이후에 실행되는 동작은 사용자 이벤트로 간주되지 않을 수 있다는 점을 다시 한번 확인할 수 있었습니다.

또 한 가지 고민이 남았습니다.

 

최신 데이터를 유지하면서도 Safari 팝업 차단을 피할 수 있는 가장 이상적인 방법은 무엇일까?

아직 완벽한 해답은 찾지 못했지만, 이번 일을 계기로 브라우저 별 동작을 검증하는 E2E 테스트의 중요성을 더 크게 느끼게 되었습니다.

 

최근 아래 영상을 보면서 QA에 대한 생각도 다시 정리하게 되었습니다.

👉 이런 개발자 되지 말자! - QA 드리븐 개발
https://youtu.be/wjTB_n-oEJ8?si=Gti1FvUCa6lECAPL

QA는 단순히 테스트를 대신해주는 역할이 아니라

제품의 품질을 보증하는 역할

이라는 점을 다시 한번 생각하게 되었습니다.

 

'Front-End' 카테고리의 다른 글

React-Native - The Basics  (1) 2024.03.17
React State 관리: 10년간의 교훈  (0) 2024.03.03
React-Native에 대해서 알아보자!  (0) 2024.02.17
Next.js Project Structure  (1) 2024.02.03
2024년 상태 관리의 종결 : zustand  (0) 2024.01.19