unsafeWindow 의 의미와 사용법
Posted 2019. 9. 12. 01:31, Filed under: 개발/UserScriptunsafeWindow 는 UserScript 를 개발하면서 함수를 덮어쓰고 싶을 때 주로 사용되는 도구 중 하나이다.
페이지 내에 전역으로 선언된 funcA 라는 이름의 함수를 덮어쓰고 싶은 경우 메타태그 블록에 @grant unsafeWindow 를 적어두고 아래와 같이 하면 된다.
unsafeWindow.funcA = function(){console.log("funcA 하이재킹됨")};
UserScript 를 가장 처음 야매로 개발하던 아주 옛날에는(지금도 야매지만) 그냥 함수를 덮어씌우는 예제를 보고 그대로 따라한 것이라, unsafeWindow 가 함수를 덮어씌우는 역할을 하는가 싶었는데 알고보니 그런 것이 아니었고 unsafeWindow를 쓰는 이유가 따로 있었다.
UserScript 가 아닌, 정상적으로 웹페이지에 직접 삽입된 코드에서는 그냥 window.funcA = function()... 라고만 적어도 함수를 덮어씌울 수 있다. 사실 이게 당연한 것이었다. 그럼 왜 UserScript 를 쓸 때는 window 대신 unsafeWindow 를 써야할까?
UserScript 내의 객체에는 일반적으로 접근할 수 없다.
'UserScript 의 window' 와 '웹사이트의 window' 는 분리되어 있고, UserScript 에서 웹사이트의 window 에는 자유롭게 접근 가능하지만 그 반대는 막혀있다. 다시 말하면 UserScript 내에서 선언된 변수나 함수는 전역으로 선언하거나 window.a 처럼 window 객체에 직접 선언하였더라도 다른 스크립트에서 접근할 수 없도록 되어있는 소리이다. 이유는 보안 및 스크립트 충돌을 방지하기 위함이다.
좀 더 구체적으로 예를 들자면, 웹사이트 운영자가 a 라는 변수를 전역 변수로 선언할 때 아래와 같이 할 수 있다.
window.a = 1; b = 1; // 전역으로 설정하는 다른 방법. strict 모드에서는 에러가 날 수 있다.
저렇게 전역으로 설정한 변수는 브라우저 개발자도구의 콘솔창에서 a, b 라고 치면 바로 값을 찍어볼 수 있다.
그런데 위 변수를 UserScript 로 작성하고 콘솔창에서 a, b 를 찍어보면 undefined 로 나온다. (기존에 웹사이트에 a, b 라는 변수가 존재하는지 여부와는 상관 없다.) 하지만 UserScript 내에서는 a와 b 라는 변수를 전역 변수로서 이용할 수 있다.
이유는 앞서 말한 것과 동일하게, UserScript 내에서 선언된 객체는 웹사이트 내의 기존 자바스크립트 코드, 다른 UserScript, 콘솔 창 등에서 접근할 수 없도록 되어있기 때문이다. 그리고 이것은 함수도 마찬가지이다.
다른 스크립트에서 내 유저스크립트 함수에 접근할 수 있게 만드려면 unsafeWindow 를 쓰자.
다시 함수를 덮어씌우는 것으로 돌아와서, 내가 덮어씌우고 싶은 funcA 라는 함수는 분명 웹사이트의 다른 함수, 예를들면 funcB 내에서 호출되고 있을 것이다.
function funcB(){funcA()}
만약 UserScript 내에서 funcA 를 window.funcA = function(){... 와 같이 덮어씌우려고 시도하면, 이제부터 funcA 는 UserScript 내의 함수를 가리키게 될 것이다.
UserScript 내에서 funcA 를 호출한다면 동일 window 내이므로 아무런 오류를 발생시키지 않겠지만, 기존에 웹사이트에 존재하던 함수인 funcB 에서 funcA 를 호출하려고 한다면 undefined 으로 나타날 것이다.
따라서 이 때 사용할 수 있는 것이 unsafeWindow 이다. unsafeWindow 는 기존 웹사이트의 window 를 가리키며, unsafeWindow 에 선언한 객체는 웹사이트의 window 에 선언된 객체와 동일하게 취급되어 웹사이트 내의 기존 자바스크립트 코드, 다른 UserScript, 콘솔 창 등에서 접근할 수 있으므로 funcB 에서 funcA 를 호출할 때 오류를 발생시키지 않을 것이다.
물론 funcB 로 UserScript 내에서 덮어씌우고, 또 funcB 를 호출하는 함수, 이벤트도 다 UserScript 내에서 덮어씌우면 unsafeWindow 를 쓰지 않아도 되겠지만 굳이 이렇게 해야 할 이유가 있을까?
unsafeWindow 의 다른 활용
1. unsafeWindow 는 다수의 UserScript 간 객체를 공유하는데 사용될 수 있다.
2. 동일 도메인 내 iframe 과 parent 프레임 간 UserScript 객체를 공유하기 위해 사용할 수 있다. 물론 GM_addValueChangeListener 나 GM_getTab 과 같은 것으로도 가능하지만 그냥 unsafeWindow 를 쓰면 무지 간단해진다.