Programmazione di Ku0re

Angular 4 Tutorial Settimo) Angular 4 HeroEditor - 4 본문

Programmazione/Angular

Angular 4 Tutorial Settimo) Angular 4 HeroEditor - 4

ku0re kuore 2017. 5. 3. 20:19

이 글은 단순히 Angular 공식사이트의 Tutorial을 정리해 놓은

Alexband님의 블로그 포스팅을 보고  Angular를 공부하면서 내용을 정리한 글입니다.

공부하실 분은 Angular 공식사이트의 Tutorial을 보시고 제 포스팅은 참고만 하세요! :)


*Angular 4는 Angular 2와 문법적으로 큰 차이는 없다고 합니다.




Angular 4 HeroEditor - 4


이번 포스팅은 Services 편이다.


서비스는 우리의 web application에서 필요한 프로세스 중 자주 쓰는 기능을 하나로 묶어서 여러 컴포넌트에서 동시에 공유해서 사용하는 것이다.


현재 Hero editor에서 영웅 데이터에 접근하는 코드들은 따로 서비스로 묶어서 사용하면 컴포넌트의 독립성을 지키고, 좀 더 뷰에 집중할 수 있도록 만들 수 있다.



구글 Angular Tutorial에서는 현재 Hero Editor가 더욱 확장되기를 원하고 있다.


그들은 여러 페이지에서 다양한 방법으로 영웅을 보여주고 싶어 하며, 최고 영웅의 랭킹이 나타나는 화면, 그리고 영웅의 세부 정보를 보고 편집하는 별도의 화면을 원한다.


이 화면들은 전부 Hero 데이터에 접근할 필요가 있다.



그럼 이제 영웅 데이터에 접근하는 서비스를 만들어 여러 컴포넌트가 같이 사용할 수 있도록 하자.


먼저 app 디렉터리에 hero.service.ts 파일을 만들자.


마찬가지로 Angular-cli를 사용하자.


cmd 또는 Terminal에서 해당 프로젝트 디렉터리로 가서 ng g service hero.service 명령어를 사용하자.



그럼 위와 같이 app 디렉터리에 hero.service.ts 파일이 생성된다.



app/hero.service.ts



정상적으로 파일이 생성되었다.


여기서 @Injectable() 데코레이터가 있다.


이 Injectable은 해당 로직이 서비스로서 주입이 가능한 클래스라는 것을 표시해 주는 것이다.


참고로 이 Injectable이 없다고 작동하지 않는 것은 아니지만, 해당 클래스가 서비스라는 것을 명확하게 표시해 주기 때문에 서비스를 작성시에는 꼭 @Injectable()을 넣어준다.



그리고 영웅 목록을 불러오는 메소드를 넣을 getHeroes()라는 메소드를 추가해 주자.



그럼 이제 app.component에 들어있는 영웅 목록도 분리해야 한다.


먼저 app 디렉터리를 만들고 mock-heroes.ts 파일을 만들자.


그리고 app. component에 있는 영웅 목록 코드를 가지고 오자.



app/mock-heroes.ts



이전에 만들었던 Hero 모델을 import 시키고 app.component에서 가져온 HEROES 목록을 Export 시켰다.


자 이러면 이제 영웅 목록을 따로 관리할 수 있게 되었다.


여기서 app.component.ts 파일 역시 수정할 부분이 있다.


바로 heroes = HEROES; 이 부분이다.


HEROES를 분리했으니 이제 heroes는 Hero 모델의 영향을 받게 되도록 한다.


그리고 영웅 목록이니 그냥 Hero가 아닌 Hero 객체의 배열로 받아야 한다.


그러니 heroes : Hero[];로 변경하도록 하자.




자 이제 다시 hero.service로 돌아와서 getHeroes() 메소드가 영웅 목록을 반환할 수 있도록 로직을 추가 하자.



app/hero.service.ts



코드를 살펴보면 먼저 Hero 모델을 Import하고 분리해냈던 HEROES 역시 Import 했다.


그리고 getHeroes() 메소드의 return type을 Hero 배열로 지정하고 HEROES를 리턴하도록 했다.


자 이제 HeroService를 사용할 준비가 되었다.



그 전에 여기서 중요한 포인트가 있다.


HeroService를 사용하기 위해서 app.component에서 HeroService의 인스턴스를 가져와야 하는데 어떻게 해야 할까?


기존에 사용하던 것처럼 heroService = new HeroService()로 해야 할까?


Angular에서는 new 연산자를 사용하지 않도록 권장하고 있다.


왜 그럴까??



만약 HeroService의 생성자에서 새로운 파라미터를 받도록 수정되었다고 한다면 해당 HeroService를 사용하는 모든 컴포넌트를 찾아가서 생성자를 넣어주고 수정해야 한다.


이는 WebService가 커질수록 굉장히 부담되는 일이 될 것이다.


추가로 컴포넌트마다 new를 사용한다면 해당 HeroService 객체는 무수히 생겨날 것이다.


그리고 해당 객체를 사용하여 받아온 Heroes를 다른 사용자와 공유하려면 어떻게 해야 할까??


마지막으로 해당 서비스 객체를 생성하는 과정이 컴포넌트에서 구현되기 때문에 어떻게 생성되는지 파악하기 쉽지 않다.



이를 Angular에서는 생성자를 이용하는 것으로 해결한다.


일단 app.component.ts로 가서 코드를 추가하자.



app/app.component.ts



먼저 constructor, 즉 생성자를 만들어서 매개변수로 private heroservice를 만들었고 타입은 HeroService 객체를 따르도록 했다.


그리고 heroes는 heroservice의 getHeroes() 메소드를 사용하여 영웅 목록을 받아왔다.


이제 heroes 변수는 영웅 목록을 리턴 받았다.


한번 제대로 동작하는지 테스트 해보자.



에러가 난다..


이유는 현재 생성자로 heroservice 매개변수가 HeroService를 정의하도록 하였지만 생성자는 이게 다 이다.


Angular의 Injector는 현재 HeroService를 만드는 방법을 모른다.


그럼 Injector가 만들 수 있도록 알려줘야한다.


그러기 위해서는 app.module.ts 파일로 가서 providers에 해당 서비스를 넣어주자.



상단에 HeroService를 Import하고 providers 배열에 HeroService를 넣어줬다.


이제 Angular는 HeroService라는 것을 만드는 제조법을 알고 해당 제조법을 사용하는 곳에 정확하게 해당 제조법을 제공할 것이다.


이제 테스트 해보자.


이제 잘 동작한다.



자 여기서 현재 HeroService의 getHeroes() 메소드는 로직에서 그냥 들어가 있다.


문제 없이 컴포넌트가 로딩되고 getHeroes() 메소드가 실행되게 하려면 어떻게 해야 할까?


생성자를 불러주는 건 좋지 않다.


생성자는 매개변수와 같은 간단한 초기화만 하고, 서버 호출과 같은 무거운 작업은 분리해서 실행해야 한다.


이제 Angular의 라이프사이클 hook을 사용할 때이다.


Angular의 라이프 사이클은 아래 그림과 같다.


영어만 봐도 대충 알 것이다.


연한 색깔들은 상태 체크에 관련되어있고 진한 색깔은 초기화와 관련되어 있다.


간단히 설명하자면 아래와 같다.


constructo : 생성자


ngOnChanges : 속성 바인딩의 상태가 변경될 때 호출


ngOnInit : 컴포넌트와 지시자가 초기화 되었을 때 호출


ngDoCheck : Angular가 자체적으로 감지할 수 없는 변경 사항을 확인한다.


ngAfterContentInit : Content가 컴포넌트 뷰에 입력되고 호출


ngAfterContentChecked() : Content가 초기화된 뒤 외부 콘텐츠를 점검한 뒤에 호출


ngAfterViewInit : 컴포넌트의 뷰 부분이 초기화되고 호출


ngAfterViewChecked : 컴포넌트의 뷰와 자식 뷰를 검사하고 호출


ngOnDestroy : Angular 직전에 호출하면 지시문 / 구성 요소가 손상된다.


대충 정리하자면 이렇다. 자세한건 공식홈페이지의 Lifecycle Hooks 페이지를 확인하자.


어쨌든 여기서 getHeroes() 메소드를 사용할 곳을 찾아보자.


영웅 목록은 뷰에 나타나야 하니 뷰에 데이터가 들어가기전 데이터를 줘야 하니 ngAfterContentInit 전의 초기화 즉, ngOnInit에 넣어줘야 한다.


그럼 app.component.ts로 가서 코드를 수정하자.



app/app.component.ts



상단에서 COmponent와 함께 OnInit를 import 시켜주고 로직 클래스에서 implements를 통해 OnInit 인터페이스를 가져왔다.


그러면 해당 메소드를 구현해야 한다.


클래스 하단에 ngOnInit()을 구현하고 heroes를 가져오는 로직을 추가했다.


자 이제 서비스도 사용했고 다 거의 됐다.


하지만 여기서 또 문제점이 있다.



현재 getHeroes()는 mock 데이터에서 가져오는 중이지만 추후에 http 메소드를 사용하여 서버에서 데이터를 가지고 올 것 이다.


이때 동기식으로 데이터를 가져오게 되면 해당 데이터의 응답이 느릴 경우 사용자가 다른 작업을 할 수 없고 기다려야 한다는 단점이 있다.


이런 현상을 방지하기 위해 우리는 비 동기식으로 코드를 바꿔야 한다.


여기서 Javascript의 문법인 Promise를 사용할 거다.


만약 Promise가 뭔지 모른다면 MDN에서 확인하고 오자.



간단히 말하자면 해당 작업이 완료되면 리턴해줄테니 콜백을 맡겨놓고 가라는 거라고 할 수 있다.


일단 한번 써보자.


먼저 hero.service를 promise를 사용하도록 변경해보자.



app/hero.service.ts



먼저 getHeroes() 메소드의 리턴 타입을 Promise<Hero[]> 배열로 변경하고 resolve 결과로 HEROES를 반환하고 있다.


이제 app.component에서 getHeroes() 메소드를 사용할 때 Promise를 받는 방식으로 변경해야 한다.


app.component 파일을 수정하자.



app/app.component.ts



바뀐 부분을 보자.


먼저 this.heroservice.getHeroes() 메소드를 불러오며 뒤에서 .then 메소드가 사용된다.


이는 Promise 정상적으로 결과를 반환했을 때, 즉 현재로서는 heroservice에서 return된 resolve(HEROES)가 정상적으로 return 되었을 때 .then으로 넘어간다.


그리고 then의 결과로 받은 result (=HEROES)는 현재 heroes 변수로 대입된다.




이제 점점 Angular의 주요 요소들이 나오고 있다고 한다.


점점 어려워 지겠지...?


다음 포스팅은 Routing에 관한 것, 컴포넌트를 경로에 맞게 재조립하는 것을 연습하는 포스팅이 되겠다.


점점 포스팅이 길어진다.. 빠잉

0 Comments
댓글쓰기 폼