Vue로 PWA 개발 - 그랜파 개발자
Vue 프로젝트 Beta Test : mylog, 일상의 기록
개발이 진행됨에 따라 소스 코드를 계속 추가해 갑니다.
마이로그 조회수
마이로그 조회수는 로그인을 하였던 하지 않았던 하루에 몇번을 조회를 하던 한사람은 하루에 한번만 조회수를 카운트 하려고 합니다.
회원과 비회원이 하루에 한 번만 조회수를 증가시키도록 조회 기록을 저장하고 관리하기 위하여 views 컬렉션을 사용하고, views 컬렉션은 각 글에 대한 조회 기록을 관리하기 위한 구조입니다. 이것은 Firestore에 저장되며, 각 사용자의 조회 기록을 날짜와 함께 저장하여 중복 조회를 방지합니다.
1. views 컬렉션
views 컬렉션은 특정 게시글(mylogId)에 대한 각 사용자의 조회 기록을 저장하는 방식으로 설계됩니다. 이를 통해 해당 사용자가 해당 게시글을 언제 마지막으로 조회했는지 확인할 수 있습니다.
views 컬렉션 구조
/views/{mylogId}/users/{userId or anonymousId}
- views 컬렉션
각 게시글에 대한 조회 기록을 저장하는 최상위 컬렉션입니다.
컬렉션 이름은 views로 지정됩니다. - mylogId(Document)
각 게시글의 고유 ID입니다. posts 컬렉션에서 게시글의 ID와 동일한 값입니다.
게시글마다 조회 기록을 별도로 관리합니다. - users 서브 컬렉션
각 게시글 안에 존재하는 서브 컬렉션입니다.
이 서브 컬렉션은 특정 게시글을 조회한 사용자들을 관리합니다.
회원은 userId를, 비회원은 고유한 anonymousId를 사용하여 사용자별로 문서를 만듭니다. - userId or anonymousId (Document)
회원인 경우, userId는 사용자의 고유 ID입니다. 비회원인 경우, 쿠키나 로컬 스토리지를 사용하여 생성한 고유한 익명 ID를 사용합니다 (anonymousId).
이 문서 안에는 해당 사용자의 조회 기록이 들어갑니다. - 조회 기록 필드
lastViewed: 사용자가 게시글을 마지막으로 조회한 시간(타임스탬프)을 저장합니다.
예를 들어, mylogId가 abc123인 게시글에 대해 사용자들이 조회한 기록이 다음과 같이 저장됩니다:
/views/abc123/users/user_1
{
lastViewed: September 6, 2024, 12:34:56 PM UTC
}
/views/abc123/users/anon_9sdfsdf
{
lastViewed: September 6, 2024, 08:15:42 AM UTC
}
- abc123: postId로, posts 컬렉션에서 해당 게시글의 고유 ID입니다.
- user_1: 회원 사용자 ID입니다.
- anon_9sdfsdf: 비회원 사용자의 고유 익명 ID입니다. 이는 쿠키나 로컬 스토리지에서 가져온 고유 값입니다.
- lastViewed: 사용자가 마지막으로 이 게시글을 조회한 날짜와 시간입니다.
조회수를 위하여 이와 같은 구조를 가지는 이유는 각 마이로그에 대해 날짜별 조회수 집계를 구현하기 위한 것입니다. 그리고 하루에 여러번 조회하는 경우에 대해 중복 조회를 방지하기 위한 목적도 있습니다.
각 마이로그의 조회 집계는 각 마이로그 컬렉션에 별도로 저장하고 있습니다.
2. 익명 ID
로그인하지 않은 사용자의 조회수 카운트를 위하여 고유의 익명 ID를 생성하여 구분을 합니다. 고유의 익명 ID를 생하기 위하여 uuidv4를 사용합니다. uuidv4는 Universally Unique Identifier version 4의 약자로, 고유한 식별자를 생성하는 표준 중 하나입니다.
UUID는 네트워크 상에서 고유성을 보장해야 하는 다양한 경우에 사용됩니다. uuidv4는 무작위(random) 데이터를 기반으로 UUID를 생성하는 방식으로, 총 128비트(16바이트) 크기의 값을 가지고 있으며 32개의 16진수로 표현됩니다.
UUID는 총 36개의 문자로 구성되며, 하이픈(-)으로 구분됩니다. 형식은 아래와 같습니다:
xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
- x는 임의의 16진수 값(0-9, a-f)을 나타냅니다.
- 4는 UUID 버전을 나타내며, 버전 4는 무작위(random) UUID임을 의미합니다.
- y는 8, 9, a, 또는 b 중 하나의 값입니다. 이는 variant라고 하며, UUID의 변종을 나타냅니다.
- 예시:
e5c0b9a2-4bf2-4cb3-a6b2-1abf4f5edc54
uuidv4는 주로 uuid 라이브러리를 사용하여 생성합니다. Node.js 환경에서 uuid 라이브러리를 설치하고 사용할 수 있습니다.
1. 설치
npm install uuid
2. 사용법
// uuidv4 불러오기
import { v4 as uuidv4 } from 'uuid';
// UUID 생성
const uniqueId = uuidv4();
console.log(uniqueId); // ex: 'e5c0b9a2-4bf2-4cb3-a6b2-1abf4f5edc54'
3. 조회수 카운트
한사람이 하루에 여러번 조회를 해도 하루에 한번만 조회수를 카운트 합니다.
- mylogId의 마이로그를 조회한 사용자의 조회 기록을 가져옵니다.
- 조회 기록이 없으면 조회 기록을 추가하고 조회수를 증가시킵니다.
- 조회 기록이 있으면 오늘 조회한 기록이 있는지 확인하여 없으면 조회 기록을 추가하고 조회수를 증가시킵니다.
src/views/MyLogView.vue
<!-- src/views/MyLogView.vue -->
<template>
<v-container>
<v-card v-if="mylog">
. . .
<!-- 글쓴이 이름, 작성일, 조회수 -->
<v-card-subtitle style="text-align:right">
{{ mylog.userName }} . {{ mylog.createdAt.toDate().toISOString().split('T')[0] }}
<span v-if="mylog.views > 0" > ({{ mylog.views }})</span>
</v-card-subtitle>
. . .
</v-card>
. . .
</v-container>
</template>
<script>
import { mapActions, mapGetters } from "vuex";
import sanitizeHtml from 'sanitize-html';
export default {
. . .
async created() {
. . .
// 조회수를 증가한다.
// 같은 회원은 몇번을 방문해도 하루 1회, 비회원도 조회수 증가시킴
await this.updateViewCount(mylogId, userId); // 조회수 업데이트 및 기록
. . .
},
. . .
methods: {
...mapActions('mylogs', [. . . 'updateViewCount', . . .]),
. . .
}
};
</script>
src/store/modules/mylogs.js
// src/store/modules/mylogs.js
import { v4 as uuidv4 } from 'uuid';
import { db, doc, collection, getDoc, getDocs, setDoc, addDoc,updateDoc, deleteDoc, arrayUnion, increment, where } from "@/firebase";
import { query, orderBy } from "@/firebase";
const state = {
. . .
};
const mutations = {
. . .
};
const actions = {
. . .
// 조회 기록 업데이트 함수
async updateViewCount({ commit }, mylogId, userId = null) {
let viewId;
let viewedToday;
// 비회원일 경우 로컬 스토리지 또는 쿠키를 사용하여 고유 ID 생성
if (!userId) {
viewId = localStorage.getItem('anonymousUserId');
if (!viewId) {
viewId = uuidv4();
localStorage.setItem('anonymousUserId', viewId);
}
} else {
viewId = userId; // 회원일 경우 사용자 ID
}
try {
const today = new Date().toISOString().split('T')[0];
const viewDocRef = doc(db, 'views', mylogId, 'users', viewId);
// Firestore에서 해당 사용자의 조회 기록을 가져옵니다.
const viewDoc = await getDoc(viewDocRef);
if (viewDoc.exists()) {
// 오늘 조회 기록이 있는지 확인
const lastViewed = viewDoc.data().lastViewed;
viewedToday = lastViewed.some(view => view === today);
}
// 조회한 내역이 없으면 조회수 추가
if (!viewedToday) {
// Firestore의 lastViewed 배열에 조회 시간을 추가
await setDoc(viewDocRef, {
lastViewed: arrayUnion(today) // 배열에 서버 시간을 추가
}, { merge: true });
// mylog 조회수 증가
const mylogRef = doc(db, "mylogs", mylogId);
try {
// 조회수 1 증가 또는 필드가 없을 때 1로 설정
await updateDoc(mylogRef, {
views: increment(1)
});
} catch (error) {
commit('setError', error);
}
}
} catch (error) {
console.log('views : ', error);
commit('setError', error);
}
},
. . .
};
. . .
export default {
namespaced: true,
state,
mutations,
actions,
getters
};
Vue PWA 프로젝트, mylog 코드
'Vue PWA mylog' 카테고리의 다른 글
mylog 댓글, 답글 (2) | 2024.11.17 |
---|---|
mylog 모아보기 (0) | 2024.11.16 |
mylog 수정 (1) | 2024.11.14 |
mylog 상세보기 (2) | 2024.11.13 |
mylog 전체 보기 (0) | 2024.11.11 |