Full-Stack Web Development: The Big Picture
Three Tier Architecture
보통 웹개발에서 말하는 3-티어 아키텍쳐는 아래의 세 영역으로 나뉜다.
- Presentaion Layer : HTML, CSS, JS
- Business Layer : Ruby, Python, Java, C++
- Data Access Layer : DBMS
반면에 Full-Stack 웹 개발은 자바스크립트를 이용하여 위의 세 layer 의 개발을 가능하도록 하는 방법을 말하며, 조금씩 다른 프레임웍을 사용할 수 있겠지만, 이 강의에서는 presentaion layer 에는 자바스크립트 프레임웍인 AngularJS, business layer 에서는 NodeJS, 그리고 Data Access Layer 영역에서는 자바스크립트를 이용한 MongoDB 를 사용하여 개발한다.
이 세 영역은 모두 자바스크리트 기반으로 만들어져 있기 때문에 서버, 브라우저, 모바일 디바이스 등 모든 영역에서 JSON 형태로 통신이 가능하다.
Course Overview
이번 강의에서 다루는 주제는 다음과 같다.
- AngularJS : 자바스크립트 프레임워크 (버전: 1.4.2)
- Web Tools : Grunt, Gulp, Yo and Yeoman
Introduction to AngularJS
Front-End JavaScript Frameworks OverView
소프트웨어 라이브러리란 어떤 동작을 실행하는 잘 정의된 인터페이스(or functions or methods)의 집합이라 할 수 있다. 재사용성과 모듈화를 통해서 더 효율적인 개발이 가능하다. 대표적인 예로 jQuery 가 있다.
소프트웨어 프레임워크란 라이브러리와는 다소 차이가 있다. 라이브러리는 이미 잘 만들어진 유용한 코드(functions)를 사용한다고 한다면, 프레임워크는 개발자가 작성한 코드가 프레임워크안에서 적절하게 실행할 수 있도록 환경을 제공하는 것이라 할 수 있다. 프레임워크는 일반적인(generic) 함수 셋을 제공하며 개발자가 구체적인 코드를 구현해야한다. 그리고 구현된 코드는 프레임워크가 필요한 경우, 어떤 임무를 완수하기 위해 불려진다. 즉, 라이브러리는 개발자에게 코드를 컨트롤 할 수 있는 권한이 주어지는 반면 프레임워크는 코드의 컨트롤 권한이 프레임워크에게 있는 것과 같다. 이번 강의에서 배울 AngularJS 나 durandal, backbone 등이 대표적이다.
10 가지 Javascript Framework
- Angular : one of the three Major JS framework
- Ember : one of the three Major JS framework
- Backbone : one of the three Major JS framework
- React : 프레임웍이라기 보다 라이브러리에 가까움
- Aurelia
- Meteor : 요즘 각광받기 시작
- Polymer
- Knockout
- Vue
- Mercury
3 가지 메이저 JS Frameworks 비교 (3 > 2 > 1)
Ember | Angular | Backbone | |
---|---|---|---|
Opinionated | 1 | 2 | 3 |
Ease of Use | 3 | 2 | 1 |
Learning Curve | 1 | 2 | 3 |
Popularity | - | 3 | - |
Introduction to AngularJS
HTML 은 static 한 문서이기 때문에 동적인 웹 어플리케이션을 지원하기에는 HTML 만으로는 한계가 있다. 보통은 자바스크립트의 DOM 객체를 이용해서 HTML 을 동적으로 만들 수 있지만, 자바스크립트 프레임웍인 Angular 를 이용하면 다음과 같은 이점을 가질 수 있다.
Solving the impedance mismatch
back end 데이터와 static content 를 출력하는데 HTML 만으로는 한계가 있기 때문에 이 문제(impedance mismatch)를 해결이 가능하도록 해준다.
Designed with CRUD applications (data-driven) in mind
데이터가 변경되면 변경된 데이터에 맞춰서 동적으로 HTML 이 update 된다. Create, Read, Update, Delete 이 네 가지를 이르는 CRUD 에 대해서는 다음 모듈에서 자세하게 다루기로 한다.
Declarative approach
Angular 는 선언적인 개발방법을 지원하는데, 이는 개발자가 원하는 것을 기술하면 Angluar 가 그에 맞게 처리해내는 것을 뜻한다(무슨 말인지 잘...)
Angular Vocabulary
앞으로 배울 Angular 의 문법은 아래와 같은 것들이 있다.
- Two-way Data Binding
- Scope
- Directives
- Templates
- Routing
- Testing
- Modules
- Controllers
- Filters
- Factory
- Service
- Provider
우선은 Two-way Data Binding 과 Directives 에 대해서 먼저 알아보자
Directives
Angular Directives 는 ng-_ or data-ng-_ 로 시작하는 HTML 속성이다. 다음의 몇가지 예를 살펴보자
- ng-app : Angular 앱을 시작과 끝을 나타낸다. 어떠한 태그에도 붙일 수 있다. 즉, 하나의 HTML 파일에 하나 이상의 Angular 앱이 존재할 수도 있다는 말과 같다.
- ng-init : 자바스크립트 변수를 선언하는것과 같다. Angular expression 을 Evaluation 한다(좀더 매끄러운 한글 표현이 필요할 것 같다). object, array 도 사용가능하다.
<p ng-init="index=1"></p>
<div ng-init="dish={name:'example', ...}"></div>
- ng-model : input value 를 변수로 바인드 한다. (Two-way data binding) ng-model 속성을 부여하면 언제든지 어떤 변수든지 변경이 가능하게 된다.
<p>Comment: {{dish.comment}}</p>
<p>Type your comment:
<input type="text" ng-model="dish.comment" />
</p>
dish object 의 comment 는 위에서 이미 정의를 한 상태다. 이때 아래의 input tag 에서 ng-model 속성을 부여하고 이미 정의된 dish.comment 를 입력하면 Two-way data binding 에 의해 처음 설정된 값이 input 태그의 변경되는 값으로 동적으로 변경된다.
양 방향에서 바인딩이 가능하기 때문에 아마도 Two-way data binding 이라고 부르는 것 같다.
- ng-repeat : 반복적인 태그를 작성할 필요가 있을때 사용할 수 있다.
Angular Expressions
- Evaluated against an Angular scope object Angular 스코프에 맞게 value 가 평가된다.
- No conditionals, loops, or exceptions 위의 사항들은 expression 으로 사용이 불가하다.
- Expressions enclosed in {{ expression }} 중괄호 두개를 겹쳐서 사용한다.
<p>6 + 5 = {{ 6 + 5 }}</p>
<h2>{{ dish.name }}</h2>
Models, Views and Controllers
The Model View Controller Framework
Design pattern is ell-documented solution to a recurring problem
디자인 패턴은 비슷하고 반복되는 문제를 매번 똑같이 구현하는 것이 아니라 재사용 가능하도록 만들어진 일종의 솔루션을 말한다.
MVC
MVC 는 소프트웨어 엔지니어링 구조에서 가장 대표적인 디자인 패턴이다. M(Model), V(View), C(Controller) 세가지 영역으로 나누어져있고, 각 영역은 독립적으로 존재한다.
- Model : 애플리케이션에서 도메인의 상태나 도메인 로직을 구현하는 영역이다. 여기서 말하는 도메인은 일반 개발 영역에서 말하는 것과 마찬가지로 소프트웨어가 다루는 특정 활동이나 지식의 범주를 뜻한다(표현이 조금 어렵다). 웹 어플리케이션단에서 보면 model 은 request 요청에 대한 상태 변화에 반응하여 특정 도메인의 행위나 상태를 관리한다고 볼 수 있다. 그리고 대게 모델의 변화는 컨트롤러에 의해 발생한다.
- View : 사용자에게 보여지는 영역이다. view 는 유저와 상호작용을 위해 form 에 모델정보를 redering 한다. 하나의 model 로 서로 다른 목적의 뷰를 만들수도 있다.
- Controller : View 와 모델 사이를 중재하는 역할을 한다. 일반적으로 컨트롤러는 유저의 input 을 받아서 모델의 상태변화를 만들어낸다. 그렇기 때문에 컨트롤러는 결과적으로 모델의 변화를 이끌어 낸다고 할 수 있다.
MVVM (Model View View-Model)
MVC 패턴의 파생된 형태중 하나인 MVVM 은 Model, View, View-Model 로 구성된다. 컨트롤러에 들어갈 비즈니스 로직이 모델이 있으며 View-Model 과 View 사이에서 데이터 바인딩이 이루어진다.
Angular Modules and Controllers
일반적으로 Angular 도 MVC 패턴을 따르고 있다고 할 수 있지만, 어떤 사람들은 Angular 가 MVVM 이 적합하다고도 한다. 또한 많은 사람들은 모델(M)과 뷰(V), 그리고 그 사이는 무엇이든 상관없다는 MVW(model-view-whatever)로 간단히 부르기도 한다.
Angular Modules
Angular Modules 은 다음처럼 스크립트 태그안에 정의한다. angular.module 메서드는 두개의 파라미터를 가지는데, 첫번째는 ngApp 속성에 정의된 이름이고 두번째는 array 인데 나중에 설명할 것이다. 아래같은 경우 ngApp 이 html 태그에 선언되어 있으므로 아래의 모듈에서 현재 페이지 모두를 관리 할 수 있게 된다.
<html ngApp="confusionApp">
...
<body>
...
<script>
var app = angular.module('confusionApp',[]);
</script>
</body>
</html>
Angular Controller
Angular 컨트롤러는 특정 태그의 ng-controller 속성으로 선언된다.
<div class="row row-content" ng-controller="menuController as menuCtrl">
</div>
<script>
var app = angular.module('confusionApp', []);
app.controller('menuController', function() {
var dishes = [item, ... ];
this.dishes = dishes;
});
</script>
모듈과 컨트롤러는 위와 같이 구현할 수 있다. 이전에 보았던 ng-init directive 는 HTML 속성으로 직접 넣어줘야하지만, 컨트롤러를 사용하면 해당 태그(여기서는 div) 내부의 데이터를 자바스크립트 코드로 컨트롤할 수 있게 된다. 컨트롤러의 두번째 파라미터인 익명함수 마지막에 this.dishes = dishes 부분은 아마도 div 태그 내에서 사용가능한 dishes 를 정의해주기 위해 자바스크립트 오브젝트인 dishes 를 this.dishes 에 할당해준게 아닌가 생각된다.
Angular Filters
필터는 서버사이드 또는 클라이언트에서 만들어진 data 를 end user 에게 잘 표현하기 위한 수단으로 사용된다. 필터는 기반 데이터를 바꿀 수는 없으며 view templates, controllers, services 등에서 사용된다. AngularJS 는 기본적으로 빌트인 필터를 여러개 제공하고 있고, 개발자 필요에 따라 커스텀 필터를 만들어 사용할 수 있다.
<div class="media-body">
<h2 class="media-headgin">{{dish.name}}
<span class="label label-danger label-xs">{{dish.label}}</span>
<!-- currency 필터는 price에 $를 붙여준다.-->
<span class="badge">{{dish.price | currency}}</span>
</h2>
<p>{{dish.description}}</p>
</div>
Angluar 의 Built-in Filters
- uppercase / lowercase : converts the text
- currency : $를 붙여준다.
- date : 날짜 포맷을 변경한다.
- filter : 특정 조건에 맞게 array 의 서브셋을 리턴한다.
- orderBy : 조건에 맞게 정렬한다.
- json, limitTo 등도 있다.
<!-- filter 예제, HTML 코드 -->
<li class="media" ng-repeat="dish in menuCtrl.dishes | filter:menuCtrl.filtText">...</li>
<!-- javascript 코드 -->
var filtText = "";
this.select = function(setTab) {
this.tab = setTab;
if (setTab === 2)
this.filtText = "appetizer"
else if (setTab === 3)
this.filtText = "mains"
else if (setTab === 4)
this.filtText = "dessert"
else
this.filtText = ""
}
위와 같이 HTML 과 javascript 코드를 작성한다. 그리고 특정 탭을 만들어 각 탭에 번호를 부여한다(setTab). 그럼 각 탭 을 눌렀을때 filtText 가 특정 문자열로 변경된다. 미리 적용해놓은 filter 에 따라서 각 li 태그가 보여지기도 하고 가려지기도 할 것이다.
Excercise Code
<!DOCTYPE html>
<html lang="en" ng-app="confusionApp">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head
content must come *after* these tags -->
<title>Ristorante Con Fusion: Menu</title>
<!-- Bootstrap -->
<link href="../bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="../bower_components/bootstrap/dist/css/bootstrap-theme.min.css" rel="stylesheet">
<link href="../bower_components/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<link href="styles/bootstrap-social.css" rel="stylesheet">
<link href="styles/mystyles.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div class="container">
<div class="row row-content" ng-controller="menuController as menuCtrl">
<div class="col-xs-12">
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" ng-class="{active:menuCtrl.isSelected(1)}">
<a ng-click="menuCtrl.select(1)" aria-controls="all menu" role="tab">The Menu</a>
</li>
<li role="presentation" ng-class="{active:menuCtrl.isSelected(2)}">
<a ng-click="menuCtrl.select(2)" aria-controls="appetizers" role="tab">Appetizers</a>
</li>
<li role="presentation" ng-class="{active:menuCtrl.isSelected(3)}">
<a ng-click="menuCtrl.select(3)" aria-controls="mains" role="tab">Mains</a>
</li>
<li role="presentation" ng-class="{active:menuCtrl.isSelected(4)}">
<a ng-click="menuCtrl.select(4)" aria-controls="desserts" role="tab">Desserts</a>
</li>
</ul>
<div class="tab-content">
<ul class="media-list tab-pane fade in active">
<li class="media" ng-repeat="dish in menuCtrl.dishes | filter:menuCtrl.filtText">
<div class="media-left media-middle">
<a href="#">
<img class="media-object img-thumbnail" ng-src="{{dish.image}}" alt="Uthapizza">
</a>
</div>
<div class="media-body">
<h2 class="media-heading">{{dish.name}}
<span class="label label-danger">{{dish.label}}</span>
<span class="badge">{{dish.price | currency}}</span>
</h2>
<p>{{dish.description}}</p>
<!-- <p>Comment: {{dish.comment}}</p> -->
<!-- <p>Type your comment: -->
<!-- <input type="text" ng-model="dish.comment"> -->
</p>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
<script src="../bower_components/angular/angular.min.js"></script>
<script>
var app = angular.module('confusionApp', []);
app.controller('menuController', function(){
this.tab = 1;
this.filtText = '';
var dished = [
{
name: 'Uthapizza',
image: 'images/uthapizza.png',
category: 'mains',
label: 'Hot',
price: '4.99',
description:'A unique combination of Indizan Uthappam (pancake) and Italian pizza, topped with Cerignola olives, ripe vine cherry tomatoes, Vidalia onion, Guntur chillies and Buffalo Paneer',
comment: 'aaaaaaa'
},
{
name: 'Uthapizza2',
image: 'images/zucchipakoda.png',
category: 'mains',
label: '',
price: '4.99',
description:'A unique combination of Indizan Uthappam (pancake) and Italian pizza, topped with Cerignola olives, ripe vine cherry tomatoes, Vidalia onion, Guntur chillies and Buffalo Paneer',
comment: ''
},
{
name: 'Uthapizza3',
image: 'images/vadonut.png',
category: 'appetizer',
label: 'New',
price: '4.99',
description:'A unique combination of Indizan Uthappam (pancake) and Italian pizza, topped with Cerignola olives, ripe vine cherry tomatoes, Vidalia onion, Guntur chillies and Buffalo Paneer',
comment: ''
},
{
name: 'Uthapizza4',
image: 'images/elaicheesecake.png',
category: 'dessert',
label: '',
price: '4.99',
description:'A unique combination of Indizan Uthappam (pancake) and Italian pizza, topped with Cerignola olives, ripe vine cherry tomatoes, Vidalia onion, Guntur chillies and Buffalo Paneer',
comment: ''
},
];
this.dishes = dished;
this.select = function(setTab) {
this.tab = setTab;
if (setTab === 2)
this.filtText = "appetizer";
else if (setTab === 3)
this.filtText = "mains"
else if (setTab === 4)
this.filtText = "dessert"
else
this.filtText = "";
}
this.isSelected = function(checkTab) {
return (this.tab === checkTab)
}
});
</script>
</body>
</html>