블로그 글 상세 보기
홈페이지의 글목록에서 글의 모든 내용을 볼 수 있음에도 불구하고,
글 상세보기 페이지가 별도로 있습니다.
이것은 글 상세보기에서 구현해야 할 기능들이 많기 때문입니다.
글 상세 보기 페이지에서
글의 저자이면 글을 수정할 수 있고
댓글, 답글을 쓸 수 있고,
구독 요청을 할 수 있습니다.
또한 저자의 블로그로 이동할 수도 있습니다.
하나씩 기능을 구현해 봅시다.
글목록에서 글을 선택하면 글의 id를 파라미터로 가지고 상세보기 페이지로 이동합니다.
글의 id를 파라미터로 받아서 글 id로 글을 로드하여 화면에 나타냅니다.
상세 보기 페이지에서는 글의 id로 글을 로드하여 화면에 출력합니다.
글의 내용을 보여줄 때는 안전한 HTML로 만들어 화면에 나타내고,
글쓴이의 Profile을 얻어 글쓴이 블로그 이름, 글쓴이 이름도 화면에 나타냅니다.
앱이 시작될 때 모든 사용자의 계정 설정 정보를 로드하여 state의 Profiles에 저장하였으므로,
이를 이용하여 글의 userId로 글쓴이의 Profile을 찾을 수 있습니다.
async fetchPost({ commit }, postId) {
commit('setLoading', true);
try {
const docRef = doc(db, "posts", postId); // Reference to the specific article document
const docSnap = await getDoc(docRef); // Fetch the document snapshot
if (docSnap.exists()) {
const post = docSnap.data(); // Set article data
commit('setPost', post);
} else {
alert("post가 없습니다.");
}
} catch (error) {
alert('fetchPost: ' + error.message);
} finally {
commit('setLoading', false);
}
},
await this.fetchPost(postId)를 실행하면
commit('setPost', post)으로 store의 상태 변수 post에 게시글이 저장되고
이것을 화면에 나타낼 수 있습니다.
PostView.vue
<!-- src/views/PostView.vue -->
<template>
<v-container>
<v-card v-if="post">
<!--글쓴이의 블로그 이름-->
<v-card-title v-if=author style="cursor: pointer; font-size:1em" @click="goToBlogs(author.userId)">
{{ getBlogName }}<v-icon>mdi-chevron-right</v-icon>
</v-card-title>
<!--카테고리-->
<v-card-text class="mb-n5" style="font-size:1em">
{{ post.category }}
</v-card-text>
<!-- 제목 -->
<v-card-title style="cursor: pointer; font-size:1.1em">
{{ post.title }}
</v-card-title>
<!-- 내용 -->
<!-- eslint-disable -->
<v-card-text style="font-size:1em" v-html="content"></v-card-text>
<!-- eslint-enable -->
<!-- 글쓴이 이름, 글쓴 날, 조회수 -->
<v-card-subtitle style="text-align:right">
{{ post.userName }} . {{ post.createdAt.toDate().toISOString().split('T')[0] }}
<span v-if="post.views > 0" > . ({{ post.views }})</span>
</v-card-subtitle>
<v-card-actions >
<v-spacer></v-spacer>
<v-btn small text v-if="isAuthor" @click="goToEditPost()">
수정 <v-icon>mdi-pencil</v-icon>
</v-btn>
</v-card-actions>
</v-card>
<div class="text-center">
<v-progress-circular v-if="loading" indeterminate></v-progress-circular>
</div>
</v-container>
</template>
<script>
import { mapActions, mapState } from "vuex";
import sanitizeHtml from 'sanitize-html';
export default {
data() {
return {
content: '',
newComment: '', // 댓글
replyingTo: null,
newReply: '',
author: null, // 저자의 이름을 가져오기 위해
isDisabled: false, // 버튼 비활성화 여부를 결정하는 상태
};
},
computed: {
...mapState('auth',['user', 'profile', 'profiles']),
...mapState('post', ['loading', 'post']),
getBlogName() {
return this.author.blogName;
},
// 저자인가? - 작성자와 로그인한 사용자 비교
isAuthor() {
return this.user && this.user.uid === this.post.userId;
},
},
methods: {
...mapActions('post', ['fetchPost']),
// content를 안전한 html로 바꿔준다.
sanitizeContent(content) {
return sanitizeHtml(content.replace(/\n/g, '<br>'), {
allowedTags: ['b', 'i', 'em', 'strong', 'p', 'br'],
allowedAttributes: {}
});
},
formatDate(date) {
if (date && date.toDate) {
return date.toDate().toISOString().replace('T', ' ').substring(0, 16);
}
return '';
},
// 로그인한 회원이 글쓴이 인가?
isOwner(userId) {
if(!this.user)
return false;
return userId === this.user.uid;
},
// 글 수정하러 가기
goToEditPost() {
this.post.updatedAt = new Date(); // 수정한 날짜를 넣자.
this.$router.push({ name: 'Edit', params: { postId: this.$route.params.id, post: this.post } });
},
},
async created() {
// 글 가져오기
const postId = this.$route.params.id; // Get the article ID from the route parameters
await this.fetchPost(postId); // 글 id로 글 가져오기
this.content = this.sanitizeContent(this.post.content); // 내용에 포한된 html을 안전한 html로 변경한다.
// 글 저자 설정정보 가져오기 - 상태에 로드되어 있는 전체 계정 설정 정보를 이용한다.
this.author = this.profiles.find(profile => profile.uids.includes(this.post.userId));
},
};
</script>
'토이 프로젝트 - Vue, Firebase로 서버리스 PWA 개발' 카테고리의 다른 글
20. Firestore로 PWA myBlog 개발 - 댓글 쓰기 (0) | 2025.02.25 |
---|---|
19. Firestore로 PWA myBlog 개발 - 글수정 (0) | 2025.02.24 |
17. Firestore로 PWA myBlog 개발 - 블로그 글 상세보기 기능들 (0) | 2025.02.21 |
16. Vue(with Vuetify)와 Firebase로 서버리스 PWA myBlog 개발 - 홈페이지 (0) | 2025.02.20 |
15. PWA myBlog 블로그 글보기 보안 sanitize-html - XSS, SQL Injection, CORS, HTTPS (0) | 2025.02.16 |