Skip to content

이번 포스에서는 비교적 큰 사이즈의 바이너리 파일을 저장하는 메커니즘인 GridFS에 대해 알아보도록 하겠습니다.

예를 들어, 블로그 같이 글을 작성하는 사이트를 만들어 DB와 연동할 경우 텍스트만이 아닌 이미지, 또는 특정 어플리케이션의 바이너리를 저장해야 할 것입니다. MongoDB는 이러한 바이너리 파일을 효율적으로 관리하는 메커니즘을 제공하는데 이것이 GridFS입니다.

GridFS를 사용해서 파일을 저장해야 하는 이유를 들면 다음과 같습니다:

  • GridFS는 MongoDB를 위해 설정한 replication이나 autosharding을 활용합니다. 이는 패일오버(Failover) 및 스케일아웃(Scale-out)을 하는데 매우 쉽습니다 (Replication과 Autosharding에 대해서는 추후 자세히 다루도록 하겠습니다).
  • GridFS는 업로드 시 (NTFS, FAT 등과 같은) 파일시스템에 대한 문제를 말끔히 해결해 줍니다. 예를 들어, 이러한 파일시스템이 문제가 될 수 있는 경우는 동일한 디렉터리 내에 엄청난 개수의 파일들을 저장하는 일입니다.
  • GridFS를 이용하면 파일 로컬성(Locality)에 대해 매우 유리합니다. 이는, MongDB가 파일 업로드 시 파일의 크기를 2GB 크기의 덩어리로 분할하기 때문입니다.

그러면 이제부터 실질적인 연습을 해 보도록 하겠습니다.

(MacOS 환경으로 설명이 진행되겠지만 Windows도 방식은 동일합니다. 즉, Unix 명령으로 진행되지만 Windows에서도 쉽게 따라할 수 있으리라 생각됩니다.)


GridFS 시작하기: mongofiles

GridFS를 시작하는 가장 쉬운 방법은 mongofiles 유틸리티를 이용하는 것입니다. 이 유틸리티는 MongoDB 설치 시 다운로드 한 폴더 내에 있습니다.

예를 들면 다음과 유사한 경로입니다:

/mongodb-osx-x86_64-version/bin/mongofiles

mongofiles는 MacOS(Unix 실행파일)나 Windows(.exe) 또는 Linux 등의 OS에 따라 확장자가 다르게 표시됩니다.

예를 들면 MacOS의 경우, 다음 그림과 같이 Unix 실행파일로 표시됩니다.

mongofiles를 사용할 경우에도 MongoDB 서버가 실행되고 있어야 합니다.

우선 mongofiles의 헬프 리스트를 확인해 보도록 하겠습니다:

$ mongofiles --help
Browse and modify a GridFS filesystem.

usage: mongofiles [options] command [gridfs filename]
command:
  one of (list|search|put|get)
  list - list all files.  'gridfs filename' is an optional prefix
         which listed filenames must begin with.
  search - search all files. 'gridfs filename' is a substring
           which listed filenames must contain.
  put - add a file with filename 'gridfs filename'
  get - get a file with filename 'gridfs filename'
  delete - delete all files with filename 'gridfs filename'
options:
  --help                                produce help message
  -v [ --verbose ]                      be more verbose (include multiple times
                                        for more verbosity e.g. -vvvvv)
  --version                             print the program's version and exit
  -h [ --host ] arg                     mongo host to connect to ( <set
                                        name>/s1,s2 for sets)
  --port arg                            server port. Can also use --host
                                        hostname:port
  --ipv6                                enable IPv6 support (disabled by
                                        default)
  -u [ --username ] arg                 username
  -p [ --password ] arg                 password
  --authenticationDatabase arg          user source (defaults to dbname)
  --authenticationMechanism arg (=MONGODB-CR)
                                        authentication mechanism
  --dbpath arg                          directly access mongod database files
                                        in the given path, instead of
                                        connecting to a mongod  server - needs
                                        to lock the data directory, so cannot
                                        be used if a mongod is currently
                                        accessing the same path
  --directoryperdb                      each db is in a separate directly
                                        (relevant only if dbpath specified)
  --journal                             enable journaling (relevant only if
                                        dbpath specified)
  -d [ --db ] arg                       database to use
  -c [ --collection ] arg               collection to use (some commands)
  -l [ --local ] arg                    local filename for put|get (default is
                                        to use the same name as 'gridfs
                                        filename')
  -t [ --type ] arg                     MIME type for put (default is to omit)
  -r [ --replace ]                      Remove other files with same name after
                                        PUT

간단한 텍스트 파일을 생성하고 이 파일을 GridFS를 통해 업로드하는 방법은 다음과 같습니다:

$ echo "Hello, world" > foo.txt
$ mongofiles put foo.txt
connected to: 127.0.0.1
added file: { _id: ObjectId('531885f2a743d75dbe96fd17'), filename: "foo.txt", chunkSize: 262144, uploadDate: new Date(1394116082899), md5: "a7966bf58e23583c9a5a4059383ff850", length: 13 }
done!

Hello, world라는 문자열을 갖는 텍스트 파일 foo.txt 파일을 생성하였고, 이 파일은 mongofiles를 실행한 폴더 내에서 찾을 수 있을 것입니다.

이제 list 옵션을 통해 업로드가 되었는지 확인해 보도록 하겠습니다:

$ mongofiles list
connected to: 127.0.0.1
foo.txt 13

이제 MongoDB에 저장된 파일을 다운로드 하기에 앞서 foo.txt 파일을 삭제하겠습니다.

폴더 내에서 delete 키를 사용해서 직접 삭제해도 괜찮고 명령을 통해 삭제해도 상관없습니다:

$ rm foo.txt

이제 폴더 내에는 foo.txt 파일이 더이상 존재하지 않습니다. MongDB에 업로드 된 foo.txt를 다운로드 해 보도록 합니다:

$ mongofiles get foo.txt
connected to: 127.0.0.1
done write to: foo.txt

mongofiles가 실행된 폴더의 파일을 살펴보면 foo.txt 파일이 보일 것입니다.

이렇게 파일이 업로드 된 후, MongoDB 명령 쉘에서 컬렉션 리스트를 살펴보면:

> db.getCollectionNames()
[ "fs.chunks", "fs.files", "school", "schools", "system.indexes" ]

fs.chunksfs.files 두 개의 컬렉션이 생성되어 있음을 확인할 수 있을 것입니다.

우선 fs.chunks 컬렉션의 내용을 살펴보면:

> db.fs.chunks.find().pretty()
{
  "_id" : ObjectId("531885f2598bf40a9446eede"),
  "files_id" : ObjectId("531885f2a743d75dbe96fd17"),
  "n" : 0,
  "data" : BinData(0,"SGVsbG8sIHdvcmxkCg==")
}

다른 컬렉션과 마찬가지로 _id가 부여되고 여기에 더하 파일 아이디 files_id가 함께 부여됩니다.

data는 파일 덩어리를 구성하는 바이너리 데이터터를 포함합니다.

이제 fs.files 컬렉션의 도큐먼트를 살펴보도록 하겠습니다:

> db.fs.files.find().pretty()
{
  "_id" : ObjectId("531885f2a743d75dbe96fd17"),
  "filename" : "foo.txt",
  "chunkSize" : 262144,
  "uploadDate" : ISODate("2014-03-06T14:28:02.899Z"),
  "md5" : "a7966bf58e23583c9a5a4059383ff850",
  "length" : 13
}

fs.files key 값들에 대해 자세하게 알아보도록 하겠습니다:

  • _id

    오브젝트 아이디입니다. 이 아이디는 fs.files 컬렉션과 fs.chunks 컬렉션이 공유합니다.

  • filename

    저장된 파일의 이름입니다.

  • chunkSize

    파일을 구성하는 각 덩어리(chunk)의 크기이며 바이트(byte)를 의미합니다. 기본적으로 256 KB이나 필요에 따라 조정할 수 있습니다.

  • uploadDate

    파일이 업로드 된 날짜입니다.

  • md5

    파일 내용의 검사 합(checksum)이며, 서버 측에서 생성됩니다. md5는 일반적으로 파일의 무결성 검사를 위한 암호화 해시(hash) 함수 중 하나입ㄴ다. md5에 대해 자세히 알고 싶다면 여기를 클릭합니다.

  • length

    파일 내용의 전체 크기이며 단위는 바이트(byte)입니다.

꽤 간단하지 않나요? 지금까지 예제를 통해 mongofiles의 옵션 --help, put, list, get에 대해서 살펴보았습니다.