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

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

#4.0 Direct Message

schema.prisma에 message모델과 room 모델을 만든다. 메세지를 보낼 때 방이 만들어지도록 한다.

message를 subscription을 통해 실시간으로 확인한다.

Subscription

subscription은 서버에 있는 것이 무엇이든 항상 들을 수 있도록 해준다.

어떤 일이 일어나고 내가 만든 조건을 충족하면 서버가 내게 어떤것을 push한다.

  1. pubsub engine을 만든다.
//pubsub.js
import { PubSub } from "apollo-server-express";

const pubsub = new PubSub();

export default pubsub;
  1. 변경된 사항에 subscribe하게 만든다.
// roomUpdate.typeDefs.js
import { gql } from "apollo-server-express";

export default gql`
    type Subscription{
        roomUpdate: Message
    }
`;
// roomUpdate.resolvers.js
import { NEW_MESSAGE } from "../../constants";
import pubsub from "../../pubsub";

export default {
    Subscription: {
        roomUpdate: {
            subscribe: () => pubsub.asyncIterater(NEW_MESSAGE)
        }//message라는 event에 대해 subscribe한다. 
    }
};

http로 request를 하게 되면 subscription은 실시간으로 일어나는 일인데 http는 stateless해서 에러가 난다. http는 한번 request를 보내고 response를 받으면 끝나기 때문이다.
이 때 사용하는 것이 웹소켓이다. 웹소켓은 connection을 열고, 그 연결을 유지하고, 실시간으로 모든 것을 주고 받는다.
웹소켓의 프로토콜은 ws / wss이다.

  1. 서버상에 ws도 가능하도록 설치해줘야한다.
    apollo Subscription reference를 참고하여 설치한다.

  2. 실시간을 사용하는 resolver에서 해당 data를 publish해준다.

// sendMessage.resolvers.js
 const message = await client.message.create({
    data: {
        payload,
        room: {
            connect: {
                id: room.id,
            },
        },
        user: {
            connect: {
                id: loggedInUser.id,
            },
        },
    },
});			//subscribe 함수명: roomUpdate
pubsub.publish(NEW_MESSAGE, { roomUpdate: { ...message } });

subscription은 큰규모의 object에 작지만 계속적으로 증가하는 변화가 있을 때나 저지연의 실시간 업데이트(ex. 채팅 앱)를 원할 때 사용하면 좋다.

Filtering Subscription

위의 방식은 변화하는 값을 다 보게된다. 우리는 특정한 변화만 보고싶으므로 필터링을 한다.

withFilter의 첫번 째 인자는 어떤 걸 subscribe할 것인지 적는것이고 두번째 인자는 그 모델 중 조건에 맞게 필터링하는 함수이다.

//roomUpdate.resolvers.js
import { withFilter } from "graphql-subscriptions";
import client from "../../client";
import { NEW_MESSAGE } from "../../constants";
import pubsub from "../../pubsub";

export default {
    Subscription: {
        roomUpdate: {
            subscribe: async (root, args, context, info) => {
                const room = await client.room.findFirst({
                    where: {
                        id: args.id,
                        users: {
                            some: {
                                id: context.loggedInUser.id,
                            }
                        }
                    },
                    select: {
                        id: true
                    }
                });
                if (!room) {
                    throw new Error("You can't see this room");
                }
                return withFilter(
                    () => pubsub.asyncIterator(NEW_MESSAGE),
                    ({ roomUpdate }, { id }) => {
                        return roomUpdate.roomId === id;
                    }//이 상태면 함수만 리턴한 것이기 때문에 호출까지 해줘야 한다.
                )(root, args, context, info);
            }
        }
    }
};

context에서 loggedInUser를 사용할 때 server.js에서 subscriptionServer를 만드는 곳에 아래 코드를 추가해야 한다. 왜냐하면 프로토콜이 달라서 토큰을 가져오는 방법이 다르기 때문이다.

// server.js
async onConnect({ token }) {
        if (!token) {
            throw new Error('Missing auth token!');
        }
        return {
            loggedInUser: await getUser(token),
        };
    }
profile
뿌셔뿌셔

0개의 댓글