[AngularJS] AngularJS 학습 (3)

2019. 4. 7. 17:37Frontend/AngularJS

반응형
AngularJS

해당 포스트는 Inflearn AngularJS 강좌를 기반으로 작성되었으며, 언급되는 개념 및 실습 과정을 담았습니다.
따라서, 코드는 단계적으로 작성되어 이전 코드와 연결되어 있습니다.

> AngularJS 학습(1) 포스트 보러가기
> AngularJS 학습(2) 포스트 보러가기

AngularJS

Service

Controller의 분리

이전 포스트에서 Controller, Directives, app을 분리했었는데
이번엔 Controller 내부의 코드를 분리하는 작업을 할 것이다.

Controller의 역할

  1. Data 관리 기능

  2. View 관리 기능

이 두가지를 분리할 것인데,
Data 관리(조작)하는 부분을 Angular의 Service 기능을 사용해서 분리할 것이다.

Angular Service

구현 방법 3가지 (service, factory, provider) 중 factory 함수 사용할 예정

TODO

서비스 구현 방법 3가지 비교

데이터 정의 분리

우리는 데이터를 controller안에 정의해뒀었다.
해당 데이터를 따로 관리하기 위해서,
storageData라는 서비스를 생성하여 get()함수를 사용해 데이터를 가져오도록 바꿔보자.

  1. 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.});
  1. 기존 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.});
  1. 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. };

모든 부분이 데이터 조작을 담당하고 있으므로 서비스로 모두 옮기자!

  1. 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.});
  1. controller에서 dataStroageremove()를 호출해준다.

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.});
  1. controller에서 dataStroageadd()를 호출해준다.

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를 사용한다. 그리고 개발자도구에서 확인할 수 있다.

@개발자도구를 켜보자

개발자도구를 켜보자

데이터가 유지되는 웹 개발

데이터를 조작하는 부분이니깐 servicelocalStroage에 값을 저장하고 꺼내오는 함수를 정의하자.

  1. localStorage 조작 함수 정의

    • _ : _은 접근제어자 역할을 하는데, _가 붙은 variable은 private한 성격을 띈다.

    • || : 논리연산자 OR을 사용해 예외처리 함

      • expr1 || expr2 = expr1true로 변환할 수 있으면 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.});
  1. 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를 사용하게 되면 내부의 코드에 따라 자동으로 업데이트 된다.

이렇게 코드를 수정하면 다음과 같이 데이터가 저장되고, 새로고침을 해도 유지되는 것을 확인할 수 있다.

Alt text

update 기능 구현

아직 아이템의 체크박스를 선택하거나 텍스트를 변경하였을 때 변동사항이 반영되지 않는다 문제가 존재한다.
이 문제를 해결해보자.

  1. 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>
  1. update()함수를 controller에 명시해준다.

    • 위의 todoItem.tpl.htmlindex.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.});
  1. 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.});

최종적으로 테스트하면 이렇게 데이터가 반영되고 유지되는 것을 확인할 수 있다.

Alt text

The end & More

해당 부분까지만 강좌는 다루고 있었는데,
실제 프로젝트를 개발하려면 네트워크 통신과 관련된 더 많은 것을 배워야 할 것 같았다.

TODO

  • 내장 서비스

    • $http : ajax통신 지원하며 ng-http를 사용해 구현한다.

    • $resource : http 서비스를 랩핑해놓은 것으로, REST API에 최적화되어 있다.

    • $q : 비동기 코드를 쉽게 처리하기 위한 promise 코드를 제공한다.

  • 테스트 : 모듈화하여 쉽게 테스트 코드를 작성할 수 있다.

  • 라우팅 : ng-route를 사용해 SPA(ajax를 사용해 브라우저 단에서 라우팅 수행하곤 한다.)를 구현할 수 있다.

YEOMAN

미리 작성된 코드 템플릿을 제공한다.
해당 코드를 참고해 angular를 어떻게 작성하며 개발하

반응형