18일차 백엔드 구조 및 파이어베이스2

osdsoonhyun·2023년 3월 2일
0

코드캠프

목록 보기
15/22
post-thumbnail
  1. Graphql-API는 어떻게 만들어졌을까? -> Graphql / Apollo-Server
  2. 백엔드 개발자 없이 개발하는 법 -> Firebase / BAAS

데코레이터 원리

function bbb(qqqq :any) {
  console.log('qqq',qqqq)
}

@bbb
class Board {

}
  • @는 함수를 의미한다.

  • class Board가 bbb 함수의 인자로 들어간다.

  • js 파일을 실행시키기 위해서는 node aaa.js를 하면 됐고, ts 파일을 실행시키기 위해서는 ts-node bbb.ts를 하면 된다.

  • node는 컴퓨터 전체에 설치했어서 실행이 가능했지만, ts-node는 설치한 적이 없다.

  • 단지 다운로드 받은 것은 node-modules이라서, ts-node bbb.ts 하게 되면 실행이 안된다.

  • node-modules 안에 ts-node가 있으므로, node-modules를 기준으로 실행시켜줘야 한다.

  • 그렇게 하기 위해, 실행 명령어를 package.json에 script로 등록을 해줘야 한다.

  • yarn dev로 실행하게 되면 class Board가 실행되는 것을 볼 수 있다.
  • 이것을 보면 Entity가 뭔지는 모르지만 typeorm 라이브러리에서 Entity라는 함수를 제공하는 것이고 class Board가 Entity라는 함수로 들어가서 테이블 만들라는 명령이 실행된다.
  • 백엔드에서 rest-api => axios 만들때 유명한 라이브러리/프레임워크 중 하나인 express
  • 백엔드에서 graphql-api => @apollo/client 만들때 유명한 라이브러리/프레임워크 중 하나인 apollo-server

ApolloServer 셋팅

  • graphql을 이용해서 api를 만들것이기 때문에 apollo-server를 설치해준다.
  • rest-api 사용하고 싶으면 express 설치하면 된다.

  • 보통 서버를 만들면 listen이 붙게 된고 서버가 실행이 돼서 누군가의 접속을 기다릴때는 포트 번호가 존재하는데, 지금은 default 포트 번호인 3000번이 실행된다. 바꾸고 싶다면 변경 가능하다.
  • http의 기본 포트 번호가 80인데, 포트번호를 80으로 사용하게 되면 생략이 가능하다.
  • https의 기본 포트 번호는 443이고, 포트 번호를 443으로 사용하면 생략 가능하다.

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

  • API틀을 생성하고 서버를 열어두어 브라우저의 요청을 기다리면 된다.
  • 그러기 위해 서버를 생성하고 api를 만들어줘야 한다.
  • apollo server를 생성하는데 두 가지가 필요하다.
  1. resolver(API)
  2. typeDefs(API type)
  • 아래 그림과 같이 만들어준다.
  • DOCS와 API를 만들어서 각각 집어넣고 둘을 그룹핑하여 server를 만들고 서버는 맨 마지막에 listen으로 기다려준다.
  • listen은 데이터베이스 연결하고 연결 성공 후인 가장 마지막에 실행이 되어야 한다.

  • cors는 서버에서 cors: true 한 줄만 추가해주면 된다.
  • 이때 해킹의 위험이 있으므로 원하는 브라우저만 허가를 하고 나머지는 막도록 하려면 true헤서 모두 풀어주는 것이 아닌 origin의 배열에 풀어주고 싶은 주소를 입력해주면 된다.
// DOCS와 api를 그룹핑하여 server를 만들어줌.
const server = new ApolloServer({
  typeDefs,
  resolvers,
  // cors: true,
  cors: {
    origin: [
      "https//naver.com",
    ]
  }
});

정리
-DOCS와 API는 생긴 것이 똑같아야 한다(제목, type이 일치해야함)
-그리고 DOCS와 API를 서버에서 합치고 누군든지 접근이 가능하게끔 cors를 true 해서 서버를 만들어줬고 서버에서 DB까지 접속이 완료된 모든 세팅이 끝나고 마지막에 다른 사람의 접근을 기다려줘야한다.

전체코드

console.log('반갑습니둥');

const aaa: number = 2;

import { DataSource } from "typeorm";
import { Board } from "./Board.postgres";

// import { ApolloServer } from '@apollo/server';
// import { startStandaloneServer } from '@apollo/server/standalone';
// import { gql } from "apollo-server";

// //commonjs -> 옛날 방식
// const { ApolloServer, gql } = require("apollo-server");

// module -> 최신 방식
import { ApolloServer, gql } from "apollo-server";

// The GraphQL schema
// const typeDefs = `#graphql
//   type Query {
//     hello: String
//   }
// `;

// DOCS
const typeDefs = gql`
  type Query {
    fetchBoard: String
  }
  
  type Mutation {
    createBoard: String
  }
`

// api
const resolvers = {
  Query: {
    fetchBoards: () => {
      return "게시글 조회에 성공하였습니다.";
    }
  },

  Mutation: {
    createBoard: () => {
      return "게시글 등록에 성공하였습니다.";
    }
  }
};

// DOCS와 api를 그룹핑하여 server를 만들어줌.
const server = new ApolloServer({
  typeDefs,
  resolvers,
  cors: true,

  // // 선택한 사이트만 풀어주고 싶을때
  // cors: {
  //   origin: [
  //     "https//naver.com",
  //   ]
  // }
});

// const { url } = await startStandaloneServer(server);
// console.log(`🚀 Server ready at ${url}`);

//DB 접속 정보
const AppDataSource = new DataSource({
  type: "postgres",
  host: "34.22.64.245",
  port: 5004,
  username: "postgres",
  password: "postgres2022",
  database: "postgres",
  synchronize: true,
  logging: true,
  entities: [Board]
});


AppDataSource.initialize()
  .then(() => {
    console.log('DB접속에 성공하였습니다.');

    // DB 연결까지 모두 끝나고, 가장 마지막에 실행(다른 사람들의 접속을 기다리기 위해서)
    server.listen(4000).then(({ url }) => {
      console.log(`🚀 Server ready at ${url}`);
  });
  }).catch((error) => {
    console.log('DB접속에 실패하였습니다,');
    console.log('원인: ');
    console.log(error);
  })

게시판 CRUD API 만들기

createBoard 데이터 DB에 등록/조회하기(Insert/find/findOne)

  • 등록 : DB에 연결을 해놨기 때문에 Board.insert만 해도 바로 접속이 되어 데이터가 들어가서 등록된다.
  • 조회 : 등록된 데이터를 모두 꺼내오는 것으로 find(), 한 개만 꺼내올 때에는 fetchBoard에서 Board.findOne({where: {number: 3 }}) 를 통해 꺼내온다.
  • Board.find()로 DB에서 꺼내온 데이터를 result에 넣어 frontend로 return 해서 보내준다.
  • api만들고 마지막으로 DOCS에 반영해줘야 한다.
  • query에서 find로 가져온 데이터는 객체를 배열에 담아 가지고 오는데 이 type을 따로 정의해줘야 한다.
  • !주의할 점! : typescript 타입과 graphql의 타입이 다르므로 구분해서 작성해야 한다.
  • 브라우저는 next.js에서 제공하는 refresh 기능이 있었지만 backend는 새로고침이 자동으로 되지 않으므로 yarn dev를 다시 실행해줘야 한다.

전체 코드

import { DataSource } from "typeorm";
import { Board } from "./Board.postgres";
// module -> 최신 방식
import { ApolloServer, gql } from "apollo-server";


// DOCS
const typeDefs = gql`

  type MyBoard {
    number: Int
    writer: String
    title: String
    contents: String
  }

  type Query {
    fetchBoard: [MyBoard]
  }
  
  type Mutation {
    createBoard: String
  }
`

// api
const resolvers = {
  Query: {
    fetchBoards: async () => {
      // 전부 꺼내기: find()
      const result = await Board.find();

      // // 한 개만 골라서 꺼내기: findOne()
      // Board.findOne({where: {number: 3 }})
      return result; // [{number:1, writer:"철수", title:"안녕하세요", contents:"반갑습니다."},{...},{...},...]
    }
  },

  Mutation: {
    createBoard: async () => {
      await Board.insert({
        writer: "철수",
        title: "안녕하세요~",
        contents: "반갑습니다~~",
      }) 
      
      return "게시글 등록에 성공하였습니다.";
    }
  }
};

// DOCS와 api를 그룹핑하여 server를 만들어줌.
const server = new ApolloServer({
  typeDefs,
  resolvers,
  cors: true,

  // // 선택한 사이트만 풀어주고 싶을때
  // cors: {
  //   origin: [
  //     "https//naver.com",
  //   ]
  // }
});

//DB 접속 정보
const AppDataSource = new DataSource({
  type: "postgres",
  host: "34.22.64.245",
  port: 5004,
  username: "postgres",
  password: "postgres2022",
  database: "postgres",
  synchronize: true,
  logging: true,
  entities: [Board]
});


AppDataSource.initialize()
  .then(() => {
    console.log('DB접속에 성공하였습니다.');

    // DB 연결까지 모두 끝나고, 가장 마지막에 실행(다른 사람들의 접속을 기다리기 위해서)
    server.listen(4000).then(({ url }) => {
      console.log(`🚀 Server ready at ${url}`);
  });
  }).catch((error) => {
    console.log('DB접속에 실패하였습니다,');
    console.log('원인: ');
    console.log(error);
  })

DBeaver로 데이터베이스에 등록되었는지 확인하기

  • yarn dev 해서 localhost:4000에 들어가서 확인한다.

받아온 인자(args)로 DB에 등록하기

  • 하드코딩한 값 말고 입력을 받고 받은 데이터를 저장해줘야 한다.
  • 없는 객체는 type을 만들어서 써줘야 한다.

  • CreateBoardInput을 따로 타입을 작성한 것.
  • 여기서 한번 더 나아가 graphql에서 return에 들어가는 type들 , 일반적으로 사용되는 type들과 입력에 들어가는 type을 input 타입으로 구분을 한다.

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

Mutation 인자

  • parent : 브라우저에서 api 요청할 때 인자를 넣는 것은 args에 들어가고 백엔드에서 백엔드 api를 호출하여 받아올 때가 있는데 이때 호출한 인자는 parent에 들어간다.
  • args : 입력 값으로 들어온 브라우저에서 받아온 값들이 들어간다.
  • contexts : request, response, header에 대한 정보가 들어온다.
  • info : api에 대한 기타 정보들 기본적인 graphql 정보가 들어온다.

현업에서의 삭제 프로세스

  • 유저가 실수로 삭제했을 경우 복구를 원하는 경우를 대비하여 나중에 돌려서 추적할 수 없기 때문에 DB에서 실제로 삭제하지는 않는다.
  • delete api에 isDeleted라는 column을 하나 더 만들어 true로 업데이트 해줌으로써 삭제시 실제로 삭제하는 것이 아니지만 삭제된 것처럼 보여준다.
  • 그럼 isDeleted가 false인 데이터들만 보여주면 된다.
  • 이것은 언제 삭제 되었는지 모르니 변형을 하여 삭제된 시간을 기록한다.
  • 삭제 시간을 명시하면 그럼 deleteAt이 null인 데이터들만 보여주면 된다.
    updateBoard: async () => {
      // Board.update({조건},{바꿀 내용})
      await Board.update({number:3},{writer:"영희"}); // 3번 게시글의 작성자를 영희로 바꿔줘.
      
      return "게시글 수정에 성공하였습니다."
    },

    deleteBoard: async () => {
      await Board.delete({number: 3}) // 3번 게시글 삭제해줘
      await Board.update({number: 3}, {isDeleted: true}) 
	// 실무에서는 실제로 삭제하지 않고, isDeleted라는 column을 추가하여 삭제되었다고 설정함
      await Board.update({number: 3}, {deletedAt: new Date() }) 
	// deletedAt이 null이면 삭제 안된 게시글, new Date() 시간이 들어가 있으면 그 시간에 삭제된 게시글

파이어베이스 BAAS 서비스 이해

Firebase란?

  • 프론트엔드 개발자가 백엔드 없이 데이터를 Firebase에 직접 넣어줄 수 있게 해주는 것.

  • BaaS(Backend As A Service) : 구글에서 백엔드를 서비스로 제공해주고 있다.

  • SaaS(Software As A Service)

  • Paas(Platform As A Service)

  • IaaS(Infra As A Service)

Google 에널리틱스 GA 설정

  1. 내 홈페이지에 얼마나 많은 사람들이 들어오는지
  2. 어떤 페이지에서 많이 나가는지
  • Authentication : 로그인 인증
  • Firestore Database : 데이터베이스 이용(데이터 등록, 조회)
  • Realtime Database : 실시간 데이터베이스(채팅)
  • Storage : 사진 저장
  • Hosting : 배포

createBoard 만들어보기

  • firebaseApp(파이어베이스접속정보)에 접속해서 "board" Collection을 가지고 온다.
  • "board" collection을 만들지 않아도 대부분의 NoSQL은 있으면 거기에 등록이 되고 만든적이 없어도 만들어서 등록이 된다. (규모가 커지게 되면 안정성에 문제가 된다.)
  • A4용지 문서를 board collection에 저장하고 두 번째 인자에 중괄호하고 적어주면 A4용지로 변해서 들어간다.

fetchBoards 만들어보기

  • firebaseApp(파이어베이스접속정보)에 접속해서 "board" Collection을 가지고 온다.
  • 가지고 온 boardCollection에서 A4용지를 조회하고 result에 담는다.
  • result의 docs 안에 배열형태로 들어오는데 객체를 하나하나 뽑아줘야 한다.
// ////// 파이어베이스 app.tsx///////////
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: "...",
  authDomain: "...",
  projectId: "...",
  storageBucket: "...",
  messagingSenderId: "...",
  appId: "...",
};

// Initialize Firebase
export const firebaseApp = initializeApp(firebaseConfig);
// ///////////////////////

  • collection 을 통해 firebase 의 cloud storage에 해당하는 컬렉션을 지정해,

  • addDoc을 통해 해당 컬렉션에 내용을 넣어준다.

  • getDocs를 통해 저장된 내용들을 꺼내어 불러올 수 있다

파이어베이스의 장단점

  • 장점 : 백엔드 개발자 없이 프론트엔드에서 다이렉트로 데이터 저장하고 쉽게 사용이 가능하다.
    메인 서비스가 아닌 곳에서 만드는데 빠르게 만들 수 있고 편리하다.
  • 단점 : 데이터 저장이 막 이뤄지므로 어떤 데이터가 들어가 있는지, 안전성 검증이 힘들다.

소소한 꿀팁

백엔드에서 rest-api 만들때 유명한 라이브러리/프레임워크 중 하나인 express
백엔드에서 graphql-api 만들때 유명한 라이브러리/프레임워크 중 하나인 apollo-server

  • Frontend, Backend, DB는 모두 server이고 제공해주려고 기다리고 있다.
  • Browser는 손님, Frontend 서버에 접속하게 되면 Html,Css,js를 제공 받는다.
  • Backend 서버한테 Browser가 api를 요청하고 backend 서버가 api를 제공해준다.
  • Backend와 DB 관계에서는 Backend가 client, DB가 Server가 된다.

commonjs vs module

const {ApolloServer, gql} = require("apollo-server");

import {ApolloServer, gql} from "apollo-server";
  • module 방식(최신 방식) : apollo-server에서 ApolloServer, gql만 골라서 가져온다.
  • commonjs 방식(옛날 방식) : apollo-server에서 모든 것을 가지고 와서 ApolloServer, gql만 골라서 사용한다.

-> 방식의 차이는 조금 있으나 모양은 비슷하지만 애초에 가져오는 양 자체가 다르기 때문에 Module 방식이 효율적이다.

라이브러리 앞에 @ 붙은 것은 회사이름이다.
ex) graphqlapi -> @apollo/client

0개의 댓글