angular

angular에서 모듈(module)사용해 보기

goldjun 2021. 11. 8. 15:06

https://kogle.tistory.com/category/Client%20Technologies/Angular?page=1 에서 참조 하였습니다.

 

 

이 때까지 component를 사용하고 template을 사용하는 법을 알았다면 이제 모듈에 대해서 알아보자.

사실 모듈에 대해서는 깊게 생각할건 없다. 그냥 컴포넌트를 모아둔 것이라고 생각하면된다.

아래 자동으로 생성되는 app.module.ts코드를 보자.

 

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';


import { AppComponent } from './app.component';


@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

 

Module은 Component를 모아서 관리한다고 하였다.

여기서 보면 NgModule이라는 데커레이터가 존재하고 그 안에 여러 파라메터들이 존재한다.

각각이 무엇을 의미하는지를 알아보자.

 

declarations - 이 모듈에서 사용 가능한 뷰 클래스를 정의한다. angular에서는 component, directive, pipe 세 종류가 존재한다.

 

exports - 다른 모듈이나 컴포넌트에서 접근할 수 있도록 선언한다. 여기서 선언하지 않는다면 이 모듈이 컴포넌트를 포함하고 있더라도 다른 모듈이나 컴포넌트에서 사용할 수 없다.

 

imports - 다른 모듈을 포함시킬 때 사용한다. 위의 예제에서는 BrowserModule을 임포트 시켰다. Browsermodule은 루트 모듈을 의미한다.

 

providers - 전역의 서비스를 해당 객체에서 사용할 수 있더록 지정한다

 

bootstrap - 루트 모듈에만 존재하는 파라메터로 메인 어플리케이션의 뷰를 선언한다.

 

그럼 이제 사용하는 예제를 한번 보도록하자.

일단 처음 만든상태에서 모듈에 앞서서 컴포넌트를 만들어 보자.

컴포넌트가 모인게 모듈이라고 하였다.

 

컴포넌트 만들기

 

 

ng generate(g) component(c) <컴포넌트 명>

 

보다시피 해당 명령어로 컴포넌트를 만들 수 있다.

본디 ng generate component 같은 형태로 쓰지만 실제로 사용할 때는 ng g c 같이 줄여서 쓰는게 일반적이다.

해당 명령어를 입력했다면 컴포넌트명 이름으로 typescript 소스가 생겼을 것이다.

여기서 컴포넌트를 만드는 위치가 매우매우매우 중요하다.

해당 명령어를 사용하는 위치가 여러분이 따로 만든 모듈이 아니라면 루트모듈에 들어가지만

만약 특정 모듈의 하위에서 실행했다면 해당 모듈로 들어간다.

위의 경우는 루트모듈에서 실행했으므로 루트모듈로 들어갔다.

 

 

보다시피 해당 컴포넌트 파일이 새로 생성됬다 내부는 아래와 같다.

 

 

여러분이 이 때까지 작성했던 모듈과 같은 형태로 되어 있음을 알 수 있다.

단 모든 파일의 이름에 해당 컴포넌트의 이름이 기재되어 있음을 확인할 수 있다.

component의 소스를 보자.

 

import { Component, OnInit } from '@angular/core';

@Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit {

constructor() { }

ngOnInit() {
}

}

위의 코드가 자동완성되었다.

사실 새로운 개념이 몇개 있지만 여기서는 설명을 하지 않도록 하겠다.

그럼 html 소스를 보자.

 

<p>
test works!
</p>

 

해당 코드가 생성 되었음을 확인할 수 있다.

즉 test라는 모듈은 app-test라는 태그에 렌더링을 시켜주는 컴포넌트인 것이다.

이제 기존의 루트 모듈을 보도록 하자.

 

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';

import {AppComponent} from './app.component';
import {TestComponent} from './test/test.component';


@NgModule({
declarations: [
AppComponent,
TestComponent
],
imports: [
BrowserModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}

루트 모듈을 보면 알수 있겠지만 자동으로 몇줄이 바뀌었다.

먼저 declarations에 TestComponent가 추가되었다.

이제 해당 컴포넌트 내부에서 해당 컴포넌트를 사용할 수 있게 된 것이다.

이제 루트 모듈의 부트스트래핑(진입지점)의 컴포넌트인 AppComponent를 보도록하자.

<app-test></app-test>

모든 걸 다 지우고 필자가 추가한 코드이다. app-test라는 태그를 붙혔다.

이 태그는 필자가 만든 test component내에 정의된 템플릿이다.

이제 호출하여보자.

 

 

제대로 작동함을 확인할 수 있다.

 

여기서 질문점이 있다. 질문점을 보기위해서 먼저 index.html을 보도록하자.

 

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MyModule</title>
<base href="/">

<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>

index.html은 외부에서 사용하는 웹을 가정해서 만들어진 샘플 코드라고 하였다.

여기서 app-root라는 태그를 썼는데 이 태그는 app component에 내장된 템플릿이다.

그럼 우리는 app-test라는 태그를 소유한 test component를 만들었는데 이 녀석을 호출할 수 없을까?

결론은 불가능 하다는 것이다.

그 이유는 한 모듈에 bootstrap으로 선언한 템플릿만 외부에서 직접적으로 사용할 수 있다.

그럼 bootstrap에 여러 컴포넌트를 선언할 수 있을까?

그렇지도 않다. bootstrap에 선언가능한것은 오직 하나뿐이다.

즉 하나의 컴포넌트만이 bootstrap될 수 있다.

나머지 템플릿들은 우리가 루트 컴포넌트를 통해서만 표현할 수 있는 것이다.

 

그럼 이제 모듈을 제작해 보자.

 

모듈 만들기

 

 

ng genrate(g) module(m) <모듈명>

 

해당 모듈을 만드는 명령어는 ng g m이다.

물론 길게도 쓸 수 있지만 굳이 그렇게 쓸 이유 없다.

 

 

어느 디렉터리에서 쓰던 상관없이 app의 하위에 만들어 진다.

이 모듈은 루트 모듈이 아니며 루트 모듈에 포함된 하위 모듈이다.

 

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

@NgModule({
imports: [
CommonModule
],
declarations: []
})
export class HumanModule { }

루트 모듈인 AppModule이 BrowserModule을 imports했던 것과는 달리

CommonModule을 imports했다. 이는 이 모듈이 루트모듈이 아니기 때문이다.

모듈은 새로 만들경우 자동으로 컴포넌트들을 제작해 주지 않는다. 그래서 우리는 컴포넌트들을 몇개 새로 만들어야한다.

여기서 중요한것은 경로이다. 아래의 컴포넌트를 생성하는 예제를 보자.

 

 

중요한것은 위치다. 해당 모듈의 하위디렉터리로 이동해서 만들어야 그 모듈의 컴포넌트로 등록된다.

그렇지 않으면 루트 모듈의 컴포넌트로 등록되어서 일일히 컴포넌트를 바인딩 하는 작업을 해줘야한다.

두번 손가지 않게 해주자.

human.module.ts

===============================================================

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ManComponent } from './man/man.component';  //추가
import { WomanComponent } from './woman/woman.component'; //추가

@NgModule({
imports: [
CommonModule
],
declarations: [ManComponent, WomanComponent]  // 추가
})
export class HumanModule { }

===============================================================

보면 HumanModule에 ManComponent와 WomanComponent가 등록된 것을 볼 수 있다.

그럼이제 소스 코드들을 수정해서 템플릿 들을 한번 사용해 보자.

 

human.module.ts

===============================================================

import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ManComponent} from './man/man.component';  //추가
import {WomanComponent} from './woman/woman.component'; //추가

@NgModule({
imports: [
CommonModule
],
exports: [
ManComponent,   //추가
WomanComponent //추가
],
declarations: [ManComponent, WomanComponent]  //추가
})
export class HumanModule {
}

===============================================================

먼저 HumanModule에 손을 대는데 ManComponent와 WomanComponent를 외부에서 사용할 수 있게 exports해준다.

===============================================================

app.module.ts

 

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';

import {AppComponent} from './app.component';
import {TestComponent} from './test/test.component';
import {HumanModule} from './human/human.module';  //추가

@NgModule({
declarations: [
AppComponent,
TestComponent,
],
imports: [
BrowserModule,
HumanModule   // 추가
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}

===============================================================

그 다음 AppModule에서 HumanModule을 import한다. 이러면 HumanModule은 AppModule에 포함된다.

 

<app-test></app-test>
<app-man></app-man>
<app-woman></app-woman>

app-component에 해당 템플릿을 선언해보고 실행해 보자.

 

 

제대로 작동하는 것을 확인할 수 있다.

---------------------------------------------------------------------------------

저번시간에 이어서 모듈과 라우터에 대해서 살펴 보겠습니다.
저번시간에는 app모듈에서 라우터 설정이 존재하는 모듈 2개를 추가하여 사용하였습니다.
app-routing모듈, routing2모듈 각각 1개씩 추가하여 사용자의 요청에 대해서 동작하게 하였습니다.

앵귤러는 싱글 어플리케이션, SPA(Single Page Application)라고 합니다.
우리는 작업을 하면서 여러개의 클래스를 만들고, 모듈 또는 컴포넌트를 만들어 기능을 완성합니다.
그런데 앵귤러를 실제 컴파일하여 Javascript로 구성을 하면 1개의 index.html파일에 여러개의 Javascript파일이 생성됩니다.

이렇게 build를 하면 html파일은 1개만 존재 합니다.

 

앵귤러는 사용자가 최초 접속을 하면 모든 파일의 내용을 읽어서 사용자의 행위에 대비합니다.
만약 100개의 페이지, 1000개의 페이지가 존재하는 앱이면 마찬가지로 100개, 1000개의 관련된 내용을 전부 읽어서 사용자의 요청에 준비를 합니다.
그러므로 앵귤러는 통상 최초 접속시 로딩속도는 느리지만, 그 이후의 동작속도는 매우 빠르다고 할 수 있습니다.

 

그런데 사용자가 접속해서 사용하는 페이지가 1~3개 이내인 경우에는 많이 불편 할 수 있습니다.

이를 대비하기 위해 늦은 초기화(lazy-loading) 방법을 사용하여 이러한 문제점을 보완 할 수 있습니다.
먼저 app모듈에서 app-routing을 imports한 부분을 주석처리 하여 줍니다.

* 대상 : app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
//import { Routing2Module } from './routing2/routing2.module';

@NgModule({
  declarations: [
    AppComponent,

  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    //Routing2Module
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

 

다음으로 app-routing모듈에서 routing2모듈을 등록하여줍니다.
이때 재미있는 점은 app-routing모듈이 부모 라우터, routing2모듈이 자식 라우터 역할을 하도록 바꾸어 주는 것 입니다.
어렵지 않습니다.

* 대상 : app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { GoodComponent } from './good/good.component';

const routes: Routes = [
  { path: 'good', component: GoodComponent }, //good요청은 컴포넌트를 불러서 처리합니다.
  { path: 'bad', loadChildren: () => import('./routing2/routing2.module').then(m => m.Routing2Module) }, //bad요청은 자식 라우터에게 전달합니다.  
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

 

good요청은 app-routing모듈이 good컴포넌트를 통해서 화면을 보여주고 있습니다.
기존과 동일한 기능입니다.
bad요청은 import를 통해서 가져온 routing2모듈이 처리하도록 바꾸었습니다.
loadChildren이라는 키 값에 화살표 함수를 통해서 콜백 행위를 지정하였습니다.
이렇게 하면 app-routing모듈이 마치 부모라우터가 되고, routing2모듈이 자식 라우터 역할을 하는 모습으로 기능이 적용 됩니다. 

그러면 이제 routing2모듈도 자식라우터 역할을 하도록 수정하여 보겠습니다.
* 대상 : routing2.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { BadComponent } from '../bad/bad.component';


const routes: Routes = [
  { path: '', component: BadComponent }, //앞선 부모가 bad로 전달하였으므로 path값은 비어있습니다.
  { path: 'hello', component: BadComponent } //path값이 채워지면 2중패스가 됩니다. ex) path : 'hello'  -> /bad/hello 
];


@NgModule({
  declarations: [],
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class Routing2Module { }

 

자세히 보면 imports부분에서 forRoot가 forChild가 된 것을 볼 수 있습니다.
부모 라우터가 forRoot함수를 사용하였다면 받는 역할을 하는 자식라우터는 forChild함수를 사용합니다.
그리고 자식 라우터에서 path 값이 채워지면 2중패스가 됩니다.

bad요청에 routing2모듈이 동작하였고 다음으로 hello 경로가 추가되여 bad컴포넌트가 동작 하였습니다.

 

이렇게 라우터를 부모 - 자식 처럼 나누게 되면 자식라우터는 사용자의 요청이 있는경우에서 동작을 하게 됩니다.
이제 최초 로딩시간을 줄이고 사용자가 요청하는 페이지에 대해서만 로딩하는 기능으로 변경을 완료하였습니다.

1개의 라우터를 사용하는 방식은 소규모 페이지에서 적합합니다.
또한, 여러 개의 라우터가 부모-자식 간의 관계를 갖지않는 방식도 마찬가지로 소규모 페이지에서 적합합니다.
그러나 개발해야되는 페이지의 갯수가 많은 경우에는 위 방식처럼 부모-자식 간의 모습처럼 라우터를 변경해 주어야 합니다.

이번시간에는 모듈과 라우터에 대해서 조금 더 살펴보았습니다.

 

 

0. Angular의 라우팅 설정은 모듈이름-routing.module.ts파일에서 지정할 수 있다.

 

1. 설정방법

  1-1 모듈을 생성한다.

  1-2 ~routing.module.ts에 매핑할 경로와 컴포넌트를 설정한다.

  1-3 app.module.ts에 생성한 모듈을 import한다.

  1-4 app.component.html에 <router-outlet>을 작성한다.

 

2. 모듈 생성이 이전 포스트를 참조하고 매핑하는 방법은 아래와 같다. 2개의 모듈을 생성했다고 가정한다.

  2-1 elements-routing.module.ts

    2-1-1 localhost:4200/elements가 경로가 설정될 때 ElementsHomeComponent가 실행된다.

 

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ElementsHomeComponent } from './elements-home/elements-home.component';


const routes: Routes = [
  { path: 'elements', component: ElementsHomeComponent }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class ElementsRoutingModule { }

 

  2-2 collections-routing.module.ts

    2-2-1 localhost:4200/collections이 지정될 때 ElementsHomeComponent가 실행된다.

 

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { CollectionsHomeComponent } from './collections-home/collections-home.component';


const routes: Routes = [
  { path: 'collections', component: CollectionsHomeComponent }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class CollectionsRoutingModule { }

 

3 기본 모듈인 app-module.ts에 생성한 모듈을 import 목록에 추가한다.

  3-0 우선 최상위 라우팅 테이블인 app-routing.module.ts를 작성한다.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { NotFoundComponent } from './not-found/not-found.component';


const routes: Routes = [
  { path: "", component: HomeComponent },
  { path: "**", component: NotFoundComponent },
]
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

 

  3-1 이 부분이 아주 중요하다.

    3-1-1 app.module.ts에서 모듈을 import하는데 순서가 아주 중요하다.

 

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ViewsModule } from './views/views.module';
import { ModulesModule } from './modules/modules.module';
import { CollectionsModule } from './collections/collections.module';
import { ElementsModule } from './elements/elements.module';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    CollectionsModule,
    ElementsModule,
    ViewsModule,
    ModulesModule,
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

 

  3-2 Angular는 모든 routing 테이블을 읽어 하나의 라우팅 테이블을 만드는 데

    3-2-1 app.module.ts의 imports에 지정된 모듈순서대로 routing 테이블이 순차적으로 적용된다.

    3-2-2 예를 들어 위 코드와는 다르게 최상위 라우팅테이블 AppRoutingModule이 제일 위에 위치하고

      3-2-2-1 AppRoutingModule의 routes에 아래처럼 ** 경로가 매핑되어 있으면 다른 모듈이 로딩 될 수가 없다.

 

4. app.component.html에 router-outlet을 설정한다.

  4-1 중요한 부분은 router-outlet이다. URL이 변경되면 Angular는 해당 컴포넌트를 찾아 router-outlet위치에 추가한다.

 

<div class="ui container">
  <div class="ui secondary pointing menu">
    <a routerLink="/elements" routerLinkActive="active" class="item">Elements</a>
    <a routerLink="/collections" routerLinkActive="active" class="item">Collections</a>
  </div>
  <div class="ui segment">
    <router-outlet></router-outlet>
  </div>
</div>
 

'angular' 카테고리의 다른 글

test  (0) 2023.12.06
Angular 환경에서 RxJS 100% 활용하기  (0) 2022.09.26
angular CRUD placeholder  (0) 2022.08.09
앵귤러 데이터 공유(share,inject)  (0) 2022.08.09
Angular : 모듈단위 Lazy Loading 라우팅 설정하기  (0) 2022.08.08