brew
를 이용해 설치해보겠습니다.
터미널을 열어 brew update
입력 후, brew install mysql
을 입력해 설치해주세요.
brew update
brew install mysql
설치가 완료되면 brew services start mysql
을 입력해주세요.
brew services start mysql
이 상태로도 사용할 수 있지만, 보안을 위해 비밀번호 설정을 해줘야합니다.
터미널에 mysql_secure_installation
을 입력합니다.
mysql_secure_installation
복잡한 비밀번호를 사용할 것이냐는 질문에N
를 입력하고 엔터를 칩니다.
New Password:
라고 나오면, 비밀번호를 입력하고 엔터를 쳐서 실행합니다.
Re-enter new password:
비밀번호를 다시 입력합니다.
익명의 유저를 삭제하는데 동의하냐는 질문에 y
를 입력합니다.
n
을 입력해 root의 원격 접속을 허용합니다.
n
을 입력해 test 데이터베이스를 유지합니다.
y
를 입력해 변경된 권한을 테이블에 적용합니다.
터미널에 mysql -u root -p
라고 입력한 뒤, 방금 설정한 비밀번호를 입력합니다.
mysql>
처럼 터미널에 바뀌면 성공입니다.
DBeaver 공식 홈페이지에 접속해서 Download를 눌러줍니다.
본인 컴퓨터 OS에 맞는 설치 프로그램을 다운로드하고 설치해줍니다.
이전에는 NestJS와 Rest-API & TypeScript를 사용했지만 이번에는 GraphQL & TypeScript를 사용하는 방법에 대해 알아보겠습니다.
Nest는 GraphQL 애플리케이션을 빌드하는데 코드 우선(code first) 및 스키마 우선(schema first) 방법을 제공합니다.
코드 우선 접근 방식에서는 데코레이터의 TypeScript 클래스를 사용하여 해당 GraphQL 스키마를 생성합니다.
이 방법은 TypeScript으로만 작업하고 언어 구문 간의 컨텍스트 전환을 피하려는 경우 유용합니다.
코드 우선 접근 방식을 사용하려면 먼저 옵션 객체에 autoShcemaFile
속성을 추가합니다.
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: 'src/commons/graphql/schema.gql`,
})
autoSchemaFile
속성 값은 자동으로 생성된 스키마라 생성될 경로입니다.
스키마 우선 접근 방식에서 진실된 소스는 GraphQL SDL(Schema Definsion Language) 파일입니다.
모든 프로그래밍 언어와 독립적이며, 통합되는 언어이고, NestJS
에서는 GraphQL 스미카를 TypeScript
의 클래스 및 인터페이스 형식으로 구현됩니다.
GraphQL 스키마를 기반으로 TypeScript 정의(클리스 또는 인터페이스 사용)를 자동으로 생성하여 중복된 상용구 코드를 작성할 필요성을 줄여줍니다.
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
typePaths: ['./**/*.graphql'],
})
스키마 우선 접근 방식을 사용하려면 먼저 옵션 개체에 typePaths
속성을 추가합니다.
typePaths
속성은 GraphQLModule
이 작성할 GraphQL SDL 스키마 정의 파일을 찾아야하는 위치를 나타냅니다.
// cat.graphql
type Query {
cats: [Cat]
cat(id: ID!): Cat
}
type Mutation {
createCate(createCateInput: CreateCatInput): Cat
}
type Subscription {
catCreated: Cat
}
type Owner {
id: Int!
name: String!
age: Int
cats: [Cat!]
}
type Cat {
id: Int
name: String
age: Int
owner: Owner
}
input CreateCatInput {
name: String
age: Int
}
아래처럼 .graphql
로 스키마를 직접 작성해줘야 합니다.
즉 code first는 typescript로 클래스를 짜면 해당 클래스에 해당하는 graphql schema를 만들어주고 schema first는 graphql schema를 먼저 짠 후 typescript 클래스나 인터페이스를 생성해줍니다.
typescript에 좀 더 익숙해지기 위해서 code first 방식으로 애플리케이션을 빌드 하겠습니다.
13-01-nestjs-with-graphql
프로젝트를 생성합니다.
터미널에 nest new 13-01-nestjs-with-graphql
를 입력해서 새로운 프로젝트를 생성해주세요.
생성된 폴더에 들어가yarn add @nestjs/graphql @nestjs/apollo @apollo/server graphql
를 GraphQL을 사용하기 위해 필요한 패키지를 설치해주세요.
// package.json
{
"name": "13-01-nestjs-with-graphql",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@apollo/server": "^4.7.1",
"@nestjs/apollo": "^11.0.5",
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/graphql": "^11.0.5",
"@nestjs/platform-express": "^9.0.0",
"graphql": "^16.6.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.5.0",
"@types/node": "18.15.11",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.5.0",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.5",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.2.0",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
nextjs docs에 따라 app.module.ts
파일의 코드를 아래와 같이 수정해줍니다.
// app.module.ts
import { Module } from '@nestjs/common';
import { ApolloDriverConfig, ApolloDriver } from '@nestjs/apollo';
import { GraphQLModule } from '@nestjs/graphql';
import { BoardsModule } from './apis/boards/boards.module';
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: 'src/commons/graphql/schema.gql',
}),
],
})
export class AppModule {}
클래스 선언 위에 @module()
를 붙여서 '얘는 모듈로 쓸꺼야' 라고 nest한테 알려주고 있습니다.
이런 골뱅이는 데코레이터라고 했습니다.
app.module
을 열어 GraphQLModule
을 가져와 프로젝트에 장착해주세요.
forRoot()
메서드는 옵션 객체를 인수로 받을 수 있으며, 이 옵션은 ApolloServer
생성자(constructor)에 전달됩니다.
서버를 실행하게되면 type이 자동으로 생성되는데 그 파일의 위치를 autoSchemaFile
을 통해 지정한다.
nest g module board
: board module 생성 명령어
nest g service board
: board service 생성 명령어
nest g resolver board
: board resolver 생성 명령어
다음 명령어를 입력하면 src
폴더안에 board
라는 폴더가 생성됩니다.
board
라는 폴더 안에는 module
service
resolver
가 생성됩니다.
// board.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class BoardService {
aaa() {
return 'Hello World!';
}
}
board.service
에 문자열 "Hello World!" 를 반환하는 getHello
라는 비즈니스 로직을 다음과 같이 만들어 주세요.
// board.resolver.ts
import { Resolver, Query } from '@nestjs/graphql';
import { BoardService } from './board.service';
@Resolver()
export class BoardResolver {
constructor(private readonly boardService: BoardService) {}
// 기존에는 GET 이지만 GraphQL에서는 Query를 사용
@Query(() => String)
getHello() {
return this.boardService.aaa();
}
}
클라이언트 아직 DB와 상호 작용할 수 있는 방법이 없습니다.
이를 해결하려면 Resolver 클래스를 만들어야 합니다.
코드 우선 방법에서 리졸버 클래스는 Resolver 함수를 정의하고 쿼리 유형을 생성합니다.
Routing 개념을 적용하여 어떤 Resolver가 어떤 Request를 수신하는지 제어합니다.
각 Resolver는 최소 1개의 Route를 가지며 각 Route는 다른 action으로 동작합니다.
constructor
에 BoardService
를 주입해 주시고 getHello
라는 Resolver 함수를 만들어 BoardService
의 getHello
비즈니스 로직을 실행시켜 주세요.
src
폴더 안에 apis
폴더를 만들어주시고 Board
폴더 전체를 apis
폴더로 안으로 이동시켜 주세요.
폴더 구성은 위와 같습니다.
yarn start:dev
를 입력해서 서버를 실행시켜주세요.
그러면 아래와 같이 commons/graphql/schema.gql
파일이 생성되었습니다.
# ------------------------------------------------------
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
# ------------------------------------------------------
type Query {
getHello: String!
}
schema.gql
파일에 getHello
의 반환될 타입이 자동으로 기록됩니다.
http://localhost:3000/graphql 에 접속해서 위와 같은 화면이 나온다면 성공입니다.
이번에는 typeorm을 활용해서 nestjs와 mysql을 연결해보겠습니다.
13-01-nestjs-with-graphql
폴더를 복사해 사본을 만들고 폴더명을 13-02-nestjs-with-typeorm
으로 변경해주세요.
nestjs에서 typeorm, mysql을 이용하기 위해 yarn add @nestjs/typeorm typeorm mysql2
을 통해 설치해주세요
직접 타입스크립트를 이요해서 entity를 작성해보겠습니다.
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Board {
@PrimaryGeneratedColumn('increment')
number: number;
@Column()
writer: string;
@Column()
title: string;
@Column()
contents: string;
}
/src/apis/board/entities/board.entity.ts
경로에 board.entity.ts
파일을 생성해주세요.
typeorm의 데코레이터를 이용해 key: value
형태로 타입을 지정해주세요.
// board.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class BoardService {
findAll(): string {
// 데이터 조회하는 로직
return '조회에 성공했습니다.';
}
create(): string {
// 데이터 등록하는 로직
return '등록에 성공했습니다.';
}
}
위와 같이 findAll
과 create
의 비즈니스 로직을 작성해주세요.
// board.resolver.ts
import { Resolver, Query, Mutation } from '@nestjs/graphql';
import { BoardService } from './board.service';
@Resolver()
export class BoardResolver {
constructor(private readonly boardService: BoardService) {}
@Query(() => String)
fetchBoards(): string {
return this.boardService.findAll();
}
@Mutation(() => String)
createBoard(): string {
return this.boardService.create();
}
}
board.resolver.ts
코드를 수정해주세요.
조회를 할 때는 Query
, 등록, 수정, 삭제를 할 때는 Mutation
을 사용합니다.
typeorm의 Query와 Mutation을 데코레이터 형태로 사용했습니다.
constructor
에 BoardService
를 주입해 주었습니다.
BoardService
의 findAll
과 create
의 비즈니스 로직을 실행시켜 주세요.
import { Module } from '@nestjs/common';
import { ApolloDriverConfig, ApolloDriver } from '@nestjs/apollo';
import { GraphQLModule } from '@nestjs/graphql';
import { BoardModule } from './apis/board/board.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Board } from './apis/board/entities/board.entity';
@Module({
imports: [
BoardModule,
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: 'src/commons/graphql/schema.gql',
}),
TypeOrmModule.forRoot({
type: 'mysql', // 데이터베이스 타입
host: 'localhost', // local 환경으로 진행
port: 3306, // mysql은 기본 포트는 3306
username: 'root', // nysql은 기본 user는 root로 지정
password: '설정한 비밀번호', // 설치 과정에서 설정한 비밀번호
database: 'myproject', // 연결할 데이터베이스명
entities: [Board], // 데이터베이스와 연결할 entity
synchronize: true, // 연결과 동시에 테이블을 초기화 혹은 생성할 것인지
logging: true, // 콘솔창에 log를 표시하는지
}),
],
})
export class AppModule {}
MySQL과 연결시켜주기 위해서 TypeOrmModule
의 옵션을 설정하였습니다.
현재 데이터베이스 myproject
가 존재하지 않기때문에 생성해주겠습니다.
우선 터미널에서 brew services restart mysql
로 재시작을 해줍니다.
그리고 DBeaver에 접속하여 좌측 상단의 플러그 아이콘을 클릭하여 비밀번호를 입력하고 Test Connection을 눌러줍니다.
통과가 되면 완료를 눌러주시면 됩니다.
🚨 Public Key Retrieval is not allowed 에러
아래와 같이Driver Properties
메뉴에 들어가allowPulicKeyRetrieval
을TRUE
로 변경해줍니다.
성공적으로 접속이 되면 아래와 같이 뜨는 것을 확인하실 수 있습니다.
현재 Databases
에는 sys
밖에 없는 것을 확인하실 수 있습니다.
그리고 Databases
에 우클릭해서 Create New Database를 눌러 데이터베이스를 하나 추가해줍니다.
이름은 app.module.ts
에 명시해준 데이터베이스와 동일하게 만들어줍니다.
그러면 Databases
안에 myproject
라는 데이터베이스가 생성된 것을 확인하실 수 있습니다.
데이터베이스를 생성했으니 이제 서버를 실행해보겠습니다.
yarn start:dev
명령어로 서버를 작동시킵니다.
그리고 DBeaver를 통해 테이블이 생성되었는지 확인해보겠습니다.
그리고 schema.gql
파일에 resolver에 지정한 반환값의 타입이 자동으로 기록됩니다.
또한 http://localhost:3000/graphql 에 접속해서 다음과 같은 화면이 나온다면 정상입니다.
13-02-nest-js-with-typeorm
폴더를 복사하고 사본을 만든 후 폴더명을 13-03-graphql-api-fetchboards
으로 변경합니다.
이전에는 fetchBoards
을 사용해 게시판을 조회했을 때는 '조회에 성공했습니다.'
라는 문자열을 반환했습니다.
이번에는 작성한 Board.entity
형식에 맞게 반환하는 API로 수정하겠습니다.
// board.entity.ts
import { Field, Int, ObjectType } from '@nestjs/graphql';
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity() // typeorm한테 알려줌(mysql)
@ObjectType() // gql한테 알려줌
export class Board {
@PrimaryGeneratedColumn('increment')
@Field(() => Int)
number: number;
@Field(() => String)
@Column()
writer: string;
@Field(() => String)
@Column()
title: string;
@Field(() => String)
@Column()
contents: string;
}
05-03-graphql-api-with-apollo-server-3
안에 index.js
안에 fetchBoards
에 있는 return
값을 복사해서 board.service.ts
의 findAll
함수 안에 return
값으로 넣어줍니다.
// board.service.ts
import { Injectable } from '@nestjs/common';
import { Board } from './entities/board.entity';
@Injectable()
export class BoardService {
findAll(): Board[] {
// 1. 데이터 조회하는 로직 => DB에 접속해서 가져오기
// return '조회에 성공했습니다.';
const result: Board[] = [
{ number: 1, writer: '철수', title: '제목입니다~', contents: '내용!!' },
{
number: 2,
writer: '영희',
title: '안녕하세요~',
contents: '배고프네요',
},
{
number: 3,
writer: '훈이',
title: '점심은 맛있게 드셨나요?',
contents: '식사는 하셨나요?',
},
{
number: 4,
writer: '맹구',
title: '안녕하세요!!!',
contents: '내용입니다~',
},
];
// 2. 꺼내온 결과 응답 주기
return result;
}
create(): string {
// 1. 데이터 등록하는 로직 => DB에 접속해서 저장하기
// 2. 저장 결과 응답 주기
return '등록에 성공했습니다.';
}
}
// board.resolver.ts
import { Field, Int, ObjectType } from '@nestjs/graphql';
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity() // typeorm한테 알려줌(mysql)
@ObjectType() // gql한테 알려줌
export class Board {
@PrimaryGeneratedColumn('increment')
@Field(() => Int)
number: number;
@Field(() => String)
@Column()
writer: string;
@Field(() => String)
@Column()
title: string;
@Field(() => String)
@Column()
contents: string;
}
한번 yarn start:dev
로 서버를 실행시켜보겠습니다.
그리고 http://localhost:3000/graphql 에서 확인해보겠습니다.
위와 같은 화면이 나온다면 정상입니다.
13-03-graphql-api-fetchboards
폴더를 복사하여 사본을 만들고 폴더명을 13-04-graphql-api-createBoard
으로 변경합니다.
createBoard
에서 Args
개별로 받아서 등록에 성공하였다는 메시지를 받아보게 수정하겠습니다.
// board.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { BoardService } from './board.service';
import { Board } from './entities/board.entity';
@Resolver()
export class BoardResolver {
constructor(private readonly boardService: BoardService) {}
@Query(() => [Board]) // graphql에게 타입을 알려준다
fetchBoards(): Board[] {
return this.boardService.findAll();
}
@Mutation(() => String)
createBoard(
@Args('writer') writer: string,
@Args('title') title: string,
@Args('contents') contents: string,
): string {
console.log(writer);
console.log(title);
console.log(contents);
return this.boardService.create();
}
}
@Args
는 데코레이터로 '@nestjs/graphql'
에서 import
해 사용하는데 gql에 arguments라고 알려줍니다.
@Args('writer') writer: string,
@Args('title') title: string,
@Args('contents') contents: string,
@Args()
데코레이터를 사용해서 객체 value 값의 타입을 지정했습니다.
@Args()
안은 gql 타입이고, 그 뒤는 타입스크립트의 타입을 의미합니다.
하지만 저렇게 하나씩 넣는게 아닌 객체하나로 통일해서 넣으려고 합니다.
src/apis/board/dto
경로에 createBoard.input.ts
를 생성하고 아래의 코드를 작성합니다.
💡
createBoardInput
은 재사용성이 없기때문에dto
라는 폴더를 새로 생성에 거기에 놓고 보관합니다
import { Field, InputType } from '@nestjs/graphql';
@InputType()
export class CreateBoardInput {
@Field(() => String)
writer: string;
@Field(() => String)
title: string;
@Field(() => String)
contents: string;
}
그리고 다시 resolver로 돌아가 args를 수정합니다.
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { BoardService } from './board.service';
import { Board } from './entities/board.entity';
import { CreateBoardInput } from './dto/createboard.input';
@Resolver()
export class BoardResolver {
constructor(private readonly boardService: BoardService) {}
@Query(() => [Board]) // graphql에게 타입을 알려준다
fetchBoards(): Board[] {
return this.boardService.findAll();
}
@Mutation(() => String)
createBoard(
@Args('createBoardInput') createBoardInput: CreateBoardInput,
): string {
console.log(createBoardInput);
return this.boardService.create();
}
}
그리고 yarn start:dev
로 서버를 실행하고 http://localhost:3000/graphql 에 들어가서 직접 요청을 해보겠습니다.
정상적으로 잘 작동합니다.
13-04-graphql-api-createboard
폴더를 복사하여 사본을 만들고 폴더명을 13-06-nestjs-with-graphql-docker-compose
으로 변경합니다.
이전에 MongoDB와 Node.js 서버를 docker container에서 실행했었습니다.
이번에는 NestJS 서버를 docker compose에서 실행시키고 local에서 실행 중인 mysql과 연결시켜보겠습니다.
dockerfile
은 다음과 같습니다.
FROM node:16
WORKDIR /backend/
COPY ./package.json .
COPY ./yarn.lock .
RUN yarn install
COPY . .
CMD node dist/main.js
실행파일은 index.js
가 아닌 dist/main.js
으로 지정해줍니다.
docker-compose.yaml
은 다음과 같습니다.
version: '3.3'
services:
backend:
build:
context: .
dockerfile: dockerfile
volumes:
- ./src:/backend/src
ports:
- 3000:3000
이번에는 volumes
가 추가되었습니다.
volumes
는 ./src
경로에 있는 파일과 docker의 backend/src
의 파일이 서로 다르면 새롭게 images를 빌드하는 옵션입니다.
이제 docker를 실행시켜주고 docker compose build
를 입력해 image를 빌드하고 docker compose up
을 입력해 container를 실행시켜보겠습니다.
🚨 The path 13-05-nestjs-with-graphql-docker-compose/src is not shared from the host and is not known to Docker. 에러
Docker에서 우측 상단 톱니바퀴를 눌러 Settings에 접속합니다.
그리고 좌측 메뉴에서 Resources -> File Sharing에 들어가서 서버를 실행할 폴더 경로를 지정해주면 됩니다.
서버는 잘 실행되었지만 NestJS가 datase를 연결하지 못했습니다.
NestJS에서는 database를 container 환경을 구성해야지만 연결이 가능합니다.
local에 있는 database를 연결할 수 없습니다.
docker-compose.yaml
파일을 수정하겠습니다.
version: '3.3'
services:
backend:
build:
context: .
dockerfile: dockerfile
volumes:
- ./src:/backend/src
ports:
- 3000:3000
database:
platform: linux/x86_64
image: mysql:latest
environment:
MYSQL_ROOT_PASSWORD: '설정한 비밀번호'
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
cap_add:
- SYS_NICE
ports:
- 3306:3306
database
는 데이터베이스를 구성하는 부분입니다.
environment
는 mysql 설정하는 부분입니다.
command
는 데이터베이스 생성하는 부분입니다.
app.module.ts
의 host와 password를 다음과 같이 변경해주세요.
docker container의 데이터베이스와 연결시켜주기 위해서 입니다.
import { Module } from '@nestjs/common';
import { ApolloDriverConfig, ApolloDriver } from '@nestjs/apollo';
import { GraphQLModule } from '@nestjs/graphql';
import { BoardModule } from './apis/board/board.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Board } from './apis/board/entities/board.entity';
@Module({
imports: [
BoardModule,
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: 'src/commons/graphql/schema.gql',
}),
TypeOrmModule.forRoot({
type: 'mysql',
host: 'database', // local -> database
port: 3306,
username: 'root',
password: 'root',
database: 'mysql',
entities: [Board],
synchronize: true,
logging: true,
}),
],
})
export class AppModule {}
password
와 database
는 꼭 default 값으로 해줘야합니다.
docker에서 새로운 환경을 구축하기때문에 mysql의 root 계정은 ID: root
, PASSWORD: root
으로 되어있으며 데이터베이스는 기본 mysql
DB로 설정해야합니다.
docker-packaging 전에 local에서 실행하던 mysql 서버를 종료시켜주세요.
.yaml
파일에 이미 3306을 사용하게 해 놓았기 때문입니다.
그리고 docker compose build
를 입력해 서버, 데이터베이서 image를 생성해주세요.
docker compose up
을 입력해 container를 실행시켜주세요.
처음에 실행 속도 차이때문에 약간의 딜레이는 있어서 retry하는 경우도 있습니다.
http://localhost:3000/graphql 에 접속하니 정상적으로 동작합니다.