postMessage

쓰리래빗츠 북 에디터에서 비주얼 에디터를 열 때 iframe을 사용합니다. 자바 스크립트로 해당 window 객체에 접근합니다.

function getVisualController() {
  for (var i = 0; i < window.frames.length; i++) {
    if (window.frames[i].visualController) {
      var result = window.frames[i].visualController;
      result.window = window.frames[i];
      return result;
    }
  }
}

그림 단락으로 유투브 동영상을 넣을 수 있습니다. 이 때도 iframe 코드를 사용합니다.

이렇게 유투브 동영상을 넣으면 다른 그림 단락을 비주얼 에디터로 편집하지 못합니다. 보안 문제입니다. 구글 크롬 웹 브라우저 콘솔에서 확인할 수 있는 예외 메시지입니다.

Uncaught DOMException: Blocked a frame with origin "http://localhost:1994" from accessing a cross-origin frame.

쓰리래빗츠 북 3.0.13 버전까지 이 문제가 있습니다.

도메인과 포트 번호가 같은 서버에서 내려받은 페이지는 자바 스크립트로 상호 작용할 수 있습니다. 그런데 유투브 동영상과 같이 다른 서버에서 내려받은 페이지가 있으면 에러가 발생합니다. 유튜브 동영상 페이지와 교류가 없어도 마찬가지입니다.

해결 방법은 있습니다. 도메인 또는 포트 번호가 다른 페이지끼리 메소드를 직접 호출하지 않고 이벤트 방식으로 메시지를 주고 받는 겁니다.

특정 window(비주얼 에디터)에서 다른 페이지가 보낼 메시지 이벤트를 기다립니다.

window.addEventListener('message', function(event) {
  var type = event.data.type;
  if (type === 'visual-editor-save') {
    ...
  } else if (type === 'visual-editor-close') {
    ...
  } else if (type === 'visual-editor-opened') {
    ...
  }
});

특정 페이지를 향해 이벤트를 호출하려면 해당 windowpostMessage 함수를 이용합니다.

var iframe = ...
iframe.contentWindow.postMessage({type: 'visual-editor-opened'}, "*");

보안 문제가 발생해서 코드를 고치는 수고가 있었지만 결과물은 예전보다 간결해졌습니다. 과거에 만든 코드는 Prototype 라이브러리를 사용하고, 최근에 작성한 일부 코드는 JQuery 라이브러리를 사용합니다. 코드 중복을 줄이기 위해 각 모듈을 iframe으로 연동할 생각입니다. 연동에 필요한 코드의 이질성은 걱정 거리입니다. 그런데 메시지를 이용하면 내부적인 구현의 상이함을 신경쓰지 않아도 될 거 같습니다.