글 목록이 길어지면 글 내용을 찾기 어려워집니다. 그래서 먼저 글 목록을 보여주고 선택한 글의 내용이 나오도록 하는 것이 좋습니다.
조건에 따라 다르게 보이기
현재 보여주어야 할 값을 main
이라는 변수에 저장하고, 이 값이 list
이면 글 목록이, content
이면 글 내용이 보이도록 구현하겠습니다.

우선 기본적으로 두 컴포넌트 다 display: none
을 통해 보이지 않도록 해놓겠습니다.
<style>
/* ... */
.post-content {
display: none;
}
</style>
<style>
.post-list {
display: none;
}
/* ... */
</style>
그리고 App 컴포넌트에서 main
의 변수를 만들고 이 값에 따라 active
라는 class를 가질 수 있도록 해보겠습니다. 그리고 이 class를 가지면 display: block
이 되도록 하겠습니다.
<template>
<!-- ... -->
<PostList :class="main == 'list' ? 'active' : null" ... />
<PostContent :class="main == 'content' ? 'active' : null" ... />
</template>
<script setup>
import { ref } from "vue";
// ...
const main = ref("list");
</script>
<style>
.active {
display: block;
}
</style>
그리고 main
값은 카테고리를 선택하면 글 목록이, 글을 선택하면 글 내용이 표시됩니다. 이를 위해 적합한 두 개의 함수가 있습니다. changeCategory
와 changePost
입니다.
const changeCategory = function (category) {
selectedCategory.value = category;
main.value = "list";
};
const changePost = function (id) {
selectedId.value = id;
main.value = "content";
};
전체 코드를 보면 다음과 같습니다.
<template>
<MenuBar @click-category="changeCategory" />
<PostList
:class="main == 'list' ? 'active' : null"
@click-post="changePost"
:selected-category="selectedCategory"
/>
<PostContent :class="main == 'content' ? 'active' : null" :id="selectedId" />
</template>
<script setup>
import { ref } from "vue";
import MenuBar from "./components/MenuBar.vue";
import PostList from "./components/PostList.vue";
import PostContent from "./components/PostContent.vue";
const main = ref("list");
const selectedCategory = ref("HTML");
const changeCategory = function (category) {
selectedCategory.value = category;
main.value = "list";
};
const selectedId = ref(null);
const changePost = function (id) {
selectedId.value = id;
main.value = "content";
};
</script>
<style>
.active {
display: block;
}
</style>
의도했던데로 잘 바뀌는 것을 볼 수 있습니다.

다만 글이 바뀌면서 이전의 글이 잠시 남아있는데, getPageBlocks
로 Notion 글을 가져와서 새로운 글이 저장되기 전까지 이전 글이 남아있기 때문입니다. getPageBlocks
를 실행하기 전에 이전 글을 지우는 코드도 추가해두겠습니다.
watch(props, () => {
// 값 비우기
blockMap.value = null;
getPageBlocks(props.id).then((b) => {
blockMap.value = b;
});
});

이제 잘 되네요! 이렇게 조건에 따라 class를 바꾸고 스타일을 적용해서 보이는 것을 조절할 수 있습니다. 이 방법도 좋지만 Vue에서는 더 편하게 사용할 수 있는 v-show
디렉티브가 있습니다.
v-show
조건에 따라 active
class를 넣고 display: none
스타일을 적용했습니다. 이와 똑같이 적용할 수 있는 디렉티브가 바로 v-show
입니다.
<tag v-show="조건"></tag>
위에서 v-bind로 class를 적용하던 부분을 v-show로 바꿔보겠습니다.
<template>
<!-- ... -->
<PostList v-show="main == 'list'" ... />
<PostContent v-show="main == 'content'" ... />
</template>
그리고 기존에 적용했던 스타일들을 모두 제거해주겠습니다.
<style>
/* .active {
display: block;
}*/
</style>
<style>
/*.post-list {
display: none;
}*/
.post-list button {
border: 0;
background-color: transparent;
color: rgb(101, 129, 139);
cursor: pointer;
}
.post-list button:hover {
color: blue;
}
</style>
<style>
@import "vue-notion/src/styles.css";
/* .post-content {
display: none;
}*/
</style>
이전 구현과 동일하게 적용되는 것을 볼 수 있습니다. display:none
이 조건에 따라 자동으로 들어가고 있습니다.

v-if
v-show
가 조건에 따라 스타일을 적용해서 보이고 숨겨주는 기능이라면 v-if
는 조건에 따라 렌더링하는 기능입니다.
<tag v-if="조건"></tag>
v-show 대신 v-if로 바꿔서 적용해보겠습니다.
<PostList v-if="main == 'list'" ... />
<PostContent v-if="main == 'content'" ... />
HTML의 코드를 보시면 조건이 맞지않는 코드는 <!--v-if-->
로 바뀌면서 아예 코드가 나타나지 않습니다.

그런데 글 내용이 전혀 나오지 않습니다. 왜냐하면 애초에 컴포넌트 자체가 없었기 때문에 watch가 작동하지 않기 때문이죠. v-if로 인해 조건이 맞으면 그 때서야 컴포넌트를 생성하고 setup함수를 실행한 다음 template을 마운트합니다.

처음엔 id가 null이지만, 글을 선택한 순간 id가 정해지면서 PostContent
가 생성됩니다. 즉, setup 함수를 실행할 때, id가 있으므로 watch를 쓸 필요가 없어집니다.
<script setup>
// ...
const props = defineProps(["id"]);
blockMap.value = null;
getPageBlocks(props.id).then((b) => {
blockMap.value = b;
});
</script>
이제 정상적으로 동작합니다.

v-else-if, v-else
Javascript if문에서 else if, else 가 있는 것처럼 v-else-if
, v-else
디렉티브도 있습니다.
<tag v-if="조건1"></tag>
<tag v-else-if="조건2"></tag> <!-- 조건1이 안 맞는 경우 -->
<tag v-else-if="조건3"></tag> <!-- 조건1, 조건2가 안 맞는 경우 -->
<tag v-else></tag> <!-- 위 조건이 모두 맞지 않을 때 -->
이 때 주의할 점은 v-if를 사용한 태그 바로 아래에 v-else-if, v-else가 들어가야 합니다.
App.vue
코드를 수정하면 이렇게도 될 수 있습니다.
<PostList v-if="main == 'list'" ... />
<PostContent v-else-if="main =='content'" ... />
<!-- 또는 -->
<PostList v-if="main == 'list'" ... />
<PostContent v-else ... />
v-show vs v-if
화면 상으로는 v-show
와 v-if
가 모두 조건에 따라 보이고 사라지게 하는 기능을 합니다. 하지만 내부 동작이 다르며, 어떤 것을 사용하는 것이 좋을까요? 다음과 같이 정리할 수 있습니다.
조건이 자주 변경된다면 v-show, 처음에 보여줄 필요가 없다면 v-if
컴포넌트에 v-if
를 적용하는 경우, setup 함수를 실행하고 템플릿을 마운트하는 긴 작업이 수행됩니다. 따라서 자주 보였다가 사라지는 경우 성능이 좋지 않을 수 있습니다. 그러나 처음에 보여주지 않아도 되는 요소라면, v-if
를 사용하여 초기 로딩 속도를 높일 수 있습니다.