본문 바로가기

Javascript & Typescript/실무와 가까워지는 Node.js 백엔드 개발

[2주차] Event API 강의 요약

본 주차에서는 주문을 만들기 위해 필요한 API를 만드는 데 있어 핵심적인 CRUD를 구현하는 기본적인 내용을 다루고 있다.

목차

  1. 파이어베이스 설정
  2. 라우팅 구조
  3. 입력 값 검증
  4. Firestore 데이터 읽기, 생성, 수정
  5. 인증 및 인가

파이어베이스 설정

앞서 데이터베이스로 Google Firebase의 Firestore를 사용하기로 했으므로 먼저 Firestore를 설정해주어야한다. 일단, 개념적으로 간단하게 설명하면 일단, Firebase에서 프로젝트를 생성하고 우리가 만든 앱이 해당 프로젝트에 접근할 수 있도록 인증 설정에서 비공개 키를 얻고 웹앱 설정을 해주면 된다. 그리고 나서 Firestore 데이터베이스를 사용 설정하고 서버에서 Firebse 라이브러리를 통해 초기화를 해주면 해당 데이터베이스를 사용할 수 있게 된다. 강의에서는 해당 과정을 자세히 설명해 주었지만 여기서는 생략하고 대신 잘 설명된 공개된 자료를 공유하는 것으로 대체한다.

 

[Firebase] Firebase 프로젝트 생성하기

Firebase 프로젝트 생성하기 Google Firebase 프로젝트를 생성해보자. Google Firebase Console 접속 파이어베이스 서비스를 이용하기 위해서는 구글 파이어베이스 콘솔에 접속해야한다. https://console.firebase..

spiralmoon.tistory.com

 

firestore 저장소 사용하기

firebase 는 실시간 데이터베이스와 Cloud Firestore 두 가지 타입의 저장소를 제공한다. 현재 (18.04.14) 기준으로 firestore 는 베타 기간이고 공식 문서에 따르면 아직 안정성은 실시간 데이터베이스에 비

jybaek.tistory.com

라우팅 구조

import { NextApiRequest, NextApiResponse } from 'next';

export default async function handle(req: NextApiRequest, res: NextApiResponse): Promise<void> {
  const { method } = req;
  log(method);

  // Todo
  
  res.status(400).send('bad request');
}

위 코드는 기본적인 Next.js Route를 보여주고 있다. 이 형태는 일반적으로 사용하는 Express에서 따온 구조로 차이점이 있다면 req, res의 타입이 다르다는 정도 뿐이다. req 객체는 클라이언트가 보낸 요청과 관련된 내용들이 포함되어 있으며 위 코드에서 예를 들면 req.method를 통해 해당 요청이 GET, POST 등 어떤 메서드 요청인지 알 수 있다. 그리고 res 객체는 해당 클라이언트에 보낼 응답에 관련된 내용으로 위 코드에서는 status 함수를 통해 응답 코드를 지정하고 send를 통해 'bad request'라는 텍스트 응답을 보내주게 된다.

입력값 검증

Next.js에서는 req.query에 다음에 해당하는 값이 포함된다.

  • 동적 라우팅으로 입력된 값 (폴더/파일명이 대괄호로 둘러쌓인 값)
  • Path 뒤에 붙은 Query 파라미터

예를 들어, pages/api/events/[eventId]/index.ts 라는 라우팅 모듈이 있고 아래와 같은 요청을 보낸다고 가정하면,

GET /api/events/test?test=1

req.qeury는 다음과 같이 데이터를 가지고 있다.

{ "eventId": "test", "test": "1" }

이와 같은 요청 데이터를 사용하여 요청을 처리하게 되는데 해당 요청이 제대로 된 형식인지 검증하는 단계가 필요하다. 클라이언트에서는 항상 올바른 요청만 보내는 것이 아니고 실수 또는 악의적으로 요청에 누락사항이나 잘못된 데이터를 보내기도 하기 때문에 이 과정을 필수적이라고 할 수 있다.

검증 방법은 단순하게 받은 모든 값에 대하여 if 구문으로 처리할 수도 있지만 이것을 처리하는 라이브러리를 사용하는 것이 더 효율적이다. 그 중 하나로 Fastify 프레임워크라는 라이브러리 내에 Ajv라는 모듈이 있다. 이 모듈은 미리 JSONSchema6라는 형식에 따라 작성된 스키마를 요청과 함께 해당 모듈에 넘겨주면 알아서 유효성을 검증해 준다.

Firestore 데이터 읽기, 생성, 수정

Firestore에서 데이터를 불러오는 기본적인 방법은 아래와 같다.

import * as admin from 'firebase-admin';

let firestore = admin.firestore()

// events 콜렉션 레퍼런스 객체
firestore.collection('events');

// 콜렉션 내 문서 레퍼런스 객체
firestore.collection('events').doc('문서 id');

또는, 아래 공식 문서 링크에서도 잘 설명되어 있다.

 

Cloud Firestore로 데이터 가져오기  |  Firebase

Google은 흑인 공동체를 위한 인종적 평등을 추구하기 위해 노력하고 있습니다. 자세히 알아보기 의견 보내기 Cloud Firestore로 데이터 가져오기 두 가지 방법으로 Cloud Firestore에 저장된 데이터를 검

firebase.google.com

참고로 스터디 리더분께서는 admin 객체를 관리하는 클래스를 싱글톤으로 따로 작성하고 그 객체를 불러와서 사용하는 식으로 코드를 작성하셨다.

  1. 데이터 읽기
    데이터 읽기는 문서 ref 객체에서 get 함수를 불러서 수행할 수 있다.
    firestore.collection('events').doc('문서 id').get();
    참고로, 조건에 따른 문서를 찾고 싶으면 doc 대신 where 함수를 사용해서 조건을 주면 된다.
  2. 데이터 생성
    데이터 생성은 콜렉션 ref 객체의 add 함수를 불러서 수행할 수 있다. id를 따로 넘겨주지 않으면 내부에서 알아서 id를 생성해준다.
    firestore.collection('events').add(data)​
  3. 데이터 수정
    데이터 수정은 문서 ref 객체에서 get을 통해 불러온 문서 객체에 update 함수를 불러서 수행할 수 있다.
    let doc = firestore.collection('events').doc('문서 id').get();
    let updatedData = {
      ..doc.data(),
      closed: true,
    };
    doc.update(updatedData);​

인증 및 인가

위와 같은 데이터 생성, 수정은 상황에 따라서 권한을 가진 사람만 가능하도록 해야할 수 있다. 예를 들어, 주문서 정보를 수정하는 것을 아무나 할 수 있게 하면 누군가의 주문이 다른 사람에 의해 실수로 삭제되는 것과 같은 상황이 발생할 수 있다.

본 스터디의 프로젝트에서는 Firebase의 인증 기능을 사용해서 로그인 및 인증정보를 얻을 수 있다. 미리 만들어진 클라이언트에서는 서버에 요청을 보낼 때, 인증 정보를 Authorization 헤더에 담아서 보낸다. 그리고 클라이언트에서 Firebase의 인증 기능을 사용해서 인증 정보를 받아왔으므로 인증이 유효한지 여부도 Firebase를 사용한다.

const token = req.headers.authorization;
if (token === undefined) {
  return res.status(400).end();
}

let userInfo: auth.DecodedIdToken | null = null;

try {
  userInfo = await FirebaseAdmin.getInstance().Auth.verifyIdToken(token);
} catch (err) {
  return res.status(400).end();
}

이렇게 하면 userInfo에 어떤 사용자가 해당 요청을 보냈는지 인증정보를 알 수 있게 된다.