연습 - 기본 웹 애플리케이션 만들기
지금까지 Ubuntu VM(가상 머신)에 MongoDB와 Node.js를 설치했습니다. 이제 실행 중인 작업을 확인하는 기본 웹 애플리케이션을 만들 차례입니다. 그 과정에서 AngularJS와 Express가 어떻게 맞는지 알 수 있습니다.
예제를 통해 효과적으로 학습할 수 있습니다. 빌드하는 웹 애플리케이션은 기본 책 데이터베이스를 구현합니다. 웹 애플리케이션을 사용하면 도서에 대한 정보를 나열하고, 새 도서를 추가하고, 기존 도서를 삭제할 수 있습니다.
여기에 표시되는 웹 애플리케이션은 대부분의 MEAN 스택 웹 애플리케이션에 적용되는 많은 개념을 보여 줍니다. 요구 사항 및 관심에 따라 고유한 MEAN 스택 애플리케이션을 빌드하는 데 필요한 기능을 탐색할 수 있습니다.
책 웹 애플리케이션의 모양은 다음과 같습니다.
MEAN 스택의 각 구성 요소에 적합한 방법은 다음과 같습니다.
- MongoDB는 도서에 대한 정보를 저장합니다.
- Express.js는 적절한 처리기로 각 HTTP 요청을 라우팅합니다.
- AngularJS는 프로그램의 비즈니스 논리와 사용자 인터페이스를 연결합니다.
- Node.js는 서버 쪽 애플리케이션을 호스트합니다.
중요
여기에서는 학습 목적으로 기본 웹 애플리케이션을 빌드합니다. MEAN 스택을 테스트하고 작동 방식을 파악할 수 있도록 하기 위한 것입니다. 이 애플리케이션은 프로덕션에 사용하기에는 보안이나 준비가 충분하지 않습니다.
Express는 어떤가요?
지금까지 VM에 MongoDB와 Node.js를 설치했습니다. 머리글자어 MEAN의 E인 Express.js는 어떤가요?
Express.js는 웹 애플리케이션 빌드 프로세스를 간소화하는 Node.js를 위해 빌드된 웹 서버 프레임워크입니다.
Express의 주요 목적은 요청 라우팅을 처리하는 것입니다. 라우팅은 애플리케이션이 특정 엔드포인트에 대한 요청에 응답하는 방법을 가리킵니다. 엔드포인트는 경로나 URI 및 요청 메서드(예: GET 또는 POST)로 이루어져 있습니다. 예를 들어 데이터베이스에 있는 모든 도서 목록을 제공하여 /book
엔드포인트에 대한 GET 요청에 응답할 수 있습니다. 사용자가 웹 양식에 입력한 필드를 기반으로 데이터베이스에 항목을 추가하여 /book
엔드포인트에 대한 POST 요청에 응답할 수 있습니다.
곧 빌드하는 웹 애플리케이션에서는 Express를 사용하여 HTTP 요청을 라우팅하고 웹 콘텐츠를 사용자에게 반환합니다. Express를 통해 웹 애플리케이션이 HTTP 쿠키를 사용하고 쿼리 문자열을 처리할 수 있습니다.
Express는 Node.js 패키지입니다. Node.js와 함께 설치된 npm 유틸리티를 사용하여 Node.js 패키지를 설치하고 관리합니다. 이 단원의 뒷부분에서 package.json
(이)라는 파일을 만들어 Express 및 기타 종속성을 정의한 다음, npm install
명령을 실행하여 이러한 종속성을 설치합니다.
AngularJS는 어떤가요?
Express와 마찬가지로 MEAN 약어의 A는 아직 설치되지 않았습니다.
AngularJS를 통해 웹 페이지의 동작 방식으로부터 웹 페이지의 모양인 HTML 코드를 분리할 수 있기 때문에 웹 애플리케이션이 쉽게 작성하고 테스트할 수 있습니다. MVC(모델–뷰–컨트롤러) 패턴 또는 데이터 바인딩 개념에 익숙한 경우 AngularJS에 익숙할 것입니다.
AngularJS는 프런트 엔드 JavaScript 프레임워크입니다. 즉, 애플리케이션에 액세스하는 클라이언트에서만 사용할 수 있어야 합니다. 즉, AngularJS는 웹 서버가 아닌 사용자의 웹 브라우저에서 실행됩니다. 또한 AngularJS가 JavaScript이기 때문에 웹 서버의 데이터를 쉽게 페치하여 페이지에 표시하는 데 사용할 수 있습니다.
실제로 AngularJS를 설치하지 않습니다. 대신 다른 JavaScript 라이브러리와 같이 HTML 페이지의 JavaScript 파일에 대한 참조를 추가합니다. 웹 페이지에 AngularJS를 포함하는 방법은 여러 가지가 있습니다. 여기서는 CDN(콘텐츠 배달 네트워크)에서 AngularJS를 로드합니다. CDN은 다운로드 속도를 개선하기 위해 이미지, 비디오 및 기타 콘텐츠를 지리적으로 분산하는 방법입니다.
이 코드를 아직 추가하지 마세요. CDN에서 AngularJS를 로드하는 예제는 다음과 같습니다. 일반적으로 HTML 페이지의 <head>
섹션에 이 코드를 추가합니다.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.2/angular.min.js"></script>
참고 항목
Angular와 AngularJS를 혼동하지 마세요. 둘 사이에 다양한 개념이 유사하지만 AngularJS는 Angular의 이전 버전입니다. AngularJS는 여전히 웹 애플리케이션을 빌드하는 데 일반적으로 사용됩니다. AngularJS가 JavaScript를 기반으로 하지만 Angular는 JavaScript 프로그램을 작성하는 프로그래밍 언어인 TypeScript에 기반합니다.
애플리케이션을 빌드하려면 어떻게 해야 하나요?
여기서는 기본 프로세스형을 사용합니다. Cloud Shell에서 애플리케이션 코드를 작성한 다음 SCP(보안 복사 프로토콜)를 사용하여 파일을 VM에 복사합니다. 그런 다음, Node.js 애플리케이션을 시작하고 브라우저에서 결과를 확인합니다.
실제로는 랩톱 또는 로컬로 실행하는 가상 머신 같은 로컬 환경에서 웹 애플리케이션을 일반적으로 작성하고 테스트합니다. Git과 같은 버전 제어 시스템에 코드를 저장할 수 있습니다. 그런 다음 Azure DevOps와 같은 CI/CD(연속 통합 및 지속적인 업데이트) 시스템을 사용하여 변경 내용을 테스트하고 VM에 업로드합니다. 이 모듈의 끝부분에서 기타 리소스를 알려드립니다.
도서 웹 애플리케이션 만들기
여기서는 웹 애플리케이션을 구성하는 모든 코드, 스크립트 및 HTML 파일을 만듭니다. 간략히 설명하기 위해 각 파일의 중요한 부분을 강조하지만 전체 세부 정보는 다루지 않겠습니다.
SSH를 통해 VM에 연결된 경우 exit
을 실행하여 SSH 세션을 나가고 Cloud Shell로 돌아갑니다.
exit
이제 Cloud Shell 세션에 다시 돌아옵니다.
파일 만들기
Cloud Shell에서 웹 애플리케이션에 대한 파일과 폴더를 만들려면 이러한 명령을 실행합니다.
cd ~ mkdir Books touch Books/server.js touch Books/package.json mkdir Books/app touch Books/app/model.js touch Books/app/routes.js mkdir Books/public touch Books/public/script.js touch Books/public/index.html
웹 애플리케이션에는 다음 폴더와 파일이 포함됩니다.
Books
는 프로젝트의 루트 디렉터리입니다.server.js
는 웹 애플리케이션의 진입점을 정의합니다. 필수 Node.js 패키지를 로드하고, 수신 대기할 포트를 지정하고, 들어오는 HTTP 트래픽을 수신 대기하기 시작합니다.package.json
은 해당 이름, 설명 및 애플리케이션이 실행해야 하는 Node.js 패키지 항목을 비롯한 애플리케이션에 대한 정보를 제공합니다.
Books/app
에는 서버에서 실행되는 코드가 포함됩니다.model.js
는 데이터베이스 연결 및 스키마를 정의합니다. 애플리케이션에 대한 데이터 모델로 생각합니다.routes.js
는 요청 라우팅을 처리합니다. 예를 들어 데이터베이스에 있는 모든 도서 목록을 제공하여/book
엔드포인트에 대한 GET 요청을 정의합니다.
Books/public
에는 클라이언트의 브라우저에 직접 제공되는 파일이 포함됩니다.index.html
에는 인덱스 페이지가 포함됩니다. 사용자가 도서에 대한 정보를 제출할 수 있는 웹 양식이 포함됩니다. 또한 데이터베이스의 모든 도서를 표시하고 데이터베이스에서 항목을 삭제할 수 있습니다.script.js
에는 사용자의 브라우저에서 실행되는 JavaScript 코드가 포함됩니다. 도서를 나열하고, 데이터베이스에 도서를 추가하고, 데이터베이스에서 도서를 삭제하는 요청을 서버에 보낼 수 있습니다.
code
명령을 실행하여 Cloud Shell 편집기를 통해 파일을 엽니다.code Books
데이터 모델 만들기
편집기에서
app/model.js
를 열고 다음 코드를 추가합니다.var mongoose = require('mongoose'); var dbHost = 'mongodb://localhost:27017/Books'; mongoose.connect(dbHost, { useNewUrlParser: true } ); mongoose.connection; mongoose.set('debug', true); var bookSchema = mongoose.Schema( { name: String, isbn: {type: String, index: true}, author: String, pages: Number }); var Book = mongoose.model('Book', bookSchema); module.exports = Book;
Important
편집기에서 파일에 코드를 붙여넣거나 코드를 변경할 때마다 "..." 메뉴 또는 바로 가기 키(Windows 및 Linux는 Ctrl+S, macOS는 Cmd+S)를 사용하여 저장하세요.
이 코드는 MongoDB에서 들어오고 나가는 데이터 전송 프로세스를 간소화하기 위해 Mongoose를 사용합니다. Mongoose는 데이터를 모델링하기 위한 스키마 기반 시스템입니다. 제공된 스키마를 사용하여 "Book"이라는 데이터베이스 문서를 정의합니다. 스키마는 한 권의 도서를 설명하는 4개의 필드를 정의합니다.
- 도서의 이름 또는 제목
- 이 책을 고유하게 식별하는 국제 표준 도서 번호(ISBN)
- 작가
- 포함된 페이지 수
다음으로 GET, POST 및 DELETE 요청을 데이터베이스 작업에 매핑하는 HTTP 처리기를 만듭니다.
HTTP 요청을 처리할 Express.js 경로를 만듭니다.
편집기에서
app/routes.js
를 열고 다음 코드를 추가합니다.var path = require('path'); var Book = require('./model'); var routes = function(app) { app.get('/book', function(req, res) { Book.find({}, function(err, result) { if ( err ) throw err; res.json(result); }); }); app.post('/book', function(req, res) { var book = new Book( { name:req.body.name, isbn:req.body.isbn, author:req.body.author, pages:req.body.pages }); book.save(function(err, result) { if ( err ) throw err; res.json( { message:"Successfully added book", book:result }); }); }); app.delete("/book/:isbn", function(req, res) { Book.findOneAndRemove(req.query, function(err, result) { if ( err ) throw err; res.json( { message: "Successfully deleted the book", book: result }); }); }); app.get('*', function(req, res) { res.sendFile(path.join(__dirname + '/public', 'index.html')); }); }; module.exports = routes;
이 코드는 애플리케이션에 대한 4개의 경로를 만듭니다. 각각에 대한 간략한 개요는 다음과 같습니다.
HTTP 동사 엔드포인트 설명 GET /book
데이터베이스에서 모든 도서를 검색합니다. POST /book
웹 양식에 제공된 사용자 필드에 따라 Book
개체를 만들고, 해당 데이터베이스에 개체를 작성합니다.DELETE /book/:isbn
데이터베이스에서 해당 ISBN으로 식별되는 도서를 삭제합니다. GET *
다른 경로가 일치하지 않는 경우 인덱스 페이지를 반환합니다. Express.js는 경로 처리 코드에 직접 HTTP 응답을 제공하거나 파일의 정적 콘텐츠를 제공할 수 있습니다. 이 코드는 모두를 보여줍니다. 처음 세 가지 경로는 도서 API 요청에 대한 JSON 데이터를 반환합니다. 네 번째 경로(기본 사례)는
index.html
인덱스 파일의 콘텐츠를 반환합니다.
클라이언트 쪽 JavaScript 애플리케이션을 만듭니다.
편집기에서
public/script.js
를 열고 이 코드를 추가합니다.var app = angular.module('myApp', []); app.controller('myCtrl', function($scope, $http) { var getData = function() { return $http( { method: 'GET', url: '/book' }).then(function successCallback(response) { $scope.books = response.data; }, function errorCallback(response) { console.log('Error: ' + response); }); }; getData(); $scope.del_book = function(book) { $http( { method: 'DELETE', url: '/book/:isbn', params: {'isbn': book.isbn} }).then(function successCallback(response) { console.log(response); return getData(); }, function errorCallback(response) { console.log('Error: ' + response); }); }; $scope.add_book = function() { var body = '{ "name": "' + $scope.Name + '", "isbn": "' + $scope.Isbn + '", "author": "' + $scope.Author + '", "pages": "' + $scope.Pages + '" }'; $http({ method: 'POST', url: '/book', data: body }).then(function successCallback(response) { console.log(response); return getData(); }, function errorCallback(response) { console.log('Error: ' + response); }); }; });
이 코드가
myApp
(이)라는 모듈과myCtrl
(이)라는 컨트롤러를 정의하는 방법을 확인합니다. 모듈 및 컨트롤러가 여기서 작동하는 방법에 대한 전체 세부 정보를 설명하지 않지만 다음 단계에서 이러한 이름을 사용하여 애플리케이션의 비즈니스 논리를 사용하여 사용자 인터페이스(HTML 코드)를 바인딩하겠습니다.앞에서는 서버에서 다양한 GET, POST 및 DELETE 작업을 처리하는 네 개의 경로를 만들었습니다. 이 코드는 이러한 동일한 작업과 유사하지만 클라이언트 쪽(사용자의 웹 브라우저)에서 시작됩니다.
예를 들어
getData
함수는/book
엔드포인트에 GET 요청을 보냅니다. 서버는 데이터베이스에서 모든 책에 대한 정보를 검색하고 응답에서 해당 정보를 JSON 데이터로 반환하여 이 요청을 처리한다는 점을 기억하세요. 응답의 JSON 데이터가$scope.books
변수에 할당되는 방식을 확인합니다. 이 코드가 다음 단계의 웹 페이지에서 사용자에게 표시되는 내용에 어떤 영향을 미치는지 알아봅니다.이 코드는 페이지가 로드될 때
getData
함수를 호출합니다.del_book
및add_book
함수를 검사하여 작동 방식을 이해할 수 있습니다. 기본 처리기는 JSON 데이터가 아닌 인덱스 페이지를 반환하기 때문에 클라이언트 쪽 코드는 서버의 기본 처리기와 일치할 필요가 없습니다.
사용자 인터페이스 만들기
편집기에서
public/index.html
를 열고 이 코드를 추가합니다.<!doctype html> <html ng-app="myApp" ng-controller="myCtrl"> <head> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.2/angular.min.js"></script> <script src="script.js"></script> </head> <body> <div> <table> <tr> <td>Name:</td> <td><input type="text" ng-model="Name"></td> </tr> <tr> <td>Isbn:</td> <td><input type="text" ng-model="Isbn"></td> </tr> <tr> <td>Author:</td> <td><input type="text" ng-model="Author"></td> </tr> <tr> <td>Pages:</td> <td><input type="number" ng-model="Pages"></td> </tr> </table> <button ng-click="add_book()">Add</button> </div> <hr> <div> <table> <tr> <th>Name</th> <th>Isbn</th> <th>Author</th> <th>Pages</th> </tr> <tr ng-repeat="book in books"> <td><input type="button" value="Delete" data-ng-click="del_book(book)"></td> <td>{{book.name}}</td> <td>{{book.isbn}}</td> <td>{{book.author}}</td> <td>{{book.pages}}</td> </tr> </table> </div> </body> </html>
이 코드는 도서 데이터를 제출할 필드가 4개인 기본 HTML 양식 및 데이터베이스에 저장된 모든 도서를 표시할 테이블을 만듭니다.
이 HTML 코드는 표준이지만
ng-
HTML 특성은 익숙하지 않을 수 있습니다. 이러한 HTML 특성이 AngularJS 코드를 사용자 인터페이스에 연결합니다. 예를 들어 추가를 선택하면 AngularJS에서add_book
함수를 호출하여 양식 데이터를 서버로 보냅니다.ng-
특성 각각이 애플리케이션의 비즈니스 논리와 연결된 방법을 이해하기 위해 여기에서 코드를 검사할 수 있습니다.
애플리케이션을 호스트하기 위한 Express.js 서버 만들기
편집기에서
server.js
를 열고 이 코드를 추가합니다.var express = require('express'); var bodyParser = require('body-parser'); var app = express(); app.use(express.static(__dirname + '/public')); app.use(bodyParser.json()); require('./app/routes')(app); app.set('port', 80); app.listen(app.get('port'), function() { console.log('Server up: http://localhost:' + app.get('port')); });
이 코드는 웹 애플리케이션 자체를 만듭니다.
public
디렉터리에서 정적 파일을 제공하고 이전에 정의된 경로를 사용하여 요청을 처리합니다.
패키지 정보 및 종속성 정의
package.json
은 해당 이름, 설명 및 애플리케이션이 실행해야 하는 Node.js 패키지 항목을 비롯한 애플리케이션에 대한 정보를 제공합니다.
편집기에서
package.json
를 열고 이 코드를 추가합니다.{ "name": "books", "description": "Sample web app that manages book information.", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/MicrosoftDocs/mslearn-build-a-web-app-with-mean-on-a-linux-vm" }, "main": "server.js", "dependencies": { "express": "~4.16", "mongoose": "~5.3", "body-parser": "~1.18" } }
해당 이름, 설명 및 라이선스를 포함하여 애플리케이션에 대한 정보 또는 메타데이터를 확인합니다.
repository
필드는 코드를 유지 관리하는 위치를 지정합니다. 참조용으로 여기에 표시된 URL에서 GitHub의 코드를 나중에 검토할 수 있습니다.
main
필드는 애플리케이션의 진입점을 정의합니다. 완전성을 위해 여기에 제공합니다. 그러나 이 진입점은 다른 사용자가 다운로드하고 사용할 수 있도록 애플리케이션을 Node.js 패키지로 게시하려는 경우에만 중요합니다.
dependencies
필드가 중요합니다. 애플리케이션에 필요한 Node.js 패키지를 정의합니다. 잠시 후 VM에 두 번째로 연결하고 npm install
명령을 실행하여 이러한 패키지를 설치합니다.
노드 패키지는 일반적으로 유의적 버전 버전 관리 체계를 사용합니다. 버전 번호는 주 버전, 부 버전 및 패치라는 세 가지 구성 요소를 포함합니다. 여기서 물결표 ~
표기법은 제공된 주 버전과 부 버전에서 최신 패치 버전을 설치하는 npm을 알려줍니다. 여기서 표시된 버전은 이 모듈을 테스트한 최신 버전입니다. 실제로 종속 패키지 각각이 제공하는 최신 기능을 사용하도록 애플리케이션을 업데이트하고 테스트하므로 시간이 지남에 따라 버전이 증가될 수 있습니다.
VM에 파일 복사
계속하기 전에 VM의 IP 주소를 사용하도록 설정해야 합니다. 준비되어 있지 않다면 Cloud Shell에서 다음 명령을 실행하여 주소를 검색합니다.
ipaddress=$(az vm show \
--name MeanStack \
--resource-group "<rgn>[sandbox resource group name]</rgn>" \
--show-details \
--query [publicIps] \
--output tsv)
echo $ipaddress
파일 편집이 완료되었습니다. 각 파일에 변경 내용을 저장했는지 확인한 다음, 편집기를 닫습니다.
편집기를 닫으려면 오른쪽 위 모서리에서 줄임표를 선택한 다음, 편집기 닫기를 선택합니다.
다음
scp
명령을 실행하여 Cloud Shell 세션에서~/Books
디렉터리의 내용을 VM의 동일한 디렉터리 이름에 복사합니다.scp -r ~/Books azureuser@$ipaddress:~/Books
더 많은 노드 패키지 설치
개발 프로세스 중에 사용하려는 노드 패키지를 더 많이 식별했다고 가정해 보겠습니다. 예를 들어 app/model.js
는 이 줄로 시작합니다.
var mongoose = require('mongoose');
애플리케이션은 Mongoose를 사용하여 MongoDB 데이터베이스에서 들어오고 나가는 데이터를 전송할 수 있습니다.
애플리케이션에는 Express.js 및 본문 파서 패키지가 필요합니다. 본문 파서는 Express가 클라이언트에서 보낸 웹 양식의 데이터로 작업할 수 있도록 하는 플러그인입니다.
VM에 연결하고 package.json
에 지정된 패키지를 설치하겠습니다.
VM에 연결하기 전에 VM의 IP 주소를 사용할 수 있는지 확인합니다. 없는 경우 이전 섹션에서 Cloud Shell 명령을 실행하여 검색합니다.
이전에 수행한 대로 VM에 대한 SSH 연결을 만듭니다.
ssh azureuser@$ipaddress
홈 디렉터리의
Books
디렉터리로 이동합니다.cd ~/Books
npm install
을 실행하여 종속 패키지를 설치합니다.sudo apt install npm -y && npm install
다음 섹션을 위해 SSH 연결을 열어 둡니다.
애플리케이션 테스트
Node.js 웹 애플리케이션을 테스트할 준비가 되었습니다.
~/Books
디렉터리에서 이 명령을 실행하여 웹 애플리케이션을 시작합니다.sudo nodejs server.js
이 명령은 들어오는 HTTP 요청을 포트 80에서 수신 대기하여 애플리케이션을 시작합니다.
별도 브라우저 탭에서 VM의 IP 주소로 이동합니다.
웹 양식을 포함하는 인덱스 페이지를 표시합니다.
데이터베이스에 몇 가지 도서를 추가해 보세요. 도서를 추가할 때마다 페이지는 도서의 전체 목록을 업데이트합니다.
데이터베이스에서 책을 삭제하려면 삭제를 선택할 수도 있습니다.