GraphQL을 검색 서버에서 활용해보고 싶었다.
앱 내에 다양한 검색들이 있었는데,
검색 부분에서 restApi보단,
endpoint를 동일하게하고, 프론트에서 원하는 데이터만 축출해가는 디자인 패턴이
더 효율적이라 생각했기 때문이다.
일단 프로젝트 배포전 임의로 셋팅 해봤다.
app.ts
import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
import apolloServer from './apollo/apolloserver';
import 'dotenv/config';
const main = async () => {
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static('public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(
cors({
origin: '*',
credentials: true,
}),
);
apolloServer.listen(process.env.PORT).then(({ url }) => {
console.log(`Listening at ${url}`);
});
};
main();
아폴로 서버를 사용했다.
실서버에선 cors를 origin에 맞게 넣어줘야겠다.
apolloserver.ts
import { ApolloServer } from 'apollo-server';
import { ApolloServerPluginLandingPageGraphQLPlayground } from 'apollo-server-core';
import schema from '../gql/schema';
import memberAuthMiddleWare from '../middlewares/memberAuth.middleware';
import resolvers from '../gql/resolvers/member.resolvers';
import 'dotenv/config';
const apolloServer = new ApolloServer({
rootValue: resolvers,
schema,
cors: {
allowedHeaders: '*',
origin: '*',
credentials: true,
},
introspection: true,
plugins: [ApolloServerPluginLandingPageGraphQLPlayground()],
context: async ({ req }) => {
const authorization = req.headers.authorization || '';
const token = await memberAuthMiddleWare(authorization);
return token;
},
});
export default apolloServer;
cors는 실 프로덕트에선 사용할 origin만 넣어야겠다.
introspection은 playground와 applo-send-box사용할 때 사용되는 것인데,
메인 프로덕트는 false로 배포해야겠다.
plugins에선 playground를 사용하겠다는 옵션을 넣었다. (또한 실 프로덕트에선 빼야함)
context는 graphql에서 미들웨어라고 생각하면 편하다.
위 코드는 멤버 토큰을 받아오고, 나중에 context.token으로 불러올 수 있다.
member.typeDefs.ts
import { gql } from 'apollo-server';
const memberTypeDefs = gql`
type MemberInfo {
memberIdx: Int
memberId: String
memberPw: String
memberJoin_type: String
memberName: String
memberPhone: String
memberEmail: String
gender: Int
memberAge: Int
dietMode: Int
height: Int
weightGoal: Int
netCarbohydrateGoal: Int
proteinGoal: Int
fatGoal: Int
caloriesGoal: Int
status: Int
profileCheck: Int
joinRoute: Int
profileImg: String
delYn: Int
createdAt: String
updatedAt: String
}
type SnsInfo {
memberIdx: Int
followingMemberIdx: Int
}
type Query {
getMemberListSearchedByWord(word: String): [MemberInfo]
getFollowingList: [SnsInfo]
}
`;
export default memberTypeDefs;
타입들을 지정해줬다. 지정한 것들은 리졸브나 뮤테이션에서 사용할 수 있다.
member.resolvers.ts
import { Context } from '../../types/context';
import memberService from '../../service/member.service';
const memberResolvers = {
Query: {
getMemberListSearchedByWord: async (_parent: any, args: any, _context: Context, _info: any) => {
const result = await memberService.getMemberListSearchedByWord(args.word);
return result;
},
getFollowingList: async (_parent: any, _args: any, context: Context, _info: any) => {
const result = await memberService.getFollowingList(context.token);
return result;
},
},
};
export default memberResolvers;
resolvers에서 사용되는 것들로, 클라이언트에서 쿼리로 요청할 때 return값을 반환한다.
memberService는 서비스 로직이다.
controller와 service로 나누던걸, resolvers와 service로 나누는 느낌이 들었다.
args는 클라이언트에서 보낸 데이터를 받는데 사용되고,
context는 아까 아폴로 서버에서 만들어 둔 미들웨어를 받는데 사용한다.
클라이언트 코드
import axios from "axios";
axios.get(serverEndPoint, {
params: {
query: `{
getMemberListSearchedByWord(word: “검색해보기“) {
memberIdx
memberName
}
}`,
},
})
.then((result) => {
console.log("hello", result);
})
.catch((err) => {
console.log(err);
});
})
restApi처럼 axios나 request를 이용해서 요청할 수 있다.
다만 엔드포인트가 똑같고, params에 query를 넣어서 요청한다.
mutations같은 경우엔 post를 이용한다.
mutations나 resovlers나 모두 post로 통일해서 이용할 수 있지만, 해당 클라이언트 코드에선 get method를 사용했다.
'Back > Node.js' 카테고리의 다른 글
[Nodejs, TypeScript] 도메인 로직 리팩토링 (0) | 2022.08.05 |
---|---|
[Nodejs, GraphQL] Middleware(미들웨어) 만들기 (0) | 2022.08.04 |
[NestJs, Prisma] Prisma CRUD (0) | 2022.07.19 |
[Nodejs] Slack Message 슬렉 메시지 보내기 slack-freinds (0) | 2022.06.23 |
[Nodejs] Express Typescript SocketIo (0) | 2022.06.13 |