자습서: Azure Cosmos DB에서 API for NoSQL 계정을 관리하는 데 JavaScript SDK를 사용하여 Node.js 웹앱 빌드

적용 대상: NoSQL

개발자는 NoSQL 문서 데이터를 사용하는 애플리케이션을 가지고 있을 수도 있습니다. Azure Cosmos DB에서 API for NoSQL 계정을 사용하여 이 문서 데이터를 저장하고 액세스할 수 있습니다. 이 Node.js 자습서에서는 Azure Cosmos DB의 API for NoSQL 계정에서 데이터를 저장하고 액세스하는 방법을 보여 줍니다. 이 자습서에서는 Microsoft Azure App Service의 Web Apps 기능에 호스트되는 Node.js Express 애플리케이션을 사용합니다. 이 자습서에서는 작업을 만들고 검색하고 완료할 수 있는 웹 기반 애플리케이션(Todo 앱)을 빌드합니다. 작업은 Azure Cosmos DB에 JSON 문서로 저장됩니다.

이 자습서에서는 Azure Portal을 사용하여 Azure Cosmos DB에서 API for NoSQL 계정을 만드는 방법을 보여 줍니다. 신용 카드 또는 Azure 구독이 없으면 다음을 수행할 수 있습니다.

  • Azure Cosmos DB 체험 계정을 설정합니다.
  • Node.js SDK 기반의 웹앱을 빌드하고 실행하여 데이터베이스와 컨테이너를 만듭니다.
  • 컨테이너에 항목 추가

이 자습서에서는 JavaScript SDK 버전 3.0을 사용하고 다음 작업을 다룹니다.

  • Azure Cosmos DB 계정 만들기
  • 새 Node.js 애플리케이션 만들기
  • Azure Cosmos DB에 애플리케이션 연결
  • 애플리케이션을 실행하고 Azure에 배포

필수 조건

이 문서의 지침을 따르기 전에, 다음 리소스가 있는지 확인합니다.

Azure Cosmos DB 계정 만들기

먼저 Azure Cosmos DB 계정을 만듭니다. 기존 계정이 있거나 이 자습서에 Azure Cosmos DB 에뮬레이터를 사용하고 있는 경우 새 Node.js 애플리케이션 만들기로 건너뛸 수 있습니다.

  1. Azure Portal 메뉴 또는 페이지에서 리소스 만들기를 선택합니다.

  2. Azure Cosmos DB를 검색합니다. 만들기>Azure Cosmos DB를 선택합니다.

  3. Azure Cosmos DB 계정 만들기 페이지의 Azure Cosmos DB for NoSQL 섹션 내에서 만들기 옵션을 선택합니다.

    Azure Cosmos DB는 여러 API를 제공합니다.

    • NoSQL, 문서 데이터용
    • PostgreSQL
    • MongoDB, 문서 데이터용
    • Apache Cassandra
    • 테이블
    • Apache Gremlin, 그래프 데이터용

    API for NoSQL에 대한 자세한 내용은 Azure Cosmos DB 시작을 참조하세요.

  4. Azure Cosmos DB 계정 만들기 페이지에서 새 Azure Cosmos DB 계정에 대한 기본 설정을 입력합니다.

    설정 Description
    구독 구독 이름 이 Azure Cosmos DB 계정에 사용할 Azure 구독을 선택합니다.
    리소스 그룹 리소스 그룹 이름 리소스 그룹을 선택하거나 새로 만들기를 선택한 후, 새 리소스 그룹에 고유한 이름을 입력합니다.
    어카운트 이름 고유 이름 Azure Cosmos DB 계정을 식별하는 이름을 입력합니다. URI를 만들기 위해 제공하는 이름에 documents.azure.com이 추가되므로 고유한 이름을 사용합니다. 이름에는 소문자, 숫자, 하이픈(-) 문자만 사용할 수 있습니다. 3~44자여야 합니다.
    위치 사용자와 가장 가까운 지역 Azure Cosmos DB 계정을 호스트할 지리적 위치를 선택합니다. 데이터에 가장 빨리 액세스할 수 있도록 사용자와 가장 가까운 위치를 사용합니다.
    용량 모드 프로비저닝된 처리량 또는 서버리스 프로비저닝된 처리량을 선택하여 프로비저닝된 처리량 모드에서 계정을 만듭니다. 서버리스를 선택하여 서버리스 모드에서 계정을 만듭니다.
    Azure Cosmos DB 체험 계층 할인 적용 적용 또는 적용 안 함 Azure Cosmos DB 무료 계층을 사용하는 경우 처음에는 1000RU/초 및 25GB의 스토리지가 계정에 무료로 제공됩니다. 체험 계층에 대해 자세히 알아보세요.
    총 계정 처리량 제한 선택 여부 이 계정에서 프로비전할 수 있는 총 처리량을 제한합니다. 이 한도는 프로비전된 처리량과 관련된 예기치 않은 요금을 방지합니다. 계정을 만든 후 이 한도를 업데이트하거나 제거할 수 있습니다.

    Azure 구독당 최대 1개의 무료 계층 Azure Cosmos DB 계정을 사용할 수 있으며 계정을 만들 때 옵트인해야 합니다. 무료 계층 할인을 적용하는 옵션이 표시되지 않으면 구독의 다른 계정에서 이미 무료 계층을 사용하도록 설정되어 있는 것입니다.

    Screenshot shows the Create Azure Cosmos DB Account page.

    참고 항목

    용량 모드서버리스를 선택한 경우 다음 옵션을 사용할 수 없습니다.

    • 무료 계층 할인 적용
    • 총 계정 처리량 제한
  5. 전역 배포 탭에서 다음 세부 정보를 구성합니다. 이 빠른 시작의 기본값을 그대로 둘 수 있습니다.

    설정 Description
    지리적 중복 사용 안 함 지역에 쌍 영역을 페어링하여 계정에서 글로벌 배포를 사용하거나 사용하지 않도록 설정합니다. 나중에 계정에 더 많은 지역을 추가할 수 있습니다.
    다중 지역 쓰기 사용 안 함 다중 영역 쓰기 기능을 사용하면 전 세계의 데이터베이스 및 컨테이너에 대해 프로비저닝된 처리량을 활용할 수 있습니다.
    가용성 영역 사용 안 함 가용성 영역은 애플리케이션의 가용성 및 복원력을 한층 향상시킬 수 있습니다.

    참고 항목

    이전 기본 사항 페이지에서 서버리스용량 모드로 선택하는 경우 다음 옵션을 사용할 수 없습니다.

    • 지리적 중복
    • 다중 지역 쓰기
  6. 필요에 따라 다음 탭에서 자세한 내용을 구성할 수 있습니다.

    • 네트워킹. 가상 네트워크에서 액세스를 구성합니다.
    • 백업 정책 주기적 또는 지속적인 백업 정책을 구성합니다.
    • 암호화. 서비스 관리형 키 또는 고객 관리형 키를 사용합니다.
    • 태그. 태그는 동일한 태그를 여러 개의 리소스 및 리소스 그룹에 적용하여 리소스를 범주화하고 통합된 청구 정보를 볼 수 있는 이름/값 쌍입니다.
  7. 검토 + 만들기를 선택합니다.

  8. 계정 설정을 검토한 다음, 만들기를 선택합니다. 계정을 만드는 데 몇 분이 걸립니다. 포털 페이지에 배포가 완료됨이 표시되기를 기다립니다.

    Screenshot shows that your deployment is complete.

  9. 리소스로 이동을 선택하여 Azure Cosmos DB 계정 페이지로 이동합니다.

    Screenshot shows the Azure Cosmos DB account page.

Azure Cosmos DB 계정 페이지로 이동하여 를 선택합니다. 다음에 만든 웹 애플리케이션에 사용할 값을 복사합니다.

Screenshot of the Azure portal with the Keys button highlighted on the Azure Cosmos DB account page

새 Node.js 애플리케이션 만들기

이제 Express 프레임워크를 사용하여 기본 Hello World Node.js 프로젝트를 만드는 방법을 알아봅니다.

  1. Node.js 명령 프롬프트와 같이 줄겨찾는 터미널을 엽니다.

  2. 새 애플리케이션을 저장하려는 디렉터리로 이동합니다.

  3. Express 생성기를 사용해서 todo라는 새로운 애플리케이션을 생성합니다.

    express todo
    
  4. todo 디렉터리를 열고 종속 요소를 설치합니다.

    cd todo
    npm install
    
  5. 새 애플리케이션을 실행합니다.

    npm start
    
  6. 브라우저에서 새 애플리케이션을 보려면 http://localhost:3000으로 이동합니다.

    Screenshot of the Hello World application in a browser window.

    터미널 창에서 Ctrl+C를 사용하여 애플리케이션을 중지하고, y를 선택하여 일괄 작업을 종료합니다.

필요한 모듈 설치

package.json 파일은 프로젝트 루트에 생성되는 파일 중 하나입니다. 이 파일에는 Node.js 애플리케이션에 필요한 다른 모듈의 목록이 들어 있습니다. 이 애플리케이션을 Azure에 배포할 때, 애플리케이션을 지원하려면 어떤 모듈을 Azure에 설치해야 하는지 이 파일을 사용하여 확인합니다. 이 자습서에서는 다음 두 패키지를 더 설치합니다.

  1. npm을 통해 @azure/cosmos 모듈을 설치합니다.

    npm install @azure/cosmos
    

Azure Cosmos DB에 Node.js 응용 프로그램 연결

초기 설정 및 구성을 완료한 후 todo 애플리케이션이 Azure Cosmos DB와 통신하는 데 필요한 코드를 작성하는 방법을 알아봅니다.

모델 만들기

  1. 프로젝트의 루트 디렉터리에서 models라는 새 디렉터리를 만듭니다.

  2. models 디렉터리에서 taskDao.js라는 새 파일을 만듭니다. 이 파일에는 데이터베이스와 컨테이너를 만드는 데 필요한 코드가 포함되어 있습니다. 또한 Azure Cosmos DB에서 작업을 읽고, 업데이트하고, 만들고, 찾는 메서드를 정의합니다.

  3. 다음 코드를 taskDao.js 파일에 복사합니다.

     // @ts-check
     const CosmosClient = require('@azure/cosmos').CosmosClient
     const debug = require('debug')('todo:taskDao')
    
     // For simplicity we'll set a constant partition key
     const partitionKey = undefined
     class TaskDao {
       /**
        * Manages reading, adding, and updating Tasks in Azure Cosmos DB
        * @param {CosmosClient} cosmosClient
        * @param {string} databaseId
        * @param {string} containerId
        */
       constructor(cosmosClient, databaseId, containerId) {
         this.client = cosmosClient
         this.databaseId = databaseId
         this.collectionId = containerId
    
         this.database = null
         this.container = null
       }
    
       async init() {
         debug('Setting up the database...')
         const dbResponse = await this.client.databases.createIfNotExists({
           id: this.databaseId
         })
         this.database = dbResponse.database
         debug('Setting up the database...done!')
         debug('Setting up the container...')
         const coResponse = await this.database.containers.createIfNotExists({
           id: this.collectionId
         })
         this.container = coResponse.container
         debug('Setting up the container...done!')
       }
    
       async find(querySpec) {
         debug('Querying for items from the database')
         if (!this.container) {
           throw new Error('Collection is not initialized.')
         }
         const { resources } = await this.container.items.query(querySpec).fetchAll()
         return resources
       }
    
       async addItem(item) {
         debug('Adding an item to the database')
         item.date = Date.now()
         item.completed = false
         const { resource: doc } = await this.container.items.create(item)
         return doc
       }
    
       async updateItem(itemId) {
         debug('Update an item in the database')
         const doc = await this.getItem(itemId)
         doc.completed = true
    
         const { resource: replaced } = await this.container
           .item(itemId, partitionKey)
           .replace(doc)
         return replaced
       }
    
       async getItem(itemId) {
         debug('Getting an item from the database')
         const { resource } = await this.container.item(itemId, partitionKey).read()
         return resource
       }
     }
    
     module.exports = TaskDao
    
  4. taskDao.js 파일을 저장하고 닫습니다.

컨트롤러 만들기

  1. 프로젝트의 routes 디렉터리에서 tasklist.js라는 새 파일을 만듭니다.

  2. 아래 코드를 tasklist.js에 추가합니다. 이 코드는 tasklist.js에서 사용되는 CosmosClient 및 async 모듈을 로드합니다. 또한 TaskList 클래스를 정의합니다. 이 클래스는 앞에서 정의한 TaskDao 개체의 인스턴스로 전달됩니다.

     const TaskDao = require("../models/TaskDao");
    
     class TaskList {
       /**
        * Handles the various APIs for displaying and managing tasks
        * @param {TaskDao} taskDao
        */
       constructor(taskDao) {
         this.taskDao = taskDao;
       }
       async showTasks(req, res) {
         const querySpec = {
           query: "SELECT * FROM root r WHERE r.completed=@completed",
           parameters: [
             {
               name: "@completed",
               value: false
             }
           ]
         };
    
         const items = await this.taskDao.find(querySpec);
         res.render("index", {
           title: "My ToDo List ",
           tasks: items
         });
       }
    
       async addTask(req, res) {
         const item = req.body;
    
         await this.taskDao.addItem(item);
         res.redirect("/");
       }
    
       async completeTask(req, res) {
         const completedTasks = Object.keys(req.body);
         const tasks = [];
    
         completedTasks.forEach(task => {
           tasks.push(this.taskDao.updateItem(task));
         });
    
         await Promise.all(tasks);
    
         res.redirect("/");
       }
     }
    
     module.exports = TaskList;
    
  3. tasklist.js 파일을 저장하고 닫습니다.

config.js 추가

  1. 프로젝트 디렉터리의 루트에서 config.js라는 새 파일을 만듭니다.

  2. 다음 코드를 config.js 파일에 추가합니다. 이 코드는 애플리케이션에 필요한 구성 설정 및 값을 정의합니다.

    const config = {};
    
    config.host = process.env.HOST || "[the endpoint URI of your Azure Cosmos DB account]";
    config.authKey =
      process.env.AUTH_KEY || "[the PRIMARY KEY value of your Azure Cosmos DB account";
    config.databaseId = "ToDoList";
    config.containerId = "Items";
    
    if (config.host.includes("https://localhost:")) {
      console.log("Local environment detected");
      console.log("WARNING: Disabled checking of self-signed certs. Do not have this code in production.");
      process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
      console.log(`Go to http://localhost:${process.env.PORT || '3000'} to try the sample.`);
    }
    
    module.exports = config;
    
  3. config.js 파일에서 Azure Portal에 있는 Azure Cosmos DB 계정의 페이지에 있는 값을 사용하여 HOST 및 AUTH_KEY 값을 업데이트합니다.

  4. config.js 파일을 저장하고 닫습니다.

app.js 수정

  1. 프로젝트 디렉터리에서 app.js 파일을 엽니다. 이 파일은 이전에 Express 웹 애플리케이션을 만들 때 생성되었습니다.

  2. 다음 코드를 app.js 파일에 추가합니다. 이 코드는 사용할 구성 파일을 정의하고, 다음 섹션에서 사용할 값을 일부 변수에 로드합니다.

     const CosmosClient = require('@azure/cosmos').CosmosClient
     const config = require('./config')
     const TaskList = require('./routes/tasklist')
     const TaskDao = require('./models/taskDao')
    
     const express = require('express')
     const path = require('path')
     const logger = require('morgan')
     const cookieParser = require('cookie-parser')
     const bodyParser = require('body-parser')
    
     const app = express()
    
     // view engine setup
     app.set('views', path.join(__dirname, 'views'))
     app.set('view engine', 'jade')
    
     // uncomment after placing your favicon in /public
     //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
     app.use(logger('dev'))
     app.use(bodyParser.json())
     app.use(bodyParser.urlencoded({ extended: false }))
     app.use(cookieParser())
     app.use(express.static(path.join(__dirname, 'public')))
    
     //Todo App:
     const cosmosClient = new CosmosClient({
       endpoint: config.host,
       key: config.authKey
     })
     const taskDao = new TaskDao(cosmosClient, config.databaseId, config.containerId)
     const taskList = new TaskList(taskDao)
     taskDao
       .init(err => {
         console.error(err)
       })
       .catch(err => {
         console.error(err)
         console.error(
           'Shutting down because there was an error settinig up the database.'
         )
         process.exit(1)
       })
    
     app.get('/', (req, res, next) => taskList.showTasks(req, res).catch(next))
     app.post('/addtask', (req, res, next) => taskList.addTask(req, res).catch(next))
     app.post('/completetask', (req, res, next) =>
       taskList.completeTask(req, res).catch(next)
     )
     app.set('view engine', 'jade')
    
     // catch 404 and forward to error handler
     app.use(function(req, res, next) {
       const err = new Error('Not Found')
       err.status = 404
       next(err)
     })
    
     // error handler
     app.use(function(err, req, res, next) {
       // set locals, only providing error in development
       res.locals.message = err.message
       res.locals.error = req.app.get('env') === 'development' ? err : {}
    
       // render the error page
       res.status(err.status || 500)
       res.render('error')
     })
    
     module.exports = app
    
  3. 마지막으로, app.js 파일을 저장하고 닫습니다.

사용자 인터페이스 빌드

이제 사용자가 애플리케이션과 상호 작용할 수 있도록 사용자 인터페이스를 빌드합니다. 이전 섹션에서 만든 Express 애플리케이션은 Jade를 보기 엔진으로 사용합니다.

  1. views 디렉터리의 layout.jade 파일은 다른 .jade 파일에 대한 전역 템플릿으로 사용됩니다. 이 단계에서는 웹 사이트를 디자인하는 데 사용되는 도구 키트인 Twitter Bootstrap을 사용하도록 수정합니다.

  2. views 폴더에 있는 layout.jade 파일을 열고 콘텐츠를 다음 코드로 바꿉니다.

    doctype html
    html
      head
        title= title
        link(rel='stylesheet', href='//ajax.aspnetcdn.com/ajax/bootstrap/3.3.2/css/bootstrap.min.css')
        link(rel='stylesheet', href='/stylesheets/style.css')
      body
        nav.navbar.navbar-inverse.navbar-fixed-top
          div.navbar-header
            a.navbar-brand(href='#') My Tasks
        block content
        script(src='//ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.2.min.js')
        script(src='//ajax.aspnetcdn.com/ajax/bootstrap/3.3.2/bootstrap.min.js')
    

    이 코드는 애플리케이션에 대한 일부 HTML을 렌더링하도록 Jade 엔진에 알리고 콘텐츠 페이지의 레이아웃을 제공할 수 있는 content라는 block을 만듭니다. layout.jade 파일을 저장하고 닫습니다.

  3. 애플리케이션에서 사용하는 뷰인 index.jade 파일을 엽니다. 파일의 콘텐츠를 다음 코드로 바꿉니다.

    extends layout
    block content
         h1 #{title}
         br
    
         form(action="/completetask", method="post")
          table.table.table-striped.table-bordered
             tr
               td Name
               td Category
               td Date
               td Complete
             if (typeof tasks === "undefined")
               tr
                 td
             else
               each task in tasks
                 tr
                   td #{task.name}
                   td #{task.category}
                   - var date  = new Date(task.date);
                   - var day   = date.getDate();
                   - var month = date.getMonth() + 1;
                   - var year  = date.getFullYear();
                   td #{month + "/" + day + "/" + year}
                   td
                    if(task.completed) 
                     input(type="checkbox", name="#{task.id}", value="#{!task.completed}", checked=task.completed)
                    else
                     input(type="checkbox", name="#{task.id}", value="#{!task.completed}", checked=task.completed)
           button.btn.btn-primary(type="submit") Update tasks
         hr
         form.well(action="/addtask", method="post")
           label Item Name:
           input(name="name", type="textbox")
           label Item Category:
           input(name="category", type="textbox")
           br
           button.btn(type="submit") Add item
    

이 코드는 레이아웃을 확장하고 layout.jade 파일에서 본 content 자리 표시자의 콘텐츠를 제공합니다. 이 레이아웃에서 HTML 양식 2개를 만들었습니다.

첫 번째 양식에는 컨트롤러의 /completeTask 메서드에 게시하여 항목을 업데이트할 수 있는 단추 및 데이터에 대한 테이블이 포함됩니다.

두 번째 양식에는 컨트롤러의 /addtask 메서드에 게시하여 새 항목을 만들 수 있는 단추 및 두 개의 입력 필드가 포함됩니다. 이들 항목은 모두 애플리케이션이 작동하는 데 필요합니다.

로컬로 응용 프로그램 실행

애플리케이션을 빌드한 후 다음 단계를 사용하여 로컬로 실행할 수 있습니다.

  1. 로컬 컴퓨터에서 애플리케이션을 테스트하려면 터미널에서 npm start를 실행하여 애플리케이션을 시작한 다음, http://localhost:3000 페이지를 새로 고칩니다. 이제 페이지가 다음 스크린샷처럼 표시됩니다.

    Screenshot of the My Todo List application in a browser.

    layout.jade 파일 또는 index.jade 파일에서 들여쓰기에 대한 오류가 발생하면 두 파일의 처음 두 줄을 공백 없이 왼쪽에 맞추어야 합니다. 처음 두 줄 앞에 공백이 있으면 이를 제거하고, 두 파일을 모두 저장한 다음, 브라우저 창을 새로 고칩니다.

  2. 항목 이름 및 항목 범주 필드를 사용하여 새 작업을 입력한 다음, 항목 추가를 선택하여 해당 속성으로 Azure Cosmos DB에서 문서를 만듭니다.

  3. 페이지가 업데이트되어 새로 만든 항목을 ToDo 목록에 표시합니다.

    Screenshot of the application with a new item in the ToDo list.

  4. 작업을 완료하려면 완료 열에서 확인란을 선택한 다음, 작업 업데이트를 선택하여 이미 만든 문서를 업데이트하고 뷰에서 제거합니다.

  5. 애플리케이션을 중지하려면 터미널 창에서 Ctrl+C를 누른 다음, Y를 선택하여 일괄 작업을 종료합니다.

App Service에 응용 프로그램 배포

애플리케이션이 로컬에서 성공하면 Azure App Service에 배포할 수 있습니다. 터미널에서 todo 앱 디렉터리에 있는지 확인합니다. 다음 az webapp up 명령을 사용하여 로컬 폴더(todo)에 코드를 배포합니다.

az webapp up --sku F1 --name <app-name>

<app_name>을 모든 Azure에서 고유한 이름으로 바꿉니다(유효한 문자는 a~z, 0~9 및 -임). 회사 이름과 앱 식별자를 조합하여 사용하는 것이 좋습니다. 앱 배포에 대해 자세히 알아보려면 Azure에서 Node.js 앱 배포를 참조하세요.

명령을 완료하는 데 몇 분 정도 걸릴 수 있습니다. 이 명령은 리소스 그룹, App Service 요금제 및 앱 리소스 만들기, 로깅 구성, ZIP 배포 수행에 대한 메시지를 제공합니다. 이 명령은 실행되는 동안 이러한 메시지를 제공합니다. 그런 다음, Azure의 앱 URL인 http://<app-name>.azurewebsites.net에서 앱을 시작하기 위한 URL을 제공합니다.

리소스 정리

이 리소스가 더 이상 필요하지 않은 경우 리소스 그룹, Azure Cosmos DB 계정 및 모든 관련 리소스를 삭제할 수 있습니다. 이렇게 하려면 Azure Cosmos DB 계정에 사용한 리소스 그룹을 선택하고, 삭제를 선택한 다음, 삭제할 리소스 그룹의 이름을 확인합니다.

다음 단계

용량 계획을 위해 기존 데이터베이스 클러스터에 대한 정보를 사용할 수 있습니다.