Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

Rails 앱에서 DynamoDB 활용하기: NoSQL 데이터베이스의 이해와 Dynamoid 젬 가이드

DynamoDB는 핵심적으로 키-값(key-value)과 문서(document) 방식의 데이터 구조를 제공하는 NoSQL 데이터베이스입니다. 이 글을 읽는 대부분의 개발자는 전통적인 관계형 데이터베이스(RDBMS)에 익숙할 것입니다. RDBMS는 잘 정의된 스키마를 바탕으로 데이터를 테이블, 행(row), 열(column)로 정규화하고, 외래 키(foreign key)를 통해 테이블 간 '관계'를 형성합니다.

반면 DynamoDB는 스키마리스(schemaless)입니다. 모든 테이블에는 기본 키(primary key)가 반드시 필요하지만, 그 외 비키(non-key) 속성에는 어떠한 제약도 존재하지 않습니다. 그렇다면 이런 특성은 언제 유리할까요? Amazon이 DynamoDB를 만들게 된 배경부터 살펴보겠습니다.

Amazon은 2012년 DynamoDB를 공개하겠다고 발표했습니다. 사실 Dynamo는 2004년 연말 쇼핑 시즌에 폭주하는 트래픽으로 인해 여러 내부 서비스가 장애를 겪으면서 개발이 시작되었습니다.

DynamoDB를 도입하면 좋은 경우

DynamoDB는 수천 명 이상의 동시 접속자를 지원하고, 초당 수만 건의 읽기/쓰기를 처리해야 하는 애플리케이션에 매우 적합합니다. 소셜 네트워크, 게임, IoT 기기가 대표적인 예이며, 실제로 Lyft, Airbnb, Redfin 같은 기업들이 DynamoDB를 활용하고 있습니다.

처음에는 트래픽이 적고 성능이 당장 필요하지 않더라도, 장기적으로 대규모 확장을 목표로 한다면 Postgres 같은 전통적인 SQL 데이터베이스보다 처음부터 DynamoDB로 시작하는 것이 유리할 수 있습니다. 다만 사용자가 복잡한 분석 쿼리를 자주 실행하는 데이터 웨어하우스나 OLAP 애플리케이션이라면 스키마리스 데이터베이스는 최선의 선택이 아닙니다. 또한 RDBMS가 저장(storage)에 최적화되어 있는 반면, DynamoDB는 컴퓨팅(compute)에 최적화되어 있습니다. Amazon은 DynamoDB가 어떤 규모에서도 한 자릿수 밀리초(millisecond) 성능을 제공하며, 하루 10조 건 이상의 요청을 처리하고, 초당 2,000만 건 이상의 피크 요청까지 감당할 수 있다고 밝히고 있습니다.

그뿐만 아니라 AWS는 데이터 보관과 보안이 중요한 애플리케이션에 DynamoDB를 더욱 매력적으로 만들어 주는 다양한 기능을 제공합니다.

  • 저장 데이터 암호화(Encryption at rest): DynamoDB는 기본 키를 포함한 모든 데이터를 암호화된 테이블로 보호합니다. 사용자는 세 가지 키 옵션 중에서 선택할 수 있습니다. AWS 소유 CMK(AWS-owned customer master key), AWS 관리형 CMK(키가 사용자 계정에 저장되고 AWS Key Management Service가 관리), 그리고 고객 관리형 CMK(사용자가 직접 생성·소유·관리)입니다. 흥미로운 점은 테이블별로 서로 다른 옵션을 적용할 수 있다는 것입니다.

  • 온디맨드 백업 및 특정 시점 복구(Point-in-time recovery): DynamoDB는 지난 약 30일간 원하는 시점으로 테이블을 복원할 수 있습니다. 실수로 인한 쓰기나 삭제를 걱정할 필요가 없어진 것입니다!

DynamoDB의 기본 구성 요소

DynamoDB는 세 가지 핵심 구성 요소로 이루어져 있습니다.

  • Table(테이블): 아이템(item)들의 집합
  • Item(아이템): 속성(attribute)들의 집합
  • Attribute(속성): 더 이상 분해할 수 없는 기본 데이터 요소

예를 들어 임대 부동산 관리 애플리케이션을 만든다면, "Houses"라는 테이블에 다음과 같은 아이템들을 담을 수 있습니다.

{
  "HouseID": 1,
  "AddressLine1": "123 Main St",
  "City": "Atlanta",
  "State": "GA",
  "Zip": 30322,
  "Rented": true,
  "Tenant": "John Smith"
}

{
  "HouseID": 2,
  "AddressLine1": "456 Square St",
  "City": "Nashville",
  "State": "TN",
  "Zip": 37211,
  "Rented": true,
  "Tenant": "Mary Jane"
}

HouseID, AddressLine1, City, State, Zip, Rented, Tenant는 모두 아이템의 속성입니다. 편의상 아이템을 SQL의 row(행)에, 속성을 column(열)에 비유할 수 있지만, DynamoDB와 같은 NoSQL 데이터베이스는 SQL과 완전히 다른 존재이므로 두 가지를 비교하는 습관은 피하는 것이 좋습니다.

item에는 고유한 기본 키가 있어야 합니다(DynamoDB는 단일 파티션 키와 복합 기본 키를 모두 지원합니다). 하지만 HouseID를 제외하면 Houses 테이블은 스키마리스입니다. 따라서 전통적인 SQL 데이터베이스에서 Zipinteger 타입으로 미리 정의해야 하는 것과 달리, 속성과 데이터 타입을 사전에 선언할 필요가 없습니다.

인덱스가 DynamoDB에서 어떻게 작동하는지 궁금할 수 있습니다. 좋은 질문입니다! Houses 테이블의 rentedzip 속성에 인덱스를 걸고 싶다고 가정해 보겠습니다(즉, 우편번호가 주어지면 해당 지역의 임대 중인 매물을 모두 조회). SQL 데이터베이스라면 다음과 같은 마이그레이션을 작성했을 것입니다.

CREATE INDEX RentedAndZipIndex
ON Houses (rented, zip);

반면 DynamoDB에서는 세컨더리 인덱스(secondary index)를 설정해야 합니다. 이를 위해서는 파티션 키(partition key)와 정렬 키(sort key)를 지정해야 하며, 이 예제에서는 zip이 파티션 키, rented가 정렬 키가 됩니다. DynamoDB의 인덱스에는 배울 내용이 훨씬 많지만, 이 글에서는 여기까지만 다루겠습니다.

Ruby와 Ruby on Rails에서 DynamoDB 사용하기

AWS는 Java, JavaScript, Node.js, .NET, PHP, Python, Ruby 등 다양한 언어용 SDK를 제공합니다. Ruby에서 DynamoDB를 사용하는 방법은 두 가지입니다.

  1. DynamoDB 로컬 다운로드: 단순히 애플리케이션을 테스트하는 용도라면 훌륭한 선택입니다. 프로덕션 환경으로 전환할 때 로컬 엔드포인트를 제거하고 웹 서비스로 연결만 하면 됩니다.
  2. DynamoDB 웹 서비스 사용: AWS 액세스 키와 자격 증명(credentials)이 필요합니다.

DynamoDB와 상호작용하려면 CLI를 다운로드하는 것이 좋으며, 그렇지 않다면 콘솔이나 API를 직접 사용할 수도 있습니다. 이 시리즈를 진행하려면 Ruby와 Ruby용 AWS SDK가 설치되어 있어야 합니다.

Rails 프로젝트라면 Dynamoid라는 유용한 젬(gem)을 추가할 수 있습니다. 물론 AWS 자격 증명은 여전히 필요합니다.

Rails 예제

1단계: 환경 설정

Gemfile에 다음 젬들을 추가하고 bundle install을 실행합니다.

gem 'dynamoid'
gem 'aws-sdk'

다음으로 config/initializers 디렉터리에 aws.rb 파일을 새로 만들고 아래 내용을 추가합니다.

Rails 앱에서 DynamoDB 활용하기: NoSQL 데이터베이스의 이해와 Dynamoid 젬 가이드

참고: 자격 증명은 반드시 환경 변수(environment variable)로 관리하세요. 절대 코드에 하드코딩해서는 안 됩니다!

2단계: 문서(Document) 정의하기

이번 예제에서는 app/models 디렉터리에 다음과 같은 house.rb 파일을 생성했습니다.

Rails 앱에서 DynamoDB 활용하기: NoSQL 데이터베이스의 이해와 Dynamoid 젬 가이드

코드를 하나씩 살펴보겠습니다.

  • 테이블을 정의할 때는 반드시 include Dynamoid::Document를 포함해야 합니다.
  • 4번째 줄에서는 테이블 이름, 키, 용량 모드(capacity mode)를 정의합니다. Dynamoid 젬에는 합리적인 기본값이 있으므로 엄밀히 말하면 생략해도 되지만, 명시적으로 정의하면 기본값을 덮어쓰게 됩니다. read_capacitywrite_capacity도 설정할 수 있지만, capacity_modeon_demand로 지정했다면 AWS가 자동으로 확장·축소하므로 이 옵션들은 무시됩니다.
  • 그다음은 테이블의 필드를 정의하는 부분입니다. 모델의 모든 필드와 데이터 타입을 정의해야 하며, 타입을 생략하면 string으로 가정됩니다.

dynamoid 젬에는 테이블을 생성해 주는 편리한 rake 태스크도 함께 제공됩니다.

rake dynamoid:create_tables

3단계: 객체 다루기

일부 코드가 낯설게 느껴질 수 있지만, 좋은 소식이 있습니다! dynamoid 젬의 가장 큰 장점은 Active Record에서 익숙하게 사용하던 기능들이 거의 동일한 방식으로 동작한다는 점입니다.

연관 관계(Associations)

Dynamoid는 다양한 연관 관계를 지원합니다. has_many, has_one, has_and_belongs_to_many, belongs_to를 ActiveRecord에서 하듯 자연스럽게 정의할 수 있습니다. 임대 부동산 앱에 Lease라는 문서 테이블이 하나 더 있다고 상상해 보면, House 테이블에는 다음과 같이 정의할 수 있습니다.

has_one :lease

그리고 Lease 테이블에는 다음과 같이 정의합니다.

belongs_to :house

유효성 검사(Validations)

Dynamoid에는 ActiveModel 유효성 검사가 내장되어 있으므로 별도의 설정 없이 바로 사용할 수 있습니다.

콜백(Callbacks)

save, update, destroy 액션에 대해 before_ 또는 after_ 콜백을 사용할 수 있습니다. 예를 들어 lease.rb에 다음과 같이 정의할 수 있습니다.

before_save :validate_signature

생성(Create)

객체를 생성하는 문법 역시 ActiveRecord와 거의 동일합니다. 다음과 같이 작성할 수 있습니다.

h = House.new(address_line_1: "123 Main St", city: "Cool City", state: "Iowa", zip: 52302)
h.rented = true
h.save

혹은 간단히 .create 메서드를 사용할 수도 있습니다.

House.create(address_line_1: "123 Main St", city: "Cool City", state: "Iowa", zip: 52302)

쿼리(Querying)

짐작하셨겠지만, 쿼리 방식 역시 ActiveRecord와 똑같습니다.

  • .find: ID로 조회
  • .where: 원하는 개수만큼 일치 조건을 지정하여 조회
  • .find_by_x: x는 속성 이름

더 알아보기

Dynamoid 젬에는 배울 내용이 훨씬 더 많지만, 이 정도면 충분히 실험해 볼 수 있을 것입니다. 공식 GitHub 페이지를 참고하여 사용 가능한 기능들을 더 자세히 확인해 보시길 권합니다.

다음 글 예고

여기까지 상당히 많은 내용을 다뤘습니다! 이 글을 통해 'NoSQL'이 무엇을 의미하는지, 그리고 스키마리스 데이터베이스가 필요한 프로젝트에 DynamoDB가 왜 좋은 선택인지 조금 더 명확하게 이해하셨기를 바랍니다. 또한 Rails 프로젝트에서 dynamoid 젬을 활용하는 기본 설정 과정도 함께 살펴보았습니다.

시리즈의 다음 글에서는 DynamoDB를 로컬에 설정하고, Ruby SDK의 다양한 기능을 직접 실습해 보겠습니다.