본 글은 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는 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가 정의되는 방법이다. 우리는 mongoose와 mongoose.Schema를 모두 포함시켜야 한다. 그 다음에, userSchema 안에 사용자의 프로필에 쓰기 위해 우리가 원하는 속성을 정의할 수 있다. meta 속성과 같이 중첩된 객체를 정의할 수도 있다.
다음은 SchemaTypes로 가능한 것들이다:
아래는 글에서 언급되지는 않았지만, mongoose v5.9.20 문서에 언급된 SchemaTypes 입니다.
그 다음에, 우리는 mongoose.model를 이용해 mongoose Model를 생성할 것이다. 우리는 여기서 특정 methods를 더 생성하는 등의 일도 할 수 있다. 여기는 비밀번호를 해시(평문을 암호화한 문장으로 만드는 것)하는 방법을 만들기에 좋은 곳이다.
// 필요한 요소들을 포함시킨다
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를 꼭 살펴보자.
우리가 이전에 생성한 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!');
});
우리가 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가 1인 user 찾기
User.findById(1, function(err, user) {
if (err) throw err;
// 해당 user 보여주기
console.log(user);
});
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);
});
우리는 특정 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가 4인 user 찾기
// 그리고 해당 user의 username을 starlord88로 갱신하기
user.findByIdAndUpdate(4, { username: 'starlord88' }, function(err, user) {
if (err) throw err;
// 업데이트 된 user를 return 한다
console.log(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가 4인 user를 찾는다
User.findByIdAndRemove(4, function(err) {
if (err) throw err;
// user를 삭제했다
console.log('User deleted!');
});
이 글이 Node.js와 MongoDB 어플리케이션에서 mongoose 패키지를 사용하는데 좋은 참조 자료가 되었으면 하는 바람입니다.
2020-07-02 : 최초 작성