중복 창 실행 방지 및 기존 창으로 포커스 되게 하기
LMS에서 Viewer 팝업이 중복으로 열리는 문제를 window.open의 name 속성과 BroadcastChannel API로 해결한 과정을 정리했다.
당사에서 개발한 페이지는 외부 버튼 클릭을 통해 진입하는 구조다. 설계 당시부터 '마이크로 프론트엔드(Micro Frontends)' 아키텍처를 의도했는지는 확실치 않으나, 현재 각각의 서비스를 기업 단위로 나누어 개발 및 관리하고 있다.
lms - 학습관리(타사)
viewer - 학습페이지(당사)
요구사항은 LMS에서 viewer 팝업창으로 열리게 되는데, 이 팝업창이 중복해서 또 열리지 않게 해달라는 것이었다.
문제 상황
동일한 viewer 창이 이미 열려 있음에도 불구하고, 사용자가 진입 버튼을 다시 눌렀을 때 viewer 창이 중복으로 생성되는 문제다.
우리 서비스 특성상 중복 창 실행을 방지하지 않음으로써 발생하는 가장 큰 문제는 데이터 정합성의 오류이다.
💡데이터 정합성
여러 데이터베이스나 시스템에 저장된 데이터들이 서로 모순 없이 일치하고 논리적으로 올바른 상태를 유지하는 것
서비스의 안정성을 위해서는 기존에 열린 창이 있다면 새 창을 띄우는 대신 이미 열려 있는 창을 식별하고 제어하는 로직이 반드시 필요했다.
해결 방법
처음 요구사항을 봤을 때, 브라우저 스토리지나 윈도우 이벤트를 활용하면 될 거 같았다. 또한 lms와도 연관이 있을거라고 판단하여 타사에 요청하는것도 고려했다.
먼저 요청 전에는 할 수 있는 것들을 최대한 해보고 요청했다.
window.open의 name 속성 활용
가장 기본적인 방법은 window.open() 메서드의 두 번째 인자인 name 속성을 활용하는 것이다.
브라우저는 동일한 이름을 가진 창이 이미 열려 있다면, 새로운 창을 띄우지 않고 기존 창의 위치를 해당 URL로 이동시키거나 포커싱한다.
// 예시 코드: 중복 이름을 지정하여 팝업 호출
const openViewer = () => {
const windowName = "LMS_Viewer_Popup";
const url = "https://viewer.company.com/study";
// 동일한 windowName이 있으면 그 창을 제어한다.
const viewerWindow = window.open(url, windowName);
if (viewerWindow) {
viewerWindow.focus(); // 이미 열려 있다면 해당 창으로 포커스 이동
}
};
사실상 이걸로 수정하는 게 가장 정답이다.
단지 내가 수정하는 게 아니라 lms측에서 수정을 해줘야 하는 사항이다.
BroadcastChannel API 활용
탭과 탭 사이의 메시지를 주고받을 수 있게 해준다. 우리는 해당 API를 활용해서 기능을 구현한 적도 있다.
// Viewer의 진입점(index.js 또는 App.js)에서 실행되는 로직
const channel = new BroadcastChannel("viewer_check_channel");
// 1. 다른 창에서 오는 메시지 리스닝
channel.onmessage = (event) => {
if (event.data === "ARE_YOU_OPEN") {
// 기존 창: "나 이미 열려 있어"라고 응답하고 자신을 앞으로 가져옴
channel.postMessage("I_AM_ALIVE");
window.focus();
}
if (event.data === "I_AM_ALIVE") {
// 새 창: 이미 열려 있는 창이 있다는 응답을 받으면 즉시 종료
alert("이미 학습 창이 열려 있어 기존 창으로 이동합니다.");
window.close();
}
};
// 2. 페이지 로드 시점에 중복 체크 메시지 발송
channel.postMessage("ARE_YOU_OPEN");
로컬에서 짧게나마 테스트했을 때, 팝업이 닫히고 요구사항대로 제대로 동작을 했다.
하지만 창이 열렸다가 닫히는 게 눈에 보이면서 사용자경험이 저하 될 수 있겠다는 생각이 들었다.(사실 기업에서는 중요하다고 생각하지 않을 듯)
결론
BroadcastChannel 방식은 ‘창을 여는 주체(LMS)’를 제어할 수 없을 때의 차선책으로 생각하고 있었다.
새 창이 잠깐 열렸다가 닫히는 과정이 생길 수 있고, 메시지 응답을 기다리기 위한 타이밍(레이스) 처리가 필요해 구현이 복잡해진다.
무엇보다 브라우저가 제공하는 window.open(name)의 기존 창 재사용 기능을 우회해서 애플리케이션 레벨에서 억지로 ‘중복 방지’를 구현하는 셈이라, 운영 환경에서는 예외 케이스(포커스 정책, 팝업 차단, 환경별 지원 범위)에 더 민감해질 수 있다.
그래서 최종적으로는, 가장 단순하고 안정적인 해법인 LMS에서 window.open의 name을 지정해서 창이 열리도록 요청하는 방향을 선택했다.
마치며
문제를 해결하는 과정에서 “당장 막는 방법”도 있었지만, 결국 중복이 생기는 원천을 손대야 한다고 판단했다.
그래서 최종적으로는 창을 여는 주체인 LMS가 window.open의 규칙을 바로잡아 기존 창을 재사용하도록 하는 방향이 가장 합리적이라고 결론 내렸다.
END OF ARTICLE