MUI 프레임워크용 크로스 플랫폼 로딩 오버레이 컴포넌트 구현

MUI 기반 하이브리드 앱이나 웹 환경에서 네이티브 대기 화면(Plus Runtime)과 표준 DOM 오버레이를 자동 분기 처리할 수 있는 로딩 상태 표시 컴포넌트입니다. 기존 프레임워크 내장 메서드의 환경별 제약 사항을 보완하여, 단일 API로 일관된 UX를 제공하는 것을 목표로 합니다.

JavaScript 구현 로직

DOM 요소의 생명주기를 추적하고, 중복 호출 시 상태를 갱신하며, 환경 감지를 통해 적절한 UI 레이어를 렌더링합니다.

(function(globalContext, muiInstance) {
  const OVERLAY_KEY = 'hybrid-loader-root';
  const MASK_KEY = 'loader-backdrop';
  const ACTIVE_STATE = 'state-active';

  function initializeLayerStructure() {
    let backdropNode = globalContext.document.getElementById(MASK_KEY);
    if (!backdropNode) {
      backdropNode = globalContext.document.createElement('div');
      backdropNode.id = MASK_KEY;
      backdropNode.setAttribute('aria-hidden', 'true');
      globalContext.document.body.appendChild(backdropNode);
    }

    let spinnerContainer = globalContext.document.getElementById(OVERLAY_KEY);
    if (!spinnerContainer) {
      spinnerContainer = globalContext.document.createElement('div');
      spinnerContainer.id = OVERLAY_KEY;
      spinnerContainer.setAttribute('role', 'alert');
      spinnerContainer.innerHTML = `
        <i class="mui-spinner mui-spinner-white"></i>
        시스템 처리 중입니다.
      `;
      globalContext.document.body.appendChild(spinnerContainer);
    }
    return { backdrop: backdropNode, container: spinnerContainer };
  }

  muiInstance.showLoading = function(displayMessage, forceEnv) {
    const promptText = displayMessage || '데이터를 불러오는 중입니다.';
    
    if (muiInstance.os.plus && forceEnv !== 'web') {
      muiInstance.plusReady(() => {
        plus.nativeUI.showWaiting(promptText);
      });
      return;
    }

    const { backdrop, container } = initializeLayerStructure();
    const statusSpan = container.querySelector('.loader-status-text');
    if (statusSpan) statusSpan.textContent = promptText;

    backdrop.classList.add('mask-active');
    container.classList.add(ACTIVE_STATE);
  };

  muiInstance.hideLoading = function(postHideAction) {
    if (muiInstance.os.plus) {
      muiInstance.plusReady(() => {
        plus.nativeUI.closeWaiting();
      });
    }

    const containerRef = globalContext.document.getElementById(OVERLAY_KEY);
    const maskRef = globalContext.document.getElementById(MASK_KEY);

    if (containerRef) {
      containerRef.classList.remove(ACTIVE_STATE);
    }
    if (maskRef) {
      maskRef.classList.remove('mask-active');
    }

    if (postHideAction && typeof postHideAction === 'function') {
      setTimeout(postHideAction, 250);
    }
  };
})(window, mui);

CSS 시각화 및 전환 효과

Flexbox 기반 중앙 정렬과 CSS 변수를 활용하여 테마 연동이 용이하도록 개선했습니다.

:root {
  --overlay-surface: rgba(15, 15, 15, 0.82);
  --text-primary: #f8f9fa;
  --anim-duration: 0.25s;
  --scale-enter: 0.85;
  --scale-exit: 1;
}

#loader-backdrop {
  position: fixed;
  inset: 0;
  background: transparent;
  z-index: 9000;
}

.mask-active {
  background: transparent;
}

#hybrid-loader-root {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 150px;
  min-height: 150px;
  padding: 20px 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  background: var(--overlay-surface);
  color: var(--text-primary);
  border-radius: 10px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  z-index: 9500;
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -50%) scale(var(--scale-enter));
  transition: opacity var(--anim-duration) ease, transform var(--anim-duration) ease, visibility var(--anim-duration);
  pointer-events: none;
}

.state-active {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, -50%) scale(var(--scale-exit));
}

#hybrid-loader-root .mui-spinner {
  width: 30px;
  height: 30px;
}

#hybrid-loader-root .loader-status-text {
  font-size: 13px;
  letter-spacing: -0.02em;
  line-height: 1.6;
  text-align: center;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}

적용 사항

구현 코드는 애플리케이션 라우터 진입점 또는 전역 유틸리티 파일에 통합하면 됩니다. 첫 번째 인자에 문자열을 전달하면 해당 텍스트로 상태 메시지가 동적 교체되며, 두 번째 인자를 지정하지 않거나 `'web'` 값을 전달하면 표준 오버레이 모드가 실행됩니다. Plus Runtime 환경에서 두 번째 인자를 명시적으로 생략한 경우 네이티브 대기 화면으로 우회 적용됩니다. 연속 호출 시 초기화되지 않고 내부 DOM 노드의 텍스트 노드만 갱신되므로, AJAX 페칭이나 페이지 전환 간격에도 안정적인 피드백을 유지합니다.

태그: mui-framework hybrid-application-development css-flexbox-layout javascript-component-design web-performance-optimization

9월 22일 02:31에 게시됨