1. 기본 라우팅 작업
이 문서는 Angular 애플리케이션에 라우터를 추가하는 방법을 설명한다.
1. 라우팅 가능한 상태로 앱 생성하기
Angular CLI로 다음과 같은 명령을 실행하면 Angular 앱을 생성하면서 AppRoutingModule
이라고 하는 라우팅 모듈을 함께 생성한다. 이 모듈은 애플리케이션의 라우팅 규칙을 관리하는 NgModule이다. routing-app
이라는 이름으로 앱을 생성하는 명령은 이렇다.
애플리케이션을 생성하는 과정에 CSS 전처리기를 사용할 것인지 물어본다. 이번 예제에서는 기본 CSS를 선택한다.
1) 라우팅할 컴포넌트 생성하기
Angular 라우터를 사용해서 화면을 전환하려면 컴포넌트가 적어도 2개는 있어야 한다. 다음 명령을 실행해서 first
라는 이름으로 컴포넌트를 생성한다.
그리고 다른 이름으로 두 번째 컴포넌트를 생성한다. 이번에는 second
라는 이름으로 컴포넌트를 생성해 보자.
Angular CLI는 컴포넌트를 생성할 때 자동으로 접미사를 붙이기 때문에, 컴포넌트 이름을 first-component
라고 지정하면 실제 컴포넌트 클래스 이름은 FirstComponentComponenet
가 된다.
Note
- 이 가이드 문서는 Angular CLI로 생성한 Angular 앱을 다룬다.
- 만약 Angular CLI를 사용하지 않는다면
index.html
파일의<head>
태그에<base href="/">
를 추가해야 한다. - 이 태그를 추가하면
app
폴더가 애플리케이션 최상위 주소"/"
와 연결된다.
2) 컴포넌트 로드하기
새로 만든 컴포넌트를 사용하려면 AppRoutingModule
파일 제일 위쪽에서 이 컴포넌트들을 로드해야 한다:
import { FirstComponent } from "./first/first.component";
import { SecondComponent } from "./second/second.component";
2. 기본 라우팅 규칙 정의하기
라우팅 규칙은 세 가지 요소로 구성된다.
AppModule
이 정의된 파일에 AppRoutingModule
을 로드하고 이 라우팅 모듈을 AppModule
의 imports
배열에 추가한다.
Angular CLI로 앱을 생성했다면 이 과정은 이미 처리되어 있다. 하지만 앱을 직접 생성했거나 이미 있는 앱을 기반으로 작업한다면 라우팅 모듈이 제대로 로드되었는지, imports
배열에 추가되었는지 꼭 확인해야 한다. 아래 코드는 --routing
플래그를 붙여서 Angular CLI로 앱을 생성했을 때 자동으로 생성된 AppModule
코드이다.
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { AppRoutingModule } from "./app-routing.module"; // AppRoutingModule을 로드합니다.
import { AppComponent } from "./app.component";
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
AppRoutingModule, // Angular CLI가 AppRoutingModule을 AppModule imports 배열에 자동으로 추가합니다.
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
라우팅 모듈이 정의된 파일에 RouterModule
와 Routes
를 로드한다.
이 과정도 Angular CLI가 자동으로 처리했을 것이다. Routes
배열을 생성하는 것과 @NgModule()
의 imports
배열과 exports
배열을 구성하는 것도 Angular CLI가 처리한다.
import { NgModule } from "@angular/core";
import { Routes, RouterModule } from "@angular/router"; // 라우터 관련 심볼을 로드합니다.
const routes: Routes = []; // 라우팅 규칙은 이 배열에 등록합니다.
// NgModule의 imports, exports 배열에 RouterModule을 등록합니다.
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
이제 라우팅 규칙을 Routes
배열에 등록한다.
라우팅 규칙(route)은 이 배열에 JavaScript 객체 형태로 등록하며, 이 객체는 프로퍼티가 2개 존재한다. 첫 번째로 path
프로퍼티는 라우팅 규칙에 해당하는 URL 주소를 지정한다. 그리고 component
프로퍼티는 해당 URL 주소에 연결될 컴포넌트를 지정한다.
const routes: Routes = [
{ path: "first-component", component: FirstComponent },
{ path: "second-component", component: SecondComponent },
];
라우팅 규칙을 애플리케이션에 등록한다.
라우팅 규칙을 모두 정의했다면 이 규칙을 애플리케이션에 등록해야 한다. 먼저 두 컴포넌트와 연결되는 링크를 추가한다. 링크는 <a>
엘리먼트에 routerLink
어트리뷰트를 사용하는 방식으로 구현한다. 이 어트리뷰트에는 사용자가 링크를 클릭했을 때 이동할 주소를 지정한다. 그리고 컴포넌트 템플릿에 <router-outlet>
을 추가한다. <router-outlet>
엘리먼트는 Angular가 애플리케이션 화면을 전환할 때 관련 컴포넌트가 표시될 위치를 지정하는 엘리먼트이다.
<h1>Angular Router App</h1>
<!-- 아래 링크를 클릭하면 AppRoutingModule에 등록된 라우팅 규칙에 따라 화면을 전환합니다. -->
<nav>
<ul>
<li><a routerLink="/first-component" routerLinkActive="active">First Component</a></li>
<li><a routerLink="/second-component" routerLinkActive="active">Second Component</a></li>
</ul>
</nav>
<!-- 라우팅된 화면은 <router-outlet> 위치에 표시됩니다. -->
<router-outlet></router-outlet>
1) 라우팅 규칙 적용 순서
Angular Router
는 라우팅 규칙 중 첫 번째로 매칭되는 라우팅 규칙을 적용하기 때문에 라우팅 규칙을 등록하는 순서가 중요한데, 구체적인 라우팅 규칙을 가장 먼저 등록하고 덜 구체적인 라우팅 규칙을 나중에 등록하는 것이 좋다. 그래서 고정된 주소를 먼저 등록하며, 그 다음에 빈 주소를 등록하고, 마지막으로 기본 라우팅 규칙을 등록한다. 그리고 브라우저가 접속한 주소에 해당하는 라우팅 규칙이 하나도 없을 때 적용되는 와일드카드 라우팅 규칙은 가장 마지막에 작성한다.
3. 라우팅 규칙으로 전달된 정보 참조하기
때로는 사용자가 화면을 전환할 때 교체되는 컴포넌트 사이에 정보를 전달해야 할 때가 있다. 애플리케이션에서 식료품 목록을 보여주고 있다고 하자. 목록으로 표시된 개별 식료품에는 고유한 id
값이 있다. 사용자가 식료품 정보를 수정하기 위해 Edit 버튼을 클릭하면 EditGroceryItem
컴포넌트가 화면에 표시될 것이다. 이 때 새로 표시되는 컴포넌트는 사용자가 어떤 식료품을 선택했는지 알기 위해 해당 상품에 대한 id
값을 전달받아야 한다.
이런 데이터는 라우티 규칙을 통해 전달할 수 있다. ActivateRouter
인터페이스를 활용하면 된다.
라우팅 규칙으로 전달된 데이터를 참조해 보자:
컴포넌트가 정의된 파일에 ActivatedRoute
와 ParamMap
심볼을 로드한다.
이 import
구문으로 로드한 클래스를 활용하면 컴포넌트에 필요한 정보를 참조할 수 있다. 각각에 대해 자세하게 알아보려면 개별 API 문서를 참고한다:
생성자에 ActivatedRoute
인스턴스를 의존성으로 주입한다:
ngOnInit()
메서드에서 ActivatedRoute
객체 안에 있는 id
인자를 참조한다:
4. 와일드카드(*
) 라우팅 규칙 등록하기
완성도 높은 애플리케이션이라면 애플리케이션이 허용하지 않는 주소로 사용자가 접근하는 상황도 자연스럽게 처리해야 한다. 이 기능을 구현하려면 와일드카드 라우팅 규칙을 추가하면 된다. 이 규칙을 등록하면 사용자가 등록되지 않은 URL로 화면을 이동하려고 할 때 와일드카드 라우팅 규칙이 적용된다.
와일드카드 라우팅 규칙을 설정하려면 routes
배열에 다음 코드를 추가하면 된다.
별표(*
, asterisk) 2개가 사용된 **
는 Angular가 와일드카드 라우팅 규칙을 구분하기 위한 문자열이다. 그리고 component
에는 와일드카드 라우팅 규칙이 적용될 때 화면에 표시할 컴포넌트를 지정한다. 일반적으로는 PageNotFoundComponent
와 같은 컴포넌트를 만들어서 404 에러 페이지를 표시하거나, 애플리케이션 최상위 주소로 리다이렉션하는 방법을 선택할 수 있다. 와일드카드 라우팅 규칙은 모든 URL과 매칭되기 때문에 라우팅 규칙 중 가장 나중에 등록해야 한다.
5. 404 에러 페이지 표시하기
404 에러 페이지를 표시하려면 와일드카드 라우팅 규칙을 등록할 때 component
에 원하는 컴포넌트를 지정하면 된다:
const routes: Routes = [
{ path: "first-component", component: FirstComponent },
{ path: "second-component", component: SecondComponent },
{ path: "**", component: PageNotFoundComponent }, // 404 에러 화면을 표시하는 와일드카드 라우팅 규칙
];
마지막에 등록된 path
가 **
인 라우팅 규칙이 와일드카드 라우팅 규칙이다. 이제 접속하려는 URL과 매칭되는 라우팅 규칙을 발견하지 못하면 이 라우팅 규칙이 적용되면서 PageNotFoundComponent
가 화면에 표시된다.
6. 리다이렉션 설정하기
리다이렉션하는 라우팅 규칙을 등록하려면 path
에 대상이 될 주소를 지정하고 redirectTo
에 리다이렉션할 주소를 지정한 뒤에 pathMatch
에 원하는 리다이렉션할 때 적용할 규칙을 지정한다.
const routes: Routes = [
{ path: "first-component", component: FirstComponent },
{ path: "second-component", component: SecondComponent },
{ path: "", redirectTo: "/first-component", pathMatch: "full" }, // `first-component` 주소로 리다이렉트 합니다.
{ path: "**", component: PageNotFoundComponent }, // 404 에러 화면을 표시하는 와일드카드 라우팅 규칙
];
이 예제에서 세 번째 추가된 라우팅 규칙은 기본 주소로 접근했을 때 first-component
주소로 이동하도록 작성한 리다이렉션 라우팅 규칙이다. 이 규칙이 와일드카드 라우팅 앞에 온다는 것도 확인한다. 이 예제에서 작성한 path: ''
는 애플리케이션을 접속하는 기본 URL(''
)을 의미한다.
7. 중첩 라우팅 규칙
애플리케이션이 점점 복잡해지다 보면 특정 컴포넌트 안에서 동작하는 라우팅 규칙을 추가하고 싶은 경우도 있다. 이렇게 중첩된 라우팅 규칙을 자식 라우팅 규칙(child route)이라고 한다. 라우팅 규칙을 중첩해서 적용하려면 컴포넌트에 <route-outlet>
을 더 추가해야 한다. 왜냐하면 첫 번째 계층에서 동작하는 라우팅 규칙은 AppComponent
의 <router-outlet>
안에서 동작하기 때문이다.
아래 예제 코드에는 자식 컴포넌트가 child-a
, child-b
2개가 존재한다. 그리고 FirstComponent
에는 <nav>
가 존재하며 AppComponent
에 있는 <router-outlet>
외에 또다른 <router-outlet>
이 추가되어 있다.
<h2>First Component</h2>
<nav>
<ul>
<li><a routerLink="child-a">Child A</a></li>
<li><a routerLink="child-b">Child B</a></li>
</ul>
</nav>
<router-outlet></router-outlet>
자식 라우팅 규칙도 일반 라우팅 규칙과 마찬가지로 path
, component
프로퍼티로 정의한다. 자식 라우팅 규칙은 부모 라우팅 규칙이 있고, 부모 라우팅 규칙의 children
배열에 정의한다는 점만 다르다.
const routes: Routes = [
{
path: "first-component",
component: FirstComponent, // 이 컴포넌트 템플릿에 <router-outlet>이 존재합니다.
children: [
{
path: "child-a", // 자식 라우팅 규칙과 연결되는 주소
component: ChildAComponent, // 라우터가 렌더링하는 자식 컴포넌트
},
{
path: "child-b",
component: ChildBComponent, // 또다른 자식 컴포넌트
},
],
},
];
8. 상대 주소 사용하기
상대 주소를 사용하면 현재 URL를 기준으로 라우팅할 주소를 지정할 수 있다. 아래 예제는 second-component
로 이동하는 링크에 상대 주소를 적용한 예제 코드이다. FirstComponent
와 SecondComponent
는 라우팅 계층 트리에서 같은 계층이 있지만 FirstComponent
안에서 SecondComponent
로 이동하는 기능을 제공하려고 한다. 결국 상위 계층으로 한 단계 이동한 후에 SecondComponent
를 찾아야 한다. 이 때 SecondComponent
로 이동하는 전체 경로를 사용하는 대신 상대 주소를 가리키는 방식으로 ../
라는 표현을 사용했다.
<h2>First Component</h2>
<nav>
<ul>
<li>
<a routerLink="../second-component">Relative Route to second component</a>
</li>
</ul>
</nav>
<router-outlet></router-outlet>
../
와 비슷하게 ./
, .
를 사용하면 현재 라우팅 계층의 빈 주소를 가리킨다.
1) 상대 주소로 이동하기
상대 라우팅 규칙을 지정하려면 NavigationExtras
객체의 relativeTo
프로퍼티를 사용하면 된다. NavigationExtras
객체는 @angular/router
에 정의되어 있는 객체이다.
화면을 이동할 때 relativeTo
옵션을 사용해 보자. 아래 예제에서 items
로 지정된 링크 인자 배열 뒤에 객체 옵션을 추가하고 이 객체에 relativeTo
프로퍼티로 ActivatedRoute
를 지정한다. 이 예제의 경우 this.route
이다.
그러면 navigate()
함수가 실행되면서 items
주소로 이동할 때 현재 라우팅 규칙에 대한 상대 주소로 이 주소를 처리한다.
9. 쿼리 인자, URL 조각 참고하기
때로는 쿼리 변수나 URL 조각 같은 라우팅 규칙 관련 정보에 접근해야 할 때가 있다. 히어로들의 여행 앱에서도 사용자가 히어로를 클릭하면 상세정보 화면으로 이동하는데, 이 때 히어로의 id
를 인자로 받아서 화면을 구성한다.
먼저, 아래 심볼들을 컴포넌트 파일에 로드한다.
import { ActivatedRoute } from "@angular/router";
import { Observable } from "rxjs";
import { switchMap } from "rxjs/operators";
그리고 현재 활성화된 라우팅 규칙(ActivatedRoute
)을 의존성으로 주입한다:
이제 컴포넌트 클래스 ngOnInit()
안에서 현재 활성화된 라우팅 규칙으로 전달되는 id
필드를 컴포넌트 클래스의 옵저버블 프로퍼티 heroes$
에 할당해 보자. 이 때 히어로 목록은 배열로 존재하며, 관련 서비스가 이미 주입되어 있고, 화면을 표시하는 템플릿 코드도 이미 준비되어 있다고 하자.
heroes$: Observable;
selectedId: number;
heroes = HEROES;
ngOnInit() {
this.heroes$ = this.route.paramMap.pipe(
switchMap(params => {
this.selectedId = Number(params.get('id'));
return this.service.getHeroes();
})
);
}
다음에는 이동하려는 컴포넌트 파일에 이런 심볼들을 로드한다:
import { Router, ActivatedRoute, ParamMap } from "@angular/router";
import { Observable } from "rxjs";
컴포넌트 클래스의 생성자에 ActivatedRoute
와 Router
를 의존성으로 주입하면 라우터 관련 정보를 참조할 수 있다:
hero$: Observable;
constructor(
private route: ActivatedRoute,
private router: Router ) {}
ngOnInit() {
const heroId = this.route.snapshot.paramMap.get('id');
this.hero$ = this.service.getHero(heroId);
}
gotoItems(hero: Hero) {
const heroId = hero ? hero.id : null;
// 히어로 객체가 전달되면 id를 가져옵니다.
// HeroList 컴포넌트로 이동합니다.
this.router.navigate(['/heroes', { id: heroId }]);
}
10. 지연 로딩
Angular 앱이 실행되는 시점에 로딩되지 않고 필요한 시점에 따로 로딩되는 모듈을 지연 로딩되는 모듈(lazy load module)이라고 한다. 모듈을 지연 로딩하면 앱 초기 실행시간이 짧아지기 때문에 사용자에게 좀 더 나은 사용성을 제공할 수 있다. 라우팅 규칙을 정의할 때 지연 로딩되는 모듈로 향하는 라우팅 규칙을 정의할 수 있다.
11. 허가되지 않은 접근 차단하기
라우팅 가드를 사용하면 허가되지 않은 앱 영역으로 사용자가 이동하는 것을 방지할 수 있다. Angular는 다음과 같은 라우팅 가드를 제공한다:
CanActivate
CanActivateChild
CanDeactivate
Resolve
CanLoad
라우팅 가드를 사용할 때는 컴포넌트가 없는(component-less) 라우팅 규칙을 따로 정의해서 자식 라우팅 규칙을 모두 보호하는 방법도 고려해볼만 하다.
Angular CLI로 가드를 생성하려면 이런 명령을 실행하면 된다:
가드 클래스에는 가드가 동작하는 로직을 작성한다. 아래 예제는 CanActivate
를 활용하는 가드 예제 코드이다.
export class YourGuard implements CanActivate {
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean {
// 인증 로직
}
}
그리고 라우팅 모듈에서 routes
배열에 라우팅 가드가 동작할 프로퍼티를 지정하면 된다. 이번 예제에서는 canActivate
프로퍼티를 사용해서 해당 라우팅 규칙을 보호하는 방식으로 구현했다.
12. 링크 변수 배열
링크 변수 배열(link parameters array)은 현재 라우팅과 관련해서 이런 정보를 담고 있다:
- 표시되는 컴포넌트와 관련된 라우팅 규칙(route)
- 라우팅 규칙 URL에 포함된 필수/옵션 라우팅 변수
RouterLink
디렉티브가 적용된 링크가 하나 있다고 하자:
이 링크가 동작하면서 인자를 함께 전달하려면 디렉티브에 바인딩되는 배열을 다음과 같이 수정하면 된다:
<a [routerLink]="['/hero', hero.id]">
<span class="badge">{{ hero.id }}</span>{{ hero.name }}
</a>
라우팅 인자는 { foo: 'foo' }
와 같은 객체 형태도 전달할 수 있다:
<a [routerLink]="['/crisis-center', { foo: 'foo' }]">Crisis Center</a>
이 세 가지 예제를 활용하면 한 계층에서 발생하는 라우팅 동작을 거의 구현할 수 있다. 그런데 위기대응센터 화면처럼 자식 라우터가 있다면 조금 다르다.
아래 코드는 위기대응센터 화면에 사용했던 기본 자식 라우팅 규칙 링클르 정의한 코드이다.
이 코드에서 이런 내용을 확인할 수 있다:
- 배열의 첫 번째 항목은 부모 라우팅 규칙
/crisis-center
를 의미한다. - 부모 라우팅 규칙에 전달되는 라우팅 인자는 없다.
- 부모 라우팅 규칙의 기본 주소가 없다면 자식 라우팅 규칙 중 하나를 지정해야 한다.
- 기본 주소를
CrisisListComponent
와 연결했다면/crisis-center/
라고 지정해야 하지만, 마지막 슬래시(/
)는 생략할 수 있다.
이번에는 위기대응센터 화면에 있는 링크 중에서 Dragon 위기로 이동하는 링크를 살펴보자:
- 배열의 첫 번째 항목은 부모 라우팅 규칙
/crisis-center
를 의미한다. - 부모 라우팅 규칙에 전달되는 라우팅 인자는 없다.
- 배열의 두 번째 항목은 자식 라우팅 규칙에 적용될 조건(
/:id
)를 의미한다. - 자식 라우팅 규칙에서는 라우팅 인자
id
를 참조해서 상세정보를 가져온다. - Dragon 위기에 해당하는
id
값(1
)은 배열 두번째 항목으로 전달한다. - 최종 주소는
/crisis-center/1
이 된다.
그러면 AppComponent
템플릿을 이렇게 정의할 수 있다:
template: `
<h1 class="title">Angular Router</h1>
<nav>
<a [routerLink]="['/crisis-center']">Crisis Center</a>
<a [routerLink]="['/crisis-center/1', { foo: 'foo' }]">Dragon Crisis</a>
<a [routerLink]="['/crisis-center/2']">Shark Crisis</a>
</nav>
<router-outlet></router-outlet>
`;
정리하자면, 애플리케이션은 라우팅 계층의 깊이와 관계없이 자유롭게 화면을 전환할 수 있다. 링크 인자 배열을 활용하면 원하는 계층으로 이동하면서, 필수/옵션 라우팅 인자를 함께 전달할 수 있다.
13. LocationStrategy
, 브라우저 URL 스타일
화면에 새로운 컴포넌트를 표시하게 되면 브라우저의 주소와 히스토리가 변경된다.
최신 HTML5 브라우저는 history.pushState
API를 제공한다. 이 API를 활용하면 서버로 새로운 페이지 요청을 보내지 않으면서 브라우저의 주소나 히스토리를 변경할 수 있다. 그리고 Angular 라우터는 새로운 페이지 로드가 필요한 URL과 구별하지 않으면서 자연스러운 URL을 처리할 수 있다.
"HTML5 pushState" 스타일로 구성되는 위기대응센터 URL은 이렇다:
오래된 브라우저는 접근하는 URL이 변경될 때 발생하는 새 페이지 요청을 생략하기 위해 해시 기호(#
)를 붙이는 방법을 사용하기도 한다. 물론 Angular에서는 이런 방식의 URL도 사용할 수 있다. 해시 URL 스타일로 구성되는 위기대응센터 URL은 이렇다:
두 가지 스타일 중 어떤 스타일을 사용할지는 Angular LocationStrategy
프로바이더를 지정하는 방법으로 결정할 수 있다.
PathLocationStrategy
: HTML5 pushState 스타일을 사용한다. 이 값이 기본값이다.HashLocationStrategy
: 해시 URL 스타일을 사용한다.
기본 상태에서는 RouterModule.forRoot()
함수에서 LocationStrategy()
를 PathLocationStrategy
로 설정하고 있다. HashLocationStrategy
를 사용하려면 앱을 부트스트랩할 때 이 값을 지정하면 된다.
14. URL 스타일 결정하기
URL 스타일은 프로젝트 개발 단계 이전에 결정하는 것이 좋다. 왜냐하면 애플리케이션이 운영 중이 상태에서는 제공되는 URL을 기준으로 사용자가 접근하기 때문이다.
대부분의 Angular 프로젝트는 기본 HTML5 스타일을 따른다. 이 스타일을 사용하면 사용자가 주소를 이해하기 쉽고 서버사이드 렌더링을 적용하기도 쉽다.
앱 페이지를 서버에서 미리 렌더링하고 제공하는 방식은 애플리케이션의 초기 실행 속도를 높이는 데에 큰 도움이 된다. 서버에서 렌더링하는 데 10초 이상이 걸리더라도 이 페이지가 미리 렌더링 된 후 사용자 디바이스에서 실행되는 것은 1초도 걸리지 않는다.
하지만 이 방식은 중간에 해시 기호(#
)가 없는 URL 스타일일 때만 가능하다.
15. <base href>
브라우저는 화면을 전환할 때 브라우저의 history.pushState
를 활용한다. pushState
를 활용하면 앱 URL 주소를 localhost:4200/crisis-center
처럼 구성할 수 있다. 앱 안에서 사용하는 URL과 서버에 요청하는 URL은 다르다.
최근에는 사용자들이 HTML5 스타일로 URL을 사용하기 때문에 최신 HTML5 브라우저도 대부분 pushState
를 제공하고 있다.
Note
- HTML5 스타일로 URL을 구성하는 것이 라우터 기본 설정이다.
- 그리고
LocationStrategy
, 브라우저 URL 스타일 섹션에서 설명한 것처럼 HTML5 스타일을 사용하는 것이 좋지만, 필요하다면 이전 스타일(#
)을 사용할 수도 있다.
pushState
방식으로 화면을 전환하려면 <base href>
엘리먼트를 반드시 index.html
파일에 설정해야 한다. 앱에서 상대 URL로 무언가를 요청하면 <base href>
에 지정된 값에 따라 CSS 파일이나 스크립트 파일, 이미지 파일을 로드한다.
<base>
엘리먼트는 <head>
태그 바로 뒤에 추가한다. 애플리케이션 최상위 경로가 app
폴더라면 index.html
파일을 이렇게 지정하면 된다:
1) HTML5 URL, <base href>
URL은 여러 가지 요소로 구성되며, URL의 각 영역은 이렇게 나눠볼 수 있다:
foo://example.com:8042/over/there?name=ferret#nose
\_/ \______________/\_________/ \_________/ \__/
| | | | |
스킴 도메인 경로 쿼리 프래그먼트
기본적으로 Angular 라우터는 HTML5 pushState
스타일을 사용하기 때문에 index.html
파일의 <head>
안에 <base href>
를 꼭 지정해야 한다.
이 태그가 없으면 브라우저가 로드하는 이미지 파일이나 CSS, 스크립트 파일을 앱 딥 링크(deep link)와 구별할 수 없다.
때로는 index.html
파일이나 <head>
엘리먼트를 수정할 수 있는 권한이 없는 경우가 있다.
이런 경우에도 HTML5 방식으로 URL을 다루려면 이렇게 하면 된다:
1] 라우터에 APP_BVASE_HREF
옵션을 지정한다.
2] CSS 파일, 이미지 파일, 스크립트 파일, 템플릿 파일 등 모든 리소스에 도메인부터 시작하는 주소를 사용한다.
3] <base href>
path
는 반드시 "/"
로 끝나야 한다. 브라우저는 path
가장 마지막에 사용된 "/"
는 자동으로 제거한다.
4] <base href>
에 쿼리 부분이 추가되면 이 쿼리는 현재 화면에 지정된 경로가 없거나 쿼리가 없는 경우에만 동작한다. 이 말은, <base href>
에 사용된 쿼리는 HashLocationStrategy
를 사용했을 때만 사용된다는 것을 의미한다.
5] 현재 접속한 URL이 최상위 URL(도메인 기본 URL)이라면 <base href>
는 사용되지 않는다. 이 경우에 Angular는 <base href>
를 무시하고 APP_BASE_HREF
에 설정된 값으로 링크를 처리한다.
6] <base href>
에 사용된 프래그먼트는 절대 사용되지 않는다.
2) HashLocationStrategy
AppModule
에서 RouterModule.forRoot()
를 실행할 때 useHash: true
옵션을 지정하는 방법으로도 HashLocationStrategy
정책을 사용할 수 있다.
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { FormsModule } from "@angular/forms";
import { Routes, RouterModule } from "@angular/router";
import { AppComponent } from "./app.component";
import { PageNotFoundComponent } from "./page-not-found/page-not-found.component";
const routes: Routes = [];
@NgModule({
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot(routes, { useHash: true }), // .../#/crisis-center/
],
declarations: [AppComponent, PageNotFoundComponent],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}