😎
Safari 팝업 차단 우회 삽질기
Kioschool
2025-05-03
키오스쿨 시연을 진행하던 중, 사소해 보이지만 사소하지 않은 버그를 맞이했다. 토스 결제하기를 누르면 원래는 ‘토스’ 앱으로 넘어가져야 한다. 그러나 토스 팝업이 뜨지 않고 그냥 주문 확인 페이지로 넘어가졌다. 상당히 당황했지만 원래 그랬던 것처럼 태연히 넘기고 다음 기능 시연을 이어나갔다.
시연이 끝나고 바로 해당 버그의 원인을 분석했다. 원인 자체는 금방 찾을 수 있었다. Safari에서 팝업 차단을 설정 했기에 토스 팝업이 뜨지 않았던 것이다. 원인을 파악했으니 문제를 해결하기 위해 인터넷에 해결책을 검색해보았다.
사파리 팝업차단 우회하기를 참고하니 생각보다 간단해 보였다. 비동기 콜을 하기 전에 비어있는 팝업을 열고, 비동기가 완료된 후 팝업의 url을 갈아끼움으로써 Safari의 팝업 차단을 우회하는 방식이었다.
BEFORE
1createOrder(workspaceId, tableNo, orderBasket, customerName)2 .then((res) => {3 // navigate to /order-complete4 window.open(tossUrl);5 })6 .catch(errorHandler);
AFTER
1let popup: Window | null = null;2popup = window.open(undefined);34createOrder(workspaceId, tableNo, orderBasket, customerName)5 .then((res) => {6 // navigate to /order-complete7 window.open(tossUrl);8 })9 .catch(errorHandler)10 .then(() => {11 popup?.location.replace(tossUrl);12 });
그러나 한가지 간과한 점이 있었다. 위 해결책은 ‘Safari’ 에서만 통하는 것이었다. 크롬이나 삼성 브라우저로 접속하게 되면 비어있는 팝업이 열린 채로 유지된다.
크롬, 사파리, 삼성 브라우저 모두 대응해보자
사파리만 AFTER 방식을 이용하고, 그 외 브라우저들은 BEFORE 방식을 이용해야 했다. 그러기 위해서 먼저 사용자의 브라우저의 종류를 파악해야 했다. 사용자의 브라우저 정보는 navigator.userAgent에 담겨있다.
Chrome
1Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.8161993 Safari/537.36
Safari
1Mozilla/5.0 (iPhone; CPU iPhone OS 18_3_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3.2 Mobile/15E148 Safari/605.1.15 NewsSapphire/30.6.430214001
Samsung Browser
1Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/27.0 Chrome/125.0.0.0 Mobile Safari/537.36 UUID/9a253c5a-6781-4974-8c72-0fede12b2926
현재 모바일 브라우저 점유율 순서대로 가장 높은 3가지 브라우저에 대해서만 대응했다. 사실 브라우저 버젼 별로도 userAgent 값들이 미세하게 다르다. 그래서 Safari와 그 외 브라우저들의 차이점을 분석했다.
Safari의 userAgent 값에는 ‘Chrome’이 들어가지 않지만, 그 외 브라우저들에는 모두 ‘Chrome’이 들어갔다.
Chrome

Safari

Samsung Browser

1const isSafari = userAgent.includes('safari') && !userAgent.includes('chrome');
userAgent
값에 'safari'가 포함되고 'chrome'이 포함되지 않는 경우 Safari 브라우저로 판단하는 isSafari 변수를 선언했다.
완성 코드
1const createOrderAndNavigateToToss = (customerName: string) => {2 const tossUrl = `${tossAccountUrl}&amount=${totalAmount}`;3 const userAgent = navigator.userAgent.toLowerCase();4 const isSafari = userAgent.includes('safari') && !userAgent.includes('chrome');56 let popup: Window | null = null;7 if (isSafari && totalAmount !== 0) {8 popup = window.open(undefined);9 }1011 createOrder(workspaceId, tableNo, orderBasket, customerName)12 .then((res) => {13 navigate('order-complete');1415 if (!isSafari && totalAmount !== 0) {16 window.open(tossUrl);17 }18 })19 .catch(errorHandler)20 .then(() => {21 if (isSafari && totalAmount !== 0) {22 popup?.location.replace(tossUrl);23 }24 });25 };
isSafari
변수를 통해 사파리와 그 외 브라우저들 모두 토스 앱으로 넘어가는 팝업을 생성할 수 있게 됐다.
참고자료