PJH's Community Site - User

박정호·2022년 12월 4일
0

Community Project

목록 보기
12/14
post-thumbnail

🚀 Start

이제 User를 클릭하면 해당 유저가 작성한 댓글, 작성한 게시글에 대한 정보를 가져오는 유저정보 페이지를 생성해보자.



⭐️ User Page

파일 생성



✔️ User Page 기능 생성

👉 api 요청 (client)

이전에 게시글 페이지에서 사용한 username 데이터를 url의 query형태로 전달하였기 때문에 usename을 사용 가능한 것이다.

// [slug].tsx
<Link  href={`/u/${post.username}`}> /u/{post.username} </Link>

즉, Router를 통해 props 기능 구현하여 해당 게시글을 작성한 User에 대한 username을 props로 받아온다.

// u/[username].tsx
 const router = useRouter();
  const username = router.query.username;

  const { data, error } = useSWR(username ? `/users/${username}` : null);


👉 api 생성 (server)

파일 생성

Route 설정

// server.ts
app.use("/api/users", userRoutes)

getUserData 핸들러 생성

1️⃣ 유저 정보 가져오기 (유저이름과 작성일자)

2️⃣ 해당 유저가 작성한 게시글 정보 가져오기 (해당 게시글의 투표,댓글 정보도 함께)

3️⃣ 해당 유저가 작성한 댓글 정보 가져오기 (해당 댓글이 있는 게시글 정보도 함께)

4️⃣ User page에 출력되는 댓글, 게시글에도 투표기능이 동작하도록 설정

5️⃣ userData에 앞서 불러온 게시글, 댓글 정보를 담아준다.

  • userData 내의 게시글,댓글을 구분하기 위하여 type을 담아준다.

  • toJSON: spread 연산자를 이용해서 새로운 객체에 깊은 복사할 때 인스턴스 상태로 하면, @Expose를 이용한 getter는 들어가지 않는다. 따라서, 객체로 바꾼후 복사한다.

6️⃣ userData에 존재하는 댓글,게시글 데이터들을 작성일자 순으로 정렬

7️⃣ user, userData를 client로 반환

const getUserData = async (req: Request, res: Response) => {
  try {
    const user = await User.findOneOrFail({ // 1️⃣ 번
      where: { username: req.params.username },
      select: ['username', 'createdAt'],
    });

    const posts = await Post.find({ // 2️⃣ 번
      where: { username: user.username },
      relations: ['comments', 'votes', 'sub'],
    });

    const comments = await Comment.find({ // 3️⃣ 번
      where: { username: user.username },
      relations: ['post'],
    });

    if (res.locals.user) { // 4️⃣ 번
      const { user } = res.locals;
      posts.forEach(p => p.setUserVote(user));
      comments.forEach(c => c.setUserVote(user));
    }

    const userData = [];

    // 5️⃣ 번
    posts.forEach(p => userData.push({ type: 'Post', ...p.toJSON() }));
    comments.forEach(c => userData.push({ type: 'Comment', ...c.toJSON() }));

    // 6️⃣ 번
    userData.sort((a, b) => {
      if (b.createdAt > a.createdAt) return 1;
      if (b.createdAt < a.createdAt) return -1;
      return 0;
    });

    return res.json({ user, userData }); // 7️⃣ 번
  } catch (error) {
    console.log(error);
    return res.status(500).json({ error: 'Something went wrong' });
  }
};

💡 class-transformer? ... @Expose getter ? ... toJSON() ?

JavaScript에는 두 가지 유형의 객체가 있다.

  • 일반(리터럴) 객체
  • 클래스(생성자) 개체

일반 개체는 Object클래스의 인스턴스인 개체이다. 때로는 표기법 을 통해 생성될 때 리터럴 객체 라고 한다.
{}클래스 개체는 자체적으로 정의된 생성자, 속성 및 메서드가 있는 클래스의 인스턴스입니다. 일반적으로 class표기법을 통해 정의힌다.

가끔 일반 자바스크립트 객체를 가지고 있는 ES6 클래스 로 변환하고 싶을 때가 있다.

예를 들어, 백엔드, 일부 api 또는 json 파일에서 json을 로드하는 경우 JSON.parse클래스 인스턴스가 아닌 일반 자바스크립트 객체를 갖게 된다.

따라서, 새 인스턴스를 마들고 모든 속성을 새 개체에 수동으로 복사해야 하므로 더 복잡한 개체 계층 구조가 있으면 잘못된 코드를 작성할 수 있다.

class-transformer 사용

따라서, 클래스 변환기를 사용하면 일반 객체를 클래스의 일부 인스턴스로 변환하거나 그 반대도 가능하다. 또한 기준에 따라 개체를 직렬화/역직렬화할 수 있다.

Expose()

class-transformer 의 데코레이터로, 직렬화 대상 필드를 지정한다.
여기서는 모든 멤버변수는 @Exclude() 로 직렬화 대상에서 제외하고, 온전히 노출에만 사용할 수 있는 getter 메소드에만 @Expose() 를 선언한다.

확인해야할 점!

getter: 메서드가 정상적으로 JSON 변환 되었는지..

하지만, 앞서본 과정처럼 spread 연산자를 이용하여 복사를 할때 인스턴스 상태로 getter가 JSON 형식이 아니므로 받아들이지 못하고 undefined가 된다.

toJSON()
따라서, 객체로 변환하는 과정이 필요한 것!


참고하자!
👉 응답/요청 객체 직렬화 (Serialization) 하기
👉 class-transformer



✔️ User Page UI 작성

1️⃣ 만약 type이 Post 라면, 게시글 데이터 가져오기

2️⃣ 만약 type이 Comment 라면, 게시글 데이터 가져오기

  • 유저이름 출력 및 url 이동
  • 게시글 제목 출력 및 url 이동
  • 커뮤니티이름 출력 및 url 이동
  • 댓글내용 출력 및 url 이동

3️⃣ 유저에 대한 정보 출력.

 <div>
      {/* 게시글, 댓글 리스트 */}
      <div>
        {data.userData.map((data: any) => { // 1️⃣ 번
          if (data.type === 'Post') {
            const post: Post = data;
            return <PostCard key={post.identifier} post={post} />;
          } 
          else { // 2️⃣ 번
            const comment: Comment = data;
            return (
              <div
                key={comment.identifier}>
                <div>
                  <i className="text-gray-500 fas fa-comment-alt fa-xs"></i>
                </div>
                <div>
                  <p>
                    <Link href={`/u/${comment.username}`}>
                      {comment.username}
                    </Link>
                    <span>commented on</span>
                    <Link href={`/u/${comment.post?.url}`}>
                      {comment.post?.title}
                    </Link>
                    <span></span>
                    <Link href={`/u/${comment.post?.subName}`}>
                      /r/{comment.post?.subName}
                    </Link>
                  </p>
                  <hr />
                  <p>{comment.body}</p>
                </div>
              </div>
            );
          }
        })}
      </div>
      {/* 유저 정보 */}
      <div>
        <div> // 3️⃣ 번
          <Image
            src="https://www.gravatar.com/avatar/0000?d=mp&f=y"
            alt="user profile"
            className="mx-auto border border-white rounded-full"
            width={40}
            height={40}
          />
          <p>{data.user.username}</p>
        </div>
        <div>
          <p>{dayjs(data.user.createdAt).format('YYYY.MM.DD')} 가입</p>
        </div>
      </div>
    </div>


📷 Photos

profile
기록하여 기억하고, 계획하여 실천하자. will be a FE developer (HOME버튼을 클릭하여 Notion으로 놀러오세요!)

0개의 댓글

관련 채용 정보