시리즈 소개
오프라인 PWA를 목표로 하는 To Do 웹앱 프로젝트 To Do Wild 를 개발하며 배운 것들을 적는 시리즈입니다.
- beta service (일단은 오프라인이 될겁니다.)
- github
결론부터 말하자면 그렇지 않다. 문서에도 써 있는 이야기다.
저장소에는 ... 여러 곳에서 사용되는 데이터가 포함되어야 합니다. 예를 들어, GNB에 표시되는 사용자 정보 ... 데이터가 있습니다.
반면에 컴포넌트에서 호스팅할 수 있는 로컬 데이터를 스토어에 포함하는 것을 피해야 합니다. ...
내가 만들고 있는 것은 투두앱이다.
<TodoForm>
따로 쓰고 <TodoItem>
따로 쓸 일이 없다.이리하여, 여기서 전역 상태 관리 라이브러리를 채용하는 것은 오버킬이라고 보았다.
그리고 지금 돌이켜 보면, 나는 그저 컴포넌트간 양방향 바인딩을 하고 싶었을 뿐이었던 것 같다.
그 방법을 공식문서에서 찾아내기까지 이상하게 오래 걸렸고, 그걸 알아내고 나서는 별 문제가 없어졌다.
v-model
v-model
은 예컨대 <input>
같은 일반 엘리먼트에 붙일 수 있다.
그러면 예컨대 <TodoItem>
같은 컴포넌트에는 붙일 수 없을까?
이렇게만 놓고 보면 서로 별 차이 없어 보이는데 안될 게 뭐냐 싶어진다.
그런데 놀랍게도, Vue 3.4 이전까지는 이게 안 돼서, 아래와 같은 삽질을 해서 구현을 했다고 한다.
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="props.modelValue"
@input="emit('update:modelValue', $event.target.value)"
/>
</template>
안 그래도 컴포넌트의 v-model
API를 알기 전까지는 나도 저 삽질을 할 각오를 잔뜩 다지고 있었다.
문서를 아무리 읽어보아도 그 수밖에 없기 때문이다.
이 개념을 우아하게 숨긴 것이 v-model
디렉티브라고 생각된다.
API 문서에 따르면 디렉티브가 맞다. 가이드 문서에서는 명토 박아서 디렉티브라고 하지는 않고 일종의 매크로라는 식의 요상한 설명이 붙어있지만...
잠시 실무로 돌아와서... Indexed DB를 쓰긴 써야 되겠는데, Indexed DB 문서를 읽어보자면, 아무래도 어딘가에서 한 번은 "저장소를 생성하고 구성"해야 하는 모양이다.
하는건 하면 되는데 이걸 어디서 어떻게 하지???
main.js
의 createApp()
전후 아무데나 구겨넣어도 될거 같은데?index.html
의 <head>
태그 안에 구겨넣어도 될거 같은데??App.vue
의 <script setup>
에서 해도 될거 같은데???셋 다 그닥 좋은 아이디어는 아니다... 넘 jQuery스럽달까...
굳이 좋은 아이디어를 짜내어보자면 대략 이렇게 요약될 것이다.
이리하여, Indexed DB 저장소 초기화를 수행할 위치로 플러그인을 선택했다.
// main.js 일부
import { createApp } from 'vue'
import IndexedDB from './plugins/IndexedDB'
const app = createApp(App)
app.use(IndexedDB)
// plugins/IndexedDB.ts
import { App } from "vue"
import TodoRepo from "../repositories/todoRepo.ts"
export default {
install: (app: App) => {
const repo = new TodoRepo()
app.provide('repo', repo)
}
}
TodoRepo
클래스에 관해서는 다음 글에서 좀더 자세히.
너무 지나치게 창의적(??)인 리포지토리라서 아무래도 굉장히 창피할 거 같으니, 어차피 창피할 예정인 다음 글에서 한꺼번에 창피해지기로 한다.
define
접두사Vue 튜토리얼 백번 읽고 거의 그대로 따라하면서도 '이게 왜 (안) 되지...?' 하면서 엉거주춤 흉내만 내다가, 오늘 웬만큼 기능 구현이 끝나고 나서야 약간 깨달아지는 바가 있어서 메모.
defineModel
함수는 왜 하필 define
을 붙여서 defineModel
이라고 부를까?
상위 템플릿에서 <Foo x-model="어쩌구" />
로 쓸 때, 여기서의 "x-model"이 뭔지 이게 어떻게 작동해야 되는 건지를 정의("define")하기 때문에 "define Model"이라고 부른다.
다른 것도 비슷하다. 대충 다음 표로 정리된다.
~를 정의하지 않으면 | 상위 템플릿에서 ~를 쓸 수 없다. |
---|---|
defineModel('foo') | x-model:foo="..." |
defineEmits(['foo']) | @foo="..." |
defineProps(['foo']) | :foo="..." |
정리해놓고 보니 이런데서 초보 티가 팍팍 난다. 자세한 것은 관련 API 문서를 더 읽어보자.