UI5

destroy 안 부르면 큰일 — UI5 메모리 누수 #shorts #SAP #UI5

▶ YouTube에서 보기

UI5 메모리 누수는 왜 발생하는가

SAP UI5는 자바스크립트 기반 SPA(Single Page Application) 프레임워크입니다. 사용자가 앱을 장시간 사용하면서 페이지 이동을 반복할 때, 사용하지 않는 컨트롤이 메모리에 계속 남아있으면 점점 응답이 느려지고 결국 브라우저가 멈출 수 있습니다. UI5에서 메모리 누수가 발생하는 주요 패턴과 해결책을 코드로 설명합니다.

누수 패턴 1: Dialog를 매번 새로 생성하고 destroy 안 함

// 잘못된 패턴: 클릭할 때마다 새 Dialog 생성
onDetailPress: function(oEvent) {
  const oDialog = new sap.m.Dialog({
    title: "상세 정보",
    content: new sap.m.Text({
      text: oEvent.getSource().getTitle()
    }),
    endButton: new sap.m.Button({
      text: "닫기",
      press: function() { oDialog.close(); }
    })
  });
  oDialog.open();
  // 닫아도 DOM에서만 사라지고 메모리에 남음
  // 100번 클릭 = 100개의 Dialog 인스턴스
}
// 올바른 패턴 A: 싱글톤 + addDependent
onDetailPress: function(oEvent) {
  if (!this._oDetailDialog) {
    this._oDetailDialog = new sap.m.Dialog({
      title: "상세 정보",
      endButton: new sap.m.Button({
        text: "닫기",
        press: () => this._oDetailDialog.close()
      })
    });
    // 뷰에 종속 등록 — 뷰 destroy 시 함께 정리
    this.getView().addDependent(this._oDetailDialog);
  }

  const sTitle = oEvent.getSource().getTitle();
  this._oDetailDialog.getContent()[0]?.setText(sTitle);
  this._oDetailDialog.open();
},

// onExit: 명시적 destroy (addDependent로 자동 처리되지만 확실히)
onExit: function() {
  if (this._oDetailDialog) {
    this._oDetailDialog.destroy();
    this._oDetailDialog = null;
  }
}

올바른 패턴 B: Fragment로 Dialog 분리

// Fragment 파일: DetailDialog.fragment.xml
// <core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core">
//   <Dialog id="detailDialog" title="상세 정보">
//     <Text id="detailText" />
//     <endButton>
//       <Button text="닫기" press=".onDialogClose" />
//     </endButton>
//   </Dialog>
// </core:FragmentDefinition>

// Controller
onDetailPress: async function(oEvent) {
  if (!this._oFragment) {
    this._oFragment = await Fragment.load({
      id: this.getView().getId(),
      name: "com.myapp.view.DetailDialog",
      controller: this
    });
    this.getView().addDependent(this._oFragment);
  }
  this.byId("detailText").setText(oEvent.getSource().getTitle());
  this._oFragment.open();
},

onDialogClose: function() {
  this._oFragment.close();
}

누수 패턴 2: 이벤트 리스너를 수동으로 detach하지 않음

// 잘못된 패턴: attachEvent 후 detach 없음
onInit: function() {
  const oEventBus = sap.ui.getCore().getEventBus();
  // onInit 때마다 핸들러가 추가됨 (라우팅 반복 시 중복 등록)
  oEventBus.subscribe("MyApp", "orderUpdated", this._onOrderUpdated, this);
},

// 올바른 패턴: onExit에서 반드시 detach
onExit: function() {
  const oEventBus = sap.ui.getCore().getEventBus();
  oEventBus.unsubscribe("MyApp", "orderUpdated", this._onOrderUpdated, this);
}

누수 패턴 3: setInterval/setTimeout 정리 안 함

// 잘못된 패턴
onInit: function() {
  this._iTimerId = setInterval(() => {
    this._refreshData();
  }, 5000);
  // 뷰가 이동되어도 타이머가 계속 실행됨
},

// 올바른 패턴
onExit: function() {
  if (this._iTimerId) {
    clearInterval(this._iTimerId);
    this._iTimerId = null;
  }
}

누수 패턴 4: List Item 바인딩 후 모델 미해제

// 뷰를 동적으로 생성/제거하는 경우
// 올바른 패턴: 뷰 제거 시 모델 바인딩 해제
destroyDynamicView: function(oView) {
  // 바인딩 해제
  oView.unbindElement();
  oView.setModel(null);

  // 뷰 destroy
  oView.destroy();
}

메모리 누수 진단 도구

// UI5 진단 도구 활성화 (개발 중)
// URL에 ?sap-ui-debug=true 추가
// Chrome DevTools → Memory 탭 → Heap Snapshot 촬영
// 작업 전/후 스냅샷 비교로 누수 확인

// UI5 자체 통계
sap.ui.getCore().getUIDirty(); // 렌더링 대기 상태
// Chrome DevTools Console
// sap.ui.getCore().byId("myDialog") // null이어야 함

메모리 누수 방지 체크리스트

  • Dialog/Popover는 싱글톤 패턴 + addDependent
  • onInit에서 등록한 EventBus 핸들러는 onExit에서 반드시 unsubscribe
  • setInterval/setTimeout은 onExit에서 clear
  • 동적으로 생성한 컨트롤은 사용 후 destroy
  • Fragment.load는 캐시 활용 (같은 Fragment를 여러 번 load하지 않음)

공식 문서

UI5 메모리 관리 가이드는 UI5 Best Practices — Memory Management에서 확인하세요.

댓글 0

아직 댓글이 없습니다.