Mongoose로 Node.js와 MongoDB 앱 쉽게 개발하기

Hayoung Jeon·2020년 7월 1일
2

Backend

목록 보기
1/1

본 글은 Chris on Code의 Easily Develop Node.js and MongoDB Apps with Mongoose을 번역한 내용입니다. 공부용으로 번역한 내용이라 의역이나 잘못된 내용, 혹은 원문에 없지만 추가적으로 설명해둔 내용 이 있을 수 있습니다. 오타, 오역 등은 댓글로 알려주세요.

Node.js와 MongoDB는 서로를 위해 개발된 한 쌍이다. JSON을 산업 전반과 자바스크립트에서 사용할 수 있게되면서, 개발이 굉장히 쉬워졌다. 그리고 이건 우리가 MEAN 스택(MongoDB, Express - Node.js의 프레임워크, AngularJS, Node)과 같은 인기있는 스택들을 사용할 수 있게 된 이유이기도 하다.

CRUD는 존재하는 모든 응용 프로그램에 필수적인 요소이다. 우리는 항상 만들고(create), 읽고(read), 갱신하고(update), 삭제(delete)할 수 있어야 한다.

오늘 우리는 Node.js, ExpressJS, MongoDB를 사용하는 앱에서 CRUD 작업을 처리하는 방법을 알기 위한 코드 샘플들을 살펴볼 것이다. 여기에는 유명한 Node 패키지인 mongoose가 필요하다.

이 코드 샘플들은 Node.js의 RESTful API를 만들기 위해 사용된 것들인데, API를 만들다보면 CRUD 기능을 수행해야 하기 때문이다. 이 명령들이 수행되는 것을 하고 싶다면, 해당 튜토리얼을 확인해보자. 이 글은 다양한 명령어와 그 사용법에 대한 참조자료에 더 가깝다. (해당 튜토리얼은 현재 게시글이 삭제되었습니다.)

Mongoose가 뭘까?

mongoose는 Node의 객체 모델링 패키지로, 기본적으로 다른 언어의 ORM과 같은 역할을 수행한다. (Laravel의 Eloquent와 유사하다)

Mongoose는 우리가 CRUD 작업을 위한 MongoDB의 명령어에 쉽고 간단하게 접근할 수 있도록 도와준다. Mongoose를 사용하기 위해서는, Node 프로젝트에 다음과 같은 명령어로 설치를 해주어야 한다:

$ npm install mongoose --save

이제 패키지가 있으니, 우리의 프로젝트에 포함을 시켜줘야 한다:

var mongoose = require('mongoose');

ES6에서는 다음과 같이 사용할 수 있습니다.

import mongoose from "mongoose";

또, MongoDB 데이터베이스와의 연결도 필요하다. (로컬이나 호스트로 연결):

mongoose.connect('mongod://localhost/myappdatabase');

자, 이제 명령어들을 살펴보자.

모델을 정의하기

CRUD 작업을 처리하기 하기 전에, 우리는 mongoose Model을 필요로 한다. 이런 모델은 우리가 정의하는 생성자이다. 이들은 우리의 데이터베이스에서 저장되고 검색될 수 있는 document를 의미한다.

Mongoose Schema : Mongoose Schema는 우리의 document에 속성을 정의하기 위해 사용된다.

Mongoose Methods : Method는 mongoose schema에서도 정의될 수 있다. 다음 문장은 'These are methods' 입니다만, 앞뒤 문맥을 살펴보았을 때 문장이 잘린 것으로 생각됩니다ㅠㅠ 관련하여 자세한 내용이 ZeroCho님의 블로그에 정리되어 있습니다.

사용자에 관한 모델 예시

// 필요한 요소들을 포함시킨다
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

// Schema를 생성한다
var userSchema = new Schema({
	name: String,
    username: { type: String, required: true, unique: true },
    password: { type: String, required: true },
    admin: Boolean,
    location: String,
    meta : {
    	age: Number,
        website: String
    },
    created_at: Date,
    updated_at: Date
});

// 이 schema는 현재까지는 별 의미가 없다
// 우리는 이 schema를 사용하는 model를 만들어야 한다
var User = mongoose.Model('User', userSchema);

// Node 앱에서 사용 가능하게 만들자
module.exports = User;

ES6에서는 다음과 같이 export 할 수 있습니다.

export default User;

이것이 Schema가 정의되는 방법이다. 우리는 mongoosemongoose.Schema를 모두 포함시켜야 한다. 그 다음에, userSchema 안에 사용자의 프로필에 쓰기 위해 우리가 원하는 속성을 정의할 수 있다. meta 속성과 같이 중첩된 객체를 정의할 수도 있다.

다음은 SchemaTypes로 가능한 것들이다:

  • String
  • Number
  • Date
  • Buffer
  • Boolean
  • Mixed
  • ObjectId
  • Array

아래는 글에서 언급되지는 않았지만, mongoose v5.9.20 문서에 언급된 SchemaTypes 입니다.

  • Decimal128
  • Map
  • Schema

그 다음에, 우리는 mongoose.model를 이용해 mongoose Model를 생성할 것이다. 우리는 여기서 특정 methods를 더 생성하는 등의 일도 할 수 있다. 여기는 비밀번호를 해시(평문을 암호화한 문장으로 만드는 것)하는 방법을 만들기에 좋은 곳이다.

Custom Method

// 필요한 요소들을 포함시킨다
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

// Schema를 생성한다.
var userSchema ...

// 이름의 끝에 String을 더해주기 위한 custom method
// 이름의 적합성을 검사하거나 포맷을 지정하는 등 더 중요한 methods도 생성 가능하다.
// 쿼리를 수행하거나 비슷한 사용자를 찾는 등의 일도 가능하다.
userSchema.methods.dudify = function() {
	// 사용자의 이름에 무언가를 더해준다.
    this.name = this.name + '-dude';
    
    return this.name;
}

// 이 schema는 현재까지는 별 의미가 없다
// 우리는 이 schema를 사용하는 model를 만들어야 한다
var User = mongoose.Model('User', userSchema);

// Node 앱에서 사용 가능하게 만들자
module.exports = User;

사용 예시

이제 custom model과 method가 있으니, 우리의 코드에서 불러올 수 있다:

// 만약 우리의 user.js 파일이 app/models/user.js에 있다면
var User = require('./app/models/user');

// chris라고 불리는 새 유저를 생성하자
var chris = new User({
	name: 'Chris',
    username: 'sevilayha',
    password: 'password'
});

// custom method를 불러와 그의 이름 뒤에 -dude를 붙여줄 것이다
// user는 이제 Chris-dude가 될 것이다
chris.dudify(function(err, name) {
	if (err) throw err;
    
    console.log('Your new name is ' + name);
});

// 데이터베이스에 저장하기 위해, 내장되어있는 save method를 사용하자
chris.save(function(err) {
	if (err) throw err;
    
    console.log('User saved successfully!');
});

이것은 굉장히 쓸모없는 custom method이지만, 어떻게 custom method를 생성하고 사용하는지를 알 수 있다. 우리는 이것을 저장 전에 비밀번호가 해시화 되었는지 확인하거나, 비밀번호를 비교하거나, 비슷한 속성을 가진 유저들을 검사하는 등의 용도로도 사용할 수 있다.

저장 전에 함수를 실행하기

우리는 created_at 변수가 언제 기록이 생성되었는지를 알 수 있기를 바란다. 우리는 Schema pre 라는 메소드를 사용해 해당 작업을 객체가 저장되기 전에 수행하도록 할 수 있다.

아래는 처음 생성 시에 created_at에, 모든 저장 시마다 updated_at에 날짜를 추가하기 위해 우리의 Schema에 추가되어야 하는 코드다.

// 매 저장 시마다 날짜를 추가한다
userSchema.pre('save', function(next) {
	// 현재의 날짜를 가져온다
    var currentDate = new Date();
    
    // update_at 값을 현재의 날짜로 바꾼다
    this.updated_at = currentDate;
    
    // 만약 created_at이 존재하지 않는다면, 해당 값을 추가한다
    if (!this.created_at)
    	this.created_at = currentDate;
        
    next();
});

이제 모든 저장 시에 우리는 그 날짜를 추가하게 될 것이다. 여기는 또한 절대 평문을 비밀번호에 그냥 저장하도록 하지 않기 위해 비밀번호를 해시하기에 좋은 위치이기도 하다.

우리는 또한 models와 schema에 statics나 indexes와 같은 더 많은 요소들을 정의할 수 있다. 더 많은 정보를 위해 mongoose docs를 꼭 살펴보자.

Create

우리가 이전에 생성한 User method를 사용할 것이다. user를 생성하기 위해, mongoose Model에 내장되어 있는 save method를 사용한다:

// user model를 포함시킨다
var User = require('./app/models/user');

// 새 user를 생성한다
var newUser = User({
	name: 'Peter Quill',
    username: 'starlord55',
    password: 'password',
    admin: true
});

// user를 저장한다
newUser.save(function(err) {
	if (err) throw err;
    
    console.log('User created!');
});

Read

우리가 users 데이터베이스에서 정보를 요청하는데에는 많은 이유가 있다. 우리는 한 명의 특정한 user가 필요하거나, 전체 user들이 필요하거나, 비슷한 user들이 필요하는 등 다양한 시나리오가 존재한다. 아래는 몇 가지 예시이다:

전체 찾기

// 모든 유저를 가져오기
User.find({}, function(err, users) {
	if (err) throw err;
    
    // 모든 유저의 객체
    console.log(users);
});

특정 한 명 찾기

// user 'starlord55' 찾기
User.find({ username: 'starlord55' }, function(err, user) {
	if (err) throw err;
    
    // user의 객체
    console.log(user);
)};

ID로 찾기

// ID가 1인 user 찾기
User.findById(1, function(err, user) {
	if (err) throw err;
    
    // 해당 user 보여주기
    console.log(user);
});

데이터 요청하기 (Querying)

MongoDB의 query syntax도 사용 가능하다.

// 지난 달에 생성된 모든 admin(관리자)을 가져온다.

// 1달 전의 날짜를 가져온다.
var monthAgo = new Date();
monthAgo.setMonth(monthAgo.getMonth() - 1);

User.find({ admin: true }).where('created_at').gt(monthAgo).exec(function(err, users) {
	if (err) throw err;
    
    // 지난달에 생성된 admin를 보여준다.
    console.log(users);
});

Update

우리는 특정 user를 찾은 뒤, 몇 가지 속성을 바꾸고, 그 내용을 다시 저장할 것이다.

User를 가져온 후, 갱신하기

// ID가 1인 user를 찾는다
User.findById(1, function(err, user) {
	if (err) throw err;
    
    // user의 location(위치)를 바꾼다
    user.location = 'uk';
    
    // user를 저장한다
    user.save(function(err) {
    	if (err) throw err;
        
        console.log('User successfully updated!');
    });
    
});

우리가 updated_at을 바꾸는 함수를 만들었기 때문에, save에서도 그 함수가 동작할 것이라는 사실을 명심하자.

찾고 갱신하기

우리가 user를 직접 찾고, 바꾸고, 저장할 필요가 없어 훨씬 쉬운 method이다. 그냥 mongodb의 findAndModify 명령어를 사용하면 된다.

// user starlord55를 찾는다
// 그리고 starylord88로 업데이트 한다
User.findOneAndUpdate({ username: 'starlord55' }, { username: 'starlord88' }, function(err, user) {
	if (err) throw err;
    
    // 업데이트 된 user를 return 한다
    console.log(user);
});

ID로 찾고 갱신하기

// ID가 4인 user 찾기
// 그리고 해당 user의 username을 starlord88로 갱신하기
user.findByIdAndUpdate(4, { username: 'starlord88' }, function(err, user) {
	if (err) throw err;
    
    // 업데이트 된 user를 return 한다
    console.log(user);
});

Delete

User를 가져온 후, 지우기

// user starlord55를 가져온다
User.find({ username: 'starlord55' }, function(err, user) {
	if (err) throw err;
    
    // user를 삭제한다
    user.remove(function(err) {
    	if (err) throw err;
        
        console.log('User successfully deleted!');
    });
});

찾고 지우기

// user starlord55를 찾는다 (원문에서는 id 4이지만, 코드를 살펴보면 username을 기준으로 찾아왔습니다.)
User.findOneAndRemove({ username: 'starlord55' }, function(err) {
  if (err) throw err;

  // user를 삭제했다
  console.log('User deleted!');
});

ID로 찾고 지우기

// ID가 4인 user를 찾는다
User.findByIdAndRemove(4, function(err) {
  if (err) throw err;

  // user를 삭제했다
  console.log('User deleted!');
});

결론

이 글이 Node.js와 MongoDB 어플리케이션에서 mongoose 패키지를 사용하는데 좋은 참조 자료가 되었으면 하는 바람입니다.

2020-07-02 : 최초 작성

profile
안녕하세요, '일리있는' 개발자를 목표로 하는, 전하영 입니다.

0개의 댓글