본문 바로가기

Javascript/NodeJS

Node.js로 뭔가 만들어보자 - 4. Mongoose를 통해 MongoDB 데이터 다뤄보기

이번 포스팅에서는 데이터를 다뤄보도록 하자.

초반 게시판 만들기 구성을 할 때, persistence는 MongoDB를 사용하기로 했다.

물론 RDBMS를 선호하거나 어쩔 수 없이 그래야한다면 MySQL 등을 사용해야겠지만 ORM 등의 개념으로 진행한다면 DAO 부분을 제외하고는 크게 다르지 않을 것이다.

이 게시판에서도 MongoDB의 데이터를 핸들링 할 때, 직접 MongoDB의 명령을 사용하지 않고 'Mongoose'라는 모듈을 통해 데이터를 핸들링 해보도록 하겠다.


Mongoose는 Node.js 모듈로써 ODM (Object Data Mapping) framework 이다. 

ODM은 Java개발 시 사용했던 hibernate 같은 일종의 ORM과 같은 개념이다. 

다시 말해서 MongoDB에 저장할 데이터들을 객체로 정의하고 이 객체를 통해 데이터를 여러가지 방법으로 핸들링해 줄 수 있게 도와주는 모듈인 것이다.

이 포스팅에서 MongoDB에 대한 설명은 나도 잘 몰라서 못하지만, 생략하지만, RDBMS와 비교한 내용들은 구글링을 하면 쉽게 찾을 수 있다.

개인적인 생각에서 NoSQL의 가장 큰 장점은 자유롭다는 것이다. 

RDBMS에서 처럼 정해진 스키마 때문에 추후 데이터 모델을 변경할 때 무언가 고생스러운 것이 없고, Join의 고통에 빠지지않을 수도 있다.

하지만 그 정해지지 않은 스키마 덕분에(?) DB안에 저장된 데이터에 어떤 column (key값)이 있는지는  저장된 모든 Document를 읽어보기 전에는 알 수가 없다.

그리고 데이터를 핸들링하는 서비스에서 client로 부터 전달된 데이터나 소스코드에서 생성한 데이터의 key 에 오타라도 있으면 그 데이터는 영영 되찾지 못하는 경우가 생길 수도 있다.

그러한 단점을 보완하면서 MongoDB의 장점을 잘 활용하기 위해 Mongoose와 같은 ODM을 활용하는 것이 나쁘지 않은 선택일 것이다.


Mongoose에는 스키마라는 개념이 있는데, 바로 이 스키마가 어떤 형태의 Document를 저장하고 사용할지 정의하는 내용이다.

Document니 스키마니 뭐가 막 나오는데, 혹시 NoSQL이 아직 생소한 분들을 위해 간략하게 RDBMS 의 몇가지 용어와 비교(?)를 해보겠다. (다른 훌륭한 블로그를 보셔도..)


 RMBMS

 MongoDB 

 Database

 Database 

 Table

 Collection 

 Row

 Document 

 Column 

 Key / Field 

 Join

 Embeded Documents 


이제부터 MongoDB에서 게시판 데이터를 사용하는 기능을 구현할텐데, 다음과 같은 순서로 진행될 것이다.

  1. MongoDB Connection
  2. Schema 정의
  3. Model 정의
  4. 데이터 CRUD


1. MongoDB Connection


MongoDB에 접속하기 위해서는 먼저 MongoDB를 설치해야하는데, 지난 포스팅에서 일단 MongoLab을 사용하기로 했다. 

언급했던 것 처럼, 무료라는 장점에 쓰는 것이니 느려서 못해먹겠다 하는 분들은 다른 public 서비스를 사용하거나 로컬에 설치해서 사용해도 무방하다. 


먼저 mongoose 모듈을 사용하기 위해 설치부터 하자.



Mongoose 튜토리얼 사이트를 학습하면 나오겠지만, Mongoose를 통한 MongoDB connection 생성 코드는 다음과 같은 패턴이다.


위 내용에서 접속정보의 경우는 코드에 포함시키기 보다는 외부 설정 파일로 따로 빼서 관리하는 편이 낫다. (코드가 원격 SCM에 저장되면서 유출될 위험도 있음)

DB 접속 정보 등 관리하기 위해서 한가지 모듈을 더 사용하자.



node-config라는 모듈은 Node Application의 설정정보를 환경에 따라 다르게 읽어서 활용할 수 있도록 도와주는 유틸리티 모듈이다.

여러가지 다른 방법들도 많겠지만, 우선 이 모듈을 활용해서 연결정보를 세팅해보자.

먼저 설정정보를 저장할 config폴더에 "default.json" 파일을 생성하자.

node-config 모듈에서 이 default.json을 기본으로 사용하도록 되어있다. (production 환경에서는 production.json 파일을 활용한다.)



위와 같이 접속 정보를 저장한 후 해당 파일은 ".gitignore" 파일에 포함시켜 원격에 저장되지 않도록 유의하도록 한다.

서버가 구동할 때 설정한 정보로 접근할 수 있도록 다음과 같은 코드를 "app.js"에 추가해준다.



정보가 알맞게 입력 되었다면 서버 구동 시 다음과 같이 접속이 잘 될 것이다.




코드를 보면 대충 알겠지만 config 모듈을 추가하게 되면 (development 환경일 경우) default.json 파일을 읽어와서 config 모듈을 통해 접근할 수 있게 된다.

위 내용에서는 MongoDB 접속 정보를 default.json 파일 내에 "db" 라는 key 로 저장하여서 해당 정보를 읽어올 수 있게 되었다.


2. Schema 정의


앞에 이야기했던 것 처럼 Mongoose에서 Schema는 모델이 어떤 모습으로 생겼는지 정의하는 개념이다. RDB에서의 DDL정도로 봐도 되겠다. (내 맘대로...)

따라서 어떤 key로 어떤 type의 데이터가 들어갈지 정도가 정의하는 내용이 될 것이다.

Mongoose에서 사용되는 Schema의 type은 총 8가지로 다음과 같다.


  1. String
  2. Number
  3. Date
  4. Buffer
  5. Boolean
  6. Mixed
  7. Objectid
  8. Array

우선 게시판의 기본적인 기능만 가질 예정이니 저장할 데이터는 최소한으로 해보자.

  num, title, content, writer, createdAt, updatedAt


이제 models 폴더 내에 schema를 정의할 파일을 만들자.



생성일과 수정일은 javascript 의 Date 함수를 통해 기본값을 세팅해주었다.


3. Model 정의


mongoose에서 model은 데이터의 CRUD에 대한 인터페이스를 정의하는 역할을 가진다.

model을 정의한 후 그 model을 통해 데이터를 저장하고 읽어오는 등의 기능을 수행할 수 있다.



위와 같이 model을 정의한 후 "new" 키워드를 통해 객체(?) 를 생성하면 인터페이스에 정의된 함수를 통해 데이터의 CRUD 등을 수행할 수 있게 된다.

mongoose.model 함수를 통해 model을 정의할 때, 첫번째 인자에 사용된 값이 document가 사용할 collection의 이름의 단수적 표현이다.(네이밍 컨벤션)

예제에서 첫번째 인자의 값을 'Board'로 정의했다면, "new" 키워드를 통해 생성된 데이터를 저장할 때 생성되는 collection의 이름은 "Boards"가 되는 것이다. (Boards라는 collection 안에 Board document들이 저장되는 형태)

이제 board schema와 model을 정의한 내용을 외부에서 사용할 수 있도록 작성해보자.



위에서 "pre" 라는 함수는 'update' 함수가 수행되기 전에 사전적으로 이루질 작업을 정의할 수 있게 해준다. 여기에서는 수정할 때 현재 시간을 "updatedAt"의 값으로 자동으로 넣을 수 있게 해주었다.


4. 데이터 CRUD


이제 정의된 model을 통해 데이터를 저장해보자.

정의된 model을 사용하기 위해서는 먼저 model을 정의한 모듈을 app.js 에 불러와야 한다.



위와 같이 model을 등록하면 되지만... 앞으로 이 서비스가 커지면서 게시판이 여러개 생길 수도 있고, 다른  종류의 데이터를 다루는 기능도 추가될 수 있다.

그렇게 되면 model 을 정의한 모듈이 추가될 때마다 매번 저 내용을 추가해주어야 하는데.. 뭐 별일 아니지만 조금 이쁘게 바꿔보도록 하자. (관심없으면 이 부분은 pass~)

먼저 "glob"이란 모듈을 설치하자.



glob은 특정 패턴으로 파일을 찾은 후 배열 형태로 제공해주는 기능 등을 가진 모듈이다. 그래서 models 폴더에 계속해서 "*.model.js" 형태로 파일들이 추가될 때, 확장되는 파일들을 자동으로 app.js에 불러올 수 있도록 구현할 수 있게 해준다.



위와 같이 작성되면 앞으로 models 폴더에 model을 추가로 등록하더라도 매번 모듈을 추가할 필요가 없게된다.


board 의 데이터를 핸들링하는 서비스를 위해서 기존에 작성했던 "/api" 에 기능을 추가해보자.

현재 작성된 내용이 알맞게 동작한다면, "/api/board" 라는 url을 통해서 board 데이터의 CRUD를 할 수 있는 라우팅 기능을 정상적으로 동작할 것이다.

정의된 model을 통해  "*.controller.js" 파일에 추가기능을 먼저 구현해보겠다.



등록된 모듈을 불러온 후 model 인터페이스에 정의된 "save" 함수를 통해 client로 부터 전달된 데이터를 추가하고 있다.

서비스를 구동한 후 postman을 통해 POST - /api/board 기능을 테스트 해보자. 



정상적으로 동작이 되었다면 MongoDB에 정말 데이터가 저장 되었는지 확인해보자.



Collections의 내용을 보면 정의했던 "board"의 복수형의 형태로 Collection이 생성된 것을 볼 수 있다. 그리고 추가한 데이터가 해당 Collection에 정의했던 SchemaType으로 저장된 것도 확인할 수 있다. 

MongoDB에서 Document는 고유의 아이디를 가지게 되는데, 따로 값을 정의하지 않으면 자동으로 "_id"라는 key로 값이 생성된다.

controller 파일에 나머지 기능들도 추가해보자.



postman을 통해 테스트 해보면 정상적으로 동작하는 것을 확인할 수 있다.