[JS] Mongoose를 통해 MongoDB 접속

hye0n.gyu·2024년 11월 23일

백엔드 환경

목록 보기
7/7
post-thumbnail

⭐ Mongoose란?

Mongoose는 Node.js에서 MongoDB와 상호작용하기 위해 사용하는 Object Data Modeling(ODM) 라이브러리이다.
MongoDB는 스키마 없이 데이터를 저장할 수 있는 NoSQL 데이터베이스이지만, Mongoose를 사용하면 스키마를 정의하여 데이터 구조를 체계적으로 관리할 수 있다.


✔ Mongoose 주요 기능

  • 스키마 정의
    데이터를 저장하기 전에 구조를 정해 관리할 수 있다.

  • 유효성 검사
    스키마를 정의하면서 데이터의 조건을 설정할 수 있다.

  • 간단한 쿼리 작성
    데이터를 검색하거나 수정하는 과정을 간소화할 수 있다.

  • 미들웨어 제공
    데이터 저장, 업데이트 등의 작업 전후에 특정 로직을 실행할 수 있다.

  • 가상 필드 지원
    데이터베이스에 저장되지 않는 계산된 값을 사용할 수 있다.

⭐ Mongo와 Mongoose 연결법

Mongo와 Mongoose를 연결하는 방법은 다음과 같다.

1. Mongoose 설치

Mongoose를 사용하려면 먼저 패키지를 설치해야 한다.

npm install mongoose

2. MongoDB 실행

MongoDB 서버가 실행 중이어야 한다.

로컬 MongoDB를 사용할 경우, 기본적으로 mongodb://localhost:27017 경로에서 실행된다.
MongoDB Atlas와 같은 클라우드 서비스를 사용할 경우, 제공되는 연결 URL을 사용한다.

3. Mongoose를 사용해 MongoDB 연결

Mongoose를 사용하여 MongoDB와 연결하는 기본 코드는 다음과 같다.

const mongoose = require('mongoose');

// MongoDB 연결
mongoose.connect('mongodb://localhost:27017/mydatabase', { 
    useNewUrlParser: true, 
    useUnifiedTopology: true 
})
    .then(() => console.log('MongoDB 연결 성공'))
    .catch(err => console.error('MongoDB 연결 실패:', err));

옵션 설명
useNewUrlParser : URL 구문 분석을 새 방식으로 처리하기 위한 옵션이다.
useUnifiedTopology : MongoDB 드라이버의 새 연결 관리 엔진을 사용하기 위한 옵션이다.



⭐ Mongoose CRUD


✔ Mongoose 찾기

Mongoose에서는 find, findOne 메서드로 데이터를 검색할 수 있다.

const User = mongoose.model('User', userSchema);

// 모든 데이터 찾기
User.find({})
    .then(users => console.log(users))
    .catch(err => console.error(err));

// 특정 조건에 맞는 데이터 찾기
User.find({ age: { $gte: 18 } }) // 나이가 18 이상인 데이터
    .then(users => console.log(users))
    .catch(err => console.error(err));

// 한 개의 데이터만 찾기
User.findOne({ name: 'Alice' })
    .then(user => console.log(user))
    .catch(err => console.error(err));

// ID로 찾기
User.findById('ObjectId값')
    .then(user => console.log(user))
    .catch(err => console.error(err));

✔ Mongoose 업데이트

Mongoose는 updateOne, updateMany, findByIdAndUpdate 메서드로 데이터를 업데이트할 수 있다.

// 한 개의 데이터 업데이트
User.updateOne({ name: 'Alice' }, { age: 26 })
    .then(result => console.log('업데이트 성공:', result))
    .catch(err => console.error(err));

// 여러 데이터 업데이트
User.updateMany({ age: { $lt: 18 } }, { isMinor: true }) // 18세 미만 데이터에 `isMinor: true` 추가
    .then(result => console.log('업데이트 성공:', result))
    .catch(err => console.error(err));

// ID로 데이터 업데이트
User.findByIdAndUpdate('ObjectId값', { email: 'updated@example.com' }, { new: true }) // 업데이트된 결과 반환
    .then(updatedUser => console.log('업데이트된 데이터:', updatedUser))
    .catch(err => console.error(err));

옵션 설명
new : 업데이트된 값을 반환하기 위해 사용한다. (기본값은 업데이트 이전 값 반환)
rawResult : MongoDB가 반환하는 원본 결과를 얻고 싶을 때 사용한다.

✔ Mongoose 삭제

Mongoose에서는 deleteOne, deleteMany, findByIdAndDelete로 데이터를 삭제할 수 있다.

// 한 개의 데이터 삭제
User.deleteOne({ name: 'Alice' })
    .then(result => console.log('삭제 성공:', result))
    .catch(err => console.error(err));

// 여러 데이터 삭제
User.deleteMany({ isMinor: true }) // 조건에 맞는 모든 데이터 삭제
    .then(result => console.log('삭제 성공:', result))
    .catch(err => console.error(err));

// ID로 데이터 삭제
User.findByIdAndDelete('ObjectId값')
    .then(deletedUser => console.log('삭제된 데이터:', deletedUser))
    .catch(err => console.error(err));

✔ 예제

const mongoose = require('mongoose');

// MongoDB 연결
mongoose.connect('mongodb://localhost:27017/mydatabase', {
    useNewUrlParser: true,
    useUnifiedTopology: true
})
    .then(() => console.log('MongoDB 연결 성공'))
    .catch(err => console.error('MongoDB 연결 실패:', err));

// 스키마 및 모델 정의
const userSchema = new mongoose.Schema({
    name: String,
    age: Number,
    email: String,
    isMinor: Boolean
});

const User = mongoose.model('User', userSchema);

// 데이터 작업 예제
async function main() {
    try {
        // 1. 데이터 찾기
        console.log('\n=== 데이터 찾기 ===');
        const allUsers = await User.find({});
        console.log('모든 사용자:', allUsers);

        const adultUsers = await User.find({ age: { $gte: 18 } });
        console.log('성인 사용자:', adultUsers);

        const singleUser = await User.findOne({ name: 'Alice' });
        console.log('Alice 사용자:', singleUser);

        const userById = await User.findById('64bfc456a789123456789abc'); // ID를 실제 값으로 변경해야 함
        console.log('ID로 찾은 사용자:', userById);

        // 2. 데이터 업데이트
        console.log('\n=== 데이터 업데이트 ===');
        const updatedUser = await User.updateOne({ name: 'Alice' }, { age: 26 });
        console.log('업데이트 결과:', updatedUser);

        const multipleUpdates = await User.updateMany({ age: { $lt: 18 } }, { isMinor: true });
        console.log('여러 데이터 업데이트 결과:', multipleUpdates);

        const updatedById = await User.findByIdAndUpdate(
            '64bfc456a789123456789abc', // ID를 실제 값으로 변경해야 함
            { email: 'updated@example.com' },
            { new: true }
        );
        console.log('ID로 업데이트된 사용자:', updatedById);

        // 3. 데이터 삭제
        console.log('\n=== 데이터 삭제 ===');
        const deletedUser = await User.deleteOne({ name: 'Alice' });
        console.log('한 개 데이터 삭제 결과:', deletedUser);

        const deletedMultipleUsers = await User.deleteMany({ isMinor: true });
        console.log('여러 데이터 삭제 결과:', deletedMultipleUsers);

        const deletedById = await User.findByIdAndDelete('64bfc456a789123456789abc'); // ID를 실제 값으로 변경해야 함
        console.log('ID로 삭제된 사용자:', deletedById);

    } catch (err) {
        console.error('오류 발생:', err);
    } finally {
        // MongoDB 연결 종료
        await mongoose.disconnect();
        console.log('MongoDB 연결 종료');
    }
}

// 함수 실행
main();


⭐ Mongoose 스키마 유효성 검사

Mongoose는 스키마를 정의할 때 데이터의 타입(type)과 기타 조건을 설정하여 기본적인 유효성 검사를 제공한다.

옵션설명예제비고
required필수 필드 여부를 지정. 값이 없으면 에러 발생.{ name: { type: String, required: true } }필수 필드
type필드 데이터의 타입을 지정 (String, Number, Boolean 등).{ age: { type: Number } }데이터 타입
min숫자 타입에 적용. 최소값 설정.{ age: { type: Number, min: 0 } }최소값 조건
max숫자 타입에 적용. 최대값 설정.{ age: { type: Number, max: 120 } }최대값 조건
match문자열 타입에 적용. 정규식을 이용해 형식 검사.{ email: { type: String, match: /.+\@.+\..+/ } }정규식 검사
enum문자열 값이 특정 목록에 포함되어야 함.{ role: { type: String, enum: ['admin', 'user'] } }허용된 값
default값이 없는 경우 기본값을 지정.{ status: { type: String, default: 'active' } }기본값 설정

✔ 예제

const userSchema = new mongoose.Schema({
    name: { type: String, required: true }, // 필수 값
    age: { type: Number, min: 0, max: 120 }, // 최소값, 최대값
    email: { type: String, match: /.+\@.+\..+/ }, // 정규식 조건
});

const User = mongoose.model('User', userSchema);

const newUser = new User({ name: 'Alice', age: -5, email: 'invalidEmail' });

newUser.save()
    .then(() => console.log('유효성 검사를 통과한 데이터 저장'))
    .catch(err => console.error('유효성 검사 실패:', err.message));

✔ 업데이트 유효성 검사

mongoose는 업데이트 유효성 검사를 사용 안 하면 데이터 업데이트시 유효성 검사를 하지 않는다.

updateOne, updateMany, findOneAndUpdate와 같은 메서드에서 유효성 검사를 하려면 runValidators 옵션을 true로 설정해야 한다.

주요 옵션

runValidators : true로 설정하면 업데이트 시에도 유효성 검사를 수행한다.
new : 업데이트된 결과를 반환받을지 여부를 결정. true로 설정하면 업데이트된 데이터를 반환하고, false로 설정하면 업데이트 전 데이터를 반환한다.

예제

const mongoose = require('mongoose');

// 스키마 정의
const userSchema = new mongoose.Schema({
    name: { type: String, required: true },
    age: { type: Number, min: 18, max: 100 }
});

const User = mongoose.model('User', userSchema);

// 데이터 업데이트 예시
async function updateUser() {
    try {
        const updatedUser = await User.findOneAndUpdate(
            { name: 'Alice' },
            { age: 15 },  // 15는 유효하지 않음
            { runValidators: true, new: true }  // 유효성 검사 실행
        );
        console.log('업데이트된 사용자:', updatedUser);
    } catch (err) {
        console.error('업데이트 오류:', err.message);
    }
}

updateUser();


⭐ Mongoose 인스턴스 메서드와 정적 메서드


✔ 인스턴스 메서드

인스턴스 메서드는 Mongoose 문서 인스턴스에 바인딩되어 있다.
즉, 문서 객체에서 호출할 수 있는 메서드로, 특정 문서에 대해 작업을 수행하는 데 사용된다.

인스턴스 메서드 정의 방법

인스턴스 메서드는 스키마 인스턴스에서 methods 객체를 사용하여 정의한다.

예제

const mongoose = require('mongoose');

// 스키마 정의
const userSchema = new mongoose.Schema({
    name: { type: String, required: true },
    age: { type: Number, required: true }
});

// 인스턴스 메서드 정의
userSchema.methods.sayHello = function () {
    return `안녕하세요, ${this.name}입니다.`;
};

const User = mongoose.model('User', userSchema);

// 인스턴스 메서드 사용
async function createUser() {
    const user = new User({ name: 'Alice', age: 25 });
    console.log(user.sayHello()); // "안녕하세요, Alice입니다."
}

createUser();

✔ 정적 메서드

정적 메서드는 모델 자체에 바인딩된 메서드로, 문서 인스턴스가 아닌 모델 클래스에서 호출된다.
정적 메서드는 모델을 통해 데이터베이스 작업을 처리하거나 모델에 관련된 작업을 정의할 때 사용된다.

정적 메서드 정의 방법

정적 메서드는 스키마의 statics 객체를 사용하여 정의한다.

예제

const mongoose = require('mongoose');

// 스키마 정의
const userSchema = new mongoose.Schema({
    name: { type: String, required: true },
    age: { type: Number, required: true }
});

// 정적 메서드 정의
userSchema.statics.findByName = function (name) {
    return this.find({ name: name });
};

const User = mongoose.model('User', userSchema);

// 정적 메서드 사용
async function searchUser() {
    const users = await User.findByName('Alice'); // User 모델에서 호출
    console.log(users); // 'Alice' 이름을 가진 사용자들 출력
}

searchUser();

this의 차이

  • 인스턴스 메서드는 특정 문서 인스턴스에 대해 동작하며, this는 인스턴스를 참조한다.
  • 정적 메서드는 모델 자체에 대해 동작하며, this는 모델을 참조한다.


⭐ 가상 속성

가상 속성은 schema.virtual 메서드를 사용하여 정의한다.
getset을 사용해 가상 속성의 값을 가져오거나 설정할 수 있다.

get 예제

const mongoose = require('mongoose');

// 스키마 정의
const userSchema = new mongoose.Schema({
    firstName: { type: String, required: true },
    lastName: { type: String, required: true },
});

// 가상 속성 정의: fullName
userSchema.virtual('fullName').get(function () {
    return `${this.firstName} ${this.lastName}`;
});

const User = mongoose.model('User', userSchema);

// 데이터 생성 및 조회 예시
async function createUser() {
    const user = new User({ firstName: 'John', lastName: 'Doe' });
    console.log(user.fullName); // "John Doe"
}

createUser();

set 예제

const mongoose = require('mongoose');

// 스키마 정의
const userSchema = new mongoose.Schema({
    firstName: { type: String, required: true },
    lastName: { type: String, required: true },
});

// 가상 속성 정의: fullName
userSchema.virtual('fullName')
    .get(function () {
        return `${this.firstName} ${this.lastName}`;
    })
    .set(function (value) {
        const [firstName, lastName] = value.split(' ');
        this.firstName = firstName;
        this.lastName = lastName;
    });

const User = mongoose.model('User', userSchema);

// 데이터 생성 및 조회 예시
async function createUser() {
    const user = new User({ firstName: 'John', lastName: 'Doe' });

    // 가상 속성 설정
    user.fullName = 'Alice Wonderland';

    console.log(user.firstName); // "Alice"
    console.log(user.lastName);  // "Wonderland"
}

createUser();


⭐ mongoose pre, post 미들웨어


pre 미들웨어

pre 미들웨어는 특정 작업이 실행되기 전에 코드를 실행할 수 있게 한다.
예를 들어, 문서를 저장하거나 업데이트하기 전에 데이터 검증 작업을 할 수 있다.

예제

const mongoose = require('mongoose');

// 스키마 정의
const userSchema = new mongoose.Schema({
    name: { type: String, required: true },
    age: { type: Number, required: true }
});

// `save` 전 미들웨어 정의
userSchema.pre('save', function (next) {
    if (this.age < 18) {
        next(new Error('나이는 18세 이상이어야 합니다.'));
    } else {
        next();  // 나이가 18세 이상이면 저장을 계속 진행
    }
});

const User = mongoose.model('User', userSchema);

// 데이터 생성 및 저장 예시
async function createUser() {
    const user = new User({ name: 'John', age: 17 });
    try {
        await user.save();
    } catch (err) {
        console.error('오류:', err.message);  // "나이는 18세 이상이어야 합니다."
    }
}

createUser();

post 미들웨어

post 미들웨어는 작업이 완료된 후에 실행된다.
예를 들어, 데이터베이스에 문서를 저장한 후에 알림을 보내거나 로그를 기록하는 작업을 할 수 있다.

예제

const mongoose = require('mongoose');

// 스키마 정의
const userSchema = new mongoose.Schema({
    name: { type: String, required: true },
    age: { type: Number, required: true }
});

// `save` 후 미들웨어 정의
userSchema.post('save', function (doc) {
    console.log(`${doc.name}님이 저장되었습니다.`);
});

const User = mongoose.model('User', userSchema);

// 데이터 생성 및 저장 예시
async function createUser() {
    const user = new User({ name: 'Alice', age: 30 });
    await user.save();  // "Alice님이 저장되었습니다." 출력
}

createUser();
profile
반려묘 하루 velog

0개의 댓글