18일차 - GraphQL-API 생성하기

류연찬·2022년 11월 18일
0

Codecamp FE07

목록 보기
18/39

Open-API 끌어오기

지난 시간에 Open-API를 끌어 오는 방법에 대해서 학습을 했습니다.
이번에는 Open-API로 새로운 페이지를 만드는 과정을 진행해보겠습니다.

// 첫 마운트시에만 요청을 보낼 수 있도록 의존성 배열을 같이 적어줍니다
// Open-API가 대부분 Rest-API 이기 때문에 대부분 axios를 이용합니다
useEffect(() => {
  new Array(9).fill(1).map(() => {
    cosnt result: any = await axios.get(
      //open API 주소
    )
    setImgUrls(result.data.message)

    //이전 데이터를 배열에 쌓아주는 역할
    setImgUrls((prev)=> [...prev, result.data.message] )
  })
}, [])

🔔 CORS Error
Open-API 업체 측에서 보안상의 이유로 아무 브라우저나 허용하지 않게 되면, 브라우저에서 못받게 되고 cors에러는 보게 됩니다.
예를 들어 네이버에서 카카오 백엔드에 요청을 하게되면 cors를 보게 됩니다.


ApolloServer 세팅

지난 시간까지 백엔드와 데이터베이스를 연결했고, 이번엔 브라우저에서 API를 요청할 수 있도록 백엔드 서버를 24시간 열어두는 과정을 진행해보겠습니다.
GraphQL을 이용해서 API를 만들것이기 때문에 apollo-server를 설치해주도록 하겠습니다.
참고로 Rest-API는 exporess(koa)를 사용합니다.

ApolloServer 설치

  1. 터미널에 yarn add graphql 을 입력해 GraphQL을 설치해주세요.
  2. 터미널에 yarn add apollo-server 을 입력해 ApolloServer를 설치해주세요.
  • 설치 확인은 package.json 에서 확인하면 됩니다.

기본 API 틀 생성 및 서버 열어두기

설치를 모두 완료했으니 API 틀을 생성하고 서버를 열어두어 브라우저의 요청을 기다려 보겠습니다.
그럴려면 서버를 생성하고 API를 만들어주어야 합니다.
우리가 아폴로 서버를 생성하는데에는 두가지가 필요합니다.

  • resolver (API)
  • typeDefs (API type)

따라서 resolver와 typeDefs를 만들어주고 서버를 열어보도록 하겠습니다.
첫번째로 ts파일에 들어가(typeorm을 설정해준 파일) { apolloServer } 를 import해주세요.

1. API의 타입 지정해주기(typeDefs)

// gql을 import하시고 진행해주세요
const typeDefs = gql`
  type Query {
  	fetchBoards: String
  }

  type Mutation {
  	createBoard: String
  }
`

2. API 만들어주기(resolver)

const resolvers = {
  Query : {
  	// 누군가 해당 API를 요청했다면 아래의 함수가 실행됩니다
  	fetchBoards: (){ return  “fetchBoards를 요청하셨습니다.}
  },
  Mutation : {
  	createBoard : (){ return “createBoard를 요청하셨습니다.}
  }
}

3. ApolloServer 서버 만들어주기

import { apolloServer } from 'apollo-server'

// typeDefs와 resolvers는 위에서 우리가 만들어준 타입과 API 입니다.
const server = new ApolloServer({
  typeDefs: typeDefs,
  resolvers: resolvers
})

4. 서버 24시간 열어주고 접속 기다리기

// 성공시 실행
.then((){
	// 서버접 속 완료시, 콘솔 표기
	console.log(”접속 완료!)
	server.listen(4000)
})

해당 과정이 없으면 서버가 열려 있지 않기떄문에 브라우저의 요청을 듣지 못합니다.

게시판 CRUD 만들기

테이블의 데이터를 삭제하고 추가하고 수정하는 일이 생길 수 있습니다.
따라서 우리는 위의 기본 기능들을 사용하기 위해 Board.postgres.ts 파일로 돌아가 BaseEntity 를 사용해 보도록 하겠습니다.

Board.postgres.ts

// Board 테이블
import { Column, Entity, PrimaryGeneratedColumn, BaseEntity } from "typeorm"

@Entity() 
export class Board extends BaseEntity{
	// 자동으로 생성되는 번호			
	@PrimaryGeneratedColumn(’increment’) 
	number: number;

	// 데이터베이스의 타입을 임의로 바꾸고 싶다면 아래와 같이 안에 적어주시면 됩니다.
	@Column({type : “text”})
	writer: string;

	@Column()
	title: string;

	@Column()
	age: number;
}

API 만들기 - createBoard

// index.ts 폴더의 resolver 부분에 작성해주시면 됩니다.

import {Board} from "Board.postgres.ts 경로"

// 받아올 인자의 타입을 지정해주기
const typeDefs = gql`
  // 인풋값의 타입을 객체로 묶어 따로 지정해주도록 하겠습니다.
  input CreateBoardInput {
  	writer: string!,
  	title: string!,
  	age: Int!
  }

  type Mutation {
  	createBoard(createBoardInput:CreateBoardInput): String
  }
`

// API 만들어주기
const resolvers = {
  Mutation : {
    createBoard : async (_: any , args: any){ 
    	// Board 테이블에 값넣기  ---> 백엔드에서 데이터 베이스에 값을 넣어주는 부분입니다.
	    await Board.insert({
    		writer: args.createBoardInput.writer,
    		title: args.createBoardInput.title,
    		age: args.createBoardInput.age
  		})
  	return "fetchBoards 요청이 완료되었습니다."
	}
}

createBoard 같은 경우에는 입력된 값을 DB에 저장해줘야 하므로 인자를 통해 입력값을 받아와야 합니다.
인자에는 parentargs가 있습니다. 지금은 parent에 사용하지 않기때문에 _ 를 넣어주도록 하겠습니다.

위의 API를 만들고 GraphQL에서 직접 데이터를 넣어보신 후 DB관리프로그램(DBeaver, MySQL webpack 등)에서 확인하시면 됩니다.

🔔 데이터의 타입을 지정해주는 typeDefs 에서 typeinput 의 차이점은?
type 은 데이터를 받아올때만 사용이 가능하고, input 은 입력값을 넣을 때 사용합니다.


API 만들기 - fetchBoard

//index.ts 폴더의 resolver 부분에 작성해주시면 됩니다.

import {Board} from "Board.postgres.ts 경로"

// 받아올 인자의 타입을 지정해주기
const typeDefs = gql`
  type Board {
  	wirter: String
  	number: Int
  	title: String
  	age: Int
  }

  type Query {
  	//여러개 가지고 오기 때문에 타입을 배열에 담아서 보내줍니다.
  	fetchBoards : [Board]
  }
`

// API 만들어주기
const resolvers = {
  // Query API 만들기
  Query: {
    // 누군가 해당 api를 요청했다면 아래의 함수가 실행됩니다
    fetchBoards: async(){
      // DB와 연결 = ORM
      // 여러개를 조회할땐 find, 하나만 조회할땐 findOne 을 사용합니다
      // isDelete는 삭제때문에 들어간 내용이니 아래에서 확인해주세요
      result = await Board.find({ where: { writer: "철수" }, isDelete: false })
      return
    }
  }
},

fetchBoard 에서는 찾은 값을 돌려줘야 하기 때문에 테이블.find 한 값을 리턴해주면 됩니다.

API 만들기 - deleteBoard

// Board.postgres.ts 폴더
// Board 테이블

import { Column, Entity, PrimaryGeneratedColumn, BaseEntity } from "typeorm"

@Entity() 
export class Board extends BaseEntity {
  // 자동으로 생성되는 번호			
  @PrimaryGeneratedColumn(’increment’) 
  number: number;

  // 데이터베이스의 타입을 임의로 바꾸고 싶다면 아래와 같이 안에 적어주시면 됩니다.
  @Column({ type : “text” })
  writer: string;

  @Column()
  title: string;

  @Column()
  age: number;

  @Column({ type: "timestamp", defult: new Date(), nullable: true })
  deletedAt: Date
}
//index.ts 폴더의 resolver 부분에 작성해주시면 됩니다.

import {Board} from "Board.postgres.ts 경로"

// 받아올 인자의 타입을 지정해주기
const typeDefs = gql`
  input CreateBoardInput {
    writer: string!,
    title: string!,
    age: Int!
  }

  type mutation {
    createBoard(createBoardInput: CreateBoardInput): String
    deleteBoard(number: Int): String
  }
`

// API 만들어주기
const resolvers = {
  mutation : {
    deleteBoards: async(_: any, args){
      // update에는 2가지가 들어갑니다. 앞에는 조건, 뒤에는 변경내용
      await Board.update({ number: args.number }, { deleteAt: new Date() })

      // await Board.delete({ writer : "철수" })
      return 
  }
},

실무에서 데이터의 삭제는 굉장히 민감한 부분입니다.
만일 필요한 데이터를 잘못삭제하게 되면, 굉장히 난감한 상황을 직면하게 됩니다.
따라서 실제로 삭제하는 것이 아닌 여러 가지 방법을 통해 삭제한 것처럼 보이도록 만들어줍니다.

🔔 실무에서 삭제하는 방법
1. column을 새롭게 만들고 isDelete의 불린값만 true로 바꿔줍니다. 그럼 isDelete가 false인 데이터들만 보여주면 됩니다.
2. 삭제 시간을 명시해줍니다. 그럼 deleteAt이 null인 데이터들만 보여주면 됩니다.

0개의 댓글