backend에서 가장 어려운 부분, 중요한 부분, update가 자주 일어나는 부분.
고래도 할 수 있다!!!!!
항상, 공식문서를 참고할것.!!!
https://www.apollographql.com/docs/apollo-server/data/subscriptions/
https://www.apollographql.com/docs/apollo-server/data/subscriptions/#enabling-subscriptions
https://www.apollographql.com/docs/apollo-server/data/subscriptions/#onconnect-and-ondisconnect
npm install graphql-ws ws @graphql-tools/schema apollo-server-core
require('dotenv').config()
import { ApolloServer } from 'apollo-server-express'
import { graphqlUploadExpress } from 'graphql-upload'
import { getUser } from './users/users.util'
import express from 'express'
import { createServer } from 'http'
import { ApolloServerPluginDrainHttpServer } from 'apollo-server-core'
import { WebSocketServer } from 'ws'
import { useServer } from 'graphql-ws/lib/use/ws'
import { makeExecutableSchema } from '@graphql-tools/schema'
import { typeDefs, resolvers } from './schema'
const PORT = process.env.PORT
const startServer = async () => {
const schema = makeExecutableSchema({ typeDefs, resolvers })
const app = express()
app.use(graphqlUploadExpress())
app.use('/static', express.static('uploads'))
const httpServer = createServer(app)
const wsServer = new WebSocketServer({
server: httpServer,
path: '/graphql',
})
//우리가 naver해외축구 같은 페이지를 볼떄, 단방향 통신, 즉, 한번data가 오고,
//다시 req가 있기 전까지 통신이 연결이 안되어 있는게 http 서버
//채팅처럼 연결이 끝어지지않고 유지가 되는게 WebSock 서버임.
// 맨처음, http서버와 wsServer를 만들어줌.
------------------------------
// const getDynamicContext = async (ctx, msg, args) => {
// if (ctx.connectionParams.token) {
// console.log(ctx)
// const loggedInUser = await getUser(ctx.connectionParams.token)
// console.log(loggedInUser)
// return { loggedInUser }
// }
// }
--------------------------참고만 할 것!, 지워더 상관 없는 부분.
const serverCleanup = useServer(
{
schema,
///schema를 담아주고,
///wsServer가 연결됬을때와 연결이 끊어 졌을때를 설정해줌.
onConnect: async (ctx) => {
// console.log(ctx.connectionParams.token)
// console.log(context)
if (!ctx.connectionParams.token) {
throw new Error('You can not listen')
}
//연결되었을때, token이 없으면 error던짐.
const loggedInUser = await getUser(ctx.connectionParams.token)
// return loggedInUser
//여기서 return 한 loggedInUser는 context로 감
//기존에는 여기서 loggedInUser를 token으로 찾아서 return해 주면,
//subscribe resolver의 context로 loggedInUser가 갔는데,
//이제는 가지 않음. 밑의 context: ~ 방법으로 subscribe resolvers에
//loggedInUser를 return함. context에~~
},
context: async (ctx) => {
const loggedInUser = await getUser(ctx.connectionParams.token)
return loggedInUser
},
///subscribr resolvers(roomUpdates등등)의 context에 loggedInUser를 보냄
onDisconnect(ctx, code, reason) {
console.log('Disconnected!')
},
},
wsServer
)
const server = new ApolloServer({
schema,
context: async (ctx) => {
// console.log(ctx.req.headers.token)
if (ctx.req) {
return {
loggedInUser: await getUser(ctx.req.headers.token),
}
///subscribe resolvers가 아닌 일반 resolvers(http 사용)의
///context에 loggedInUser를 보냄
//원래는 else 부분은 webSocket연결시 context에 loggendInUser를
//보내는 방법인데 작동을 안함. else이후 부분은 없어도 됨.
} else {
const {
connection: { context },
} = ctx
return {
loggedInUser: context.loggedInUser,
}
}
},
//이후 부분은 공식문서를 따라한 것임.
plugins: [
ApolloServerPluginDrainHttpServer({ httpServer }),
{
async serverWillStart() {
return {
async drainServer() {
await serverCleanup.dispose()
},
}
},
},
],
})
await server.start()
server.applyMiddleware({ app })
httpServer.listen(PORT, () => {
console.log(
`Server is now running on http://localhost:${PORT}${server.graphqlPath}`
)
})
}
startServer()
!!!check
이전( 몇달전??)까지는(2022.4.29기준) subscriptions-transport-ws를 사용해서
subscription을 구현했으나, (2022.4.29 기준) 현재는 graphql-ws 를 이용해서
subscription을 구현함.!!!!
개인적으로 backend에서 가장 어렵고 중요한 부분임...