Programmazione di Ku0re

Angular 4 Tutorial Nono) Angular 4 HeroEditor - 5-2 본문

Programmazione/Angular

Angular 4 Tutorial Nono) Angular 4 HeroEditor - 5-2

ku0re kuore 2017. 5. 4. 05:38

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

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

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


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




Angular 4 HeroEditor - 5-1




출처: http://ku0re.tistory.com/ [Programmazione di Ku0re]

출처: http://ku0re.tistory.com/ [Programmazione di Ku0re]

출처: http://ku0re.tistory.com/ [Programmazione di Ku0re]

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

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

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


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




Angular 4 HeroEditor - 5-1




출처: http://ku0re.tistory.com/ [Programmazione di Ku0re]

출처: http://ku0re.tistory.com/ [Programmazione di Ku0re]

출처: http://ku0re.tistory.com/ [Programmazione di Ku0re]

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

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

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


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




Angular 4 HeroEditor - 5-2


자 이제 이전 포스팅에 이어서 Routing 부분을 마무리 지어보자.


5-1에서 dashboard를 추가하고 메인화면에서 dashboard와 Heroes 목록을 이동하는 링크를 추가했다.


이번엔 여기서 HeroesDetail 컴포넌트를 분리하려고 한다.


Dashboard에서 영웅을 클릭해서 영웅 상세화면으로 가고 싶을 때는 어떻게 해야 할까?


영웅 목록 컴포넌트의 영웅 데이터를 가지고와서 비교해야 할까?


그럼 URL은 어떻게 구성되어야 할까?


추가로 현재 HeroesDetail 컴포넌트는 Heroes 목록 컴포넌트 밑에 붙어 있다.


그럼 영웅 상세화면을 보려면 무조건 영웅 목록을 같이 봐야 할까?



이런 문제를 해결하기 위해 HeroesDetail 컴포넌트는 독립적인 화면으로 구성하고 영웅의 id에 따른 URL로 이동하도록 만들려고 한다.


그럼 먼저 경로를 추가해 보자.



app/app.module.ts



path로 detail/:id가 추가되었다.


여기서 /:id가 어떤 것인지 예상이 될 것이다.


당연히 영웅의 id 값이 들어오고 결국 주소는 http://localhost:4200/detail/11 이런식으로 들어가게 된다.



자 이젠 HeroDetail 컴포넌트가 변경되어야 한다.


기존의 부모 컴포넌트로부터 영웅 데이터를 받는 것이 아닌 URL의 param으로 영웅 id를 받아서 해당 id의 영웅을 보여줘야 한다.


변경해보자.



app/hero-detail/hero-detail.component.ts



(현재 heroService에 getHero를 만들지 않았기 때문에 에러가 나는 것은 당연하다.)



뭔가 많이 추가됐다.


차근차근 살펴보면 Import 부분에서는 ActivatedRoute는 현재 경로에 대한 정보를 얻을 수 있는 모듈이다.


Location은 경로와 관련된 다양한 함수를 사용할 수 있는 모듈이다.


그리고 HeroService가 추가되었다.


그리고 rxjs의 SwitchMap을 사용하기 위해 import 해둔 부분이 보인다.



로직 부분을 보면 생성자에 heroService와 ActivatedRoute, 그리고 location에 관한 서비스를 사용하도록 명시해두고, ngOnInit 부분에서는 Rxjs를 사용하여 heroService의 getHero 메소드를 사용하여 얻어온 데이터를 현재 Hero 변수에 삽입하고 있다.


여기서 중요하게 볼 부분은 ngOnInit 부분이다.


설명하자면 this._route.params 이 부분은 Angular에서 만든 ActivatedRoute 모듈의 params 기능을 사용하여 현재 경로의 매개변수를 탐색하여 Params 형식의 Observable을 리턴하는 부분이다.



그럼 여기서 Observable이 뭘까? 이 부분에 관해서는 Rxjs를 알아야 한다.


Angular 2에서 Rxjs를 적극 도입했는데(아직 초보라 Angular 4와의 차이가 어느 정도인지는 모르겠다. 참고만 하자.), Rxjs는 기존의 절차형 프로그래밍 방식에서 반응형 프로그래밍 방식으로 바꿔주는 라이브러리이다.


간략하게 설명해서 이 부분에 대해 인터넷에서 가장 많은 예제로 나와있는 것은 바로 Excel이다.


엑셀에서 C 셀에 A셀+B셀이라고 공식을 적어두면 C에서는 A와 B를 합친 값이 나오게 된다.


거기에 A나 B의 값을 바꾸면 C는 자동으로 합계를 계속 출력하게 된다.



반응형 프로그래밍은 엑셀의 예처럼 A+B=C 라는 데이터 흐름을 만들고 A, B를 관찰해서 해당 값이 바뀌면 C에게 알려주게 되고, 변경된 A, B의 값은 데이터 흐름을 통해 다시 C의 결과로 나타나게 된다.


내부에서 이벤트를 발생시켜 외부로 방출하는 것이 아닌, 미리 데이터 스트림을 만들어 두고 외부에서 반응이 생기면 해당 데이터 스트림을 따라서 결과가 나오게 되는 것이다.



다시 코드로 돌아가서 이 데이터 흐름을 만들기 위해 필요한 것은 관찰 대상과 관찰자다.


현재 코드에서 this._route.params에서 ActivatedRoute의 params는 Observable을 리턴한다고 했다.


바로 이 Observable이 관찰 대상이 된다.


그럼 Observable<Params>가 리턴되기 때문에 Params 형식, 즉 매개변수 형식의 관찰 대상이 리턴되었고, 이는 현재 경로로 들어오는 매개변수를 관찰 대상으로 보는 것이다.



이어서 .switchMap은 현재 관찰 대상의 값을 가지고, 새로운 공식에 적용시키는 Rxjs의 함수다.


위 코드를 살펴보면 관찰 대상이 된 매개변수를 가지고 heroService의 getHero()에 적용시키는 것을 볼 수 있다.


+params['id']는 매개변수 중 id 매개변수를 뜻하는 것이다.


마지막으로 subscribe()는 관찰자를 뜻한다.


해당 데이터 흐름을 관찰해서 결과를 도출해내는 함수가 되며 데이터 흐름을 따라 마지막으로 나온 데이터, 즉 여기서는 getHero의 결과가 된다.


해당 Hero 데이터를 현재 컴포넌트의 Hero 변수에 넣게 된다.


Observable => 데이터 흐름 => Subscribe() 이런 식이다.


이어서 HeroService에 getHero를 만들어보자.



app/hero.service.ts



getHero를 만들었다.


id를 매개변수로 받고 getHeroes()를 이용하여 받은 영웅 목록 중 id가 일치하는 영웅을 리턴하도록 하였다.


여기서 Promise가 에러가 난다.


사실 이 부분에서 많이 헤맸다..


Alexband님의 블로그와 똑같이 했지만 나만 에러가 났다..(Angular 4로 넘어오면서 강제하는 건가 모르겠다.. 나중에 알게 되면 내용을 추가하겠다.)

(혹시 아시는 분은 댓글로 간단히 남겨주시면 감사하겠습니다!)


에러 내용은 이랬다.



Promise를 사용하려면 Promise<T> 형태로 한 개의 인수가 필요하다는 것.


뭘 넣어야하나 몰라서 역시 구글신께 맡기기로 했다.


구글신께서 역시나 구글신의 형제 신이신 스택오버플로우를 맨 위에 띄워주셨다.(정말 찬양.. 후..)


그 해결 법은 인수로 <any>를 주는 걸로 해결됐다.




자 이제 문제도 해결됐으니 다시 본론으로 들어가자.



그런데 튜토리얼에서는 또 하나의 버튼을 추가하려 한다.


바로 뒤로가기 버튼이다.


heroDetail 컴포넌트에 추가하여 사용자의 편의성을 높이려 한다는 데..


구글이 튜토리얼을 급하게 만들었나보다.. 참 자꾸 왔다 갔다..


먼저 heroDetail 컴포넌트에 뒤로가기 버튼을 추가하자.



app/hero-detail/hero-detail.component.html



그리고 goBack() 함수를 추가한다.



app/hero-detail/hero-detail.component.ts



맨 밑에 .subscribe(hero => this.hero = hero);에서 에러가 났다.


사실 이 부분에서 아까 그 app/hero.service.ts의 Promise에 인수를 붙여야 하는 것을 알았다.(그 만큼 중간 점검이 중요하다.)


그 Promise에 <any> 인수를 붙여주니 이 에러도 함께 해결됐다.(아마 위에 추가한 내용대로 했다면 이 에러는 나오지 않을 것이다.)




자 이제 이어서 dashBoard에서 영웅을 클릭하면 바로 상세보기 페이지로 가도록 만들어야 한다.


그러기 위해서는 현재 div로 되어있는 부분을 a 태그로 바꿔야 한다.




app/dashboard/dashboard.component.html



*ngFor가 들어가는 부분을 a 태그로 변경하였다.


여기서 routerLink 부분을 보면 경로와 매개변수가 배열로 들어간 것을 확인할 수 있다.


자 이제 저장하고 결과를 확인해 보자.


아까 나왔던 에러 메세지 없이 정상적으로 잘 나온다.


a 태그로 인해 영웅 이름에 링크가 걸려 클릭할 수 있게 됐다.



클릭하면 상세화면으로 잘 이동 되는 것을 확인할 수 있다.



이어서 해야 할 작업은 현재 app.module.ts에서 지정한 Route를 분리해내는 것이다.


현재로서는 경로가 4개 밖에 없지만, 실제 서비스를 만들 때는 엄청난 수의 경로를 만들 것이고, 더불어 권한 관리를 하며 경로에 대한 접근을 제한할 수도 있다.



이를 효율적으로 관리하기 위해 라우팅을 위한 새로운 모듈을 만들 것이다.


app 폴더에 app.routing.module.ts 파일을 만들자.



app/app.routing.module.ts



어떤 방식인지 살펴보자.


일단 route에 관련된 부분을 변수로 처리했다.


이렇게 하면 나중에 export를 할 수 있으며, 새로운 라우팅 모듈이 추가 되었을 때도 모듈 패턴을 명확히 할 수 있다.


app.module.ts에서 사용했던 RouterModule.forRoot(routes)를 import 했다.


그리고 후에 경로 접근을 제어할 providers를 추가할 수 있다.


라우팅 파일을 만들었으니 이젠 app.module.ts 파일을 손 보자.



app/app.module.ts



app.module 파일이 훨씬 단순해졌다. 참 보기 좋다.


아까 만든 라우팅 파일을 import하고 app.module에서는 해당 라우팅 파일을 imports에 추가만 해주었다.



자 이제 Angular 튜토리얼에서 뭘 하라는지 확인해보자.


이번에는 영웅 목록 페이지에서 미니 상세보기를 만들라고 한다.


영웅 목록에서 영웅을 클릭하면 미니 상세보기 화면이 나오고 해당 미니 상세보기 화면에서 버튼을 클릭하여 상세 화면으로 가는 방식을 원한다.


그럼 추가해보자.



app/heroes/heroes.component.html



기존에 있던 app-hero-detail 태그를 지우고 새로운 미니 상세보기 화면을 넣었다.


여기서 새롭게 사용된 구문이 있는데 영웅의 이름을 대문자로 표시하기 위해서 pipe 기능을 사용했다.


{{ selectedHero.name | uppercase }} 가 그 부분이다.



자 이제 마지막으로 버튼을 클릭하면 영웅 상세보기로 가도록 gotoDetail() 함수를 작성하자.



app/heroes/heroes.component.ts