티스토리 뷰
vue로 프론트엔드 개발을 하던 도중, 글로벌 범위로 사용되는 Footer 에서 router
안에 들어있는 특정 DOM에 접근해야 할 일이 생겼다(정확히 말하자면 DOM안에 특정 class가 존재하는지 유뮤를 확인해야 했다). 이런 상황에서는 늘 그랬듯이 Footer 스크립트 내에서 document.querySelector('.특정클래스')
함수를 호출하여 해당 DOM에 접근하려고 시도했는데, 결과값을 null
로 반환되었다.
파일 구조는 아래와 같이 구성되어있다.
//App.vue
<template>
<NavBar />
<router-view />
<Footer />
</template>
<script>
import NavBar from "./components/NavBar.vue";
import Footer from "./components/Footer.vue";
export default {
components: { NavBar, Footer },
};
</script>
// NavBar.vue
<template>
<nav>
<router-link :to="{ name: 'MainPage' }">Main</router-link>
</nav>
</template>
// Home.vue
<template>
<h1 class="home">This is home</h1>
</template>
// Footer.vue
<template>
<footer>this is footer</footer>
</template>
<script>
import { onMounted } from "vue";
export default {
setup() {
onMounted(() => {
const navEl = document.querySelector("nav");
const homeEl = document.querySelector(".home");
console.log(navEl); // <nav...> - found
console.log(homeEl); // null - not found
});
},
};
</script>
뭔가 이상하다고 생각이 들었다. router
내부에 있는 DOM요소(위 코드상에서는 'home'이라는 클래스)에 접근하는 건데 왜 못 찾는 걸까? 혹시나 하는 마음에 똑같이 글로벌로 사용되는 nav
를 찾아보았을 때는 역시 잘 찾고 있었다. 내 경험상 vue에서 특정 DOM을 찾지 못하는 문제에 부딪혔을 때는 일단 setTimeout
으로 딜레이를 걸어서 찾으면 해결되는 경우가 많았다. 그래서 일단 아래와 같이 코드를 수정해보았다.
// Footer.vue
<script>
export default {
setup() {
onMounted(() => {
setTimeout(()=>{
const navEl = document.querySelector("nav");
const homeEl = document.querySelector(".home");
console.log(navEl); // <nav...> - found
console.log(homeEl); // <h1...> - found
},1000)
});
},
};
</script>
아니나 다를까, 시간을 두고 DOM을 찾았더니 성공하였다. 우선 가장 무식한(비효율적인) 방법으로는 문제를 해결하기는 했다.
이런 문제 상황의 원인은 바로 App.vue
에서 router
가 mount 될 때까지 미세하게나마 시간이 소요되기 때문이었다. 반면에 Footer.vue
는 router
내부에 있지 않고 App.vue
에 독립적으로 존재하기 때문에 mount되는 시간이 훨씬 짧다. 따라서 Footer.vue
에서의 onMounted()
가 호출되는 시점에는 아직 router
내부의 Home.vue
의 DOM이 mount되기 이전 시점이었기 때문에 DOM에 접근할 수 없었고, 같은 레벨에 있는 NavBar.vue
에는 접근이 가능했던 것이다.
개발할 때는 워낙 빠릿하게 작동해서 눈치채기는 어렵지만 사실 router
가 mount되기까지의 시간딜레는 결코 무시할 수 없는 수준으로 꽤 긴 시간이다. 그렇다면 앞으로도 계속 setTimeout
으로 강제 딜레이를 넣어줘야 하는 걸까? 당연히 아니다.vue-router
에서는 이런 문제가 발생할 줄 알고 미리 router.isReady()
라는 비동기 함수를 만들어 놓았다. 문제를 해결한 코드를 먼저 보자.
// Footer.vue
<script>
import { onMounted } from "vue";
import router from "@/router";
export default {
setup() {
onMounted(async () => {
await router.isReady();
const navEl = document.querySelector("nav");
const homeEl = document.querySelector(".home");
console.log(navEl); // <nav...> - found
console.log(homeEl); // <h1...> - found
});
},
};
적용은 간단하다. querySelector()
로 찾기 이전 라인에 await router.isReady()
를 적어주기만 하면 된다. 콘솔로 찍히는데 약간의 시간 딜레이가 있기는 하지만, setTimeout
으로 하드코딩하는 것보단 훨씬 낫다.
'Dev' 카테고리의 다른 글
vue-router를 통한 페이지간 데이터 전달 (0) | 2023.04.24 |
---|---|
vue에서 input을 커스텀 컴포넌트화 시키기 (0) | 2023.04.04 |
vue3 modal 모달 열고 닫기 (0) | 2023.03.09 |
vue3 modal transition(모달 트랜지션) (0) | 2023.03.09 |
vue3 route transition(라우트 트랜지션) (0) | 2023.03.09 |