PWA

ChatGPT와 함께 PWA Blog 개발 - 리스트에 글쓴이

그랜파 개발자 2024. 8. 21. 15:03

56. 글리스트에 작성자 이름 추가

ChatGPT에게 물었습니다.

게시글 리스트에 user의 이름과 작성날짜를 나타내고 싶어.

I want to display the user's name and creation date in the list of posts.

To display the user's name and the creation date in the list of posts, you need to store the user's name and the creation date when you save the post. Here is how you can implement this:

게시물 목록에 사용자 이름과 생성 날짜를 표시하려면 게시물 저장 시 사용자 이름과 생성 날짜를 저장해야 합니다. 이를 구현하는 방법은 다음과 같습니다.

Steps

  1. Modify the post submission to include the user's name and creation date.
    사용자 이름과 생성 날짜를 포함하도록 게시물 제출을 수정합니다.
  2. Fetch and display the user's name and creation date in the list of posts.
    게시물 목록에서 사용자 이름과 생성 날짜를 가져와 표시합니다.

1. 게시물 제출 수정

1. Modify the Post Submission

When you save a post, include the user's name and the creation date in the document.
게시물을 저장할 때 문서에 사용자 이름과 생성 날짜를 포함하세요.

views/WritePost.vue

<template>
  <v-container>
    <v-form @submit.prevent="submitPost">
      <v-text-field v-model="title" label="Title" required></v-text-field>
      <v-textarea v-model="content" label="Content" rows="10" required></v-textarea>
      <v-btn type="submit" color="primary">Submit</v-btn>
    </v-form>
  </v-container>
</template>

<script>
import { db, auth } from '../firebase';

export default {
  data() {
    return {
      title: '',
      content: '',
    };
  },
  methods: {
    async submitPost() {
      const user = auth.currentUser;
      if (user) {
        try {
          await db.collection('users').doc(user.uid).collection('posts').add({
            title: this.title,
            content: this.content,
            userName: user.displayName, // Ensure the user's name is available
            createdAt: new Date(),
          });
          this.$router.push('/user-posts');
        } catch (error) {
          console.error("Error writing document: ", error);
        }
      } else {
        console.error("User not authenticated");
      }
    },
  },
};
</script>

2. 사용자 이름과 생성 날짜를 가져오고 표시합니다.

2. Fetch and Display the User's Name and Creation Date

Modify the component that displays the list of posts to include the user's name and the creation date.
사용자 이름과 생성 날짜를 포함하도록 게시물 목록을 표시하는 구성 요소를 수정합니다.

views/UserPosts.vue

<template>
  <v-container>
    <v-alert v-if="error" type="error" dismissible>{{ error.message }}</v-alert>
    <v-progress-circular v-if="loading" indeterminate></v-progress-circular>
    <v-list v-if="!loading">
      <v-list-item v-for="post in posts" :key="post.id" @click="goToPostDetail(post.id)">
        <v-list-item-content>
          <v-list-item-title>{{ post.title }}</v-list-item-title>
          <v-list-item-subtitle>
            <div>{{ post.userName }}</div>
            <div>{{ formatDate(post.createdAt) }}</div>
          </v-list-item-subtitle>
          <v-list-item-subtitle>{{ post.content }}</v-list-item-subtitle>
        </v-list-item-content>
      </v-list-item>
    </v-list>
  </v-container>
</template>

<script>
import { db, auth } from '../firebase';

export default {
  data() {
    return {
      posts: [],
      loading: false,
      error: null
    };
  },
  async created() {
    this.loading = true;
    const user = auth.currentUser;
    if (user) {
      try {
        const querySnapshot = await db.collection('users').doc(user.uid).collection('posts').get();
        this.posts = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
      } catch (error) {
        this.error = error;
      } finally {
        this.loading = false;
      }
    } else {
      this.error = { message: 'User not authenticated' };
      this.loading = false;
    }
  },
  methods: {
    goToPostDetail(postId) {
      this.$router.push({ name: 'PostDetailView', params: { id: postId } });
    },
    formatDate(date) {
      if (date && date.toDate) {
        return date.toDate().toLocaleString();
      }
      return '';
    }
  }
};
</script>

Summary 요약

  • Modify Post Submission: Include the user's name and creation date when saving a post.
    게시물 수정: 게시물을 저장할 때 사용자 이름과 작성 날짜를 포함합니다.
  • Fetch and Display: Fetch these additional fields and display them in the list of posts.
    가져오기 및 표시: 이러한 추가 필드를 가져와 게시물 목록에 표시합니다.

By following these steps, you will be able to display the user's name and the creation date in the list of posts.
다음 단계에 따라 게시물 목록에 사용자 이름과 생성 날짜를 표시할 수 있습니다.

ChatGPT가 시키는 대로 했습니다.

1. views/WritePost.vue 수정

2. views/UserPosts.vue 수정

3. src/viewa/HomeView.vue 수정

4. 실행

Source Code

1. src/router/index.js

// src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '@/views/HomeView.vue';
import LoginView from '@/views/LoginView.vue';
import RegisterView from '@/views/RegisterView.vue';
import ProfileView from '@/views/ProfileView.vue';
import AboutView from '@/views/AboutView.vue';

import WritePost from '@/views/WritePost.vue';
import UserPosts from '@/views/UserPosts.vue';
import PostDetailView from '@/views/PostDetailView.vue';

import store from '../store';

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'HomeView',
    component: HomeView
  },
  { 
    path: '/user-posts', 
    name: 'UserPosts', 
    component: UserPosts, 
    meta: { requiresAuth: true } 
  },
  { 
    path: '/write-post', 
    name: 'WritePost', 
    component: WritePost, 
    meta: { requiresAuth: true } 
  },
  { 
    path: '/post/:id', 
    name: 'PostDetailView', 
    component: PostDetailView
  },
  {
    path: '/login',
    name: 'LoginView',
    component: LoginView
  },
  {
    path: '/register',
    name: 'RegisterView',
    component: RegisterView
  },
  {
    path: '/profile',
    name: 'ProfileView',
    component: ProfileView,
    meta: { requiresAuth: true }
  },
  {
    path: '/about',
    name: 'AboutView',
    component: AboutView
  }
];

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
});


router.beforeEach((to, from, next) => {
  const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
  const user = store.state.auth.user;

  if (requiresAuth && !user) {
    next('/login');
  } else {
    next();
  }
});


export default router;

2. src/App.vue

<!--src/App.vue -->
<template>
  <v-app>
    <v-navigation-drawer app>
      <v-list>
        <v-list-item link>
          <router-link to="/">Home</router-link>
        </v-list-item>
        <v-list-item link v-if="user">
          <router-link to="/write-post">글쓰기</router-link>
        </v-list-item>
        <v-list-item link v-if="user">
          <router-link to="/user-posts">나의 글</router-link>
        </v-list-item>
        <v-list-item link  v-if="!user">
          <router-link to="/login" >로그인</router-link>
        </v-list-item>
        <v-list-item link v-if="!user">
          <router-link to="/register">계정 만들기</router-link>
        </v-list-item>
        <v-list-item link v-if="user">
          <router-link to="/profile">계정 정보</router-link>
        </v-list-item>
        <v-list-item link>
          <router-link to="/about">About</router-link>
        </v-list-item>
      </v-list>
    </v-navigation-drawer>

    <v-app-bar app>
      <v-toolbar-title>My Blog App</v-toolbar-title>
      <v-spacer></v-spacer>
      <v-btn icon @click="doLogout" v-if="user">
        <v-icon>mdi-logout</v-icon>
      </v-btn>
    </v-app-bar>

    <v-main>
      <router-view></router-view>
    </v-main>
  </v-app>
</template>

<script>
import { mapActions, mapGetters } from 'vuex';

export default {
  name: 'App',
  computed: {
    ...mapGetters('auth', ['user'])
  },
  methods: {
    ...mapActions('auth', ['logout']),
    doLogout() {
      this.logout()
    }
  }
};
</script>

3. src/views/WritePost.vue

<!-- src/views/WritePost.vue -->
<template>
  <v-container>
      <v-form @submit.prevent="submitPost">
        <v-text-field v-model="title" label="Title" required></v-text-field>
        <v-textarea v-model="content" label="Content" rows="10" required></v-textarea>
        <v-btn type="submit" color="primary">Submit</v-btn>
      </v-form>
  </v-container>
</template>

<script>
import { db, auth } from '../firebase';

export default {
  data() {
    return {
      title: '',
      content: '',
    };
  },
  methods: {
    async submitPost() {
      const user = auth.currentUser;
      if (user) {
        try {
          await db.collection('posts').add({
            title: this.title,
            content: this.content,
            userId: user.uid,
            userName: user.displayName, // Ensure the user's name is available
            createdAt: new Date(),
          });
          //this.$router.push('/post-list');
          this.$router.push('/');
        } catch (error) {
          console.error("Error writing document: ", error);
        }
      } else {
        console.error("User not authenticated");
      }
    },
  },
};
</script>

4. src/viewa/HomeView.vue

<!-- src/viewa/HomeView.vue -->
<template>
  <v-container>
    <v-alert v-if="error" type="error" dismissible>{{ error.message }}</v-alert>
    <v-progress-circular v-if="loading" indeterminate></v-progress-circular>
    <v-list v-if="!loading && posts.length">
      <v-list-item v-for="post in posts" :key="post.id" @click="goToPostDetail(post.id)">
        <v-list-item-content>
          <v-list-item-title>{{ post.title }}</v-list-item-title>
          <v-list-item-subtitle>
            <div>{{ post.userName }}</div>
            <div>{{ formatDate(post.createdAt) }}</div>
          </v-list-item-subtitle>
          <v-list-item-subtitle>{{ post.content }}</v-list-item-subtitle>
        </v-list-item-content>
      </v-list-item>
    </v-list>
    <v-alert v-if="!loading && !posts.length" type="info">No posts available</v-alert>
  </v-container>
</template>

<script>
export default {
  name: 'Home',
  computed: {
    posts() {
      return this.$store.state.posts.posts;
    },
    loading() {
      return this.$store.state.posts.loading;
    },
    error() {
      return this.$store.state.posts.error;
    }
  },
  created() {
    this.$store.dispatch('posts/fetchPosts');
  },
  methods: {
    goToPostDetail(postId) {
      this.$router.push({ name: 'PostDetailView', params: { id: postId } });
    },
    formatDate(date) {
      if (date && date.toDate) {
        return date.toDate().toLocaleString();
      }
      return '';
    }
  }
};
</script>

5. src/views/UserPosts.vue

Copy<!-- src/views/UserPosts.vue -->
 <template>
  <v-container>
    <v-alert v-if="error" type="error" dismissible>{{ error.message }}</v-alert>
    <v-progress-circular v-if="loading" indeterminate></v-progress-circular>
    <v-list v-if="!loading">
      <v-list-item v-for="post in posts" :key="post.id" @click="goToPostDetail(post.id)">
        <v-list-item-content>
          <v-list-item-title>{{ post.title }}</v-list-item-title>
          <v-list-item-subtitle>
            <div>{{ post.userName }}</div>
            <div>{{ formatDate(post.createdAt) }}</div>
          </v-list-item-subtitle>
          <v-list-item-subtitle>{{ post.content }}</v-list-item-subtitle>
        </v-list-item-content>
      </v-list-item>
    </v-list>
  </v-container>
</template>

<script>
import { db, auth } from '../firebase';

export default {
  data() {
    return {
      posts: [],
      loading: false,
      error: null
    };
  },
  async created() {
    this.loading = true;
    const user = auth.currentUser;
    if (user) {
      try {
        const querySnapshot = await db.collection('posts').where('userId', '==', user.uid).get();
        this.posts = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
      } catch (error) {
        this.error = error;
      } finally {
        this.loading = false;
      }
    } else {
      this.error = { message: 'User not authenticated' };
      this.loading = false;
    }
  },
  methods: {
    goToPostDetail(postId) {
      this.$router.push({ name: 'PostDetailView', params: { id: postId } });
    },
    formatDate(date) {
      if (date && date.toDate) {
        return date.toDate().toLocaleString();
      }
      return '';
    }

  }
};
</script>