import { useCallback, useEffect, useRef } from 'react';
import MobileAppBridge from 'app/libs/MobileAppBridge';
import { closeKakaoInAppBrowser } from 'app/libs/snsUtils';
import useSsrUserAgent from 'app/hook/common/useSsrUserAgent';

const closerOptions = {
	browser: {
		onNotClosed: undefined,
	},
};

export function useUniversalCloser(options = closerOptions) {
	const ua = useSsrUserAgent();
	const {
		isApp, isAndroid, isKakao,
	} = ua();
	const { browser } = useRef(options).current;
	const prevCheckTimer = useRef(null);

	useEffect(() => {
		const onPrevCheck = () => (clearTimeout(prevCheckTimer.current));
		window.addEventListener('pageshow', onPrevCheck, { capture: true });
		window.addEventListener('pagehide', onPrevCheck, { capture: true });
		window.addEventListener('beforeunload', onPrevCheck, { capture: true });
		window.addEventListener('popstate', onPrevCheck, { capture: true });

		return () => {
			clearTimeout(prevCheckTimer.current);
			window.removeEventListener('pageshow', onPrevCheck, { capture: true });
			window.removeEventListener('pagehide', onPrevCheck, { capture: true });
			window.removeEventListener('beforeunload', onPrevCheck, { capture: true });
			window.removeEventListener('popstate', onPrevCheck, { capture: true });
		};
	}, []);

	const handleAppClose = useCallback(() => {
		if (isApp) {
			if (isAndroid) {
				MobileAppBridge().destroyNativeWindow();
			} else {
				window.close();
			}
		}
	}, [isAndroid, isApp]);

	const handleKakaoClose = useCallback(() => {
		if (isKakao) {
			closeKakaoInAppBrowser();
		}
	}, [isKakao]);

	const handleBrowserClose = useCallback(() => {
		if (isApp || isKakao) {
			return;
		}

		const { onNotClosed } = browser;
		window.close();
		if (onNotClosed) {
			setTimeout(() => {
				onNotClosed();
			}, 1000);
		}
	}, [browser, isApp, isKakao]);

	const handleCheckPrevPageClose = useCallback(() => {
		const { onNotClosed } = browser;

		const windowType = isApp && 'isApp' || isKakao && 'isKakao' || 'close';
		const windowCloseMethod = {
			isApp: MobileAppBridge().destroyWebView,
			isKakao: closeKakaoInAppBrowser,
			close: window.close,
		}[windowType];

    // ios 사파리는 length 2이하로 해야댈듯?

		if (window.history.length === 1) {
			windowCloseMethod();
			return;
		}

		window.history.back();
		prevCheckTimer.current = setTimeout(() => {
			windowCloseMethod();
			if (onNotClosed) {
				onNotClosed();
			}
		}, 1000);
	}, [isApp, isKakao, browser]);

	const handleUniversalClose = useCallback(() => {
		handleAppClose();
		handleKakaoClose();
		handleBrowserClose();
	}, [handleAppClose, handleBrowserClose, handleKakaoClose]);

	return {
		handleAppClose,
		handleKakaoClose,
		handleBrowserClose,
		handleUniversalClose,
		handleCheckPrevPageClose,
	};
}

export default null;

카카오 종료 스크립트

export const closeKakaoInAppBrowser = () => {
	const {
		navigator: { userAgent },
		location,
	} = window;

	location.href = (/iPad|iPhone|iPod/.test(userAgent))
		? 'kakaoweb://closeBrowser'
		: 'kakaotalk://inappbrowser/close';
};

최신 정리

import { useCallback, useEffect, useRef } from 'react';
import MobileAppBridge from 'app/libs/MobileAppBridge';
import { closeKakaoInAppBrowser } from 'app/libs/snsUtils';
import useSsrUserAgent from 'app/hook/common/useSsrUserAgent';

const closerOptions = {
	browser: {
		onNotClosed: undefined,
	},
};

export function useUniversalCloser(options = closerOptions) {
	const ua = useSsrUserAgent();
	const {
		isApp,
		isAndroid, 
		isKakao,
	} = ua();
	const { browser } = useRef(options).current;
	const prevCheckTimer = useRef(null);

	useEffect(() => {
		const onPrevCheck = () => (clearTimeout(prevCheckTimer.current));
		window.addEventListener('pageshow', onPrevCheck, { capture: true });
		window.addEventListener('pagehide', onPrevCheck, { capture: true });
		window.addEventListener('beforeunload', onPrevCheck, { capture: true });
		window.addEventListener('popstate', onPrevCheck, { capture: true });

		return () => {
			clearTimeout(prevCheckTimer.current);
			window.removeEventListener('pageshow', onPrevCheck, { capture: true });
			window.removeEventListener('pagehide', onPrevCheck, { capture: true });
			window.removeEventListener('beforeunload', onPrevCheck, { capture: true });
			window.removeEventListener('popstate', onPrevCheck, { capture: true });
		};
	}, []);
	
	// 카카오톡 인앱, 자사앱, 브라우저 윈도우 닫기
	const closeCrossPlatform = useCallback(() => {
		if (isServer) {
			return;
		}
		
		if (isKakao) {
			window.location.href = isIOS
				? 'kakaoweb://closeBrowser'
				: 'kakaotalk://inappbrowser/close';
				
			return;
		}
		
		if (isApp) {
			appBridge().closeWebview();
			
			return;
		}
		
		window.close();
	}, []);

	const goBackOrClose = useCallback(() => {
		if (isServer) {
			return;
		}
	
		const { onNotClosed } = browser;

		const windowType = isApp && 'isApp' || isKakao && 'isKakao' || 'close';
		const windowCloseMethod = {
			isApp: MobileAppBridge().destroyWebView,
			isKakao: closeKakaoInAppBrowser,
			close: window.close,
		}[windowType];

    // ios 사파리는 length 2이하로 해야댈듯?
    const historyLength = window.history.length;
    const shouldCloseHistoryLength = isIOS && isSafari ? 2 : 1;

		if (historyLength === shouldCloseHistoryLength) {
			windowCloseMethod();
			return;
		}

		window.history.back();
		prevCheckTimer.current = setTimeout(closeCrossPlatform, 600);
	}, [isApp, isKakao, browser]);

	return {
		closeCrossPlatform,
		goBackOrClose,
	};
}