개발 관련/Vue

[vue.js]v-if와 v-show에 대해 알아보자

Hago하고 2021. 6. 10. 18:41
반응형

v-if는 참 편리한 녀석이다.

boolean 값에 따라 html 요소들이 동작하는 방향을 결정할 수 있다보니, 컴포넌트를 표시할 때에도 쓰이고, 각종 로직에 쓰인다. 나는 아직 경험이 많지 않다보니, v-if를 통해 보여지는 컴포넌트를 결정하는 로직을 많이 짰다.

 

하지만 오늘 드디어 일이 났다.

 

싱글 컴포넌트 라이브러리를 제작하게 되면서, v-if를 사용하는 부분에서 오류가 났다.

아래는 문제의 해당 코드이다.

 

Process.vue

<template>
  <div class="process-container">
      <LoadingSpinner v-if="isProcessing" />
      <DisplayStatus v-else v-on:processCompleted="processCompleted" />
  </div>
</template>

<script>
import LoadingSpinner from './common/LoadingSpinner.vue';
import DisplayStatus from './DisplayStatus.vue';
export default {
    components: {
        LoadingSpinner,
        DisplayStatus
    },
    data() {
        return {
            isProcessing: false,
        }
    },
    methods: {
      processCompleted() {
            this.isProcessing = false;
            console.log('completed');
      }
    },
    mounted() {
        this.isProcessing = true;
    }
}

 

DisplayStatus.vue

<template>
    <div>
        <h1>{{ status }}</h1>
    </div>
</template>

<script>
export default {
    data() {
        return {
            status:'',
        }
    },
    methods: {
        async getStatus() {
            const dataId = this.$store.state.dataId;
            const response = await this.$store.dispatch('GETSTATUS',dataId);
            if(response.data.status == 'done') {
                this.$emit('processCompleted');
                this.status = 'Done!!';
                console.log(this.status);
                clearInterval(this.loading);
            }else if(response.data.status == 'failed') {
                clearInterval(this.loading);
                this.warring = true;
                this.isProcessing = false;
                this.status = 'Something Wrong...';
            }
        },
    },
    mounted() {
        this.status = 'processing';
        this.loading = setInterval(this.getStatus, 5000);
    }
}
</script>

<style>

</style>

위와 같이, async & await를 통해 store에 있는 GETSTATUS를 실행시켜 axios로 데이터를 받아오고, 해당 status 값이 done 이면 상위 컴포넌트인 Process.vue로 이벤트를 보내 보여지는 화면이 바뀌도록 하고 싶었다.

 

하지만... 내 눈에 보이는 것은 무한히 돌아가는 LoadingSpinner 뿐...

Process.vue에 콘솔을 찍어봤지만 찍히지 않았다. DisplayStatus.vue에서 이벤트도 제대로 발생하는걸 확인했는데,

왜 전달되지 않을까?

 

이곳 저곳을 살펴본 결과 이런 상황에는 v-if 대신 v-show를 사용해야 한다고 했다.

 

 v-if  는 디렉티브를 사용한 엘리먼트의 이벤트 리스너와 자식 컴포넌트들이 DOM에 제거되거나 삽입되는 조건부 렌더링이다.  v-show  는 DOM에 항상 삽입되어 있고  display  style을 통해 화면에 표시될지 결정되는 조건부 렌더링이다.

 v-if  는 토글비용(화면에 그려지거나 그려지지 않는데 사용되는 비용)이 높지만 초기 렌더링 비용이 낮다. 반면,   v-show 토글 비용이 낮고 초기 렌더링 비용이 높다. 때문에 자주 변하는 컴포넌트에 조건부 렌더링을 사용해야 한다면  v-show  를 사용하는 것이 좋다. 런타임 시 조건이 바뀌지 않는다면  v-if  를 사용하는 것이 좋다.

 

그러니까 v-if로 사용하면 초반에 이미 조건인 isProcessing 때문에 DisplayStatus 컴포넌트는 DOM에서 아예 없는 것이 된다.

 

아예 없는 컴포넌트에서 아무리 이벤트를 보내도 처음에 DOM이 생성될때 이미 해당 컴포넌트가 없었기에 실행이 되지 않았던 것이다.

 

이 문제는 v-show로 바꿔주니 한 번에 해결 되었다.

 

간단히 생각해보건데, v-if는 정적인 느낌이고, v-show는 동적인 느낌이다. 약간 v-show가 실시간 바인딩하면서 조건을 감시하는 느낌..? 물론 v-if도 따로 컴포넌트를 만들어서 다루는게 아닌, 같은 컴포넌트에서 로직 처리를 한다면 조건이 바뀌면 바로바로 반응하지만, 다른 컴포넌트에서 이벤트를 보내고 받아서 컴포넌트를 조정하는 것은 v-show를 사용해야 가능했다.

 

혹시나 나처럼 삽질하는 분들이 계실까봐 오늘도 정리정리..!

 

반응형