이제 본격적으로 채팅서비스 API를 만들어 보자!
GraphQL에서 쿼리를 작성하기 위해서는 일단 쿼리에 대한 타입이 필요하다. 우리가 만들 채팅 서비스에서 필요한 채팅에 관련된 타입을 정의해보자
타입 정의
type Chat{
id: Int!
writer: String!
content: String!
}
src/api/chat/shared/Chat.graphql
에 Chat
이라는 타입을 만들었다. 앞으로 우리는 이 타입을 사용해 채팅을 조회하고 구독하는데 사용할 것이다. 구조는 간단하게 id, 그리고 작성자와 내용으로 구성했다.
채팅 조회 구현
채팅을 조회하려면 채팅이 저장되었는 공간이 필요하다. 실제 서비스라면 db를 사용하거나 다른 저장소를 사용했겠지만 이번 채팅서비스는 간단하게 구현하는 것이 목표이기 때문에 메모리에 배열을 사용해 저장하려고 한다.
export default [
{
id: 1,
writer: 'chanyeong',
content: 'first chat!',
},
];
src/api/chat/shared/chatList.ts
에 다음과 같이 초기 채팅 데이터를 가지고 있는 배열을 만들어준다. 앞으로 모든 채팅은 이 배열에 저장할 것이다.
자! 이제 채팅 조회에 대한 API를 작성할 것이다. API를 만들기 위해서는 기본적으로 해당 API의 타입이 정의된 graphql
파일과 행동이 정의된 resovlers
함수를 구현해야 한다.
type Query {
getChatList: [Chat!]!
}
src/api/chat/getChatList/getChatList.graphql
에 먼저 타입부터 정의해보자. 데이터를 조회하는 API이기 때문에 Query
타입으로 선언 후 쿼리에 대한 이름을 작성해 줬다. 옆에 반환 타입을 지정해 줬는데 좀 전에 shared
파일에 생성한 Chat
의 배열타입을 반환 타입으로 지정해줬다.
저 타입을 다른 파일에 작성했지만 사용할 수 있는 이유는 이전 포스트에서 모든 타입을 병합한 뒤 서버에 스키마로 넣어줬기 때문에 다른 파일에 작성한 타입도 사용할 수 있다.
npm run types
# ✔ Parse configuration
# ✔ Generate outputs
이제 우리가 작성한 graphql
파일로 타입스크립트 타입을 생성해줘야 한다. 이전 포스트에서 설정한 대로 다음 명령어를 통해 타입을 생성하자! 정상적으로 타입이 생성 되었다면 위와 같은 메시지가 표시된다. (서버는 실행중이여야 한다.)
import { Resolvers } from '../../../types';
import chatList from '../shared/chatList';
const resolvers: Resolvers = {
Query: {
getChatList: () => chatList,
},
};
export default resolvers;
src/api/chat/getChatList/getChatList.resolvers.ts
에 resolvers
함수를 작성해보자! 파일 이름에는 무조건 .resolvers.
를 넣어줘야 나중에 graphql서버에서 인식할 수 있다. (이전 포스트에서 병합 설정)
다음과 같이 resolvers
함수에는 쿼리의 타입(Query, Mutation, Subscription)과 그 안에 쿼리의 이름을 입력 후 API 함수를 작성하면 된다. 채팅을 조회하는 API이기 때문에 요청 시 아까 만든 chatList
배열을 그대로 반환시켜 줬다.
다음과 같이 playground에서 쿼리를 실행시켜 봤는데 정상적으로 채팅을 반환하는 것을 확인할 수 있다.
채팅 작성 구현
채팅 조회도 구현 했으니 이제 채팅을 작성하는 API도 구현해보자! 우선 아까와 같이 쿼리의 타입과 resolvers
함수부터 정의해야 한다.
type AddChatResonse {
result: Boolean!
error: String
}
type Mutation {
addChat(writer: String!, content: String!): AddChatResonse!
}
src/api/chat/addChat/addChat.graphql
에 다음과 같이 타입을 작성해준다. 좀 전의 getChatList
의 타입과는 조금 다르다. 일단 쿼리의 반환 타입을 새로 생성해 줬다. Mutation
은 데이터 조작이 발생하는 쿼리이기 때문에 성공과 실패를 의미하는 result
와 에러가 발생했을 경우 에러 메시지를 담을 error
를 타입으로 명시해줬다.
그리고 Mutation
타입 안에 addChat
이라는 쿼리를 정의해줬다. 매개변수로는 작성자와 내용을 받고 좀 전에 만든 AddChatResonse
라는 반환 타입을 갖도록 했다.
이제 다시 npm run types
를 통해 타입을 생성하고 resolvers
함수를 정의해야 한다.
import { Resolvers } from '../../../types';
import chatList from '../shared/chatList';
const resolvers: Resolvers = {
Mutation: {
addChat: (_, { writer, content }) => {
const newChat = { id: chatList.length + 1, writer, content };
chatList.push(newChat);
return { result: true };
},
},
};
export default resolvers;
src/api/chat/addChat/addChat.resolvers.ts
에 다음과 같이 resolvers
함수를 작성한다. addChat
메서드의 두 번째 매개변수로 입력받은 작성자와 내용을 받아 새로운 채팅을 만들어 기존의 chatList
배열에 추가시켜주고 성공했다는 result
를 반환했다.
작성한 API를 확인해보니 정상적으로 채팅이 추가되는 것을 볼 수 있다.
getChatList쿼리를 다시 실행시켜 정상적으로 채팅이 들어갔는지도 확인해 보았다. 방금 추가한 채팅이 정상적으로 잘 들어간 것을 확인할 수 있다. 😁
채팅 구독 구현
채팅에서 가장 중요한 기능은 실시간으로 새로 작성된 채팅을 불러오는 것이다. 그렇기 때문에 채팅을 구독하는 기능을 구현해야 한다. 사용자는 채팅목록에 채팅이 추가되는 것을 구독하고 있다가 상대방이 채팅을 작성해 채팅목록에 추가하면 실시간으로 사용자의 화면으로 새롭게 추가된 채팅을 가져와야 한다.
type Subscription {
subChat: Chat
}
src/api/chat/subChat/subChat.graphql
파일에 다음과 같은 타입을 작성한다. subChat
이라는 쿼리로 구독을 하면 새로운 데이터를 Chat
타입으로 보내주는 코드이다.
타입을 작성했으니 역시 npm run types
로 타입을 생성한다.
import { Resolvers } from '../../../types';
export const NEW_CHAT = 'NEW_CHAT';
const resolvers: Resolvers = {
Subscription: {
subChat: {
subscribe: (_, __, { pubsub }) => {
return pubsub.asyncIterator(NEW_CHAT);
},
},
},
};
export default resolvers;
src/api/chat/subChat/subChat.resolvers.ts
에 다음과 같이 resolvers
함수를 작성한다. Subscription
은 함수를 작성할 때 subChat
이라는 쿼리 이름안에 다시한번 객체로 감싸서 subscribe
라는 프로퍼티 안에 함수를 작성해야 한다.
세 번째 인자로 context
객체 안의 pubsub
을 받았는데 지난 포스트에서 Apollo Server를 설정할 때 context
객체 안에 pubsub
을 생성 후 넣어줬기 때문에 사용할 수 있다.
pubsub
은 subscription
기능을 사용할 때 구독, 발행 시 사용할 수 있는 객체이다. 현재 subChat
에서는 NEW_CHAT
이라는 이름으로 데이터를 구독하기위해 사용되었다.
NEW_CHAT
은 데이터를 발행할 때도 사용해야 하기 때문에 export
키워드를 사용해 다른 코드에서도 접근할 수 있어야 한다.
import { Resolvers } from '../../../types';
import chatList from '../shared/chatList';
// NEW_CHAT 불러오기
import { NEW_CHAT } from '../subChat/subChat.resolvers';
const resolvers: Resolvers = {
Mutation: {
addChat: (_, { writer, content }, { pubsub }) => {
const newChat = { id: chatList.length + 1, writer, content };
chatList.push(newChat);
// NEW_CHAT이름으로 데이터 발행
pubsub.publish(NEW_CHAT, { subChat: newChat });
return { result: true };
},
},
};
export default resolvers;
여기까지 했으면 아직 구독자는 채팅이 추가되었는지 추가되지 않았는지 확인할 수 없다. 그러니 채팅 추가 API에 채팅을 추가하고 NEW_CHAT
이라는 이름으로 데이터를 발행해 줘야 한다.
src/api/chat/addChat/addChat.resolvers.ts
에 다음과 같이 NEW_CHAT
을 불러오고 데이터를 발행하는 코드를 추가한다.
그리고 playground에서 subChat
쿼리를 실행 후 addChat
쿼리로 채팅을 추가해 보니 정상적으로 구독된 채팅 데이터가 넘어오는 것을 확인할 수 있었다.
이것으로 채팅 서비스의 Apollo Server 개발도 끝나게 되었다.
다음 포스트는 GraphQL을 사용한 React Client 환경 설정에 대한 내용을 다룰 예정이다.!