VueJS/Nuxt

[VueJS] NuxtJS에서의 window.open 사용

SongMinu 2021. 8. 30. 20:44
728x90

회사에서 옛날에 만들어진 BackboneJS + Spring 기반 프로젝트의 대부분 기능을 NuxtJS + NodeJS (+VuetifyJS)로 새로 만들면서 디자인, 기능 등 새롭게 만들었다.

기존 기능들 중에 window.open을 이용해서 팝업을 통해 처리되는 기능들이 정말 많았는데.

레이아웃 구조를 완전히 바꾸거나 모달을 이용해 대부분 처리했다. (개인적으로 팝업을 사용하는 것을 선호하지 않음)

하지만, 특정 몇몇 기능의 경우 이건 무조건 팝업으로 해야 사용자가 편하다는 부분이 있어서 만들게 되었는데,

하는 과정에서 생각해야 할 부분도 좀 있고, 생각지도 못한 부분들도 몇 가지 발생했던 김에 블로그에 글을 작성하기로 했다.


1. 생각해야할 부분

2. 소스코드

3. store 문제

 

1. 생각해야할 부분

먼저 NuxtJS는 프로젝트 생성 시 기본적으로 만들어주는 pages 디렉터리 안의 구조에 맞춰 자동으로 라우터 정보를 만들어 준다.

여기서 3가지의 고민이 생겼었다.

1. 보통 팝업에서는 특정 페이지를 팝업으로 보여줄 때, 해당 페이지 영역만 보여주고 메뉴나 헤더, 푸터 등은 보여주지 않기 때문에 layout 파일을 생각해야 한다.

2. 페이지를 팝업으로 보여줄 때 이미 만들어져 있는 소스는 layout이 default로 지정되어 있는데 팝업용 layout 지정이 가능할까? 팝업용으로 파일을 또 만들어야 할까?

3. 페이지가 아닌 components 디렉터리에 있는 특정 컴포넌트 파일만 팝업으로 보여줘야 할 경우는?

 

우선 1번은 NuxtJS에서 만들어주는 layout 디렉터리에 팝업용 vue 파일을 생성해서 팝업창일 뜰 땐 해당 파일로 layout을 지정해주면 된다.

layout 디렉터리에 메뉴나 헤더, 푸터 등은 제외한 popup.vue파일을 생성

 

layout/popup.vue 파일 template 구조
layout/default.vue 파일 template 구조

두개를 비교해보면 메뉴나 하단 등의 컴포넌트를 제외한 걸 볼 수 있다.

 

 

2번은 좀 더 뒤에 소스코드로 설명

3번의 경우 좀 고민을 많이 했다. 팝업을 열어주는 소스가 window.open('url', 'name', 'options') 이렇게 구성되어 있는데.

url로 열기 위해선 pages 디렉터리에 있어야하고, components에 있는 파일은 url로 열 수가 없으니 pages에 따로 구성을 해봐야겠다 라고 생각하고 작업을 시작했다.

 

2. 소스코드

먼저 예제로 만든 화면은 이렇다.

왼쪽 버튼은 컴포넌트 파일을 팝업으로 띄우고, 오른쪽 버튼은 화면에 보여지고 있는 페이지를 팝업으로 띄우게 해 놨다.

<template>
  <v-container fluid>
    <v-layout column>
      <v-card>
        <v-card-text>
          <v-row>
            <v-col cols="6">
              <v-btn @click="open_component" width="100%">component 파일 팝업 띄우기</v-btn>
            </v-col>
            <v-col cols="6">
              <v-btn @click="open_page" width="100%">page 파일 팝업 띄우기</v-btn>
            </v-col>
          </v-row>
        </v-card-text>
      </v-card>
    </v-layout>
  </v-container>
</template>

<script>
export default {
  layout() {
    if (opener) {
      console.log('This is window.open');
      return 'popup'
    }
  },
  data() {
    return {
      pop: null,
    }
  },
  methods: {
    open_component() { 
      this.pop = window.open("/test/popup/popup", "open_component", "width=500, height=150");
    },
    open_page() { 
      this.pop = window.open("/test/window_popup", "open_page", "width=1024, height=768");
    },
  },
}
</script>

<style>

</style>

페이지 팝업을 먼저 설명하면,

layout은 layout: 'popup' 이런 식으로만 사용이 가능한 줄 알았는데 함수처럼 사용이 가능했다.

페이지가 그냥 열리면 opener 값은 null이고 window.open으로 열린 페이지는 opener에 값이 생기기 때문에 현재 열린 게 팝업인지 아닌지를 구분이 가능하다. 

layout을 함수로 써서 팝업이면 popup 레이아웃을, 아니면 그냥 그대로 기본 default 레이아웃으로 되도록 해준다.

PAGE 파일 팝업 띄우기를 누르면 이렇다.

둘 다 똑같은 pages/test/window_popup.vue 파일이지만 layout을 함수로 바꿈으로써 layout파일을 변경이 가능하다.

그래서 2번 고민의 경우는 생각보다 쉽게 해결이 되었다.

예제로는 동일한 파일로 했지만 만약 사용할 때 다른 페이지를 팝업으로 띄운다면 해당 페이지 파일을 열어서

layout() { if(opener) return 'popup' } 

이렇게 작성해주면 된다.

 

이제 가장 고민했던 컴포넌트 파일을 팝업으로 여는 부분이다.

아무리 생각해도 window.open시 url을 넣어주어야 하는데 컴포넌트 디렉터리에 있는걸 pages에 구조를 만들지 않고 하는 방법이 떠오르지 않았다.

document.write 이었는지 하다 포기해서 기억이 잘 안 나는데 url을 넣지 않고 빈 윈도우를 만들어서 밀어 넣는 게 방법이었던 것 같은데

이건 일단 vue 기능부터 시작해서 vuetify의 컴포넌트와 css가 다 적용되지 않기 때문에 포기했고, 부모의 스타일 등을 복사해서 밀어 넣는 것도 똑같아서 포기했다.

그래서 결국은 그냥 pages 디렉터리에 팝업용 디렉터리를 따로 만들어 주었다.

이런 식으로 pages 디렉터리에 popup이라고 디렉터리를 생성해 주었고 소스는 컴포넌트 파일의 import와 layout지정 말곤 없다.

뭔가 추가적으로 작업을 하고 싶다면 팝업창에서 보여지는 컴포넌트에 대한 레이아웃 말곤 손 안대는 것이 좋다고 본다...

이렇게 작성 후 위에 COMPONENT 파일 팝업 띄우기 버튼을 누르면 결과는 이렇다.

팝업으로 컴포넌트 파일을 열고 싶다면 위와 같은 방식으로 컴포넌트마다 파일을 생성해 주어야 한다.

이 방법 말고는 도저히 생각나는 게 없었다.

 

그리고 이렇게 만들고 나서 생각 못했던 부분들이 있는데.

store가 문제였다.

 

3. store 문제

A라는 데이터 리스트를 보여주는 컴포넌트 화면에서 팝업을 띄우고, 팝업에서 데이터를 등록을 했다. 

둘 다 같은 store 파일을 바라보고 있고, 

부모 쪽 컴포넌트에서 데이터는 이런 식으로 store 파일에서 가져오고 있다.

팝업에서 데이터를 등록하면 actions를 실행해서 정상처리가 되면 새로운 데이터를 commit을 해주기 때문에

일반적으로 모달을 이용했을 땐 commit 되는 순간 데이터를 다시 가져왔다.

하지만, 팝업에선 이게 먹히질 않는다. 아무래도 브라우저를 새로 띄우면 같은 store을 보긴 해도 분리가 되는 것 같다.

해결은 이런 식으로 했다.

window.function을 만들어 준 다음

 

팝업에서 정상 등록 처리가 되면 부모 쪽 함수를 실행하게끔 해주면 되긴 한다.

호출되면 부모 쪽에서 다시 데이터 리스트를 가져오는 actions를 실행해서 최신 데이터를 가져오게 한다.

하지만 결국 이렇게 하면 팝업에서 등록 완료 후 api 요청 한번, 부모에서 api 요청 한번, 총 2번을 하게 된다.

그렇다고 또 팝업으로 열리는 컴포넌트 파일에서 해당 부분을 빼자니 팝업이 아닌 곳에서도 사용하는 컴포넌트 파일이라 할 수가 없다.

파일을 2개 만들면 컴포넌트를 수정해야 할 때 2개를 수정해야 하는 것도 문제고...

 

또 하나 발견됐던 문제는 위에 처럼 store가 분리되어서 생겼던 문제이다.

한 페이지에 있는 컴포넌트에서 특정 데이터를 store에 저장 후 팝업창에서 해당 데이터를 사용하고자 했었다.

이건 또 이런 방법이 있다.

컴포넌트가 팝업으로 열리면서 created가 실행되면 opener.$nuxt.$store.getters... 이걸로 부모로 접근해서 부모가 보고 있는 store에 접근해서 데이터를 가져올 수 있다.

예시로 짠 소스로 그냥 저렇게 해놨지만, 실제 사용한다면 해당 컴포넌트 파일이 팝업이 아닌 곳에서 사용할 수도 있으니

이렇게 처리하는 게 좋다고 본다.

아니면 computed에서도 가능할 것 같긴 하다...

 

요구사항이다 보니 어쩔 수 없이 만든 기능이긴 하지만, 못 만들지 않을까 했었는데 어떻게 방법은 찾아서 신기하긴 했다.

반응형