💡vecel이용예정
- vercel을 만든 팀이 넉스트.js를 만들었습니다
- 서버리스함수에서 서울 리전을 선택할 수 있어서 넷니파이 서버리스보다 빠릅니다.
- 단점은 엣지펑션이 없다는 부분입니다.
💡포인트
단일페이지구조로 만들지만 여러페이지로 관리를 할수 있도록 vue router를 설치해 이용할 예정입니다.
필요한 패키지 및 플러그인 설치
$ npm create vite@latest 폴더이름 $ cd 폴더이름 $ code . -r $ npm i $ npm install vue-router pinia $ npm i -D eslint eslint-plugin-vue sass
vite.config.js
export default defineConfig({ plugins: [vue()], alias: [ {find :'~', replacement: `${__dirname}/src`}, ] })
.eslintrc.json
{ "env": { "browser": true, "node": true }, "extends": [ "eslint:recommended", "plugin:vue/vue3-recommended" ], "rules": { "semi": ["error", "never"], "quotes": ["error", "single"], "eol-last": ["error", "always"], //빈줄 하나 허용 "vue/html-closing-bracket-newline": ["error", { "singleline": "never", "multiline": "never" }], "vue/html-self-closing": ["error", { "html": { "void": "always", "normal": "never", "component": "always" }, "svg": "always", "math": "always" }], "vue/comment-directive": "off", "vue/multi-word-component-names": "off" } }
store>workspace.js
import {defineStore} from 'pinia'
export const useWorkspaceStore = defineStore('workspace', {
state() {
return {
}
},
getters: {
},
actions: {
}
})
main.js
import { createApp } from 'vue'
import {createPinia} from 'pinia'
import App from './App.vue'
createApp(App)
.use(createPinia())
.mount('#app')
rotues에 Home.vue와 Workspace.vue를 만들어줍니다.
3-1 routes>index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from './Home.vue'
import Workspace from './Workspace.vue'
export default createRouter({
history: createWebHistory(),
scrollBehavior: () => ({top: 0}),
routes: [
{
path: '/',
component: Home
},
{
path: '/workspaces', //나중에
component: Workspace
}
]
})
3-2 main.js
import { createApp } from 'vue'
import router from './routes'
import App from './App.vue'
createApp(App
.use(router)
.mount('#app')
3-3 App.vue , RouterView연결
<template>
<RouterView />
</template>
3-4 main.js
import { createApp } from 'vue'
import {createPinia} from 'pinia'
import router from './routes'
import App from './App.vue'
createApp(App)
.use(createPinia())
.use(router)
.mount('#app')
목록이 생성된 후에 워크스페이스가 생성된 것인지, 또는 워크스페이스가 생성되어야 목록이 구성되는 것인지 딜레마에 빠질 수 있습니다.
일단, 워크스페이스를 하나정도 생성하고 목록을 가져오는 코드를 먼저 만들어보겠습니다.
워크스페이스 속에 들어가는 내용은 모두 div요소 안에 위치하게 됩니다. 또한 이 div안에서 특정한 영역에 css를 넣을 수 있도록 설계가 되어 있습니다(contentitable
이용)
원시HTML의 보안취약점
웹사이트에서 임의의 HTML을 동적으로 렌더링하면 XSS 취약점 (opens new window)(https://en.wikipedia.org/wiki/Cross-site_scripting)으로 쉽게 이어질 수 있고 이는 매우 위험할 소지가 있습니다. HTML 보간법은 반드시 신뢰할 수 있는 콘텐츠에서만 사용하고 사용자가 제공한 콘텐츠에서는 절대 사용하면 안 됩니다.
따라서 프로젝트에서는 서버쪽에서 이를 방지를 설정을 하게 됩니다. 단, div
,br
을 제외한 나머지를 막게 설정을 해 div안에서 특정한 영역에 css를 넣을 수 있도록 설계했습니다.
다른 곳에서도 사용을 할 수 있도록 CRUD 함수를 store안에 작성합니다.
import {defineStore} from 'pinia'
export const useWorkspaceStore = defineStore('workspace', {
state() {
return {
}
},
getters: {
},
actions: {
//CRUD
async createWorkspace() {
const res = await fetch('https://asia-northeast3-heropy-api.cloudfunctions.net/api/notion/workspaces', {
method: 'POST',
headers: {
'content-type': 'application/json',
'apikey': 'FcKdtJs202204',
'username': 'Rohyoungseo'
},
body: JSON.stringify({
// parentId: '',
title: '처음 만드는 워크스페이스',
content:'내용...',
// poster: ''
})
})
const workspace = await res.json()
console.log(workspace)
this.readWorkspaces()
}
}
})
Workspace.vue
<template>
<h1>Workspace!</h1>
<button @click="workspaceStore.createWorkspace">
워크스페이스 생성!
</button>
</template>
<script>
import { mapStores } from 'pinia'
import { useWorkspaceStore } from '~/store/workspace'
export default {
computed: {
...mapStores([useWorkspaceStore])
}
}
</script>
Home.vue
<template>
<h1>Home!</h1>
</template>
http://localhost:3000/workspaces
를 확인해보면 아래와 같이 화면이 렌더링되며, 워크스페이스 생성 버튼을 누르면 콘솔에 출력되는 것을 확인할 수 있습니다.