투케이2K

174. (TWOK/LOGIC) [Web] 자바스크립트 window.open 팝업창 새창 열기 수행 시 브라우저 팝업 차단 권한 체크 로직 정리 본문

투케이2K 로직정리

174. (TWOK/LOGIC) [Web] 자바스크립트 window.open 팝업창 새창 열기 수행 시 브라우저 팝업 차단 권한 체크 로직 정리

투케이2K 2026. 3. 5. 09:36
728x90
반응형

[로직 정리]

정리 로직 : Web / JavaScript

상태 : [Web] 자바스크립트 window.open 팝업창 새창 열기 수행 시 브라우저 팝업 차단 권한 체크 로직 정리

 

[설 명]

// --------------------------------------------------------------------------------------
[사전) 설정 및 정보 확인 사항]
// --------------------------------------------------------------------------------------

1. 제 목 : [Web] 자바스크립트 window.open 팝업창 새창 열기 수행 시 브라우저 팝업 차단 권한 체크 로직 정리


2. 테스트 환경 : Web / JavaScript / Chrome / window.open / 팝업 차단 권한


3. 사전) ✅ window.open 기능 간략 설명 : 

  >> window.open() 은 브라우저에서 새 창이나 새 탭을 열거나, 기존에 이름 붙인 창을 재사용할 때 사용하는 함수입니다

  >> window.open() 은 주로 외부 링크를 새 탭으로 열거나, 팝업(작은 창) 형태로 보조 UI를 띄울 때 사용됩니다

  >> window.open() target 주요 인자 값 : 

    - '_blank': 새 탭/창

    - '_self': 현재 창(일반 링크와 동일)

    - '_parent', '_top': 프레임/아이프레임 계층에 영향


4. 사전) ✅ window.opener.postMessage 기능 간략 설명 : 

  >> window.opener : 

    - 현재 창을 열어준 부모 창(Window 객체) 을 참조합니다

    -  A 페이지에서 window.open()으로 B 팝업을 열면, B에서 window.opener는 A를 가리킴

  >> postMessage : 

    - 서로 다른 출처(origin) 간에도 안전하게 메시지를 전달할 수 있는 브라우저 내장 API입니다

    - window.opener.postMessage(...)는 팝업(자식 창) → 부모 창 방향으로 메시지를 보내는 패턴입니다

  >> 부모에서 자식메시지 전송 간략 코드 : 

    let popupRef = window.open('/popup.html', 'child', 'width=480,height=640');
    
    popupRef?.postMessage(
          { type: 'INIT', payload: { theme: 'dark' } },
          event.origin // 혹은 'https://your-app.example.com'처럼 명시
        );

  >> 자식에서 부모에게 메시지 전송 간략 코드 : 

    if (window.opener) {
      window.opener.postMessage({ type: 'READY' }, window.location.origin /* 또는 부모의 origin 명시 */);
    }


5. 사전) 👉 window.open 시 옵션 설정 주요 참고 사항 간략 설명 : 

  >> noopener 사용해 window.open 지정 시 window.opener 가 null 이 됩니다

    - rel="noopener" 는 보안을 위해 오프너(opener) 연결을 끊는 옵션입니다.

    - noopener 가 있으면 브라우저가 의도적으로 이 참조를 제거하여 window.opener === null 로 만듭니다.

  >> 교차 출처(Cross-origin) 시 주의사항

    - 반드시 targetOrigin을 정확히 지정하세요.

    - '*' 옵션은 모드 오리진 허용을 설정하는 옵션이므로 사용 시 주의가 필요합니다

// --------------------------------------------------------------------------------------






// --------------------------------------------------------------------------------------
[로직 설명]

// --------------------------------------------------------------------------------------

1. [부모] 브라우저 자바스크립트 GLOBAL_WINDOW_OPEN_FLAG 전역 변수 선언 수행

  >> var GLOBAL_WINDOW_OPEN_FLAG = false; 

  >> 해당 전역 변수 플래그 값을 사용해 window.open 팝업창 열기 권한 체크 수행


2. [부모] 브라우저 자바스크립트 window.onload = async function() { }; 부분에 window.addEventListener('message') [자식] 이 전달한 메시지 수신 리스너 등록 수행

  window.onload = async function() { 

    // ........ 기타 코드 작성

    // ✅ [자식] 이 전달한 메시지 수신 리스너 등록
    window.addEventListener('message', (event) => {
      try {
        console.warn("window.addEventListener(message) : receive : ", JSON.stringify(event));
        console.warn('[event.data] : ', event.data, ' / [event.origin] : ', event.origin);

        // ✅ TWOK_POPUP_READY : Window 오픈 기능 체크 완료
        if (event.data.type !== null && event.data.type !== undefined && event.data.type !== '' && event.data.type == 'TWOK_POPUP_READY'){
          console.log('window.open : TWOK_POPUP_READY : success');
          
          GLOBAL_WINDOW_OPEN_FLAG = true; // ✅ 전역 변수 플래그 변경 >> 하위 windowOpenEnabled 함수에서 전역 변수 플래그 값 체크 로직 사용

          setTimeout(() => {
            alert('\n' + 'window.open 팝업창 열기 권한 체크가 완료 되었습니다 !!' + '\n');
          }, 2500);

        }

      }
      catch(error){
        console.error("window.addEventListener(message) : exception : ", error);
      }

    });

  };


3. [부모] 브라우저에서 window.onload = async function() { }; 부분에 자동으로 브라우저 접속 시 팝업창 활성 권한 체크 함수 호출 수행

  window.onload = async function() { 

    // ........ 기타 코드 작성

    // ........ [자식] 이 전달한 메시지 수신 리스너
    window.addEventListener('message', (event) => { });

    // ✅ 팝업창 활성 권한 체크 함수 호출 수행
    windowOpenEnabled();

  };


4. [부모] 브라우저 windowOpenEnabled 함수에서 [자식] 브라우저 동적 html 및 자바스크립트 코드 작성 후 window.open 새창 열기 수행 실시

  function windowOpenEnabled() {

    GLOBAL_WINDOW_OPEN_FLAG = false; // ✅ 전역 변수 플래그 변경

    try {

      // -----------------------------------------------
      // 1) window open 시 화면 사이즈 및 옵션 정의 수행
      // -----------------------------------------------
      const w = window.screen.availWidth; // 가용 화면 크기 구하기
      const h = window.screen.availHeight; // 가용 화면 크기 구하기

      //const w = 100; // 고정 사이즈 지정
      //const h = 100; // 고정 사이즈 지정

      const features = [ // features 구성
        `width=${w}`,
        `height=${h}`,
        'left=0',
        'top=0',
        'toolbar=no',
        'menubar=no',
        'location=no',
        'status=no',
        'resizable=no',
        'scrollbars=no',
        // 'noopener', // ✅ window.postMessag 위해 주석 처리
        // 'noreferrer', // ✅ window.postMessag 위해 주석 처리
      ].join(',');


      // -----------------------------------------------
      // 2) 스크립트 시퀀스를 깨서 HTML 파서가 조기 종료하지 않도록 보호 함수 정의
      // -----------------------------------------------

      function protectClosingScriptTag(code) {
        return String(code).replace(/<\/script/gi, '<\\/script');
      }


      // -----------------------------------------------
      // 3) 엔티티 (&lt; &gt;) → 실제 문자(< >) 로 디코드 함수 정의
      // -----------------------------------------------
      function decodeHtmlEntities(str) {
        const div = document.createElement('div');
        div.innerHTML = str;
        return div.textContent; // 또는 div.innerText
      }


      // -----------------------------------------------
      // 4) 주입할 스크립트 코드 작성 (필요 시 자유롭게 수정)
      // -----------------------------------------------    
      const scriptCode = `
        window.onload = function() {
          console.log('window open success');

          setTimeout(() => {

            try { 

              // ✅ 부모 리스너에게 이벤트 전송
              window.opener.postMessage({ type: 'TWOK_POPUP_READY', payload: { ts: Date.now() } }, '*'); 

              console.log('window.opener.postMessage : success');

              setTimeout(() => {
                console.log('setTimeout window.close');

                window.close(); // ✅ open 된 팝업창 닫기 처리
              }, 2000);

            } 
            catch(err) { 
              console.error('window.opener.postMessage : exception : ', err); 

              window.close(); // open 된 팝업창 닫기 처리
            }

          }, 1000); 

        };
      `;


      // -----------------------------------------------
      // 5) 엔티티 상태의 HTML 뼈대 작성
      // -----------------------------------------------
      const htmlBaseEntities = `&lt;!doctype html&gt;
        &lt;html&gt;
        &lt;head&gt;
          &lt;meta charset=&quot;utf-8&quot;&gt;
          &lt;title&gt; twok2kDynamicScript &lt;/title&gt;
          &lt;style&gt; body { margin: 0; height: 100vh; display: flex; justify-content: center; align-items: center; font-size: 40px; background-color: #000; color: #fff; } &lt;/style&gt;
        &lt;/head&gt;
        &lt;body&gt; window open 상태 체크 중 입니다 ... 잠시만 기다려주세요. (3초 뒤에 팝업창이 자동으로 닫힘 됩니다)          
        &lt;/body&gt;
        &lt;/html&gt;`;


      // -----------------------------------------------  
      // 6) 엔티티 상태의 </body>를 찾아 <script> 를 삽입 (여기선 엔티티 상태 유지)
      // -----------------------------------------------
      const composedEntities = htmlBaseEntities.replace(
        /&lt;\/body&gt;/i,
        `&lt;script&gt;${protectClosingScriptTag(scriptCode)}&lt;/script&gt;&lt;/body&gt;`
      );


      // -----------------------------------------------
      // 7) 최종 문자열을 실제 HTML로 디코드
      // -----------------------------------------------
      const finalHtml = decodeHtmlEntities(composedEntities);


      // -----------------------------------------------
      // 8) Blob URL 생성
      // -----------------------------------------------
      const blob = new Blob([finalHtml], { type: 'text/html;charset=utf-8' });
      const url = URL.createObjectURL(blob);


      // -----------------------------------------------
      // 9) window open 팝업 열기 
      // -----------------------------------------------
      const popup = window.open(url, '_blank', features);


      // -----------------------------------------------
      // 10) 메모리 정리: 약간 지연 후 Blob URL 해제 (열림 직후 바로 revoke 하면 로드 실패할 수 있음)
      // -----------------------------------------------    
      startLoading(); // 로딩 프로그레스 동작

      setTimeout(() => {
        console.log('setTimeout window.open permission check : ', GLOBAL_WINDOW_OPEN_FLAG);

        stopLoading(); // 로딩 프로그레스 종료

        try {
          URL.revokeObjectURL(url);

          if (GLOBAL_WINDOW_OPEN_FLAG == false){ // ✅ 전역 변수 플래그 값 체크 : 이벤트를 수신 받지 못함 : window open 차단 상태
            setTimeout(() => {

              alert('\n' + 'window.open 팝업창 권한이 비활성 상태 입니다 .. 상단 주소 표시줄에서 팝업창 표시 [허용] 을 해주세요.' + '\n');

            }, 1000);

          }

        }
        catch (err){
          console.error('window.open permission check : exception : ', err);
        }

      }, 3000); // ✅ 3초 대기 권한 확인

    } catch (exception) {
      console.error('windowOpenEnabled : Exception : ', exception);
    }

  };


5. window.open 새창 팝업창 열기 권한이 [부여] 된 경우 

  >> [자식] 브라우저가 열리면서 window.opener.postMessage [부모] 에게 메시지 전달 수행

  >> [부모] 브라우저에서는 window.addEventListener('message', (event) => { }); 에서 자식이 전달한 메시지가 감지 되며, 팝업창 권한 활성 상태 alert 표시 수행


6. window.open 새창 팝업창 열기 권한이 [부여 되지 않은] 경우

  >> windowOpenEnabled 함수에서 3초 뒤에 동작 되는 setTimeout 함수에서 전역 변수 Flag 값을 체크 수행

  >> 전역 변수 Flag 값이 false 인 경우 팝업창 활성 권한이 부여 되지 않았습니다. alert 표시 수행

// --------------------------------------------------------------------------------------






// --------------------------------------------------------------------------------------
[참고 사이트]
// --------------------------------------------------------------------------------------

[간단 소스] 자바스크립트 window.open 사용해 새창 팝업창 열기 후 window.opener.postMessage 메시지 전달 수행

https://kkh0977.tistory.com/8668

https://blog.naver.com/kkh0977/224202970329


[간단 소스] 자바스크립트 동적 html 코드 작성, new Blob 및 URL.createObjectURL 사용해 window.open 새창 열기

https://kkh0977.tistory.com/8452

https://blog.naver.com/kkh0977/224097132149


[자바스크립트 브라우저 사이즈, 해상도 구하기 및 window open 윈도우 창 열기 실시]

https://kkh0977.tistory.com/847

https://blog.naver.com/kkh0977/222392594793?trackingCode=blog_bloghome_searchlist


[자바스크립트 window open close 사용해 현재 브라우저 창 닫기 수행 - 브라우저 종료]

https://blog.naver.com/kkh0977/223203344998?trackingCode=blog_bloghome_searchlist

// --------------------------------------------------------------------------------------
 
728x90
반응형
Comments