2019. 4. 7. 17:37ㆍFrontend/AngularJS
해당 포스트는 Inflearn AngularJS 강좌를 기반으로 작성되었으며, 언급되는 개념 및 실습 과정을 담았습니다.
따라서, 코드는 단계적으로 작성되어 이전 코드와 연결되어 있습니다.
AngularJS
Service
Controller의 분리
이전 포스트에서 Controller, Directives, app을 분리했었는데
이번엔 Controller 내부의 코드를 분리하는 작업을 할 것이다.
Controller의 역할
Data 관리 기능
View 관리 기능
이 두가지를 분리할 것인데,
Data 관리(조작)하는 부분을 Angular의 Service
기능을 사용해서 분리할 것이다.
Angular Service
구현 방법 3가지 (service
, factory
, provider
) 중 factory
함수 사용할 예정
TODO
서비스 구현 방법 3가지 비교
데이터 정의 분리
우리는 데이터를 controller
안에 정의해뒀었다.
해당 데이터를 따로 관리하기 위해서,
storageData
라는 서비스를 생성하여 get()
함수를 사용해 데이터를 가져오도록 바꿔보자.
services.js
파일을 생성하여 서비스파일을 따로 관리해준다.서비스 이름을
todoStorage
로 정의하였다.storage
변수는todos
라는 객체와get()
함수를 포함한다.storage
를 리턴한다. 외부에서get()
함수를 사용해 데이터에 접근할 수 있다.
services.js
1.// 서비스 이름 : todoStorage -> 해당 키 값으로
2.angular.module('todo').factory('todoStorage', function() {
3. var storage = {
4. todos: [
5. {
6. title: '요가 수업',
7. completed: false,
8. createdAt: Date.now()
9. },
10. {
11. title: '앵귤러 학습',
12. completed: false,
13. createdAt: Date.now()
14. },
15. {
16. title: '운동하기',
17. completed: true,
18. createdAt: Date.now()
19. }
20. ],
21. get: function() {
22. return storage.todos;
23. }
24. };
25.
26. return storage;
27.});
기존
controller
에서todoStorage
서비스로부터 데이터를 받아온다.todoStroage
를 매개변수로 받아 접근할 수 있다.서비스에서 생성한
get()
함수를 통해todos
데이터를 받아온다.
controller.js
1.angular.module('todo').controller('TodoCtrl', function($scope, todoStorage) {
2. $scope.todos = todoStorage.get();
3.
4. // 여기서부턴 기존 코드와 동일
5. $scope.remove = function(todo) {
6. ...
7. };
8.
9. $scope.add = function(newTodoTitle) {
10. ...
11. }
12.});
index.html
에서services.js
스크립트를 포함해준다.
index.html
1.<script src="services.js"></script>
그럼 정상 동작한다!!
remove 분리
마찬가지로, controller
에 모두 작성해두었던 삭제 기능을 분리해보자.
기존 코드는 다음과 같다.
controller.js (remove 부분)
1. $scope.remove = function(todo) {
2. // todos 배열에서 todo index를 찾는다.
3. var idx = $scope.todos.findIndex(function (item) {
4. return item.id == todo.id;
5. });
6.
7. // idx가 유효할 때 삭제해준다.
8. if (idx > -1) {
9. // 배열 객체에서 제공되는 함수인 splice를 이용하면 원하는 위치에 요소를 삭제할 수 있다.
10. // idx 위치부터 1개의 요소를 삭제한다.
11. $scope.todos.splice(idx, 1)
12. }
13. };
모든 부분이 데이터 조작을 담당하고 있으므로 서비스로 모두 옮기자!
dataStorage
서비스의remove()
함수를 생성해준다.
services.js
1.angular.module('todo').factory('todoStorage', function() {
2. var storage = {
3. todos: ...,
4. get: function() {
5. return storage.todos;
6. },
7. // remove 함수를 추가해주었다!
8. // 기존 코드를 copy&paste 한 경우, 스코프 등을 맞게 수정해야 한다.
9. remove: function(todo) {
10. var idx = storage.todos.findIndex(function (item) {
11. return item.id == todo.id;
12. });
13. \
14. if (idx > -1) {\
15. storage.todos.splice(idx, 1)
16. }
17. }
18. };
19.
20. return storage;
21.});
controller
에서dataStroage
의remove()
를 호출해준다.
controller.js
1. $scope.remove = function(todo) {
2. todoStorage.remove(todo);
3. };
add 분리
마찬가지로, controller
에 모두 작성해두었던 추가 기능을 분리해보자.
기존 코드는 다음과 같다.
controller.js (add 부분)
1. $scope.add = function(newTodoTitle) {
2. // create new todo
3. var newTodo = {
4. title: newTodoTitle,
5. completed: false,
6. createdAt: Date.now()
7. };
8.
9. // push into todos
10. $scope.todos.push(newTodo);
11.
12. // view : input field를 비워준다.
13. $scope.newTodoTitle = null;
14. }
값이 추가된 후 입력창을 비워주는 부분(마지막 줄 코드)은 View 관리 기능이므로, controller
에 그대로 놔둔다.
이외의 코드들은 data 관리 부분이므로 서비스로 모두 옮기자!
services.js
1.angular.module('todo').factory('todoStorage', function() {
2. var storage = {
3. todos: ...,
4. get: ...,
5. remove: ...,
6. // add 함수를 추가해주었다!
7. // 기존 코드를 copy&paste 한 경우, 스코프 등을 맞게 수정해야 한다.
8. add: function(newTodoTitle) {
9. var newTodo = {
10. title: newTodoTitle,
11. completed: false,
12. createdAt: Date.now()
13. };
14.
15. storage.todos.push(newTodo);
16. }
17. };
18.
19. return storage;
20.});
controller
에서dataStroage
의add()
를 호출해준다.
controller.js
1. $scope.add = function(newTodoTitle) {
2. todoStorage.add(newTodoTitle);
3. $scope.newTodoTitle = null;
4. }
localStorage
우리가 만든 웹서비스를 새로고침하면, 모든 데이터가 다 날아간다!
데이터를 일정하게 유지하기 위해 DB를 사용하지만, 우리는 웹 개발만 다루고 있으므로
localStorage
를 사용하여 개발할 것!
localStorage?
로컬 스토리지와 세션 스토리지는 HTML5에서 추가된 저장소
간단한 key-value 형식의 저장소
localStorage
라는 변수로 접근할 수 있다.저장 :
localStorage.setItem(key, value)
가져오기 :
localStorage.getItem(key)
입력은 무조건
String
타입으로 처리된다.약 최대 5mb의 용량을 가진다.
Chrome의 경우 SQLite를 사용한다. 그리고 개발자도구에서 확인할 수 있다.
데이터가 유지되는 웹 개발
데이터를 조작하는 부분이니깐 service
에 localStroage
에 값을 저장하고 꺼내오는 함수를 정의하자.
localStorage 조작 함수 정의
_
:_
은 접근제어자 역할을 하는데,_
가 붙은 variable은private
한 성격을 띈다.||
: 논리연산자OR
을 사용해 예외처리 함expr1 || expr2
=expr1
을true
로 변환할 수 있으면expr1
을 반환하고, 그렇지 않으면expr2
를 반환
services.js
1.angular.module('todo').factory('todoStorage', function() {
2. // 상수는 대문자로 선언
3. let TODO_DATA = 'TODO_DATA';
4.
5.
6. var storage = {
7. // todos는 이제 localStorage에서 꺼내온 값을 보관해 놓는 용도로만 사용한다.
8. todos: [],
9. // localStorage로 부터 TODO_DATA로 저장해둔 값을 꺼내옴
10. _getTodoItem: function() {
11. return JSON.parse(localStorage.getItem(TODO_DATA)) || [];
12. },
13. // localStorage로 부터 TODO_DATA로 값을 저장
14. _saveTodoItem: function(data) {
15. localStorage.setItem(TODO_DATA, JSON.stringify(data));
16. },
17. ...
18.});
localStorage 조작 함수를 사용한 다른 기능 코드 재작성
add
,remove
:_saveTodoItem(data)
함수를 사용해 변경된 데이터 값을 저장한다.get
:_getTodoItem
함수를 사용해 데이터 값을 불러온다.
services.js
1. var storage = {
2. ...,
3. get: function() {
4. // angular.copy를 사용하여 데이터 가져옴
5. angular.copy(storage._getTodoItem(), storage.todos);
6. return storage.todos;
7. },
8. remove: function(todo) {
9. var idx = storage.todos.findIndex(function (item) {
10. return item.id == todo.id;
11. });
12.
13. if (idx > -1) {
14. storage.todos.splice(idx, 1)
15. }
16. // 변동 사항 저장
17. storage._saveTodoItem(storage.todos);
18. },
19. add: function(newTodoTitle) {
20. var newTodo = {
21. title: newTodoTitle,
22. completed: false,
23. createdAt: Date.now()
24. };
25.
26. storage.todos.push(newTodo);
27. // 변동 사항 저장
28. storage._saveTodoItem(storage.todos);
29. }
30. };
31.
32. return storage;
33. });
굳이
angular.copy
를 사용하는 이유
digest cycle을 효율적으로 사용하기 위해 - 참고
$digest()
는 위의 동작원리에서 설명한 대로 현재까지 생성된 모든$scope variable
들의 watcher를 실행하여 값이 변화된 variable의 값을 최신값으로 업데이트해주는 일을 합니다.
$digest()
은 Angular Context 외에서 들어오는 데이터를 모델에 업데이트를 할 때 개발자가 인위적으로 업데이트를 해야하며 이 때 사용하는 것즉,
angular.copy
를 사용하게 되면 내부의 코드에 따라 자동으로 업데이트 된다.
이렇게 코드를 수정하면 다음과 같이 데이터가 저장되고, 새로고침을 해도 유지되는 것을 확인할 수 있다.
update 기능 구현
아직 아이템의 체크박스를 선택하거나 텍스트를 변경하였을 때 변동사항이 반영되지 않는다 문제가 존재한다.
이 문제를 해결해보자.
todoItem.tpl.html
수정하여 특정 이벤트 발생시update()
함수 실행시키기체크박스가 클릭되었을 때
update()
를 실행한다.text 수정 후 focus out 되었을 때
update()
를 실행한다.
todoItem.tpl.html
1.<div class="input-group mb-3">
2. <div class="input-group-prepend">
3. <div class="input-group-text">
4. <!-- ng-click 이벤트를 추가해준다 -->
5. <input type="checkbox" ng-model="todo.completed" ng-click="update()">
6. </div>
7. </div>
8. <!-- ng-blur 이벤트를 추가해준다 -->
9. <input type="text" ng-model="todo.title" ng-blur="update()">
10. <div class="input-group-append">
11. <button class="btn btn-danger" type="button" ng-click="remove(todo)">삭제</button>
12. </div>
13.</div>
14.<date>{{ todo.createdAt | date : 'yyyy-MM-dd HH:mm:ss'}}</date>
update()
함수를controller
에 명시해준다.위의
todoItem.tpl.html
은index.html
에서todo-item
이라는 디렉티브를 사용해서 내장하고 있다.해당 코드는
<body ng-app="todo" ng-controller="TodoCtrl">
내부에 선언되어있다.즉,
todo
모듈의TodoCtrl
컨트롤러에update()
함수를 명시해줘야한다.
controllers.js
1.angular.module('todo').controller('TodoCtrl', function($scope, todoStorage) {
2. ...
3.
4. $scope.update = function() {
5. // 서비스의 함수를 사용해 업데이트 한다.
6. todoStorage.update();
7. }
8.});
todoStorage
서비스에update()
함수를 정의한다.업데이트 하는 것도 데이터 조작에 속하므로 이곳에 코드를 작성하여 관리한다.
services.js
1.angular.module('todo').factory('todoStorage', function() {
2. let TODO_DATA = 'TODO_DATA';
3.
4. var storage = {
5. todos: [],
6. ...,
7. update: function(todo) {
8. storage._saveTodoItem(storage.todos);
9. }
10. };
11.
12. return storage;
13.});
최종적으로 테스트하면 이렇게 데이터가 반영되고 유지되는 것을 확인할 수 있다.
The end & More
해당 부분까지만 강좌는 다루고 있었는데,
실제 프로젝트를 개발하려면 네트워크 통신과 관련된 더 많은 것을 배워야 할 것 같았다.
TODO
내장 서비스
$http
:ajax
통신 지원하며ng-http
를 사용해 구현한다.$resource
: http 서비스를 랩핑해놓은 것으로,REST API
에 최적화되어 있다.$q
: 비동기 코드를 쉽게 처리하기 위한promise
코드를 제공한다.
테스트 : 모듈화하여 쉽게 테스트 코드를 작성할 수 있다.
라우팅 :
ng-route
를 사용해SPA
(ajax
를 사용해 브라우저 단에서 라우팅 수행하곤 한다.)를 구현할 수 있다.
YEOMAN
미리 작성된 코드 템플릿을 제공한다.
해당 코드를 참고해 angular
를 어떻게 작성하며 개발하