티스토리 뷰

Dev

vue3 modal 모달 열고 닫기

hjhj97 2023. 3. 9. 15:28

모바일 환경에서 모달창이 떠 있는 상태에서 모달을 닫는 방법은 여러가지가 있을 수 있는데, 그 중 첫 번째 방법은 모달 창 내부에 별도의 'X'버튼을 표시해서 이 영역을 터치하면 닫히게 하는 방법이 있다. 가장 직관적인 방법이지만 버튼을 위한 별도의 공간을 확보해야 하기 때문에 디자인상 의도치 않은 공간을 차지한다는 단점이 있다. 이런 단점을 해결하기 위한 두 번째 방법은 모달 바깥의 영역을 터치를 감지했을 때 모달을 닫는 방법이 있다. 다만 이 방법 역시 명시적인 '닫기'버튼이 보이지 않기 때문에 이러한 UI/UX가 낯선 사용자에게는 모달을 어떻게 닫아야 하는지 헤맬 수도 있다는 단점이 있다. 세 번째 방법은 (안드로이드 환경에서) 뒤로가기 버튼을 눌렀을 때 모달이 닫히게 하는 방법이다. 이 방법의 경우, 모달창과 직접적인 상호작용이 일어나지 않으므로 개발자 입장에서는 뒤로가기 이벤트를 감지해야 하는 등 꽤나 까다로운 작업이다. 이번 글에서는 그 방법에 대해서 설명하겠다.
지난 글에서 작성했던 TeleportModal.vue 를 재사용 한다.

뒤로가기를 눌렀을 때 모달창이 닫히게 만들기 위해서는, 모달창이 열려있는 상태를 window 전역객체의 history에 저장(push)해야 한다. 이때 사용하는 함수가 window.history.pushState()함수이다. 이 함수에 대한 자세한 설명은 MDN을 참고하기 바란다.
pushState()함수의 첫 번째 인자로는 state를 전달해야 하는데, 정해진 양식이 있는 것이 아니라 개발자가 식별할 수 있는 정보를 담아서 객체 형태로 넘기기만 하면 된다. 나는 {page : 'modal'} 객체를 넣었다.

// TeleportModal.vue
// template

  <teleport to="#desktop-modal">
    <div class="modal">
      <transition name="slide-up" appear>
        <div class="modal-content" >
          <h1>This is Modal</h1>
        </div>
      </transition>
    </div>
  </teleport>

// script
export default{
  setup(){
    onMounted(() => {
      history.pushState({ page: 'modal' }, document.title);
    });
  }
}

그리고 모달창 버튼은 임시로 TopMenuBar.vue에다가 위치시키고, v-if를 통해 modalStoremodal값에 따라서 표출여부를 결정하였다.

// TopMenuBar.vue
<template>
  <nav>
    ...
    <router-link to="" @click="modalStore.openModal()">Modal</router-link>


    <TeleportModal v-if="modalStore.modal" />
  </nav>
</template>

모달창이 열리고, 닫히는 상태는 vue의 전역상태관리 라이브러리 pinia를 통해서 관리한다. 모달창을 열기 위해서는 modalStore.openModal()함수를 호출하면 된다. 이 함수가 호출되면 state.modal의 값이 true가 되면서 v-if로 걸려있었던 <TeleportModal>이 렌더링된다.

// stores/modal.js
import { defineStore } from "pinia";

export const useModalStore = defineStore("modal", {
  state: () => {
    return {
      modal: false
    };
  },
  actions: {
    openModal() {
      this.modal = true;
    },
    closeModal() {
     this.modal = false;
  }
});

이제 버튼을 누르면 모달창이 켜지면서 동시에 history에도 모달이 켜져 있는 상태가 추가될 것이다. 이 상태에서 '뒤로가기' 버튼을 누르면 모달창이 꺼짐을 확인해볼 수 있다.
여기까지가 위에서 설명했던 모달창은 닫는 3가지 방법 중에서 세 번째 방법이고 첫 번째 방법과 두 번째 방법도 설명하겠다.
우선 '닫기'버튼을 눌러서 모달창을 닫기 위해서는 modalStore.closeModal()함수를 호출하면 된다. 모달 바깥의 영역을 터치했을 때 닫히게 하려면 @vueuse/core 가 제공하는 onClickOutside함수를 이용하면 된다. vue의 template ref로 모달 영역을 잡아준 다음에 onClickOutside 함수의 인자로 넣고, 콜백으로 modalStore.closeModal()해주면 된다.

다만 이렇게 했을 경우 문제가 하나 생긴다.
모달이 열릴 때(onMounted할 때) history.pushState()를 해주었기 때문에 또 다른 페이지가 이미 열려있는 상태이다. 여기서 뒤로가기를 누르면 이 상태가 닫히므로 아무 문제가 없지만, 뒤로가기를 누르지 않고 첫 번째, 두 번째 방법으로 모달을 닫을 경우에는 closeModal함수 호출과 함께 반드시 현재 페이지를 스스로 pop 해주어야 한다. 즉 history.back()을 호출해주어야 한다.

댓글