참조: http://dev.w3.org/html5/postmsg




HTML5에서는 기존에 없던 메세지 통신방식이 새로 추가된다.
일반적인 Windows API에서 사용하던 메세지 이벤트를 이용하는 방식과 거의 동일한 형태라고 볼 수 있다.

아래와 같은 방식이다.

1. postMessage()를 이용하여 메세지를 목표의 메세지큐에 넣는다.
2. dispatch를 통하여 메세지큐에서 메세지를 뽑아서 목표에 전달. (혹은 메세지루프가 서브클래싱 되었거나)
3. onmessage 이벤트 핸들러에서 메세지를 받아서 처리한다.

다음은 핵심이 되는 MessageEvent의 인터페이스

interface MessageEvent : Event {
    readonly attribute any data;
    readonly attribute DOMString origin;
    readonly attribute DOMString lastEventId;
    readonly attribute WindowProxy source;
    readonly attribute MessagePortArray ports;
    void initMessageEvent(in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in any dataArg, in DOMString originArg, in DOMString lastEventIdArg, in WindowProxy sourceArg, in MessagePortArray portsArg);
};

각 멤버에 대한 설명
data: 메세지의 데이터
origin: 메세지의 origin, server-sent events와 cross-document messaging에서 사용됨
lastEventId: server-sent events를 위한 속성으로 최종 이벤트 ID
source: cross-document messaging을 위한 속성으로 source window의 WindowProxy
ports: cross-document messaging과 channel messaging을 위한 속성으로 보내진 메세지의 MessagePortArray




Web Messaging은 크게 두가지 방식이 있다.
cross-document messaging과 channel-messaging이다.




1. cross-document messaging

참조: http://dev.w3.org/html5/postmsg/#web-messaging

이전까지의 웹개발에서는 보안상의 이유로 웹브라우저에서 서로 다른 도메인간의 문서들이 서로에게 영향을 미칠 수 없도록 되어 있다.
이로 인해 보안은 좋아졌으나 서로 다른 도메인간에 통신이 꼭 필요한 경우에 브라우저내에서 간편하게 통신할 수 있는 방법이 원천봉쇄되는 역효과도 생겨났다.
이런점을 해소하기 위해 cross-document messaging을 사용할 수 있다.

window 객체마다 추가된 postMessage() 라는 method를 이용하여 메세지를 보내는 것이 가능하고,
window.onmessage 이벤트 핸들러를 붙여서 메세지를 받는 것이 가능하다.

postMessage의 원형은 다음과 같다.

window.postMessage(message, targetOrigin, [, ports]);

message: 보내고자 하는 메세지. 문자열뿐만 아니라 객체형태도 보낼 수 있다.
targetOrigin: 보안을 위한 매개변수. 보내고나 하는 문서와 origin이 맞지 않다면 메세지는 보내지지 않는다. 만약 origin에 상관없이 메세지를 보내고 싶다면 targetOrigin 값을 '*' 로 하면 된다.
ports: 메세지 포트. TCP 통신의 소켓이나 IPC에서 사용하는 파이프 정도의 개념인 듯. 생략가능한 매개변수이다.

origin이 유일한 보안 가이드라인인것 같으므로 실제 사용시 유의해야 할 듯.



다음은 cross-document messaging을 이용하는 예제이다.
index.html에 iframe 2개를 박아서 N:N 으로 통신할 수 있도록 만들었다.
origin은 보안에 관계없이 그냥 '*'로 했다.


index.html
<!DOCTYPE html>
<html>
<head>

<script>
// send
function sendMessage(id, msg)
{
    document.getElementById(id).contentWindow.postMessage(msg, '*');   
}

// recv
window.onmessage = function(e)
{
    document.getElementById('data').innerHTML += e.data+'<br />';
}
</script>
</head>

<body>

<iframe id="frame_1" src="1.html" style="width: 320px; height: 240px;"></iframe>
<iframe id="frame_2" src="2.html" style="width: 320px; height: 240px;"></iframe>
<br /><br />
<button onclick="sendMessage('frame_1', 'main: hello?');">프레임1로 보내기</button>
<button onclick="sendMessage('frame_2', 'main: hello?');">프레임2로 보내기</button>
<br />
<div id="data" style="border: 1px solid black; widht: 300px; height: 200px; overflow: auto;"></div>

</body>
</html>



1.html
<script>
// recv
window.onmessage = function(e)
{
    document.getElementById('data').innerHTML += e.data+'<br />';
}

// send
function sendMessage(isParent)
{
    if (isParent) window.parent.postMessage('frame_2: hello?', '*');
    else window.parent.document.getElementById('frame_2').contentWindow.postMessage('frame_1: hello?', '*');
}
</script>

<button onclick="sendMessage(true);">부모창으로 보내기</button>
&nbsp;
<button onclick="sendMessage(false);">frame_2로 보내기</button>
<br />
<div id="data" style="border: 1px solid red; width: 300px; height: 180px; overflow: auto;"></div>


2.html
<script>
// recv
window.onmessage = function(e)
{
    document.getElementById('data').innerHTML += e.data+'<br />';
}

// send
function sendMessage(isParent)
{
    if (isParent) window.parent.postMessage('frame_2: hello?', '*');
    else window.parent.document.getElementById('frame_1').contentWindow.postMessage('frame_2: hello?', '*');
}
</script>

<button onclick="sendMessage(true);">부모창으로 보내기</button>
&nbsp;
<button onclick="sendMessage(false);">frame_1로 보내기</button>
<br />
<div id="data" style="border: 1px solid red; width: 300px; height: 180px; overflow: auto;"></div>








2. channel-messaging

참조: http://dev.w3.org/html5/postmsg/#channel-messaging

Web-Messaging의 또다른 표준으로,
postMessage()와 onmessage를 이용하여 통신한다는 점은 동일하지만,
MessageChannel 객체를 생성하여 ports를 이용하여 통신한다는 점이 다르다.
cross-document messaging에 비하여 메세지 통신을 좀 더 구조화시킬 수 있다는 장점이 있다.
채널구성과 포트배분에 따라서 얼마든지 체계화 된 소스코드를 구성할 수 있지 않을까.




channel-messaging의 통신방식은 다음과 같다.

1. 메인위도우에서 MessageChannel 객체를 생성한다. (이와 동시에 MessageChannel 객체의 프로퍼티로 port 2개가 생성된다.)
2. 통신할 객체에 postMessage를 이용하여 port 하나를 넘겨준다. 넘겨준 포트는 통신할 객체가 사용할 send/recv 인터페이스가 된다.
3. 메인윈도우에서 생성한 2개의 포트 중 통신할 객체로 넘겨준 port 외에 나머지 port는 메인윈도우에서 사용할 send/recv 인터페이스가 된다.
4. postMessage로 넘겨받은 port로 postMessage()와 onmessage 이벤트 핸들러를 구성하여 send/recv를 수행한다.




다음은 간단한 pseudo 코드

// 메인윈도우에서 MessageChannel 생성
var channel = new MessageChannel();

// 다른 윈도우에 port 하나 넘겨주기
otherWindow.postMessage('hello', [channel.port2], '*');

// 메인윈도우는 이제 남은 port1을 이용해 send/recv를 한다.
// send
channel.port1.postMessage('hi?');
// recv
channel.port1.onmessage = function (e)
{
    alert(e.data);
}

// 다른 윈도우도 위와 같은 형태로 넘겨받은 port2를 이용하여 send/recv를 구성한다.






channel-messaging에 사용되는 인터페이스는 아래와 같다.

MessageChannel
[Constructor]
interface MessageChannel {
    readonly attribute MessagePort port1;
    readonly attribute MessagePort port2;
};


MessagePort
typedef sequence<MessagePort> MessagePortArray;

interface MessagePort {
    void postMessage(in any message, in optional MessagePortArray ports);
    void start();
    void close();

    // event handlers
        attribute Function onmessage;
};
MessagePort implements EventTarget;








다음은 2개의 iframe을 박아서 각 iframe과 통신하는 channel-messaging 예제이다.

index.html
<!DOCTYPE html>
<html>
<head>

<script>
var channel = new Array();
// 페이지 수에 맞게 채널 생성
for (var i=0; i<2; i++) {
    channel[i] = new MessageChannel();
    // port1을 넘겨주고 port2로 통신
    channel[i].port2.onmessage = function(e){
        document.getElementById('data').innerHTML += e.data+'<br />';
    };
}

window.onload = function()
{
    // 각 iframe 페이지에 port1을 넘겨준다.
    // 문서에는 postMessage 함수의 매개변수 순서가 다르게 되어 있는데 테스트에 사용한 크롬브라우저는 매개변수의 순서가 아래와 같다.
    document.getElementById('frame_1').contentWindow.postMessage('', [channel[0].port1], '*');
    document.getElementById('frame_2').contentWindow.postMessage('', [channel[1].port1], '*');
}
</script>
</head>

<body>

<iframe id="frame_1" src="1.html" style="width: 320px; height: 240px;"></iframe>
<iframe id="frame_2" src="1.html" style="width: 320px; height: 240px;"></iframe>
<br /><br />
<button onclick="channel[0].port2.postMessage('from Main to 1st doc');">프레임1로 보내기</button>
<button onclick="channel[1].port2.postMessage('from Main to 2nd doc');">프레임2로 보내기</button>
<br />
<div id="data" style="border: 1px solid black; width: 300px; height: 200px; overflow: auto;"></div>

</body>
</html>



1.html
<script>
var port = null;
window.onmessage = function(e)
{
    port = e.ports[0];
    port.onmessage = function(e) {
        document.getElementById('data').innerHTML += e.data+'<br />';
    };
}
</script>

<button onclick="port.postMessage('from iframe');">부모창으로 보내기</button>
<br />
<div id="data" style="border: 1px solid red; width: 300px; height: 180px; overflow: auto;"></div>
Posted by bloodguy
,