본문 바로가기

Node.js 도전기

Node.js_3 Layerd Architecture 로 리팩토링하기 ! (1)

이번 포스팅은 개인 과제이지만 프로젝트라고 하기엔 좀 애매한 수준이라

Node.js 카테고리에 남긴다. 

 

기존 express 프레임워크를 통해 서버를 구축하고 회원가입, 로그인, 게시글 CRUD 등 간단하게 만든 API들을 

이번 주차에 배운 3 Layerd Architecture 형식으로 계층을 나누어 리팩토링을 시도하였다. 

 

기존에는 Sequelizer ORM을 사용했으나 금번에는 Prisma를 사용했기에 DB를 다시 연결하는 부분을 먼저 정리하도록 하겠다.

[해보고자 하는 것]
1. 기존 코드를 3 Layerd Architecture 형식으로 리팩토링
- 기본 폴더 구조 세팅

2. ORM 변경 (Prisma)

 

#1.  리팩토링을 위한 폴더 구조 세팅

3 계층형 아키텍쳐(3 Layerd Architecture)의 경우 이름에서도 알 수 있듯 3가지 계층으로 구성된다. 

  1. 컨트롤러 (Controller) : 어플리케이션의 가장 바깥 부분으로 요청(Request)/응답(Response)을 처리한다.
  2. 서비스 (Service) : 어플리케이션의 중간 부분으로 핵심적인 동작이 많이 일어나는 부분이다. 핵심적인 비즈니스 로직이 수행된다.
  3. 저장소 (Repository) : 어플리케이션의 가장 안쪽 부분으로 데이터베이스와 맞닿아 데이터 베이스와 통신하는 계층이다.

기존에는 router라는 파일 안에 위 내용이 모두 포함되어 있었으나 효율적인 유지보수, 관리를 위해 각 계층을 명확하게 분리해서 유지하게한다.

- 관심사를 분리하여 코드를 명확하게 인지할 수 있다.
- 계층은 서로 독립적이고, 의존성이 낮아 모듈을 교체하더라도 코드 수정이 용이
- 계층별 단위 테스트를 작성할 수 있어 테스트 코드를 좀 더 용이하게 구성

 

이러한 3 계층형 아키텍쳐를 구현하기 위해 기본적으로 아래와 같이 폴더구조를 세팅했다. 

내 프로젝트 폴더 이름
├── api-docs // REST Client 관리용 폴더
│   └── products.http
│   └── users.http
├── node_modules
├── prisma
│   └── schema.prisma
├── src
│   ├── app.js
│   ├── controllers
│   │   └── products.controller.js
│   │   └── users.controller.js
│   ├── middlewares
│   │   ├── auth.middleware.js
│   │   └── error.middleware.js
│   ├── repositories
│   │   └── products.repository.js
│   │   └── users.repository.js
│   ├── routes
│   │   ├── products.router.js
│   │   └── users.router.js
│   ├── services
│   │   └── products.service.js
│   │   └── users.service.js
│   └── utils
│       └── prisma
│           └── index.js
├── .env
├── .prettierrc
├── package-lock.json
├── package.json
└── READEME.md

 

API 동작 순서를 간단하게 짚어보자면 아래와 같다. 

 1) 클라이언트가 요청(req) 시 해당하는 URL에 맞은 컨트롤러가 수신
 2) 컨트롤러는 요청을 처리하기 위해 서비스 호출
 3) 서비스는 필요한 데이터를 가져오기 위해 레포지토리에게 데이터 요청
 4) 서비스는 받은 데이터를 가공하여 컨트롤러에게 전달
 5) 컨트롤러는 서비스의 결과물을 클라이언트에게 전달

 

#2.  ORM 변경

Prisma는 Sequeilze 와 같이 데이터베이스를 쿼리문이 아닌 자바스크립트로 이용할 수 있게 해주는 ORM이다. 

각 회사마다 사용하는 DB가 다르고 그에 따라 사용하는 ORM이 다르기 때문에 다양한 프로그램을 사용해보는 중인것 같다. 

 

역할은 거의 비슷하기 때문에 처음 데이터 베이스와 연결 시에만 신경을 쓰면 되고,

이후에는 기존과 동일한 방법으로 모델을 설정하고 테이블을 생성하고 등 다양하게 사용하면 된다. 

 

1) 사용하지 않는 의존성(dependency) 삭제 

맨 처음 사용했던 Mongoose와 이번에 사용한 Sequelizer 관련 의존성을 package.json 에서 삭제해준다. 

 

2) prisma 라이브러리 설치

나는 맨처음부터 npm을 사용했기 때문에 계속해서 npm으로 설치하고 사용하는데 아래와 같이 명령어를 입력하여 라이브러리를 설치한다.

npm i prisma @prisma/client

 

함께 설치되는 @prisma/client는 Node.js 에서 Prisma를 사용할 수 있게 해준다. 

 

3) prisma 초기화 작업

npx prisma init

명령어를 입력하면 prisma 폴더가 자동으로 생성되며 그 안에 prisma.schema 라는 파일이 생성된다.

추가로 root 폴더에 .env / .gitignore 파일이 자동으로 생성해주는 아주 착한 녀석이다. 

 

prisma.schema 는 이름에서도 알 수 있듯이 데이터베이스를 설정하기 위해 사용하는 파일이니 절대 지우지 말것 !

위 파일 외에도 자동으로 생성되는 폴더나 파일은 삭제하게 되면 prisma의 정상적인 사용이 어려울 수 있다. 

 

4) prisma schema 파일 설정하기

스키마 파일 안에는 맨 처음 generator 부분과 / datasource 부분이 기재되어 있다.

이 때 datasource 부분을 우리가 원하는 설정으로 손봐야 한다. 

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

provider는 데이터베이스 엔진의 유형을 의미하고, 

url은 데이터 베이스 연결을 위한 url이다. 

url 안에는 계정정보와 비밀번호가 들어가기 때문에 환경변수로 관리하고 있다. (.env) 

 

따라서 .env 파일 안에서 내가 사용할 DB주소와 계정정보를 알맞게 입력해야 한다. 

 

5) Prisma model 설정

같은 스키마 파일 안에서 데이터베이스에서 사용할 모델을 설정해야한다. 

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

model Users {
  userId  Int  @id  @default(autoincrement()) @map("userId")
  userName  String  @map("userName")
  email  String  @unique  @map("email")
  password  String  @map("password")

  createdAt  DateTime @default(now()) @map("createdAt")
  updatedAt  DateTime @default(now()) @map("updatedAt")

  Products  Products[]

  @@map("Users")
  }

model Products {
  productId  Int  @id  @default(autoincrement())  @map("productId")
  UserId Int  @map("UserId")
  productName  String  @map("productName")
  contents  String  @map("contents")
  status  Status  @default(FOR_SALE) @map("status")

  createdAt  DateTime @default(now()) @map("createdAt")
  updatedAt  DateTime @default(now()) @map("updatedAt")


Users  Users @relation(fields: [UserId], references: [userId], onDelete: Cascade)

@@map("Products")
}

enum Status {
  FOR_SALE
  SOLD_OUT
}

 

전체적인 구조는 아래와 같다.

model [테이블명] {
// 모델 설정 내용

컬럼명 옵션
(@id 옵션으로 Primary Key 설정)


관계설정

@@map(MySQL에서 사용할 이름) // 미사용 시 모두 소문자 치환

}

 

6) 모델을 데이터베이스에 적용하기

모델을 잘 생성했다면 아래 명령어를 통해서 설정한 모델을 데이터베이스에 적용하여 테이블을 설정할 수 있다.

npx prisma db push

 

여기까지가 리팩토링을 위해 폴더구조를 만들고 prisma 사용을 위한 전 단계이다. 

 

다음 포스팅에서는 실제로 3 계층형 아키텍쳐 구조로 리팩토링을 진행하며 발생했던 문제들을 정리해보도록 하겠다.