인스타그램 클론코딩 5일차 - BE

박병준·2021년 7월 26일
0
post-thumbnail

#2.6 Followers

@relation으로 단방향 관계의 relation을 지정해줄 수 있다.
예를 들어 B에서 A를 follow하면 A의 follwers에 B가 자동으로 추가된다.
A.followers[ B ]
B.following[ A ]

schema.prisma에서 user model에 followers와 following을 추가하고 migrate해준다.

//schema.prisma
model User {
  .
  .
  . // relation끼리의 이름이 같아야한다.
  followers User[]   @relation("FollowRelation", references: [id])
  following User[]   @relation("FollowRelation", references: [id])
  .
  .
}

followUser의 typeDefs와 resolvers를 만들어준다.
username을 통한 follow

//followUser.typeDefs.js
import { gql } from "apollo-server-express";

export default gql`
    type followUserResult{
        ok: Boolean!
        error: String
    }
    type  Mutation{
       followUser(username: String!): followUserResult!
   }
`;

connect로 연결해줄 때 unique한 field로만 연결해줄 수 있다.
connect 대신 disconnect를 써주면 연결이 해제된다. 즉, unfollow된다.

//followUser.resolver.js
const resolverFn = async (_, { username }, { loggedInUser }) => {
    const isUser = client.user.findUnique({ where: { username } });
    if (!isUser) {
        return {
            ok: false,
            error: "That user does not exist",
        };
    }
    await client.user.update({
        where: {
            id: loggedInUser.id,
        },
        data: {
            following: {
                connect: {
                    username,
                }
            }
        }
    });
    return {
        ok: true
    };
};

#2.7 See Followers & Following

include는 원하는 관계를 갖고 오게 해준다.

prisma는 DB에 양이 엄청 많은 data를 로딩하는 경우 서버에 부담이 갈 수 있으므로 기본적으로 관계를 로딩하고 있지 않는다.

작은양의 data를 가져올 때는 include를 통해 직접 로딩을 허락해 줘야한다.

//seeProfile.resolvers.js
import client from "../../client";

export default {
    Query: {
        seeProfile: (_, { username }) => client.user.findUnique({
            where: {
                username,
            },
            include: {
                following: true,
                followers: true,
            }
        })
    }
};

#2.8 Paginations

위처럼 한번에 많은 data를 불러내지 않고 한 page씩 불러내는 것을 pagination이라 한다.

Pagination 중 2가지 종류를 알아볼 것이다.

Offset Pagination

장점: 원하는 page로 이동할 수 있다.
단점: 만약 200,000개의 data를 skip하고 10개의 data를 가져오려면 200,000개의 data도 가져와야한다.

//seeFollowers.resolvers.js
import client from "../../client"

export default {
    Query: {
        seeFollowers: async (_, { username, page }) => {
            const isUser = await client.user.findUnique({
              where: { username }, select: { id: true } 
            }); //user 존재여부만 확인할 때는 user의 모든 data를 가져오지 않고 id만 가져온다.
            if (!isUser) {
                return {
                    ok: false,
                    error: "User is not found"
                };
            }
            const followers = await client.user
            .findUnique({ where: { username } })
            .followers({
                take: 5,
                skip: (page - 1) * 5,
            });
          //user들 중 username을 following한 사람 수를 찾는 query
            const totalFollowers = await client.user.count({
                where: { following: { some: { username, } } }
            });// 배열을 다 가져올 필요 없이 개수를 세고 count한 수만 가져온다.
            return {
                ok: true,
                followers,
                totalPages: Math.ceil(totalFollowers / 5),
            };
        }
    }
};

Cursor-based Pagination

장점: 규모가 용이하게 커질 수 있다. (무한 스크롤에 쓰임)
단점: 원하는 page로 이동 할 수 없다.

lastId를 받아와 마지막 id부터 skip만큼 다음 부터 take수 만큼 가져온다.
이 때 처음부터 일 경우 lastId가 없을 경우 skip은 0으로 해주고 cursor는 없는 것으로 한다.

// seeFollowing.resolvers.js
import client from "../../client"

export default {
    Query: {
        seeFollowing: async (_, { username, lastId }) => {
            const isUser = await client.user.findUnique({ where: { username }, select: { id: true } });
            if (!isUser) {
                return {
                    ok: false,
                    error: "User is not found"
                };
            }
            const following = await client.user.findUnique({ where: { username } }).following({
                take: 5,
                skip: lastId ? 1 : 0,
                ...(lastId && { cursor: { id: lastId } })
            });
            return {
                ok: true,
                following,
            };
        }
    }
};

#2.9 Computed Field

computed fields는 graphql schema에 정의돼 있지만 데이터베이스에는 없는 것으로 매번 request를 받을 때마다 계산이 된다.

query에서 user를 request할 때 graphql이 DB에서 user를 가져오고 DB에 없는 field를 확인하고 resolvers에서 찾는다. 찾으면 해당 resolver를 DB에서 가져온 user로 실행한다.

resolver의 첫번째 인자는 root로 parent를 의미한다. 그래서 부모유저와 토큰의 로그인한 유저를 비교할 수 있다.

// users.resolvers.js
import client from "../client";

export default {
    User: {
        totalFollowing: ({ id }) => //following 수
            client.user.count({
                where: {
                    followers: {
                        some: {
                            id,
                        }
                    }
                }
            }),
        totalFollowers: ({ id }) => //follower 수
            client.user.count({
                where: {
                    following: {
                        some: {
                            id,
                        }
                    }
                }
            }), //user가 로그인한 자신인지 확인
        isMe: ({ id }, _, { loggedInUser }) => {
            if (!loggedInUser) {
                return false;
            }
            return id === loggedInUser.id;
        }, // user가 follow한 사람인지 확인
        isFollowing: async ({ id }, _, { loggedInUser }) => {
            if (!loggedInUser) {
                return false;
            }
            const exist = await client.user.count({
                where: {
                    username: loggedInUser.username,
                    following: {
                        some: {
                            id
                        }
                    }
                }
            });
            return Boolean(exist);
        }
    }
};
profile
뿌셔뿌셔

0개의 댓글

관련 채용 정보