Next.js+Typescript+GraphQL-Apollo+MongoDB 개발환경 구축

HyosikPark·2020년 12월 14일
3

next.js+graphql+mongodb

목록 보기
1/3
post-custom-banner

조합 선택 이유

혼자서 풀스택 커뮤니티 사이트를 개발하고 싶은데 아는 언어는 자바스크립트 밖에 없다. express+redux로 사이트를 만들어 본 경험이 있는데 상태관리가 아주 복잡하고 생산성이 떨어져 redux를 최대한 피하고 싶었다.

mongodb는 자바스크립트로 활용 가능하고 graphql+apollo를 사용하면 상태관리 필요없이 resolver에서 처리된 데이터를 원하는 컴포넌트에서 불러와 사용하면 되기 때문에 매우 편리하다.

Next.js는 리액트를 사용하여 seo 친화적인 웹을 제작하기 위해서 선택하게 되었고, 한번 배워보고 나니 seo와 상관없이 코드스플릿, ssg, ssr 동적라우팅 등의 이점이 많아 앞으로 자주 애용할 것 같다. 개인적으로 cra보다 훨씬 좋은 것 같다.

typescript는 개발을 처음 접할 때 코드만 더 불어나는 것 같아서 싫어했는데 타입 추론이 되는 부분까지 굳이 건들지 않고 적응해가면서 사용하다보면 매우 좋다. 글자 하나 틀려서 디버깅시에 고생하는 등 경험을 해보니 js가 참 싫어진다..

그리고 serverless로 배포하기 위해 서버는 따로 구축하지 않고 nextjs 개발환경에서 작업하기로 했다.

글을 조리있게 쓰지 못하는거 보니 초보티가 팍파난다. 개발자 되려면 한참 멀었다.

mongoose vs mongodb

이전에는 nextjs없이 개발환경을 설정해봤는데 redux+express+mongodb || react+gql-apollo+mongodb 개발 설정과는 많이 달랐고, 특히 사이트를 참고하면서 mongodb로 데이터베이스에 connect했는데 mongodb가 뭔지 몰라 mongoose로 모델링하고 mongoose 메소드로 데이터베이스에 접근하여 충돌이 일어나는 등 삽질을 많이 했다.

Mongodb는 데이터베이스 자체이고 Mongoose는 Mongodb 의 객체 모델링 도구라고 정의한다. 둘 다 노드에서 설치 가능하고 둘 중 하나만 골라서 데이터베이스에 connect하여 사용할 수 있는데 메소드가 다르고 장단점이 있다.

mongoose는 스키마, 모델을 구성하여 데이터베이스에 저장될 자료 형태 및 속성에 제한을 두어 일관된 형태를 유지하여 안전하게 데이터를 저장할 수 있는 장점이 있지만 db에 접근하기 위해 모델링을 하고 메소드를 사용하는 측면에서 mongodb보다 조금 불편하다고 느껴질 수 있다.

반대로 mongodb는 db에 connect하면 바로 접근할 수 있어 편하지만 타입없이 자료를 작성하거나 가져오기 때문에 실수하지 않게 조심해야 한다.

환경 구축하기

npx create-next-app .
npm i apollo-server-micro graphql mongodb
npm run dev

//pages/api/graphql.ts
import { ApolloServer, gql } from 'apollo-server-micro';

const typeDefs = gql`
  type Query {
    sayHello: String
  }
`;

const resolvers = {
  Query: {
    sayHello(parent, args, context,info) {
      return 'Hello World';
    },
  },
};

export const config = {
  api: {
    bodyParser: false,
  },
};

const apolloServer = new ApolloServer({ typeDefs, resolvers });
export default apolloServer.createHandler({ path: '/api/graphql' });

http://localhost:3000/api/graphql에 접속하면 Apollo-Server PlayGround 활성화
npm i graphql-tools dotenv

// .env
MONGO_DB_URI=mongodb+srv://<username>:<password>@cluster0.ovklx.mongodb.net/<dbname>?retryWrites=true&w=majority

// pages/api/graphql.ts

import { ApolloServer, gql } from 'apollo-server-micro';
import { makeExecutableSchema } from 'graphql-tools';
import { MongoClient } from 'mongodb';

require('dotenv').config();

const typeDefs = gql`
  type User {
    id: ID!
    firstName: String!
    lastName: String!
    blog: String
    stars: Int
  }

  type Query {
    users: [User]!
  }
`;

const resolvers = {
  Query: {
    users(_parent, _args, _context, _info) {
      return _context.db
        .collection('users')
        .findOne()
        .then((data) => {
          return data.users;
        });
    },
  },
};

const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
});

let db;

const apolloServer = new ApolloServer({
  schema,
  context: async () => {
    if (!db) {
      try {
        const dbClient = new MongoClient(process.env.MONGO_DB_URI, {
          useNewUrlParser: true,
          useUnifiedTopology: true,
        });

        if (!dbClient.isConnected()) await dbClient.connect();
        db = dbClient.db('next-graphql'); // database name
      } catch (e) {
        console.log('--->error while connecting via graphql context (db)', e);
      }
    }

    return { db };
  },
});

export const config = {
  api: {
    bodyParser: false,
  },
};

export default apolloServer.createHandler({ path: '/api/graphql' });

Apollo server의 context에서 return한 값은 모든 resolver의 메소드 context인자로 들어가기 때문에 db를 context에 연결해두고 사용한다. 또한 apollo server의 context는 endpoint에 접속해있을 때에 주기적으로 실행된다.
config변수의 객체는 무엇인지 아직 잘 모르겠다. 차차 공부하면서 알아가봐야겠다.

post-custom-banner

0개의 댓글