티스토리 뷰

728x90

작년 여름인가 잘 되던 서비스가 갑자기 되지 않은 적이 있었다. alert과 confirm이 동작하지 않아 검증로직의 실행에 문제가 생겼기 때문이다. 문제는 크롬 브라우저에서 iframe으로 실행되고 있는 프로그램의 alert, confirm, prompt 기능을 제거해버렸기 때문이다. 우리 시스템은 메인 시스템 외에 별도의 인터넷 서비스를 위해서 공개 홈페이지의 서비스 일부 신청 페이지가 iframe으로 들어가 있다.

 

하지만 일주일 정도지나 전세계의 수많은 개발자들의 원성으로 다시 크롬은 원상복구 되었다.

그리고 12월 2일에 아래 내용처럼 deprecation 결정을 지연하기로 했지만 언제든지 적용 가능한 상태에 있다.

 

After talking to developers and considering the options, we have decided to postpone the launch of this deprecation while we investigate adding a feature policy, which may take some time. We will provide several months of advance notice in the future when we decide to re-enable it, and the enterprise policy and origin trial opt-outs will be available at that point.

 

크롬의 이슈 페이지는 아래와 같다.

 

Remove alert(), confirm(), and prompt for cross origin iframes - Chrome Platform Status

Motivation The current UI for JS dialogs (in general, not just for the cross-origin subframe case) is confusing, because the message looks like the browser’s own UI. This has led to spoofs (particularly with window.prompt) where sites pretend that a part

chromestatus.com

 

해당 이슈에 대한 진행은 아래를 참고 하면 된다.

 

1065085 - chromium - An open-source project to help move the web forward. - Monorail

 

bugs.chromium.org

 

1. 갑작스럽게 발생한 문제로 인해서 긴급하게 Front End 개발자에게 Custom Alert, Confirm을 만들라는 이야기를 하고, 혹시나 몰라서 나도 가능한 코드를 만들어 보았다. 

  1-1 사실 alert이나 confirm은 custom으로 구현하기 어렵지 않다.

  1-2 하지만, 문제는 confirm인데 우리 시스템의 프로그램의 검증로직의 중간에 사용자의 선택을 기다리는 부분이 있다.

  1-3 이걸 callback으로 구현해서 해당 위치에서 confirm 띄우고 yes를 선택하면 callback을 실행하면 될 것 처럼 보이지만 서비스가 4개가 묶여 있는 거라 분리하기도 힘들고 검증로직 역시 너무 복잡해서 조건마다 분기 문을 다시 작성해야 한다.

  1-4 즉 진짜 중간에 멈추지 않는 이상 해결책이 너무 복잡한 상황이다.

 

2. 이 문제가 발생하고 바로 2일도 안 지나서 구글의 정책은 일단 보류가 되어 서버스가 재개되어 상황은 일단 종료되었지만, 근본적인 해결이 필요해서 개발자가 해결하기를 기다렸는데, 브라우저 실행을 중간에 멈추는 건 불가능하다는 이야기를 했다. 주변에서도 해결한 사람이 없다는 이야기만 한다.

  2-1 사실 stackoverflow를 검색해도 callback 사용정도만 나와있지 정확하게 해결책을 제대로 제시한 곳은 없어보이긴 한다.

  2-2 그런데 이건 이중 async/await으로 검증 로직 중간에 실제 정지하도록 만들 수 있다.

  2-3 물론 정지를 해도 브라우저에서 지원하는 alert과 confirm과는 다르게 브라우저를 마우스로 조작할 수 있기 때문에 modal처리를 해야 한다. 이 부분은 단순한 문제가 알아서 해결하길

 

이전 구조

<style>
스타일 지정
</style>
<script type="text/javascript">

변수블럭

$(document).ready(function(){

온로딩 및 이벤트 함수 부분

    $("#btnCvplApply").on("click", function(){

        사전 검증로직 블록

        // 전자고지(이메일) 유효성 검사
        if(checkEmailTyp && checkEmail){
            if (!fncEmailRegist(gojiYn)) return;
        }
        
        // 전자고지(휴대폰) 유효성 검사
        if(checkSmsTyp && checkEmail){
            if(!fncSmsRegist(smsGojiYn)) return;
        }
        
        사후 검증 및 분기로 서비스 호출블록
    });
});

************* 인증함수들 ************
</script>
//이메일고지 유효성 검사
function fncEmailRegist(gojiYn){
	검증함수 boolean 돌려줌
}

//문자고지 유효성 검사
function fncSmsRegist(smsGojiYn){
	검증함수 boolean 돌려줌
}

 

변경 구조

0. 실제 코드는 서비스 신청용 2000라인, 별도의 지원 함수를 위한 파일에 1000라인의 코드가 들어 있어 필요한 부분만 남겼다. 좋은 코드는 아니다.

1. 가장 위의 babel, polyfill은 ie11를 지원하기 위해서 넣은 건데 이건 돌아간다는 프로토타입으로 만든 것이기 때문에 실제로 사용할 경우에는 컴파일을 한 소스를 넣어주는 게 좋다.

2. 스타일과 custom confirm 창은 혹시 참고가 될지 몰라 남겨뒀다.

3. 2중 aynch/await으로 브라우저 실행을 멈출 수 있다는 것을 보여주기 위함이다.

4. 비동기 함수 안에서 비동기 함수 ui.confirm가 실행된다. (이부분 빼고 포스트를 작성해서 다시 채워넣었음)

 

<!-- pilseong, babel, polyfill ES6 async, awake 문법 사용을 위한 라이브러리 import --> 
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.10.4/polyfill.min.js"></script>

<style>
/*숫자목록 스타일 */
.ol_decimal{
    display: block;
    list-style-type: decimal;
    margin-top: 0.2em;
    margin-bottom: 0.2em;
    margin-left: 0;
    margin-right: 0;
    padding-left: 20px;
}

/* pilseong customer confirm 모달의 화면 표출 정의 */
.example {
    padding: 20px;
}
input[type="button"] {
    padding: 5px 10px;
    margin: 10px 5px;
    border-radius: 5px;
    cursor: pointer;
    background: #ddd;
    border: 1px solid #ccc;
}
/* input[type="button"]:hover {
    background: #ccc;
} */
.confirm {
    display: none;
}
.confirm > div:first-of-type {
    position: fixed;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.5);
    top: 0px;
    left: 0px;
    z-index: 1;
}
.confirm > div:last-of-type {
    padding: 10px 20px;
    background: white;
    position: absolute;
    width: auto;
    height: auto;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    border-radius: 5px;
    border: 1px solid #333;
    z-index: 100;
}
.confirm > div:last-of-type div:first-of-type {
    min-width: 150px;
    padding: 10px;
}
.confirm > div:last-of-type div:last-of-type {
    text-align: right;
}
</style>

<!-- pilseong, babel, polyfill ES6 async, awake 문법 사용을 위한 정의--> 
<script type="text/babel" data-presets="es2015,stage-2">

변수선언 블록

$(document).ready(function(){

    ready 구문 및 이벤트 처리 구문 블록

    $("#btnCvplApply").on("click", function(){
        // pilseong, 검증로직은 checkRoutine 안에 들어가야 한다.
        // pilseong, 버튼을 누르는 실제로는 checkRoutine이 async이기 때문에 바로 종료된다.
        checkRoutine();
    });

});

// pilseong, custom confirm 함수 정의
const ui = { confirm: async (message) => createConfirm(message)   }
const createConfirm = (message) => {
  return new Promise((complete, failed)=>{
    $('#confirmMessage').text(message)

    $('#confirmYes').off('click');
    $('#confirmNo').off('click');
        
    $('#confirmYes').on('click', ()=> { $('.confirm').hide(); complete(true); });
    $('#confirmNo').on('click', ()=> { $('.confirm').hide(); complete(false); });
   
    // pilseong, 아래의 계산 부분은 스크롤이 있어도 화면 한가운데 confirm이 나오도록 조정
    // Get the document offset :
    var offset = $(document).scrollTop(),

    // Get the window viewport height
    viewportHeight = $(window).height(),

    // cache your dialog element
    $myDialog = $('.confirm > div:last-of-type');

    // now set your dialog position
    $myDialog.css('top',  (offset  + (viewportHeight/2)) - ($myDialog.outerHeight()/2));
    
    $('.confirm').show();
  });
}

// pilseong, async, await을 사용하기 때문에 비동기함수로 정의한다.
const checkRoutine = async (text, delIdx) => {

	검증로직이 여기로 이동되었다. 동일한 코드를 사용하면 된다.
    
    사전 검증 블록
		
    // 전자고지(이메일) 유효성 검사
    if(checkEmailTyp && checkEmail){
        // pilseong, fncEmailRegist가 종결될 때 까지 대기 
        if (!(await fncEmailRegist(gojiYn))) return;
    }
    
    // 전자고지(휴대폰) 유효성 검사
    if(checkSmsTyp && checkEmail){
        // pilseong, fncSmsRegist가 종결될 때 까지 대기
        if(!(await fncSmsRegist(smsGojiYn))) return;
    }
    
    사후 검증 블록	
}

  검증 함수 블록

</script>

      <!-- pilseong, custom confirm html 테그 -->
      <div class="confirm">
        <div></div>
        <div>
          <div id="confirmMessage">Confirm text</div>
          <div>
            <input id="confirmYes" type="button" value="예" />
            <input id="confirmNo" type="button" value="아니오" />
          </div>
        </div>
      </div>

 

// 전자고지 유효성 검사
// pilseong, customer conform을 사용하기 위하여 비동기로 변경
async function fncEmailRegist(gojiYn){

    이메일 검증로직 boolean 리턴 검증 블록
    
      var result = await ui.confirm('법인(또는 단체)의 경우 사업자등록증(등기부등본), 신청자 신분증, 위임장(또는 재직증명서)등은 처리담당자가 추가로 서면으로 제출받아야합니다. 그래도 계속 진행하시겠습니까?'); 
      if(result) { //yes
        if(saNo.length !=  10 ){		
          alert_msg('법인 사업자번호를 확인하시고 입력하세요.');
          $("#saNo").select();
          return false;
        }
      }
  
    검증 블록 추가
  
}

//문자고지 유효성 검사
// pilseong, customer conform을 사용하기 위하여 비동기로 변경
async function fncSmsRegist(smsGojiYn){

  문자고지 검증로직 boolean 리턴
  
       var result = await ui.confirm('법인(또는 단체)의 경우 사업자등록증(등기부등본), 신청자 신분증, 위임장(또는 재직증명서)등은 처리담당자가 추가로 서면으로 제출받아야합니다. 그래도 계속 진행하시겠습니까?');
       if(result) { //yes
       //alert_msg("saNo.length : " + saNo.length);
         if(smsSaNo.length !=  10 ){        
           alert_msg('법인 사업자번호를 확인하시고 입력하세요.');
           $("#smsSaNo").select();
           return false;
         }
       }
         
         
    검증블록 추가
  
}
728x90
댓글