JavaScript / window.open()으로 새 창 열기와 부모·자식 값 전달

window.open()은 사용자의 클릭에 반응해 새 창이나 탭을 엽니다. 반환된 창 참조와 postMessage()를 사용하면 부모 창과 자식 창이 값을 주고받을 수 있습니다. 팝업 차단, 동일 출처 정책, 메시지의 출처 확인을 함께 처리해야 합니다.

새 창 열기와 팝업 차단 확인

형식은 window.open(url, name, features)입니다. URL은 열 문서, name은 재사용할 창의 이름, features는 요청할 창 크기 같은 옵션입니다. 창 대신 새 탭이 열리거나 요청한 크기·위치가 무시될 수 있으며, 반환값이 null이면 새 창을 열지 못한 경우입니다. 팝업은 버튼 클릭처럼 직접적인 사용자 동작 안에서 여세요.

const child = window.open('child.html', 'valueExample', 'width=420,height=320');
if (!child) console.log('팝업이 차단되었거나 창을 열 수 없습니다.');

사용자의 클릭 없이 페이지 로드 직후 자동으로 여는 방식은 팝업 차단에 걸리기 쉽습니다. 위 URL은 같은 폴더의 child.html을 가리킵니다. MDN의 window.open() 문서에서 반환값과 브라우저 제한을 확인할 수 있습니다.

부모와 자식 사이의 값 전달 방식

원래 창은 window.open()의 반환값으로 새 창을 가리키고, 새 창은 허용된 경우 window.opener로 연 창을 가리킵니다. 다만 서로 다른 출처의 문서에는 상대 창의 DOM을 직접 읽거나 수정할 수 없습니다. 같은 출처여도 열린 창이 다른 사이트로 이동할 수 있으므로 값을 전달할 때는 메시지의 목적지와 보낸 사람을 확인하는 방식이 적합합니다.

postMessage(message, targetOrigin)의 두 번째 인자에는 받을 창의 정확한 출처를 지정합니다. 수신 측에서는 event.origin과 event.source를 모두 확인한 뒤, 필요한 데이터 형식도 검사합니다. 예제는 두 파일을 같은 출처에서 제공하므로 location.origin을 사용합니다. 다른 출처와 통신한다면 양쪽에 허용할 정확한 출처를 각각 지정해야 합니다. MDN의 postMessage() 문서에 보안 조건이 설명되어 있습니다.

전체 예제: 두 파일로 양방향 전달

아래 첫 코드를 parent.html, 둘째 코드를 같은 폴더의 child.html로 저장합니다. 그 폴더에서 python -m http.server 8000을 실행하고 브라우저에서 http://localhost:8000/parent.html을 엽니다. 파일을 직접 여는 file: 방식은 출처 처리가 브라우저마다 달라 이 예제에 사용하지 않습니다.

parent.html — 클릭할 때 새 창을 열고, 자식 창의 준비 메시지를 받은 뒤 현재 값을 보내며, 결과 메시지를 받아 입력란에 반영합니다.

<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>부모 창</title>
</head>
<body>
<label for="value">부모 창의 값</label>
<input id="value" value="처음 값">
<button id="open" type="button">자식 창 열기</button>
<p id="status" role="status"></p>
<script>
const value = document.querySelector('#value');
const status = document.querySelector('#status');
let child = null;

document.querySelector('#open').addEventListener('click', () => {
  child = window.open('child.html', 'valueExample', 'width=420,height=320');
  status.textContent = child ? '새 창을 열었습니다.' : '팝업이 차단되었습니다.';
});

window.addEventListener('message', event => {
  if (event.origin !== location.origin || event.source !== child) return;
  if (event.data?.type === 'ready') {
    child.postMessage({type: 'initial', value: value.value}, location.origin);
  } else if (event.data?.type === 'result' && typeof event.data.value === 'string') {
    value.value = event.data.value;
    status.textContent = '자식 창의 값을 받았습니다.';
  }
});
</script>
</body>
</html>

child.html — 부모 창이 보낸 초기값을 표시하고, 사용자가 수정한 값을 버튼 클릭으로 돌려보냅니다.

<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>자식 창</title>
</head>
<body>
<label for="child-value">자식 창의 값</label>
<input id="child-value">
<button id="send" type="button">부모 창에 보내기</button>
<p id="child-status" role="status"></p>
<script>
const input = document.querySelector('#child-value');
const status = document.querySelector('#child-status');

if (!window.opener) {
  status.textContent = '부모 창에서 열어 주세요.';
} else {
  window.addEventListener('message', event => {
    if (event.origin !== location.origin || event.source !== window.opener) return;
    if (event.data?.type === 'initial' && typeof event.data.value === 'string') {
      input.value = event.data.value;
      status.textContent = '부모 창의 값을 받았습니다.';
    }
  });
  window.opener.postMessage({type: 'ready'}, location.origin);
}

document.querySelector('#send').addEventListener('click', () => {
  if (!window.opener || window.opener.closed) {
    status.textContent = '부모 창이 닫혔습니다.';
    return;
  }
  window.opener.postMessage({type: 'result', value: input.value}, location.origin);
  status.textContent = '부모 창에 값을 보냈습니다.';
});
</script>
</body>
</html>

부모 입력란의 값을 바꾼 뒤 자식 창을 열면 그 값이 자식 입력란에 보입니다. 자식에서 다시 값을 고치고 보내기 버튼을 누르면 부모 입력란에 반영됩니다. 새 창이 차단되면 부모 화면에 차단 메시지가 보입니다. 값을 보낸 뒤에는 자식 창을 닫고, 서버는 터미널에서 Ctrl+C로 중지할 수 있습니다.

창 참조와 보안상 주의할 점

자식이 로드되기 전에 부모가 바로 메시지를 보내면 수신 리스너가 아직 없을 수 있습니다. 예제는 자식의 ready 메시지를 받은 뒤 초기값을 전송하여 이 순서 문제를 피합니다. window.opener가 없거나 부모가 닫힌 경우도 처리합니다.

자식이 부모 창을 제어할 필요가 없는 단순 외부 링크에는 noopener를 사용해 opener 연결을 끊는 편이 안전합니다. 이 예제는 양방향 소통이 목적이므로 연결을 유지하고, 두 파일을 같은 신뢰할 수 있는 출처에서 제공하며, 메시지마다 출처·창 참조·자료형을 확인합니다. MDN의 window.opener 문서도 참고하세요.