08-04-nestjs-with-typeorm 폴더에서 node_modules 와 dist 폴더만 삭제하고 나머지는 복사하여 이름을 08-05-nestjs-with-typeom-api 로 바꿔준다.
08-05-nestjs-with-typeom-api → src → apis → boards→ boards.resolver.ts 파일에서
createBoard API 를 아래와 같이 만든다.
// boards.resolver.ts
import { Query, Resolver, Mutation } from '@nestjs/graphql';
import { BoardsService } from './boards.service';
import { Board } from './entities/board.entity';
@Resolver()
export class BoardsResolver {
constructor(private readonly boardsService: BoardsService) {}
@Query(() => [Board], { nullable: true })
fetchBoards(): Board[] {
return this.boardsService.findAll();
}
@Mutation(() => String)
createBoard(
@Args('writer') writer: string,
@Args('title') title: string,
@Args({ name: 'contents', nullable: true }) contents: string,
): string {
return this.boardsService.create(writer, title, contents);
}
}
Query 를 등록, 수정, 삭제할 때는 Mutation 을 사용한다. typeorm의 Query와 Mutation을 데코레이터 형태로 사용했다.@Args('writer') writer: string,
@Args('title') title: string,
@Args('contents') contents: string,
@Args() 데코레이터를 사용해서 객체 value 값의 타입을 지정했다.
@Args() 안은 gql 타입이고, 그 뒤는 타입스크립트의 타입을 의미한다.
08-05-nestjs-with-typeom-api → src → apis → boards → boards.service.ts 파일에서
findAll 과 create 의 비즈니스 로직을 다음과 같이 만들어준다.
// boards.service.ts
import { Injectable } from '@nestjs/common';
import { Board } from './entities/board.entity';
@Injectable()
export class BoardsService {
findAll(): Board[] {
// 1. DB에 접속 후, 데이터를 조회 => 데이터를 조회했다고 가정
const result = [
{
number: 1,
writer: '철수',
title: '제목입니다~~',
contents: '내용이에요!!!',
},
{
number: 2,
writer: '영희',
title: '영희입니다~~',
contents: '영희이에요!!!',
},
{
number: 3,
writer: '훈이',
title: '훈이입니다~~',
contents: '훈이이에요!!!',
},
];
// 2. DB에서 꺼내온 결과를 브라우저에 응답(response) 주기
return result;
}
create(writer, title, contents): string {
// 1. 브라우저에서 보내준 데이터 확인하기
console.log(writer);
console.log(title);
console.log(contents);
// 2. DB에 접속 후, 데이터를 저장 => 데이터 저장했다고 가정
// 3. DB에 저장된 결과를 브라우저에 응답(response) 주기
return '게시물 등록에 성공하였습니다.';
}
service 파일을 만드는 이유는 중복되는 부분들을 다른 API에서도 사용하기 위함으로,
실제로 사용되는곳은 resolver 에서 최종적으로 사용되게 된다.
이번에는 작성한 Board entity 형식에 맞게 반환하는 API로 수정해 보자
반환값이 문자열이 아니라, Board 객체를 주어야 하는데 Board는 string처럼 기본으로 있는 타입이 아니므로 직접 만들어줘야 한다.
이전에 설명했듯이 코드 우선(code first) 와 스키마 우선(schema first) 방법 두 가지가 있는데, 최신 방법인 코드 우선(code first)을 사용한다.

08-05-nestjs-with-typeom-api → src → apis → boards → entities → board.entity.ts 파일로 들어와서 다음과 같이 수정해준다.
// board.entity.ts
import { Field, Int, ObjectType } from '@nestjs/graphql';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity() // MySQL용
@ObjectType() // GraphQL용
export class Board {
@PrimaryGeneratedColumn('increment')
@Field(() => Int) // GraphQL용
number: number;
@Column() // MySQL용
@Field(() => String)
writer: string;
@Column()
@Field(() => String)
title: string;
@Column()
@Field(() => String)
contents: string;
}
Board 클래스 위에 @ObjectType() 데코레이터를 추가하여 객체 형태의 GraphQL 타입으로 바꿔 주었다.@Field() 데코레이터를 추가하여 GraphQL Field 임을 알려주었고, 타입을 지정해주었다.이제 이 파일의 Board를 export 하면 Board 타입을 사용할 수 있다.
이제 fetchBoards의 반환값을 여러 개의 Board 객체를 주는 것으로 수정해보자
여러 개의 객체를 담기 위해 배열로 지정해야 한다.
resolver 와 service 파일을 수정하였다면 아래와 같이 schema.gql 파일에서 자동으로 Board 타입 이 만들어진 것을 확인할 수 있다.

플레이그라운드를 실행시켜서 결과를 확인해 보자
아래와 같이 요청을 보내고 결과를 리턴받으면 성공적으로 완성한 것이다

이번에는 인자를 다른 방식으로 받아보자. 방금 만든 API에서는 writer, title, contents 각각을 하나씩 하나씩 전달받았다.
그런데, 지금은 세 개밖에 입력 받지 않았지만, 더 복잡하고 많은 입력을 받아야 한다면 이 방식은 효율적이지 않다.
그래서 객체로 묶어서 전달받는 방식으로 변경해보자
board.entity.ts 파일에서 Board 클래스를 @ObjectType() 을 이용해서 만든것처럼 InputType 을 만들어줘야 한다.

08-05-nestjs-with-typeom-api → src → apis → boards → dto 폴더를 생성한다. 그리고 create-board.input.ts 파일을 만들어 진행한다.

DTO (data transfer object)
: 데이터 전송 객체. 즉, 네트워크 간에 데이터를 어떤 식으로 보낼지를 정의한 객체
input 파일을 아래와 같이 작성해준다.
// create-board.input.ts
import { InputType, Field } from '@nestjs/graphql';
@InputType() // GraphQL
export class CreateBoardInput {
@Field(() => String)
writer: string;
@Field(() => String)
title: string;
@Field(() => String)
contents: string;
}
@InputType()는 gql 에게 “이건 InputType이야" 라고 알려준다.
08-05-nestjs-with-typeom-api → src → apis → boards → boards.resolver.ts 파일로 돌아와 resolver에 적용시켜 보자
// boards.resolver.ts
import { Query, Resolver, Mutation, Args } from '@nestjs/graphql';
import { BoardsService } from './boards.service';
import { CreateBoardInput } from './dto/createBoard.input';
import { Board } from './entities/board.entity';
@Resolver()
export class BoardsResolver {
// 생성자를 통해 의존성 주입받기
constructor(private readonly boardsService: BoardsService) {}
// 1번 api - 게시판 조회
@Query(() => [Board], { nullable: true })
fetchBoards(): Board[] {
return this.boardsService.findAll();
}
// 2번 api - 게시판 작성
@Mutation(() => String)
createBoard(
// @Args('writer') writer: string,
// @Args('title') title: string,
// @Args('contents') contents: string,
@Args('createBoardInput') createBoardInput: CreateBoardInput,
): string {
return this.boardsService.create({createBoardInput});
}
}
08-05-nestjs-with-typeom-api → src → apis → boards → boards.service.ts 파일도 수정해 준다.
// boards.service.ts
import { Injectable } from '@nestjs/common';
import { Board } from './entities/board.entity';
import { CreateBoardInput } from '../dto/create-board.input';
interface IBoardsServiceCreate {
createBoardInput: CreateBoardInput;
}
@Injectable()
export class BoardsService {
findAll(): Board[] {
// 1. 데이터를 조회하는 로직 => DB에 접속해서 데이터 꺼내오기
const result = [
{
number: 1,
writer: '철수',
title: '제목입니다~~',
contents: '내용입니다!!!',
},
{
number: 2,
writer: '철수',
title: '제목입니다~~',
contents: '내용입니다!!!',
},
{
number: 3,
writer: '철수',
title: '제목입니다~~',
contents: '내용입니다!!!',
},
];
// 2. 꺼내온 결과 응답 주기
return result;
}
create({ createBoardInput }: IBoardServiceCreate): string {
// 1. 브라우저에서 보내준 데이터 확인하기
console.log(createBoardInput.writer);
console.log(createBoardInput.title);
console.log(createBoardInput.contents);
// 2. 데이터를 등록하는 로직 => DB에 접속해서 데이터 저장하기
// 3. DB에 저장이 잘 됐으면, 결과를 브라우저에 응답(response) 주기
return '게시물 등록에 성공하였습니다!!';
}
}
boards.resolver.ts 파일에서 boards.service.ts 파일로 createBoardInput 을 보내주고 있으므로 args 대신에 createBoardInput 으로 받아줘야 한다.
interface IBoardsServiceCreate {
createBoardInput: CreateBoardInput;
}
- 객체 type을 만들 때는 interface를 활용하여 만들 수 있다.
- resolver 파일에서 보내준 createBoardInput 은 dto 폴더에 존재하는 CreateBoardInput 이다.
따라서, dto 폴더에서 해당 함수가 존재하는 파일을 import 해야한다.- 이렇게 만들어 준 IBoardServiceCreate는
{ createBoardInput }의 타입을 명시해 준다.

interface 들은 재사용성을 위해 하나로 모아 따로 관리를 해주면 편하다.
08-05-nestjs-with-typeom-api → src → apis → boards 폴더 안에
interfaces 폴더를 추가하고, 해당 폴더 내에 boards-service.interface.ts 파일을 만들어 준다. interface 전용 파일이다.
boards.service.ts 파일에서 작성한 interface 부분을 boards-service.interface.ts 파일로 옮겨준다.
// boards-services.interface.ts
import { CreateBoardInput } from '../dto/create-board.input';
export interface IBoardsServiceCreate {
createBoardInput: CreateBoardInput;
}
boards.service.ts 파일에서 사용할 것이기에 export를 붙여준다.interface를 분리시킨 수정된 boards.service.ts 파일은 아래와 같다.
// boards.service.ts
import { Injectable } from '@nestjs/common';
import { Board } from './entities/board.entity';
// Import 한 interface
import { IBoardsServiceCreate } from './interfaces/boards-service.interface';
@Injectable()
export class BoardsService {
findAll(): Board[] {
// 1. 데이터를 조회하는 로직 => DB에 접속해서 데이터 꺼내오기
const result = [
{
number: 1,
writer: '철수',
title: '제목입니다~~',
contents: '내용입니다!!!',
},
{
number: 2,
writer: '철수',
title: '제목입니다~~',
contents: '내용입니다!!!',
},
{
number: 3,
writer: '철수',
title: '제목입니다~~',
contents: '내용입니다!!!',
},
];
// 2. 꺼내온 결과 응답 주기
return result;
}
create({ createBoardInput }: IBoardsServiceCreate): string {
// 1. 브라우저에서 보내준 데이터 확인하기
console.log(createBoardInput.writer);
console.log(createBoardInput.title);
console.log(createBoardInput.contents);
// 2. 데이터를 등록하는 로직 => DB에 접속해서 데이터 저장하기
// 3. DB에 저장이 잘 됐으면, 결과를 브라우저에 응답(response) 주기
return '게시물 등록에 성공하였습니다!!';
}
}
안전한 코드를 위해서는 꼭 타입을 명시해줘야 한다.
파일 작성을 완료한 뒤 yarn start:dev 명령어를 통해 서버를 올려보면, schema.gql 파일을 보시면 자동으로 Input 타입 이 만들어진 것을 확인할 수 있다.

플레이그라운드에 접속해 DOCS 를 보시면 Input 이 추가된 것을 확인할 수 있다.

createBoard 를 실행시켜서 다음과 같은 화면이 나온다면 성공적으로 api를 만든것이다.

터미널 창을 확인해 보면 platyground에서 입력한값이 console.log() 형식에 맞게 잘 출력된 것을 확인할 수 있다.
