[JavaScript] Node와 Angular를 이용하여 간단한 TODO App 예제 샘플 만들어보기 Develop Tip

최근 MEAN.IO 에 대한 이야기를 들었습니다.

그림 하단에 나오듯이 Angular.js, node.js express (node module) mongoDB를 이용하여 JavaScript 로만 Full App Stack을 만들 수 있는 방법이라 하더군요.

그런데 관련 샘플을 찾다가,
"Single Page Applicatiion with Node and Angular" 라는 소개 블로그를 보았습니다.

이 블로그의 내용을 따라하면서 샘플 앱을 만들어 보도록 하겠습니다.

여기서 잠깐, SPA(Single Page Application) 이라고 있습니다.

정의만 간단히 살펴보면 SPA(단일 페이지 앱) 또는 SPI (단일 페이지 인터페이스) 라는 것은 데스크탑 응용프로그램과 같이 유연하고 여러 기능을 제공하는 목적을 달성하고자 하는 단일 웹 페이지에 적합한 웹 앱으로 정의 될 수 있습니다.

결국 위의 데스크탑 App 처럼 웹 브라우저로 동일한 기능의 App을 사용하고자 하는 목적을 달성하고자
X-Internet, RIA 에 이어 SPA로 용어가 정리되고 있군요.

자 다시 본론으로 돌아와서,

만들어 보려고 하는 것은,

  • 할일 모듬을 만들거나 끝내거나 하는 간단한 SPA
  • Mongoose를 이용한 할일을 MongoDB에 저장
  • Express (MVC) framework를 이용
  • RESTful Node API 생성
  • 사용자 UI와 API를 접속하기 위하여 Angular 이용
1) 기본 설정


1.1) 파일 구조



1.2) Node 모듈 설치

$ mkdir -p ~/workjs/mean_todo
$ cd ~/workjs/mean_todo
$ vi package.json
  {
  "name"         : "node-todo",
  "version"      : "0.0.0",
  "description"  : "Simple todo application.",
  "main"         : "server.js",
  "author"       : "Scotch",
  "dependencies" : {
    "express"    : "~3.4.4",
    "mongoose"   : "~3.6.2"
    }
  }
$ npm install


1.3) Node 설정

위에 package.json 에서 server.js 가 메인 시작 프로그램이고 이곳에서 다음과 같은 일을 할 것입니다.

  • app 설정
  • DB 연결
  • Mongoose 모델 생성
  • RESTful API 라우트 정의
  • Frontend Angular app을 위한 라우트 정의
  • 브라우저에서 보이도록 포트 리슨

$ cd ~/workjs/mean_todo
$ vi server.js
// server.js
// set up ========================
var express  = require('express');
var app      = express(); // create our app w/ express
var mongoose = require('mongoose'); // mongoose for mongodb

// configuration =================
// connect to mongoDB database on modulus.io
mongoose.connect('mongodb://localhost/todo');

app.configure(function() {
// set the static files location /public/img will be /img for users
app.use(express.static(__dirname + '/public'));
// log every request to the console
app.use(express.logger('dev'));
// pull information from html in POST
app.use(express.bodyParser());
// simulate DELETE and PUT
app.use(express.methodOverride());
});
// listen (start app with node server.js) ======================================
app.listen(8080);
console.log("App listening on port 8080");

위에 PyCharm으로 열었습니다.
지난번에 [PyCharm]으로 HTML, Javascript, Less 이용하기 (기초) 와 같이 열어 보았습니다.

1.4) DB 설정

$ brew update
$ brew install mongodb
$ cd ~/workjs/mean_todo
$ mkdir mongodb
$ mongod --dbpath ./mongodb



2) 응용프로그램 구조




3) Node API 생성


3.1) Todo Model

위와 같이 configuration과 listen 사이에

// define model =================
var Todo = mongoose.model('Todo', {
text : String
});

를 넣었습니다.


3.2) RESTful API 라우터

다시 server.js 를 열어

위와 같이 listen 위에 

// routes ======================================================================

// api ---------------------------------------------------------------------
// get all todos
app.get('/api/todos', function(req, res) {

// use mongoose to get all todos in the database
Todo.find(function(err, todos) {

// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err)
res.send(err)

res.json(todos); // return all todos in JSON format
});
});

// create todo and send back all todos after creation
app.post('/api/todos', function(req, res) {

// create a todo, information comes from AJAX request from Angular
Todo.create({
text : req.body.text,
done : false
}, function(err, todo) {
if (err)
res.send(err);

// get and return all the todos after you create another
Todo.find(function(err, todos) {
if (err)
res.send(err)
res.json(todos);
});
});

});

// delete a todo
app.delete('/api/todos/:todo_id', function(req, res) {
Todo.remove({
_id : req.params.todo_id
}, function(err, todo) {
if (err)
res.send(err);

// get and return all the todos after you create another
Todo.find(function(err, todos) {
if (err)
res.send(err)
res.json(todos);
});
});
});

를 넣어 줍니다.


3.3) node 디버깅 및 GET 테스트

자 이제는 원본에 없는 내용인데 PyCharm으로 디버깅 하는 것을 살펴보겠습니다.

위와 같이 Local Node 실행을 하나 만들었습니다.

디버깅을 돌려봅니다~~

몽고 DB를 foreground 에서 돌리는데 위와 같이 다섯개씩 mongoose 가 Connection pool을 맺어 두는군요.

브라우저로 http://localhost:8080/api/todos 를 넣으면, get 이 호출되므로

위와 같이 Todo.find 안의 res.json(todos) 에서 브레이킹 되는군요.

PyCharm 으로 Node 개발까지 모두 끝내주다니... 끝내줍니다. (^^)



4) Angular로 Frontend 개발


4.1) Frontend 라우트 정의

다시 server.js 에서 

위와 같이

// application -------------------------------------------------------------
app.get('*', function(req, res) {
        // load the single view file
        // (angular will handle the page changes on the front-end)
res.sendfile('./public/index.html');
});

를 추가 했습니다.


4.2) Angular core.js 설정

위와 같이 (public 폴더를 만들고, 그 아래)에 core.js 를 만들어

// public/core.js
var scotchTodo = angular.module('scotchTodo', []);

function mainController($scope, $http) {
$scope.formData = {};

// when landing on the page, get all todos and show them
$http.get('/api/todos')
.success(function(data) {
$scope.todos = data;
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});

// when submitting the add form, send the text to the node API
$scope.createTodo = function() {
$http.post('/api/todos', $scope.formData)
.success(function(data) {
$scope.formData = {}; // clear the form so our user is ready to enter another
$scope.todos = data;
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};

// delete a todo after checking it
$scope.deleteTodo = function(id) {
$http.delete('/api/todos/' + id)
.success(function(data) {
$scope.todos = data;
console.log(data);
})
.error(function(data) {
console.log('Error: ' + data);
});
};
}

를 입력하고 저장합니다.

Angular module (scotchApp)와 컨트롤러 (mainController)를 만들었습니다.


4.3) FrontView index.html


public 폴더에 index.html 을 만듦니다.

  • Angular 모듈과 컨트롤러를 지정
  • 모든 할일을 구하여 페이지 초기화
  • todos 를 계속하여 수행
  • todo를 생성하기 위한 폼
  • 만약 체크되었다면 삭제
public 에 index.html을 새로 만들어

위와 같이,

<!-- index.html -->
<!doctype html>

<!-- ASSIGN OUR ANGULAR MODULE -->
<html ng-app="scotchTodo">
<head>
<!-- META -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"><!-- Optimize mobile viewport -->

<title>Node/Angular Todo App</title>

<!-- SCROLLS -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"><!-- load bootstrap -->
<style>
html { overflow-y:scroll; }
body { padding-top:50px; }
#todo-list { margin-bottom:30px; }
</style>

<!-- SPELLS -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script><!-- load jquery -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script><!-- load angular -->
<script src="core.js"></script>

</head>
<!-- SET THE CONTROLLER AND GET ALL TODOS -->
<body ng-controller="mainController">
<div class="container">

<!-- HEADER AND TODO COUNT -->
<div class="jumbotron text-center">
<h1>I'm a Todo-aholic <span class="label label-info">{{ todos.length }}</span></h1>
</div>

<!-- TODO LIST -->
<div id="todo-list" class="row">
<div class="col-sm-4 col-sm-offset-4">

<!-- LOOP OVER THE TODOS IN $scope.todos -->
<div class="checkbox" ng-repeat="todo in todos">
<label>
<input type="checkbox" ng-click="deleteTodo(todo._id)"> {{ todo.text }}
</label>
</div>

</div>
</div>

<!-- FORM TO CREATE TODOS -->
<div id="todo-form" class="row">
<div class="col-sm-8 col-sm-offset-2 text-center">
<form>
<div class="form-group">

<!-- BIND THIS VALUE TO formData.text IN ANGULAR -->
<input type="text" class="form-control input-lg text-center" placeholder="I want to buy a puppy that will love me forever" ng-model="formData.text">
</div>

<!-- createToDo() WILL CREATE NEW TODOS -->
<button type="submit" class="btn btn-primary btn-lg" ng-click="createTodo()">Add</button>
</form>
</div>
</div>

</div>

</body>
</html>

위와 같이 저장합니다.



5) 실행 및 기동


우측 상단의 실행 단추를 눌러 server.js 노드 앱을 기동시키고...

디폴트 라우트에 의하여 public/index.html 이 동작되었습니다.

열심히 네 개의 할일을 넣었더니만...

게중, 하나를 체크하였더니 ... 우왕 이렇게 쉽게 SPA 를 만들다니...

mongo 로 확인을 해 보면.. 이렇게 잘 연동이 되었습니다.

단일 mongoose로 ORM 모델링을 하여 바로 연동하여 이것을 RESTful 로 붙이는 것이 가장 간단한 앱을 쉽게 만들 수 있는 지름길 이군요.

AxisJ의 장기영 팀장님 강의를 들으니 RESTful 중에서 put 은 update 만을 위한 것이라는 것도 기억에 남네요...


어느분께는 도움이 되셨기를...

핑백

덧글

  • 구글노예 2015/03/29 10:47 # 삭제 답글

    요즘 새로운 프로젝트로 인하여 Web Framework에 대한 검색 중 보게 되었습니다.
    SPA를 통하여 프로젝트 하나 정도 시도해 보고 싶네요.
    Mobile Web 기반에서 얼마나 성능을 보일지 모르겠지만, 현재 AngularJs와 JQM을 통하여 좀 더 다양한 기능을 구현 할 수 있을 것 같습니다.
    얼마나 성능을 내보일지 모르겠습니다.

    좋은 내용 잘 봤습니다. 감사합니다.
  • 지훈현서아빠 2015/03/29 15:23 #

    도움이 되셨다면 저의 보람입니다~ ^^
  • 와이빈 2015/05/01 01:21 # 삭제 답글

    깔끔한 정리 덕에 잘 배우고 갑니다 ^^
    angualr의 경우 CDN 말고 설정하는 방법은 없나요? npm을 이용해서 설치를 했는데, 연결하는 방법을 잘 모르겠더라고요.

    CDN으로 하는 것과 서버에 저장해두고 사용하는 것이 차이점을 알 수 있을까요?
  • 와이빈 2015/05/01 01:21 # 삭제 답글

    깔끔한 정리 덕에 잘 배우고 갑니다 ^^
    angualr의 경우 CDN 말고 설정하는 방법은 없나요? npm을 이용해서 설치를 했는데, 연결하는 방법을 잘 모르겠더라고요.

    CDN으로 하는 것과 서버에 저장해두고 사용하는 것이 차이점을 알 수 있을까요?
  • 미르아빠 2016/08/03 19:01 # 삭제 답글

    자료를 찾다보면 왠만하면 읽고 지나가기 바쁜데
    여기 도저히 그냥 못가겠습니다 ㅎㅎ
    고맙단 인사 꼭 해야될것같습니다.
    왠만한 책보다 도움이 더 되었습니다.
    종종 들러 다른글도 많이 읽어 보겠습니다.
    좋은 하루 되십시오
  • 지훈현서아빠 2016/08/03 19:03 #

    도움이 되신것 같아 저의 보람을 느낍니다.
    고맙습니다~ ^^
  • 빌리캥 2016/12/20 20:00 # 삭제 답글

    ㅎ 제 이름이 현서 인데 nodejs angularjs 구글링하다가 눈에 떠서 왔는데 많은 도움이 됐습니다.

    감사합니다. ^^
  • 지훈현서아빠 2016/12/21 09:19 #

    ㅎㅎ 중1인 제 딸과 같은 좋은 이름을 가지고 계시는 군요~ ^^
    도움이 되셨다니 저의 보람입니다.
댓글 입력 영역

구글애드텍스트