관련지식
html, javascript, cross-domain

웹사이트 안에서 다른 웹사이트의 화면을 iframe 으로 보이게 하는 것은 드문일이 아닙니다. 그런데 iframe 내의 컨텐츠 길이가 고정되어있지 않다면 리사이징이 매우 애매해집니다. 어떻게 하면 될까요?

무엇이 문제인가?

A사이트의 화면에서 iframe 태그에 B사이트의 URL을 사용했습니다. iframe 자체는 A사이트의 것이므로 iframe의 크기는 A사이트의 화면 소스에서 제어할 수 있습니다. 그러나 iframe 안의 문서는 B사이트의 것입니다. 따라서 크로스 도메인 정책에 의해 A사이트에서는 B사이트의 문서 길이를 알아낼 수가 없습니다.

즉 서로 다른 도메인에서 문서의 길이를 어떻게 전달하는지가 관건입니다.

iframe 이중으로 쓰기

상당히 오랫동안 사용해온 방식입니다. B사이트에서 숨은 상태의 iframe 태그를 만들고 A사이트의 URL을 호출하는 방식입니다.

먼저 B사이트는 iframe의 A사이트를 호출할때 자신의 문서 길이를 전달합니다.

  1. $(function() {
  2. var maxHeight = Math.max(document.body.scrollHeight,
  3. document.documentElement.scrollHeight,
  4. document.body.offsetHeight,
  5. document.documentElement.offsetHeight);
  6. document.getElementById("ifrm2").src = "iframe2.html#" + maxHeight;
  7. });

B사이트에서 호출한 A사이트의 화면에서는 parent.parent 로 상위에 있는 화면과 동일한 도메인이기 때문에 함수를 호출할 수 있습니다. 따라서 아래처럼 호출만 하면 됩니다.

  1. var height = location.hash.substring(1);
  2. parent.parent.setHeight(height);

이 방식은 브라우저 버전에 상관없이 쓸수 있기 때문에 예전부터 많이 사용한 방식이지만 이 방식조차 몰라서 못 쓴 경우도 있을것으로 생각되네요.

postMessage 이벤트 전달

크로스 도메인간 안전한 메시지 전달을 위해 postMessage() 함수가 있습니다. B사이트의 화면에서 A사이트쪽으로 postMessage를 호출하면 A사이트에서 message 이벤트가 발생하여 postMessage를 통해 전달한 데이터를 받아볼 수 있는 것입니다. 물론 타겟을 어디로 정하느냐에 따라 iframe, 팝업 화면으로도 전달 할 수 있습니다.

보내는쪽)

  1. window.top.postMessage({
  2. method : 'resize',
  3. height : maxHeight
  4. });

받는쪽)

  1. var postMessageHandler = function(event) {
  2. var method = event.data.method;
  3. var height = event.data.height;
  4. switch(method) {
  5. case 'resize':
  6. document.getElementById("ifrm3").style.height = height + 'px';
  7. break;
  8. }
  9. };
  10. window.addEventListener("message", postMessageHandler);

낮은 버전의 IE에서는 사용할 수 없지만 대부분의 최신 브라우저가 지원하므로 PC환경에서도 사용을 검토해볼만 합니다.

정리

글의 시작은 ‘iframe 리사이징’으로 시작했지만 글의 중요한 내용은 크로스 도메인간 데이터를 어떻게 주고 받을수 있을것인가 하는 것입니다.

첫번째 방법인 iframe을 한번 더 쓰는 방법은 모든 브라우저에서 사용 가능하지만 높이뿐 아니라 다른 데이터를 전달하고자 한다면 데이터 구조화 문제와 노출 문제가 생길 것입니다.

그러나 두번째 방법인 postMessage() 함수를 이용한 방법은 object를 그대로 전달할 수 있기 때문에 구조화된 데이터를 유지할 수가 있습니다. 따라서 하위버전의 IE를 고려하지 않아도 되는 환경이라면 postMessage()를 사용하는 것이 리사이즈 뿐 아니라 크로스 도메인간 데이터를 전달하기에 매우 좋습니다.

샘플 파일은 총 4개가 있고 iframe.html 로 실행해보시면 됩니다.

최종 샘플

iframe.html)

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>CSS Template</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. </head>
  8. <body>
  9. <iframe id="ifrm1" src="iframe1.html" style="height:100px"></iframe>
  10. <iframe id="ifrm3" src="iframe3.html" style="height:100px"></iframe>
  11. <script>
  12. function setHeight(height) {
  13. document.getElementById("ifrm1").style.height = height + 'px';
  14. }
  15. var postMessageHandler = function(event) {
  16. var method = event.data.method;
  17. var height = event.data.height;
  18. switch(method) {
  19. case 'resize':
  20. document.getElementById("ifrm3").style.height = height + 'px';
  21. break;
  22. }
  23. };
  24. window.addEventListener("message", postMessageHandler);
  25. </script>
  26. </body>
  27. </html>

iframe1.html)

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>CSS Template</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  8. </head>
  9. <body>
  10. <div style="border:1px solid red; height:300px;">
  11. </div>
  12. <iframe id="ifrm2" style="display:none"></iframe>
  13. <script>
  14. $(function() {
  15. var maxHeight = Math.max(document.body.scrollHeight,
  16. document.documentElement.scrollHeight,
  17. document.body.offsetHeight,
  18. document.documentElement.offsetHeight);
  19. document.getElementById("ifrm2").src = "iframe2.html#" + maxHeight;
  20. });
  21. </script>
  22. </body>
  23. </html>

iframe2.html)

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>CSS Template</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. </head>
  8. <body>
  9. <script>
  10. var height = location.hash.substring(1);
  11. parent.parent.setHeight(height);
  12. </script>
  13. </body>
  14. </html>

iframe3.html)

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>CSS Template</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
  8. </head>
  9. <body>
  10. <div style="border:1px solid red; height:300px;">
  11. </div>
  12. <iframe id="ifrm2" style="display:none"></iframe>
  13. <script>
  14. $(function() {
  15. var maxHeight = Math.max(document.body.scrollHeight,
  16. document.documentElement.scrollHeight,
  17. document.body.offsetHeight,
  18. document.documentElement.offsetHeight);
  19. window.top.postMessage({
  20. method : 'resize',
  21. height : maxHeight
  22. });
  23. });
  24. </script>
  25. </body>
  26. </html>