iframe과 씨름을 시작한 이유:
웹과 안드로이드 앱에서 같이 사용하는 웹페이지를 웹앱(리액트 사용하고 있다.)에 포함시키기에는 부담스러웠다. 웹 서버가 죽더라도 동작할 수 있는 정적 페이지 서버를 준비하고 싶었기에 최소한의 html+css+js로 구성된 페이지를 만들고 이를 iframe으로 보여주는 페이지(이하 내부 페이지)를 만들게 되었다.
cross-origin frame:
보여주는 페이지와 iframe의 페이지가 각각 다른 서버를 사용하고 동일 도메인으로 맞췄음에도 ‘ Uncaught DOMException: Failed to read the ‘contentDocument’ property from ‘HTMLIFrameElement’: Blocked a frame with origin “___” from accessing a cross-origin frame.’ 에러가 발생하였다. 포트만 다른 경우였음에도 발생하였기에 document.domain을 상위 도메인으로 맞춰서 문제를 해결하였다.
높이 얻어오기 1차 시도: iframe.onload
처음에는 iframe의 onload 이벤트 발생하는 시점에서 iframeElement.contentDocument의 body에 접근해서 높이를 얻어왔는데 이 시점의 높이는 내부 페이지 전체가 로딩하기 전 시점이어서 불완전한 높이가 얻어졌다.
높이 얻어오기 2차 시도: iframe.contentDocument.readyState
두 번째 시도는 iframe으로 호출되는 페이지의 readyState와 onload 이벤트를 사용하여 높이를 얻어오는 방법을 시도하였는데 서버 렌더링 시에는 1차 시도와 마찬가지로 페이지가 모두 로딩되기 전에 호출되는 문제가 발생하였다. (나중에 다시 얘기하겠지만 이 방식에 문제가 있는 것은 아니였다.)
높이 얻어오기 3차 시도: window.postMessage
혹시나 iframe에서 불려지는 시점의 문제인가 싶어서 window.postMessge를 사용하여 body가 완료된 후 100ms 뒤에 높이를 측정하여 전달을 해봤는데 거의 대부분의 경우에는 성공하였으나 10번에 1번 정도 높이를 얻어오는데 실패하는 경우가 발생하였다. 작업할 때 겪은 문제점은 origin이었는데 window.parent.postMessage()로 보낼 때 origin을 설정하여도 받는 쪽의 origin은 프로토콜과 도메인만 받을 수 있었다.
높이 얻어오기 4차 시도: 문제는 리액트.
iframe의 호출 시점을 봐서는 문제가 없었기 때문에 상위 호출하는 리액트에서 문제를 찾기 시작하였다. 리액트에서 window에 안전하게 접근할 수 있는 시점은 componentDidMount 시점이다. 여기에서 message 이벤트를 듣기 시작하였는데 문제는 iframe의 문서 로딩이 빨라서 마운트된 시점에 벌써 로딩이 완료되는 문제점이 발생하였다.
결론:
리액트에서 iframe의 높이를 설정하기 위해서는 도메인 설정, 내부 문서 로딩 완료 시점에 높이를 얻어오고 componentDidMount 시점에 이미 내부 문서가 로딩이 완료되었는 지를 체크할 수 있으면 된다.
혹시 힌트를 좀 더 주실수 있나요 ㅜㅜ?
componentDidMount 시점에 이미 내부 문서가 로딩이 완료되었는 지를 체크 하는 걸 어떻게할지 고민입니다.
componentDidMount() {
this.iframe.onload = () => {
로 아이프레임 로딩 시점을 잡으려고 시도해 보았습니다…
1. iframe의 속성을 먼저 확인하시고요. https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement
2. iframe의 문서를 사용하기 위한 문서에 대해서 찾아보시기를 바랍니다.
3. 1,2의 과정이 완료되었다면 document의 이벤트를 찾아보고 문서가 사용가능한 상태를 찾아보세요.
DOM 스펙 문서를 읽어보신다면 해결하실 수 있을거에요~