포트폴리오 페이지를 작업하면서 문제가 생겼다.
문제를 확인하기 전에 내 포트폴리오 사이트 동작 원리를 확인해보자.
// src/items/blog.js
export default [
{
number: 0,
title: "뷰스타그램",
content: "Vue로 만들어본 instagram",
project: `BaseLanguage : Vue3.X, javascript, scss
Server, DB : node.js, express, mySql
Library, Plug-in : JWT-token, axios, Vuex, FaceBook Auth, instagram filterBox`,
gitUrl: "https://github.com/SoominYim/vuestagram",
url: null,
image: require("@/assets/img/vuestagram_01.png"),
contents: `
메인(로그인) 페이지
<img style="width : 600px;"src="${require("../img/vuestagram_01.png")}"/>
회원가입 페이지
<img style="width : 600px;"src="${require("../img/vuestagram_02.png")}"/>
로그인 후 피드
<img style="width : 420px;"src="${require("../img/vuestagram_03.png")}"/>
기존 React로 만들어진 인스타그램을 Vue로 리팩토링하여 작업
`,
},
{
number: 1,
title: "Today-lunch",
content: "오늘 점심 뭐먹지",
project: `BaseLanguage : Vue3.X, javascript, scss
Server, DB : null
Library, Plug-in : Vuex`,
gitUrl: "https://github.com/SoominYim/today-lunch",
url: "https://soominyim.github.io/today-lunch",
image: require("@/assets/img/today-lunch.png"),
contents: `매일 점심 정하기가 귀찮아 만들어 본 랜덤 메뉴정하기
<img style="width : 600px;"src="${require("../img/today-lunch.png")}"/>
원하는 음식이 나올때마다 누르는 한국인의 정서를 없애기 위해
첫 째로 나온 음식이후엔 선택이 되지 않게 했다.
랜덤 로직을 직접 짜는게 가장 오래 걸렸다.
각 country로 랜덤으로 뽑아 배열의 푸시하는 방식으로 로직을 구성하였다.
`,
},
{
number: 2,
title: "Todo",
content: `fire-base에 저장 되는 나만의 Todo List`,
project: `BaseLanguage : React, javascript, scss
Server, DB : Firebase
Library, Plug-in : googleAuth`,
gitUrl: "https://github.com/SoominYim/reactTodo",
url: null,
image: require("@/assets/img/reactTodoImg.png"),
contents: `초기 리액트 공부를 위해 만들었던 TODO LIST
<img style="width : 600px;"src="${require("../img/reactTodoImg.png")}"/>
`,
},
{
number: 3,
title: "Tetris",
content: `간단한 웹 테트리스`,
project: `BaseLanguage : Html, javascript, scss
Server, DB : Firebase
Library, Plug-in : googleAuth,
`,
gitUrl: "https://github.com/SoominYim/tetris",
url: "https://soominyim.github.io/tetris/",
image: require("@/assets/img/tetrisImg.png"),
contents: `공부를 위해 만들었던 테트리스.
<img style="width : 600px;"src="${require("../img/tetrisImg.png")}"/>
지금 봐도 낭만이 있다.
Moving Blocks : ← →
Block Change Direction : ↑
Slow Drop: ↓
Fast Drop : Space bar
간단한 기능까지 구현 했다.`,
},
{
number: 4,
title: "vue-tetris",
content: "휴대용 게임기처럼 만든 테트리스",
project: `BaseLanguage : Vue2.X, javascript, less
Server, DB : null
Library, Plug-in : Vuex,
`,
gitUrl: "https://github.com/SoominYim/vue-tetris",
url: "https://soominyim.github.io/vue-tetris/",
image: require("@/assets/img/vue-tetris.png"),
contents: `기존의 구현했던 테트리스와는 다르게 좀 더 퀄리티 있는
테트리스 게임을 만들고 싶었다.
<img style="width : 600px;"src="${require("../img/vue-tetris.png")}"/>
모바일에서도 즐길 수 있게 native하게 구성되었다.
level, sound, pause, start Line 구성, 다음에 나올 block 등등의 기능들을
추가하여 온전한 미니게임으로 즐길 수 있다.
`,
},
{
number: 5,
title: "jwt-login",
content: "jwt token 기반 로그인 기능 구현 project",
project: "node.js express project",
gitUrl: "https://github.com/SoominYim/jwt-login",
url: null,
image: "",
contents: "",
},
{
number: 6,
title: "socket-io",
content: "socket-io 를 이용한 채팅방 구현 project",
project: "vue node.js express project",
gitUrl: "https://github.com/SoominYim/vue-socket",
url: null,
image: "",
contents: "",
},
{
number: 7,
title: "vue-books",
content: "vue.js 2.X version 의 shopping mall project",
project: "vue project",
gitUrl: "https://github.com/SoominYim/vue-books",
url: null,
image: "",
contents: "",
},
];
우선 프로젝트의 기본적인 내용들을 String type으로 작성하여
// src/view/BlogProject.vue
<template>
<div>
<div class="container" @click="goBack">
</div>
<div class="wrap">
<div class="scrolle">
<h1>{{ blogs[$route.params.id].title }}</h1>
<h2>{{ blogs[$route.params.id].content }}</h2>
<h4">{{ blogs[$route.params.id].project }}</h4>
<div">{{ blogs[$route.params.id].contents }}</div>
<div class="btn-wrapper">
<div class="btn-wrapper__container">
<div class="btn-inner">
<a class="btn-inner__text" :href="blogs[$route.params.id].gitUrl" target="_blank">view
code</a>
</div>
</div>
</div>
<div class="btn-wrapper" v-if="blogs[$route.params.id].url !== null">
<div class="btn-wrapper__container">
<div class="btn-inner">
<a class="btn-inner__text" :href="blogs[$route.params.id].url" target="_blank">preview</a>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "BlogProject",
props: {
blogs: Array,
},
methods: {
goBack() {
this.$router.go(-1);
},
},
};
</script>
작성 되어있는 src/items/blog.js 를 props data로 받아와 mustaches(이중 중괄호)를 통해 data bind를 했다.
하지만 이렇게 mustaches로 작성 할 경우엔 받아온 String type의 data들은 줄바꿈이 되지 않는다.
또한 이미지 태그를 그대로 text로 인식한다. (모든 태그들을 전부 text로 인식)
그러므로 v-html 디렉티브를 사용하여 메소드를를 작성해주어야 한다.
여기서 포인트는 method로 메소드를 작성하면 안된다.
왜 그런지는 가이드 라인에 말을 빌려 살펴보자.
computed 속성 대신 메소드와 같은 함수를 정의할 수도 있다.
최종 결과에 대해 두 가지 접근 방식은 서로 동일하지만 차이점은
computed 속성은 종속 대상을 따라 저장(캐싱)된다는 것 이다.
computed 속성은 해당 속성이 종속된 대상이 변경될 때만 함수를 실행한다.
즉 props로 가져온 blogs data가 변경되지 않는 한, computed 속성들을 여러 번 요청해도 계산을 다시 하지 않고
계산되어 있던 결과를 즉시 반환한다.
또한 Date.now()처럼 아무 곳에도 의존하지 않는 computed 속성의 경우 절대로 업데이트되지 않는다는 뜻이다.
이에 비해 메소드를 호출하면 렌더링을 다시 할 때마다 항상 함수를 실행한다
캐싱이 왜 필요할까 생각해보자.
계산에 시간이 많이 걸리는 computed 속성인 A를 가지고 있다고 가정해보자.
이 속성을 계산하려면 거대한 배열을 반복해서 다루고 많은 계산을 해야 하는데 A 에 의존하는
다른 computed 속성값도 있을 수 있다.
만드신 분께선 캐싱을 하지 않으면 A 의 getter 함수를 꼭 필요한 것보다 더 많이 실행하게 되니
캐싱을 원하지 않는 경우 메소드를 사용하라고 하신다.
그렇다면 하라는데로 작성을 해보자.
// src/view/BlogProject.vue
<template>
<div>
<div class="container" @click="goBack">
</div>
<div class="wrap">
<div class="scrolle">
<h1>{{ blogs[$route.params.id].title }}</h1>
<h2>{{ blogs[$route.params.id].content }}</h2>
<h4 v-html="project"></h4>
<div v-html="contents"></div>
<div class="btn-wrapper">
<div class="btn-wrapper__container">
<div class="btn-inner">
<a class="btn-inner__text" :href="blogs[$route.params.id].gitUrl" target="_blank">view
code</a>
</div>
</div>
</div>
<div class="btn-wrapper" v-if="blogs[$route.params.id].url !== null">
<div class="btn-wrapper__container">
<div class="btn-inner">
<a class="btn-inner__text" :href="blogs[$route.params.id].url" target="_blank">preview</a>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "BlogProject",
props: {
blogs: Array,
},
computed: {
project() {
const blog = this.blogs[this.$route.params.id];
if (blog && blog.project) {
return blog.project.split('\n').join('<br/>');
}
return '';
},
contents() {
const blog = this.blogs[this.$route.params.id];
if (blog && blog.contents) {
return blog.contents.split('\n').join('<br/>');
}
return '';
}
},
methods: {
goBack() {
this.$router.go(-1);
},
},
};
</script>
기존에 있던 줄바꿈, 태그사용이 필요한 오브젝트는 mustaches 작성된 것을 지우주고 v-html 디렉티브로 작성하였다.
백틱으로 작성된 blogs의 내용들은 \n로 표기되어 파싱된다. 그렇다면 이 \n들을 <br/>로 넣어주자.
이러면 자연스럽게 img태그들도 v-html로 파싱되어 잘 나올 것이다.
'밥줄 > Vue.js' 카테고리의 다른 글
[Vue.js]Web browser에서 Source maps 숨기기 (0) | 2024.01.31 |
---|---|
[Vue.js] Side Project Tools 만들기 (1) (1) | 2024.01.23 |
TypeScript intellisense is disabled on template 해결 (0) | 2022.08.31 |
[Vue.js]Life Cycle 간단요약 (0) | 2022.07.06 |
[Vue.js 오류] Component name "" should always be multi-word (0) | 2022.07.06 |