«

popup 창에서 리다이렉트 이후 window.opener 사라지는 문제 해결하기

서론

웹 프로그램을 개발할 때 현재 열려 있는 페이지를 그대로 유지하면서 사용자에게 다른 액션을 유도하기 위해서 우리는 Popup으로 새로운 창을 열도록 개발하는 경우가 있다. 예를 들어서 i-PIN 인증을 처리하는 경우 회원가입 화면에서 i-PIN 인증 화면을 Popup으로 열어서 사용자 인증을 거친 이후 인증이 완료되면 열었던 창으로 결과를 던져주는 경우가 있다. JavaScript로 Popup을 사용하여 새로운 창을 열고 Popup에서 처리한 결과를 Popup을 열게한 window.opener에 접근해서 이벤트를 전달하는 것은 어렵지않게 이미 알려진 방법으로 해결할 수 있다. 하지만 특정 브라우저에서 Popup한 창에 열려진 사이트가 Redirect를 하여 다른 host로 이동을 할 경우 window.opener 객체를 잃게 되버리는 문제를 발견했다. 이번 포스팅은 JavaScript로 Popup을 사용하는 방법 중에 Popup의 호스트가 이동후 opener에 접근할 수 있는 방법에 대해서 소개한다.

설명의 편의를 위해 Popup을 생성하여 열게한 창을 부모창, Popup으로 새롭게 열려진 창을 자식창이라고 정의하고 설명한다.

JavaScript로 Popup 열기

JavaScript로 Popup 창을 여는 방법은 여러가지가 있지만 가장 간단하게 다음과 같이 할 수 있다. 부모창 에서 window.open() 메소드를 사용하여 자식창을 새롭게 만들 수 있다. 이 때 자식창의 고유 이름을 지정하거나 _self, _blank와 같이 예약된 이름으로 만들 수 있고 자식창의 속성을 부여할 수 있다.

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <script src="parentWin.js"></script>
</head>
<body>
  <button onclick="onPopupWindow()">팝업창 열기</button>
</body>
</html>
// filename : parentWin.js

function onPopupWindow(){
	window.open("popup.html", "_blank", "top=10, left=10, width=400, height=400");
}

자식창에서 부모창 접근

Popup으로 열린 자식창에서 특정 작업이 처리된 이후 부모창에 데이터를 넘겨주거나 이벤트를 처리해야할 때 다음과 같이 window.opener 객체를 이용하여 접근할 수 있다. 예를 들어 자식창에서 부모창에 열려있는 페이지를 다른 페이로 이동시키고 싶을 경우 자식창에서 다음과 같이 할 수 있다.

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <script src="childWin.js"></script>
</head>
<body>
  <button onclick="doRedirectOpener()">부모창 페이지 이동</button>
</body>
</html>
// filename : childWin.js

function doRedirectOpener(){
	var parentWindow = window.opener;
	parentWindow.location.href = 'http://blog.saltfactory.net'
};

자식창에서 부모창에 접근하기 위해서는 window.opener를 참조하면 부모창의 윈도우객체를 참조할 수 있다. 위 예제는 window.opener를 이용해 부모창window객체를 접근해 window.location.href 값을 수정하여 새로운 HOST로 부모창의 페이지를 이동하게 한 예제이다.

자식창의 HOST가 리다이렉트 되는 경우

만약 Popup으로 자식창을 Popup으로 열었는데 특정 일을 처리하고 난 이후 자식창의 HOST가 변경될 경우를 생각해보자. 예를 들어 i-PIN 인증창을 Poup으로 연다고 가정하자.

자식창의 플로우는 다음과 같다. 설명의 편의를 위해서 우리 호스트는 http://blog.saltfactory.net 이라고 하고 i-PIN 인증 업체 호스트는 http://i-pin.org 이라고 한다.

  1. 부모창에서 자식창을 popup으로 연다. 이때 URL은 나의 HOST의 child.html이다
  2. checkIPIN.html : 필요한 데이터를 조합해서 i-PIN 인증 업체의 URL iPin.html으로 리다이렉트를 한다.
  3. iPinMain.html : 외부 업체 사이트에서 필요한 작업을 처리하고 결과를 나의 HOST의 result.html으로 리다이렉트 한다.
  4. result.html : 외부에서 전달한 값을 가지고 부모창에 접근하여 결과를 액션을 취한다.

이와 같은 경우 최종적으로 자식창에서 결과를 부모창에 전달하기 위해서 window.opener를 사용하면 undefined 에러를 발생시킨다. 다시 말해서 HOST가 변경되면서 처음 자식창이 열렸을 때의 window.opener가 유지가 되지 않고 변경이 되는 문제를 가지게 되는 것이다. 이 문제는 Internet Explore의 보안설정에 의해서 발생한다. 이 문제를 해결하기 위해서 사용자에게 보안 설정을 해지하거나 변경해야한다고 공지할 수는 없기 때문에 우리는 이 문제를 해결하기 위한 방법을 찾게 되었다.

자식창이 Popup으로 열려서 URL이 다른 HOST로 이동하게 되면 window.opener를 유지할 수 없는 문제를 해결하기 위해서 iframe을 이용하여 자식창의 HOST는 변경하지 않고 iframe안에서만 페이지가 변경될 수 있도록 실험하였다. 자식창window객체는 변경되지 않기 때문에 자식창에서는 부모창을 접근할 수 있는 window.opener를 잃지 않을 것으로 생각했다. 결과 페이지인 result.html 페이지 안에서 iframe에 접근하면 iframe을 가지고 있는 자식창을 접근할 수 있고, 자식창에서 부모창으로 접근할 수 있기 때문이다.

부모창에서 자식창을 열때 iframe을 만들고 iframe안에서 자식창 URL인 http://blog.saltfactory.net/child.html을 열도록 하였다.

// filename : parentWin.js

function onPopupWindow(){
	var win =  window.open(null, '_blank', "top=10, left=10, width=400, height=400");
	win.document.write('<iframe width="100%", height="100%" src="http://blog.saltfactory.net/child.html" frameborder="0" allowfullscreen></iframe>')
}

child.html에서는 내부적으로 http:///i-pin.org/iPin.html로 페이지를 전환후에 작업을 모두 처리한 이후 결과를 http://blog.saltfactory.net/result.html으로 다시 리다이렉트를 시켜주었다. 결과 받은 result.html은 다음과 같은 소스코드를 가지고 있다.

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <script src="child-resultWin.js"></script>
</head>
<body>
  <button onclick="onCloseSendResultToOpener()">이 창을 닫고 회원가입을 계속 진행합니다.</button>
</body>
</html>
// filename : child-resultWin.js

function onCloseSendResultToOpener(){
	var childWindow = window.parent;
	var parentWindow = childWindow.opener;
	parentWindow.location.href = 'http://blog.saltfactory.net/account.html'
};

다시 말해서 i-PIN 인증창이 자식창 안의 iframe안에 열려있고, 처리가 완료된 이후 result.html자식창iframe 안에 열려 있는 것이다. 자식창에서 리다이렉트하는 모든 사이트들은 부모창에서 자식창을 열때 만들어 놓은 iframe안에서 동작하고 있기 때문에 마지막 결과를 받은 result.html에서 iframe으로 접근하고, iframe에서는 자식창에 접근하고 마지막으로 자식창에서 부모창으로 접근할 수 있는 것이다.

결론

부모창에서 JavaScript를 이용하여 자식창을 Popup으로 열게되면 자식창에서 window.opener를 참조하여 부모창 에 접근할 수 있다. 하지만 자식창에 다른 HOST로 리다이렉트가 되는 경우 window.opener객체가 사라지는 문제를 발견했다. 이 문제를 해결하기 위해서 자식창을 Popup으로 열때 iframe을 생성하여 iframe 내부에서 페이지가 열리고 다른 HOST로 리다이렉트 되게 하였다. 마지막 결과를 처리하는 최종 자식창의 페이지에서 부모창에 접근하기 위해서 iframe을 매개체로 상위 객체로 접근하게 되면 자식창의 페이지가 다른 HOST로 리다이렉트 될 때 window.opener 객체가 undefined되는 문제를 해결 할 수 있었다.

참고