[백엔드는 처음이라..] 3. Express와 디렉토리

DD·2021년 9월 14일
9
post-thumbnail

백엔드는 처음이라 두 번째 주제는 Express와 디렉토리이다.

Nodejs 환경에서 서버를 구성하는 프레임워크는 Express 외에도 Koa.js, Hapi.js, Nest.js 등이 존재하지만, Express가 가장 많이 사용되고 사용하기 쉽다고 알려져있다.

Express는 미들웨어, 라우팅을 사용해서 서버를 쉽게 구성할 수 있다.


미들웨어 middleware

미들웨어 함수는 요청 오브젝트(req), 응답 오브젝트 (res), 그리고 애플리케이션의 요청-응답 주기 중 그 다음의 미들웨어 함수 대한 액세스 권한을 갖는 함수입니다.

  • Express 공식 문서는 미들웨어를 위와 같이 설명하고 있다. 쉽게 말하자면 "서버에 도착한 요청(request)들이 응답(response)될 때 까지 거쳐가는 공통적인 처리장치"라고 할 수 있다. 이 역시 와닿지 않으니 예시를 들어보자.

  • 서버가 처리하는 요청을 크게 구분하자면 비로그인 상태여도 가능한 것 / 로그인 상태여야 하는 것 두 가지로 나눌 수 있을 것이다. 토큰 방식의 인증 처리를 구현한 서버라는 가정하에, 로그인이 된 상태의 요청인지 확인하기 위해서는 해당 토큰의 유효성을 검사하고 그 결과에 따른 처리가 필요하다.

  • 이 경우 해당 요청에서 token을 얻어내서, 유효성을 검사하고, 그 결과에 따라 통과시키거나 에러를 발생하는 등의 공통적인 처리를 하는 checkTokenMiddleware를 만들어서 사용할 수 있는 것이다.

  • 이러한 미들웨어는 서버에 들어오는 모든 요청에 대해 적용할 수도 있고, 특정 라우터에만 적용할 수도 있다. 위의 예시의 경우 로그인이 필요한 요청들의 라우터에 등록해놓을 수 있을 것이다.

  • 위의 로그인를 확인하는 것처럼, 여러가지 요청 처리 로직이 공통적으로 가지고 있는 로직을 수평 관심사 혹은 공통 관심사라고 한다. 미들웨어는 이 수평/공통 관심사를 일괄적으로 처리하는 함수라고 다시 표현할 수 있을것이다.


라우팅 (Routing)

URI(또는 경로) 및 특정한 HTTP 요청 메소드(GET, POST 등)인 특정 엔드포인트에 대한 클라이언트 요청에 애플리케이션이 응답하는 방법을 결정하는 것

  • Express 공식 문서는 라우팅을 위와 같이 설명하고 있다. 쉽게 말하자면 "HTTP 요청의 method와 URI에 따라서 그 처리 로직을 구분하는 것, 또는 처리 함수를 달리하는 것"이라고 할 수 있다. 예를 들어보자
// app.js
import express from 'express';

const app = express();

app.use('/api', apiRouter);


// auth.js
import { Router } from 'express';
const authRouter = Router();

authRouter.post('/signup', authController.signup);
authRouter.post('/signin', authController.signin);
  • express app을 생성하고, use 메소드를 통해 app 인스턴스에 authRouter 미들웨어를 바인딩한다.

  • authRouter는 post 메소드 요청 중, 지정된 path에 해당하는 응답(signup, signin)을 두 번째 인자에 등록한 미들웨어로 처리한다.


디렉토리 구조

  • 이전에 Express로 구성했던 웹 어플리케이션 서버의 디렉토리 구조를 예시
.
`-- src
    |-- config
    |-- controllers
    |-- database
    |-- errors
    |-- middlewares
    |-- migrations
    |-- models
    |-- routes
    |-- services
    `-- uitls

config

환경 설정을 위한 폴더

  • 환경 변수처럼 프로젝트의 가장 밑부분(?)과 관련한 작업을 위한 폴더이다.
  • 우리팀은 이 폴더에 DB config, dotenv(process.env를 설정) 파일들이 존재했다.

database

데이터베이스 상위 폴더

  • DB 관련 로직 중에서도 상위(?)에 있는 생성, 초기 연결 등을 위한 로직이 담긴다

models

데이터베이스의 각 테이블(모델)에 해당하는 파일들 모음

  • user, category, record등 실제 DB의 테이블 형태를 가지는 class 객체를 담아둔다.

  • sequelize를 사용했기에 테이블을 객체화할 필요가 있음!

  • 다른 팀은 entity로 명명하기도 하는 듯

routes

라우터 모음. path와 유사한 폴더 구조를 가진다

  • 서버에 요청이 들어올 때 URI의 path에 따라 필요한 controller로 이어주는 역할만 한다.

  • 중요한건 다른 처리 없이 요청과 그 처리를 담당하는 controller를 이어주는 역할만 한다는 것!

controllers

Request, Response를 담당한다.

  • Request 데이터를 가공하고, 필요한 service에 데이터를 전달한다

  • service의 처리에 따라 클라이언트에 Response하는 역할을 담당

  • 비즈니스 로직은 service에게 맡긴다.

  • HTTP 관련 에러는 여기서 발생시켜야한다

services

비즈니스 로직을 담당한다.

  • 서비스는 오직 비즈니스 로직 처리를 담당한다. 따라서 Requeest, Response를 전달 받지도 않고, 그 존재에 대해 알 필요도 없다

  • 데이터베이스에 직접 접근하는게 아니라 data access layer에게 접근 관련 처리를 맡기고 전달받은 값으로 비즈니스 로직을 진행한다.

  • 또한 상태 코드 또는 헤더와 같은 HTTP 전송 계층과 관련된 것들을 반환하지도 않는다.

  • 즉 에러가 발생하더라도 그 에러가 400인지 같은 코드 넘버를 발생시키는게 아니라 비즈니스 로직에 대한 에러만 발생시켜야 한다.

  • 단, 우리팀은 편의를 위해 여기서 HTTP 에러를 발생시켰다..

migrations

data access layer. 데이터베이스와 통신하는 영역

  • 데이터베이스에 접근해서 CRUD 동작을 하고 그 처리를 반환한다.

  • 여기서 발생하는 에러는 모두 500에러로 처리되겠지만, 그렇다고 여기서 직접 500 에러가 담긴 에러 객체를 생성하는게 아니다 그 역할은 controller가 맡고 있다.

utils

이 프로젝트 뿐 아니라 어디에서든 쓸 수 있는 범용성 있고 재사용 가능한 유틸들 모음

  • lib 폴더와 비슷하면서 다를 수 있는데, 내가 이해한 바로는 lib는 이 프로젝트에서 주로 사용하는 것, utils는 어느 프로젝트에서든 사용할 수 있는 것으로 구분하고 있다.
  • 아주 단순한 예시로 날짜를 얻어내는 함수라던가..
  • 다만 우리팀은 미션 진행이 느렸다보니 유틸폴더에 아무것도 없다..

errors

구체적인 에러 객체를 생성하기 위한 파일 모음

  • HTTP Error, Service Error 등 Error 객체를 상속 받으면서 에러 종류에 따라 가지는 속성이 다르다.
  • 우리 프로젝트에서는 처음에 HTTP/Service로 구분하려다가 HTTP 하나로 묶어서 사용했지만 프로젝트가 더 커진다면 종류도 많아지면서 구분해야할 듯 싶다.

middlewares

미들웨어를 모아두는 폴더

  • 미들웨어란 Request가 공통적으로 거쳐가야할 공통 관심사/수평 관심사를 처리하기 위한 장치다.
  • 단 "모든" 요청이 거쳐야하는 것도 있지만, Router에 따라 부분적으로 적용되는 미들웨어도 존재한다. 컨트롤하기 나름
profile
기억보단 기록을 / TIL 전용 => https://velog.io/@jjuny546

0개의 댓글