Chapter - MongoDB Basic
NoSQL Database?
MongoDB는 NoSQL 데이터베이스이다. NoSQL은 매우 넓은 범위에서 사용하는 용어로 관계형 테이블의 레거시한 방법을 사용하지 않는 데이터 저장소를 말한다.
NoSQL 데이터베이스에서는 데이터를 행과 열이 아닌, 체계적인 방식으로 저장한다. NoSQL 데이터베이스의 예는 도서카드 목록함에서 MongoDB 같은 보다 정교한 데이터 저장소에 이르기까지 다양한 개념을 뜻한다. 따라서 NoSQL이 의미하는 데이터베이스의 범위가 너무 넓기 때문에, 좀 더 좁혀 이해하겠다.
MongoDB는 NoSQL 도큐먼트 데이터베이스이다. MongoDB는 데이터를 도큐먼트의 형태로 저장한다.
도큐먼트는 컬레션에 저장되며, 이것이 MongoDB가 NoSQL 도큐먼트 데이터베이스로 분류되는 이유이다.
정리하자면, MongoDB 데이터베이스는 NoSQL 도큐먼트 데이터베이스이다.
NoSQL 기반의 비관계형 데이터베이스는 다음과 같은 경우에 많이 사용한다.
- 비구조적인 대용량의 데이터를 저장하는 경우
NoSQL 데이터베이스는 관계에 중점을 둔 SQL 데이터베이스보다 자유로운 형태로 데이터를 저장할 수 있으므로 필요에 따라서 데이터 유형을 추가할 수 있다. 소프트웨어 개발에 정형화 되지 않는 많은 양의 데이터가 필요한 경우, NoSQL이 효율적일 수 있다. - 클라우드 컴퓨팅 및 저장공간을 최대한 활용하는 경우
NoSQL은 데이터베이스를 클라우드 기반으로 쉽게 분리 할 수 있도록, 저장 공간을 효율적으로 사용한다. 시스템이 커지면서 DB를 증설해야 하는 시점이 오면, SQL에서는 수직적 확장의 형태로 증설한다. 수직적으로 확장된 데이터베이스는 관리가 어려워질 수 있는데에 반해, NoSQL은 수평적 확장의 형태로 증설하므로, 이론상 무한대로 서버를 계속 분산시켜 DB를 증설할 수 있다. - 빠르게 서비스를 구추갛고 데이터 구조를 자주 업데이트 하는 경우
NoSQL의 경우 스키마를 미리 준비할 필요가 없어서, 개발을 빠르게 해야하는 경우에 적합하다. 시장에 빠르게 프로토타입을 출시해야 하는 경우나, 소프트웨어 버전별로 많은 다운타임(데이터베이스의 서버를 오프라인으로 전환하여 작업하는 시간) 없이 데이터 구조를 자주 업데이트 해야하는 경우에는 일일이 스키마를 수정해주어야 하는 관계형 데이터베이스 보다 NoSQL 기반의 비관계형 데이터베이스가 더 효율적이다.
Atlas Cloud
MongoDB에서는 아틀라스(Atlas)로 클라우드에 데이터베이스를 설정한다.
아틀라스는 GUI와 CLI로 데이터를 시각화, 분석, 내보내기, 그리고 빌드하는 데에 사용할 수 있다. 아틀라스 사용자는 클러스터를 배포 할 수 있고, 클러스터는 그룹화된 서버에 데이터를 저장한다.
인스턴스들의 모임을 클러스터라고 하며 하나의 시스템처럼 작동한다.
단일 클러스터에서 각각의 인스턴스는 동일한 복제본을 가지고 있고 이 모음을 레플리카 세트라고 한다.
클러스터를 이용하여 배포할 경우, 이는 자동으로 레플리카 세트를 생성한다.
이 서버는 레플리카 세트(Replica set)로 구성되어 있고, 레플리카 세트는 동일한 데이터를 저장하는 몇 개의 연결된 MongDB 인스턴스의 모음이다.
- 인스턴스는 특정 소프트웨어를 실행하는 로컬 또는 클라우드의 단일 머신이다. 이 경우에서 인스턴스는 클라우드에서 실행되는 MongoDB이다.
레플리카 세트는 데이터의 사본을 저장하는 인스턴스의 모음이다.
인스턴스 중 하나에 문제가 발생하더라도, 데이터는 그대로 유지되며, 나머지 레플리카 세트의 인스턴스에 저장된 데이터로 작업 가능하다.
도큐먼트나 컬렉션을 변경할 경우, 변경된 데이터의 중복 사본이 레플리카 세트에 저장된다.
이 설정 덕분에 레플리카 세트의 인스턴스 중 하나에 문제가 발생하더라도 데이터는 그대로 유지되며, 레플리카 세트의 애플리케이션에서 나머지 작업을 할 수 있다. 이 과정을 위해 클러스터(서버 그룹)를 배포하면, 자동으로 레플리카 세트가 구성된다.
용어 정리
- 레플리카 세트
동일한 데이터를 저장하는 소수의 연결된 머신을 뜻한다. 레플리카 세트 중 하나에 문제가 발생하더라도, 데이터를 그대로 유지할 수 있다. - 인스턴스
로컬 또는 클라우드에서 특정 소프트웨어를 실행하는 단일 머신, MongoDB에서는 데이터베이스이다. - 클러스터
데이터를 저장하는 서버 그룹으로 여러 대의 컴퓨터를 네트워크를 통해 연결하여 하나의 단일 컴퓨터처럼 동작하도록 제작한 컴퓨터를 뜻한다.
MongoDB Document
도큐먼트는 객체와 같이 데이터를 필드 - 값 쌍(Field - Value pair)으로 저장하고 구성한다.
도큐먼트에서 필드는 데이터의 고유한 식별자이고, 값은 주어진 식별자와 관련된 데이터를 뜻한다.
{
<field> : <value>,
<field> : <value>,
"name" : "jortier",
"title" : "TIL",
"age" : "???"
}
// 객체와 생긴게 똑같다?
이러한 도큐먼트의 모음을 컬렉션이라고 한다. 그리고 데이터베이스는 여러개의 컬렉션으로 구성된다.
용어 정리
- 도큐먼트(Document)
필드 - 값 쌍으로 저장된 데이터 - 필드(Field)
데이터 포인트를 위한 고유한 식별자 - 값(Value)
주어진 식별자와 연결된 데이터 - 컬렉션(Collection)
MongoDB의 도큐먼트로 구성된 저장소이다.
일반적으로 도큐먼트 간의 공통 필드가 있다.
데이터베이스 당 많은 컬렉션이 있고, 컬렉션 당 많은 도큐먼트가 있을 수 있다.
JSON vs. BSON
shell을 이용하여 도큐먼트를 조회하거나 업데이트 할 때, 도큐먼트는 JSON(JavaScript Object Notation)형식으로 출력된다. JSON 형식으로 도큐먼트를 작성하기 위해서는, 다음과 같은 조건을 만족해야 한다.
- {} 중괄호로 도큐먼트가 시작하고, 끝나야 한다.
- 필드와 값이 콜론(:)으로 분리되야 하고, 필드와 값을 포함하는 쌍은 쉼표(,)로 구분된다.
- 문자열인 필드도 쌍따옴표("")로 감싸야 한다.
도큐먼트는 모두 중괄호({})로 시작하고 끝나고, 각 필드와 값의 상은 쉼표(,)로 구분된다.
그리고 필드와 값의 사이에는 콜론(:)이 작성되어 있고, 각 필드는 쌍따옴표("")를 사용하여 문자열로 표현했다.
이 조건중 어느 하나라도 충족되지 않는다면, 이는 유효한 도큐먼트가 아니다.
JSON 형식은 읽기 쉽고, 많은 개발자들이 사용하기 편리한 형태를 가지고 있다.
그렇기 때문에 JSON 형식은 데이터를 저장하는 좋은 방법 중 하나이다.
그러나 JSON의 형태로 데이터를 저장할 때 단점도 존재한다.
JSON은 텍스트 형식이기 때문에 읽기 쉽지만, 파싱이 느리고 메모리 사용이 비효율적이다.
그리고 JSON은 기본 데이터 타입만을 지원하기 때문에, 사용할 수 있는 데이터 타입에 제약이 있다.
이런 문제점을 해결하기 위한 방안으로 BSON(Binary JSON) 형식으로 도입했다.
BJSON은 컴퓨터의 언어에 가까운 이진법에 기반을 둔 표현법이다. 따라서 JSON보다 메모리 사용이 효율적이며 빠르고, 가볍고, 유연하다. 뿐만 아니라, BSON의 사용으로 더 많은 데이터 타입을 사용할 수 있다.
MongoDB는 JSON형식으로 작성된 것은 무엇이든 데이터베이스에 추가할 수 있고, 쉽게 조회할 수 있다.
그러나 그 내부에서 속도, 효율성, 유연성의 장점이 있는 BSON으로 데이터를 저장, 사용하고 있다.
Importing & Exporting
데이터를 가져오거나(import), 내보내는(export) 경우에 따라 효율적인 데이터 형식이 존재한다.
MongDB의 데이터는 BSON의 형태를 저장이 되고, 보통 읽기 쉬운 JSON의 형태로 출력된다.
만약 단순히 백업 저장을 하기 위해서라면 가볍고 빠른 BSON의 형태를 사용하는 편이 좋다.
그러나 데이터를 내보낸 후, 조회를 하거나 출력을 해야한다면 사람이 읽기 쉬운 JSON의 형식이 바람직하다.
그래서 조건에 따라 가져오거나 내보낼 때 사용 가능한 명령어가 각각 존재한다.
JSON 형식으로 데이터를 가져오고 내보내기 위한 명령어는 mongoimport와 mongoexport가 있다.
BSON 형식으로 가져오고 내보내기 위한 명령어 mongorestore와 mongodump가 있다.
// Export data in BSON
mongodump --uri "<Atlas Cluster URI>"
// Export data in JSON
mongoexport --uri "<Atlas Cluster URI>"
--collection=<collection name>
--out=<filename>.json
이를 사용하기 위해서 Atlas Cluster URI가 필요하다.
해당 URI는 일반 웹의 URI와 형식이 같고, username, password, cluster 주소로 이루어져있다.
mongodump를 하는 경우에는 별다른 쿼리가 없지만, mongoexport를 하는 경우에는 해당 데이터베이스의 컬렉션 이름, 파일 이름까지 정확하게 작성해줘야 한다.
// Import data in BSON dump
mongorestore --uri "<Atlas Cluster URI>"
--drop dump
// Import data in JSON
mongoimport --uri "<Atlas Cluster URI>"
--drop=<filename>.json
아까와 마찬가지로 URI를 사용해서 작성되며, 기존에 있는 데이터를 삭제하기 위한 옵션인 drop 쿼리문은 선택적으로 사용할 수 있다.
Chapter - MongoDB CRUD
CREATE - insert 명령어
모든 MongoDB 도큐먼트는 모든 도큐먼트가 _id필드를 기본으로 반드시 가지고 있어야 한다는 공통점이 있다.
이 _id필드의 값은 각 도큐먼트를 구별하는 역할을 한다.
도큐먼트 내 필드와 값이 똑같다 할지라도, _id 값이 다르면 서로 다른 도큐먼트로 간주한다.
반면에, 도큐먼트 내 필드와 값이 다르다고 하더라도, _id 값이 같다고 하면 서로 같은 도큐먼트로 여겨 에러를 발생시킨다.
따라서 각 도큐먼트는 고유한 _id 값을 가지고 있어야 한다. 새로운 도큐먼트를 추가할 때, _id 값에 임의적으로 고유한 값을 생성해서 사용할 수도 있지만 보통은 ObjectId 타입(12byte, 24char)의 값으로 사용한다.
또한 도큐먼트를 추가할 때, _id 필드와 값을 특정하지 않았다면, 자동적으로 _id 필드가 생성되고 값에 ObjectId 타입이 할당된다.
Insert 명령어를 사용하면, 주어진 도큐먼트 배열의 인덱스 순서로 작업이 실행된다.
그러나 ordered를 추가하면, 순서에 상관없이 고유한 _id를 가진 도큐먼트는 모두 컬렉션에 삽입된다.
READ - find 명령어
find 명령어는 전체가 실습으로 이루어져 있고 따로 설명이 없기에 추후에 실습을 진행하면서 따로 정리하자.
update
updateOne은 주어진 기준에 맞는 다수의 도큐먼트 중 첫번째 도큐먼트 하나만 업데이트한다.
updateMany는 쿼리문과 일치하는 모든 도큐먼트를 업데이트한다.
delete
deleteOne은 주어진 기준에 맞는 다수의 도큐먼트 중 첫번째 도큐먼트 하나를 삭제한다.
deleteMany는 쿼리문과 일치하는 모든 도큐먼트를 삭제한다.
이번 챕터는 대부분 실습으로 이루어져 있다. 나중에 코드스테이츠에서 제공하는 실습을 해보고 공식문서를 통해 정리하도록 하겠다.
Chapter - Advanced CRUD
비교 연산자
지정된 값이 서로 같거나 같지 않은지 여부를 확인할 수 있는 $eq(Equal to)와 $ne(Not Equal to)가 있으며, $gt(Greater Than)와 $lt(Less Than)로 주어진 값보다 큰 지, 작은 지를 비교할 수 있다. $gte(Greater Than Equal to)와 $lte(Less Than or Equal to)로는 크거나 동일한 지, 작거나 동일한 지를 비교할 수 있다.
{field : {operator : value}}와 같은 문법으로 작성하여 사용한다.
Equal to | === | $eq |
Not Equal to | !== | $ne |
Greater Than | > | $gt |
Less Than | < | $lt |
Greater Than or Equal to | >= | $gte |
Less Than or Equal to | <= | $lte |
쿼리 연산자는 데이터베이스 내에서 데이터를 찾는 다양한 방법을 제공한다.
비교연산자를 사용하면 특정 범위 내에서 데이터를 찾을 수 있다.
비교 연산자의 구문은 field : 뒤에 중괄호를 묶인 연산자, 콜론, 값이 온다.
비교 연산자를 지정하지 않으면 $eq가 기본 연산자로 사용된다.
논리 연산자
MQL(MongoDB Query Language)에는 $and, $or, $nor, 그리고 $not이라는 네 가지 논리 연산자가 있다.
- $and는 지정된 모든 쿼리 절을 충족하는 도큐먼트를 반환한다.
- $or는 쿼리 절 중에 하나라도 일치하는 도큐먼트가 있다면 해당 도큐먼트를 반환한다.
- $nor는 모든 절과 일치하지 않는 도큐먼트를 반환한다.
- $not은 $nor의 단일 버전같이 뒤의 조건을 만족하지 않는 모든 도큐먼트를 반환한다.
$and, $or 및 $nor는 유사한 구문을 사용하며 연산자가 작동할 절의 배열 앞에 위치한다. $not은 조금 다른 방식으로 단순히 뒤에 오는 조건을 부정하기 때문에 배열 구문이 필요하지 않는다.
$and는 특별한 연산자다. 왜냐하면 기본적으로 쿼리에 이미 존재하기 때문이다. 쿼리를 실행할 때 기본적으로 $eq가 함축되어 있는 것처럼 유사한 논리가 $and에도 있다. $and는 쿼리에 대해 참이어야 하는 여러 기준이 있는 경우 기본적으로 쿼리에 이미 함축되어 있다. 함축된 $and의 또 다른 예는 동일한 필드에 여러 조건을 적용할 때 볼 수 있다.
$and는 언제 명시적으로 포함할까? 일반적인 규칙으론 쿼리에 동일한 연산자를 두 번 이상 포함해야 할 때 $and를 명시한다.
논리 연산자에 대해 정리하자면
- 논리 연산자의 구문은 $와 연산자 이름, 절로 구성된 배열이다.
- $not의 경우에는 배열이 아닌 절이 온다.
- 논리 연산자가 지정되지 않은 경우 $and가 기본 연산자로 사용된다.
- 동일한 연산자를 쿼리에 두 번 이상 포함해야 하는 경우, $and를 명시적으로 사용해야한다.
표현 연산자
- $expr를 사용하여 쿼리 내에서 집계 표현식(Aggregation Expression)을 사용할수 있다.
- $expr를 이용해 변수와 조건문을 사용할 수 있다.
- $expr를 사용하여 같은 도큐먼트 내의 필드들을 서로 비교할 수 있다.
(확실히 실습을 따라하지 않으면 내용이 부실하다.)
- 표현 연사자로 더욱 복잡한 쿼리를 작성할 수 있으며 동일한 도큐먼트 내의 필드들을 비교할 수 있다.
- $는 필드의 값을 참조할 수 있다.
배열 연산자
- $size 배열 연산자는 지정된 배열 필드가 주어진 길이와 정확히 일치하는 모든 도큐먼트들이 있는 커서를 반환한다.
- $all 배열 연산자는 지정된 배열 필드의 배열 순서와 관계없이 지정된 모든 요소가 포함된 모든 도큐먼트들이 있는 커서를 반환한다.
- 배열 연산자를 쓰지않고 배열 필드를 쿼리하는 경우 배열을 지정한다면 해당 배열과 정확히 일치 하는 배열을 가진 도큐먼트를 찾는다.
- 문자열로 요소를 지정한다면 해당 요소가 배열에 포함된 모든 도큐먼트를 찾는다.
배열 연산자와 Projection
- 쿼리 조건과 일치하는 항목을 반환할 때 프로젝션을 사용하여 특정한 필드만 결과로 가져올 수 있다.
- 1과 0을 사용하여 필드를 포함할지 여부를 지정할 수 있지만 기본적으로 포함되는 _id 필드를 명시적으로 제외하는 경우 이외에는 필드를 포함하거나 제외하는 등 혼용해서 0과 1을 사용할 수 없다.
- $elemMatch는 find 명령의 쿼리 및 프로젝션 부분에서 모두 사용할 수 있는 배열 연산자이다.
- $elemMatch는 지정된 쿼리와 일치하는 요소가 하나 이상있는 배열 필드를 가지고 있는 도큐먼트를 찾을 수 있다.
- 지정된 기준과 일치하는 요소가 하나 이상있는 배열 요소만 프로젝션 한다.
배열과 서브 도큐먼트 쿼리하기
- MQL에서 점 표기법을 사용하여 서브 도큐먼트의 요소를 참조할 수 있다.
- 배열에 점 표기법을 사용하려면 배열에서 요소의 위치를 지정해야 한다.
Chapter - Aggregation Framework
- Aggregation Framework는 MongoDB에서 데이터를 쿼리하는 가장 간단한 방법 중 하나이다.
- MQL을 사용하는 모든 작업을 Aggregation Framework를 사용하여 수행 할 수도 있다.
- Aggregation Framework에서는 파이프라인의 단계에 따라 데이터를 처리할 수 있다.
Chapter - Index
저장된 데이터의 크기가 커질수록, 쿼리의 결과를 얻기까지 기다리는 시간은 점점 늘어난다. 쿼리의 속도가 느려지는 경우(slow query)를 해결하기 위한 방법 중 하나로 인덱싱을 사용할 수 있다.
Index?
인덱스란 무엇일까? 책 부록의 색인(인덱스) 페이지를 예로 볼 수 있다. 색인은 찾고자 하는 대상(이름 또는 주제)이 가나다순으로 나열되어 있고, 각 대상의 위치가 표시되어 있다. 이 위치를 참조하면 책 전체를 살필 필요 없이, 해당 대상이 언급된 페이지로 바로 이동할 수 있다. 데이터베이스의 인덱스도 이와 유사하다.
createIndex
인덱스를 생성하기 위해서는 createIndex()라는 메소드를 사용한다. createIndex의 파라미터로 인덱스를 적용할 필드를 전달한다. 이에 따른 값을 1로 지정하면 오름차순으로, -1로 하면 내림차순으로 정렬한다. 이때 필드 명을 1개를 지정하면 단일 필드 인덱스, 필드명이 2개 이상이면 다중 인덱스(compound index)이다.
db.collection_name.createIndex({<fieldName> : 1, <fieldName> : -1})
인덱스에는 이 외에도 createIndex() 메소드의 두번째 파라미터로 속성을 추가할 수 있다.
db.collection_name.createIndex({<fieldName> : 1}, {<attributeProperty> : true})
인덱스에 적용할 수 있는 속성은 다음과 같다.
- Unique(유일함 속성)
_id 필드와 같이 컬렉션에 단 한개의 값만 존재 할수 있는 속성이다. 이 속성을 각 인덱스에 적용할 수 있다. - Partial(부분적 속성)
도큐먼트의 조건을 정해 일부 도큐먼트에만 인덱스를 적용한다. 이 속성을 사용하면, 필요한 부분에만 인덱스를 사용하여 효율적으로 쿼리를 할 수 있다. - TTL(Time-To-Live)
이 속성은 Date 타입 혹은 Date배열 타입의 필드에 적용가능하다. 이 속성을 사용하면 특정 시간이 지난 후, 도큐먼트를 컬렉션에서 삭제한다.
getIndexes, dropIndex
생성된 인덱스를 조회할 때는 getIndexes 메소드를 사용한다.
db.collection_name.getIndexes()
/*
* 결과값
*
* 인덱스를 생성하지 않았을 때는 이렇게 _id값만 인덱스로 생성되어 있음
* [{
* "v" : 2,
* "key" : {
* "_id" : 1
* },
* "name" : "_id_"
* }]
*/
만약 여기서 인덱스를 createIndex로 생성한다면, 생성된 인덱스는 배열에 삽입된다.
생성된 인덱스를 삭제할 때는 dropIndex 메소드를 사용한다.
db.collection_name.dropIndex(name)
// or
db.collection_name.dropIndex({<field>:1})
인덱스를 하나씩 생성하거나 삭제할 때에는 위의 두 가지 방법을 사용할 수 있다.
getIndexes 메소드를 사용하면, 해당 인덱스마다 "name"이라는 필드를 지정할 수 있다는 것을 확인할 수 있다.
따라서 dropIndex 메소드의 파라미터로 name 필드의 값을 입력하면 된다.
삭제하려는 인덱스의 필드와 지정했던 값을 파라미터로 전달해주어도 삭제가 가능하다.
모든 인덱스를 삭제하고 싶을 때에는 다음과 같이 복수형의 메소드를 사용한다.
db.collection_name.dropIndexes()
이번 코드스테이츠 코스는 이론과 실습이 환상의 조화를 이루는 챕터였다.
현재 2차 백신 2일차인데 몸살이 와서 실습을 하지 못했다.
조금 컨디션이 회복되면 무조건 실습을 해봐야겠다.