레거시 현대화 · Legacy Frontend

연쇄 팝업이 차단되자 iframe과 postMessage로 단계 전환하기

브라우저 정책에 따라 끊기던 다단계 화면을 다이얼로그·iframe·postMessage로 전환했습니다. 기존 팝업 호출도 같은 화면을 사용할 수 있게 유지했습니다.

JavaScriptJSPiframepostMessageLegacy Frontend

문제의 출발점

레거시 웹 시스템에서는 window.open()으로 업무 단계를 나눈 화면을 자주 볼 수 있습니다. 한 팝업이 닫힌 뒤 다음 팝업을 여는 흐름이 이어지면 브라우저 정책에 따라 동작이 달라질 수 있습니다.

실제 화면도 첫 팝업에서 저장한 뒤 현재 창을 닫고 다음 입력 팝업을 여는 구조였습니다. 선택 건수가 늘어 처리 시간이 길어지면 후속 단계가 열리지 않는 조건을 재현했습니다.

후속 팝업이 최초 클릭의 직접 결과로 인정되지 않았습니다

첫 번째 팝업은 사용자 클릭 안에서 실행되므로 허용될 수 있습니다. 반면 비동기 저장, 반복 입력과 현재 창 종료 뒤 실행되는 후속 팝업은 최초 사용자 동작과 직접 연결된 호출로 인정되지 않을 수 있습니다.

function completeStep() {
  saveCurrentStep().then(function (result) {
    window.close();
    window.opener.openNextStep(result.businessKey);
  });
}

해결 목표는 업무 단계 전환이 새 창 생성에 의존하지 않게 만드는 것이었습니다.

부모 화면이 단계 전환을 관리하게 했습니다

개선 뒤에는 부모 화면이 다이얼로그를 열고 그 안의 iframe 하나를 재사용합니다. 아래 예시에서는 실제 화면 ID와 URL, 크기 값만 일반화했습니다.

<div id="workflowDialog" hidden>
  <iframe id="workflowFrame" title="업무 단계"></iframe>
</div>
function openWorkflow(url) {
  const frame = document.getElementById("workflowFrame");
  frame.src = url;
  openModal("workflowDialog");
}

부모는 다이얼로그의 열기·닫기와 현재 단계를 관리하고, 자식은 자신의 입력과 저장 결과만 관리합니다. 화면 간 결합은 opener의 함수 직접 호출 대신 정해진 메시지 형식으로 제한했습니다.

postMessage로 저장 결과를 전달했습니다

자식 화면은 저장이 끝나면 일반화한 이벤트와 필요한 최소 데이터만 부모에 보냅니다.

window.parent.postMessage(
  {
    type: "workflow:advance",
    payload: { businessKey: result.businessKey }
  },
  window.location.origin
);

부모는 출처와 메시지 타입을 확인한 뒤 다음 화면으로 전환합니다.

window.addEventListener("message", function (event) {
  if (event.origin !== window.location.origin) return;
  if (!event.data || typeof event.data.type !== "string") return;

  if (event.data.type === "workflow:advance") {
    loadNextStep(event.data.payload.businessKey);
  }

  if (event.data.type === "workflow:complete") {
    closeModal("workflowDialog");
    reloadParentView();
  }
});

이 예시는 targetOrigin, event.origin과 메시지 타입을 확인하는 범위까지 보여 줍니다. payload.businessKey의 존재 여부와 자료형은 검사하지 않으므로, 메시지 종류를 늘리거나 다른 출처와 연계할 때는 페이로드 스키마 검증을 별도로 추가해야 합니다.

기존 팝업 호출도 같은 화면을 사용하게 했습니다

같은 레거시 화면이 iframe과 기존 팝업 양쪽에서 호출될 수 있었습니다. 영향 범위를 통제하기 위해 실행 환경을 구분해 두 경로를 함께 지원했습니다.

function notifyComplete(businessKey) {
  if (window.self !== window.top) {
    window.parent.postMessage(
      { type: "workflow:complete", payload: { businessKey } },
      window.location.origin
    );
    return;
  }

  if (window.opener && !window.opener.closed) {
    window.opener.reloadParentView();
  }
  window.close();
}

이 호환 코드는 기존 호출부를 깨지 않고 단계적으로 전환하기 위해 남겼습니다.

검증 범위

  • 결과 상세 화면에서 다건 등록
  • 임시저장 상태에서 이어서 등록
  • 첫 단계에서 다음 입력 화면으로 전환
  • 완료 후 다이얼로그 종료와 부모 화면 갱신
  • 본사 적용 후 해외법인의 동일 흐름
  • 기존 팝업 경로의 회귀 여부

브라우저 팝업 허용 여부와 관계없이 업무 단계가 하나의 다이얼로그 안에서 이어지고, 기존 팝업 호출부도 깨지지 않는 것을 확인했습니다.

정리

부모 화면이 단계 전환을 맡고, 자식 화면은 저장 결과만 메시지로 알리도록 역할을 나눴습니다. 덕분에 후속 단계가 새 창을 여는 시점에 영향을 받지 않게 됐습니다.