티스토리 뷰

무제

몽고디비 시작하기

몽고디비는 일단 관계형 데이터베이스가 아니므로 SQL을 사용하지 않는다.

 

몽고디비란?
  • 비관계형 데이터베이스 (NoSQL)

  • 성능을 최우선으로 생각, 관계형 데이터베이스에 비해 시스템의 자원을 적게 소모하여 많은 사용자를 수용할 수 있음

  • 컬렉션 : 여러 데이터가 모인 하나의 단위.

    • RDB의 테이블은 컬럼에 정의된 자료형에 맞게 데이터를 넣어야 하지만, 컬렉션에는 제약이 없다.
  • 데이터베이스 : 컬렉션의 집합

  • 각각의 컬렉션은 여러 개의 문서 객체를 가질 수 있음

    • 문서 객체 : 속성들의 집합

데이터베이스 > 컬렉션 > 문서 객체 > 속성

 

몽고디비 사용을 위한 프로그램 설치
  • macOS를 사용한다면 brew를 이용하여 몽고디비를 설치해주자. 도움받은 링크

  • mongod 명령어로 몽고디비 실행

  • mongo 명령어로 몽고디비 데이터베이스에 연결할 수 있는 셸 실행

    • use 명령어로 사용할 데이터베이스 지정
    • db.users.insert({}) : 사용하는 데이터베이스에 users 컬렉션을 만들고 파라미터로 전달한 JSON 객체 추가
    • db.users.find().pretty() : users 컬렉션에 있는 모든 문서 객체 반환

 

익스프레스에서 몽고디비 사용하기

sudo npm install mongodb --save

const MongoClient = require("mongodb").MongoClient

몇 가지를 간단하게 정리하자면,

  • MongoClient.connect() 의 콜백 함수로 들어온 client 인자에서 client.db(db명) 과 같이 작성하여 데이터베이스를 가져올 수 있다.
  • database.collection(컬렉션명) 과 같이 작성하여 컬렉션을 가져올 수 있다. 가져온 컬렉션을 users 변수에 담았다고 가정하자.
  • users.find({}).toArray() 과 같이 작성하여 특정 조건에 맞는 문서 객체를 찾고, 그 결과를 배열로 만든 값이 toArray 메소드의 콜백 함수의 두 번째 인자로 들어가는 것 같다.
  • users.insertMany([{}]) 과 같이 작성하여 컬렉션에 JSON 객체를 넣을 수 있다.

책이랑 지금 사용법이랑 다른 점이 아래에 있다.

const MongoClient = require("mongodb").MongoClient
let database
function connectDB() {
    const databaseUrl = "mongodb://localhost:27017"
    //아랫줄 사용법 많이..바뀌었음
    MongoClient.connect(databaseUrl, { useNewUrlParser: true }, (err, client) => {	//1
        if(err) throw err
        let db = client.db("db")	//2
        database = db	//3
    })
}
  1. connect 의 인자로 { useNewUrlParser: true } 를 적지 않으면 deprecatedError 가 발생한다. 조만간 없어질 거니까 뭐 이렇게 하라는 것 같은데 일단 경고 메세지가 나오는게 보기 싫어서 구글링을 통해 위의 코드를 작성해주었다.
  2. 책에서는 databaseUrlmongodb://localhost:27017/db 처럼 하고 콜백 함수의 두 번째 인자를 db 로 하고 2와 3의 코드를 database = db 한줄로 끝내는데 이렇게 하면 나중에 데이터베이스에 접근하려 할 때 에러가 발생한다.

또한 req.param() 과 같은 코드로 URL의 인자를 받아오는 코드는 deprecated 되었다. 상황에 맞게 req.query (GET), req.params (Semantic URL), req.body (POST) 와 같은 코드를 사용하자.

connect() 연결 > collection() 컬렉션 참조 > find() 문서 찾기


추가로 책에서 입력값과 데이터베이스에 있는 값을 검증하고 데이터를 추가하는 함수를 변수에 할당했는데, 그냥 함수의 역할을 하는 것은 function 키워드를 사용해서 함수로 선언해주는 것이 코드를 보기에 편할 것 같다.

변수에 함수를 할당하는 것, 함수를 선언하는 것. 취향 차이인지 아니면 뚜렷한 성능 차이가 있는지 아직은 모르겠다.


데이터베이스 관리 도구

로보몽고

위의 링크에서 GUI에서 데이터베이스 관련 작업이 가능한 툴을 내려받을 수 있다.

 

몽구스로 데이터베이스 다루기

몽고디비의 NoSQL 특성상 컬렉션에 들어갈 수 있는 데이터의 제약이 없으나 이는 데이터를 조회할 때 조건을 명시하기 어렵다는 단점을 가져온다.

이러한 문제를 해결하기 위해 스키마(Schema)를 만들고, 그 스키마에 따라 문서 객체를 저장하는 것이 때로는 편리할 때가 있다.

오브젝트 맵퍼(Object Mapper) : 자바스크립트 객체와 데이터베이스 객체를 서로 매칭하여 바꿀 수 있게 하는 것

몽구스를 활용하여 위와 같은 기능을 사용할 수 있다.

sudo npm install mongoose --save


몽구스 모듈 사용하기
메소드 이름설명
connect()mongoose 사용하여 데이터베이스에 연결.
연결 후 mongoose.connection 객체를 사용해 연결 관련 이벤트 처리 가능
Schema()스키마 정의하는 생성자
model()모델 정의

mongoose.Schema() 에 JSON 객체로 각 속성에 적합한 스키마 타입 을 지정

스키마 타입설명
String문자열
Number숫자
Boolean이진
Array배열
Buffer버퍼 (바이너리 데이터)
Date날짜
ObjectId각 문서마다 만들어지는 ObjectId
Mixed혼합
자바스크립트 객체구체적으로 타입 지정
- type: 자료형 지정
- required: 반드시 들어가야 하는 속성인가?
- unique: 유일한 값이 들어가야 하는 속성인가?

자바스크립트 객체로 스키마 타입을 지정하려면 아래와 같이 작성해주면 된다.

const UserSchema = new mongoose.Schema({
    id: {type: String, required: true, unique: true},
    password: {type: String, required: true},
    name: String,
    age: Number,
    createdAt: Date,
    updatedAt: Date
})

mongoose.model() 에 특정 컬렉션이 사용할 스키마를 지정, 모델 반환

mongoose.model("users", UserSchema)

 

몽구스로 사용자 인증하기

모델 객체 또는 모델을 사용해서 만들어 낸 인스턴스 객체에는 데이터베이스의 컬렉션에 들어 있는 문서 객체를 조회, 수정, 삭제할 수 있는 메소드가 들어 있다.

메소드 이름설명
find()컬렉션 데이터 조회, 콜백 함수로 조회 결과 전달
save()모델 인스턴스 객체의 데이터 저장, 콜백 함수로 저장 결과 전달
update()컬렉션 데이터 조회 후 업데이트, where 메소드와 함께 사용됨
remove()컬렉션 데이터 삭제

이전 방법으로 코드를 작성하면 addUser 부분에서 데이터 저장이 실패했다고 웹페이지에 뜨지만 실제로는 데이터베이스에 잘 저장되게 된다.

save 메소드의 콜백 함수의 인자로 에러만 넘기는데, 그러면 addUser 함수를 호출할 때 콜백 함수의 두 번째 인자에는 아무것도 들어가지 않게 된다.

그렇다고 save 메소드의 두 번째 인자를 임의로 주어 넘겨도 insertedCountundefined 다. 흠...

아무튼 find 메소드는 인자에 조회 조건을 넣고 콜백 함수에서 결과를 처리한다.

save 메소드는 저장할 모델의 인스턴스를 만들어 그 인스턴스에서 메소드를 호출하고 콜백 함수에서 결과를 처리한다.

 

인덱스와 메소드 사용하기

빠른 검색을 위해 각각의 속성에 인덱스를 만들 수 있다.

const UserSchema = new mongoose.Schema({
    id: {type: String, required: true, unique: true},
    password: {type: String, required: true},
    name: {type: String, index: 'hashed'},
    age: Number,
    createdAt: {type: Date, index: {unique: false, expires: '1d'}},
    updatedAt: Date
})

id 속성은 unique하므로 자동으로 인덱스가 만들어짐

name 속성에 들어가는 객체에는 인덱스 속성값이 hashed로 되어 있음

createdAt 속성에 들어가는 객체에는 인덱스가 설정되는 값의 유효 기간도 지정함

애초에 인덱스가 데이터베이스에서 어떤 역할을 하는지부터 알아야 할 필요가 있겠다...

인덱스를 사용하면 검색 속도가 빨라지므로 조회가 필요한 속성 에는 인덱스를 만들어두는 것이 좋다.


스키마 객체에 메소드를 추가할 수 있다. 함수의 이름과 함수 객체를 파라미터로 전달한다.

  • static(name, fn) : 정적 메소드 등록
  • method(name, fn) : 인스턴스 메소드 등록

 

사용자 리스트 조회 기능 추가

static 으로 스키마 객체에 메소드를 추가한다.

UserSchema.static("findById", (id, callback) => {
    return UserModel.find({ id }, callback)
})
UserSchema.static("findAll", callback => {
    return UserModel.find({ }, callback)
})

책에는 return this.find({ id: id }, callback) 와 같이 나와있는데 이렇게 하면 에러가 난다.

Arrow Function을 써서 this 바인딩이 안된건가 뭔가는 모르겠는데 아무튼 위와 같이 써주면 정상적으로 동작하게 된다.

정적 메소드를 추가해 주었으니 기존에 정의해 주었던 사용자 검증 / 사용자 추가 함수를 UserModel.findById , UserModel.findAll 을 사용해서 수정해줄 수 있다.

데이터의 요소의 _doc 속성은 각 문서 객체 정보를 담고 있어 _doc 에서 그 안에 있는 속성 값에 접근할 수 있다.

 

비밀번호 암호화하여 저장하기

  1. 회원가입할 때 비밀번호를 암호화하여 데이터베이스에 저장한다.
  2. 로그인할 때 사용자가 입력한 비밀번호를 암호화하고 데이터베이스에 저장된 암호화된 비밀번호와 비교한다.

virtual 함수 사용하기

virtual() 함수는 문서 객체에 실제로 저장되는 속성이 아닌 가상의 속성을 지정할 수 있다.

문서 객체에는 hashed_password 라는 속성이 있고, 스키마에는 password 라는 가상 속성이 있다고 가정하자. 그러면 사용자를 추가하거나 인증할 때 password 가상 속성에 접근하지만 실제 데이터베이스에는 password 라는 가상 속성이 존재하지 않고 hashed_password 속성만 존재하는 것이다.

password 가상 속성에 값을 넣은 후 문서 객체를 저장할 때 set() 메소드로 지정한 함수가 필요한 작업을 수행하며, 문서 객체를 조회할 때는 get() 메소드로 지정한 함수가 실행된다.

뭔소리지?

 

스키마 객체의 virtual() 함수 사용법 알아보기
UserSchema
        .virtual("info")	//1
        .set(info => {		//2
            let splitted = info.split(" ")
            this.id = splitted[0]
            this.name = splitted[1]
        })
        .get(() => {		//3
            return `${this.id} ${this.name}`
        })
  1. 가상 속성 info 를 스키마에 설정한다.
  2. info 에 값을 넣었을 때 실행할 메소드를 정의한다.
  3. info 에 접근했을 때 실행할 메소드를 정의한다.

일단 위와 같이 작성하고 실행시키면 ValidationError 가 난다.

Arrow Function이 아닌 function 키워드로 콜백 함수를 등록하면 잘 실행된다.


UserSchema 위에 console.log(this) , 콜백 함수 첫 줄에 console.log(this) 를 작성하고, function 키워드로 콜백 함수를 등록한 것과 Arrow Function으로 콜백 함수를 등록한 것의 결과를 비교해 보았다.

  • function : 첫 번째 this는 전역 객체, 두 번째 this는 UserSchema 객체를 가리키는 것 같다.
  • Arrow Function : 둘 다 전역 객체를 가리키는 것 같다.

this 바인딩 문제인 것이 확실해 보이는데... 관련 자료를 찾아봐야겠다.


비밀번호 암호화하여 저장하는 코드 적용하기

sudo npm install crypto --save

const crypto = require("crypto")
let encrypted = crypto.createHmac("sha1", inSalt).update(plainText).digest("hex")

 

하..........왜케 이해가 안되냐.....재미도없고.......

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함