1. /src/tsconfig.json

tsconfig. json 내에서 compileroption > paths 항목 내에 아래와 같이 적용할 경우, 각 ts 파일에서, 상대 경로를 사용할 수 있다. ././ 형태의 파일간 상대경로가 아니라 @app/_helper 와 같이 절대경로처럼 사용가능


...
"compilerOptions": {
    "baseUrl""./",
    "paths": {      
      "@app/*": ["src/app/*"],
      "@environments/*": ["src/environments/*"],
      "@app/interface/*": ["src/app/interface/*"]
    },
...


2. /src/styles.ts

styles.scss or styles.less 상에서, styles sheet를 작성할 경우, 전역으로 style이 적용된다.


3. /src/polyfills.ts

polyfills.ts의 경우, ng new 명령어를 통해 프로젝트를 생성할 때, Angular CLI에 의해 생성되는 파일이다.

Angular 9가 제공하는 기능들 중 몇가지는 일부 브라우저에서는 지원하지 못하기 때문에, 지원하지 않는 브라우저에서도 (생략된 기능)해당 기능을 구현하여 사용할 수 있도록 해주는 Library를 polyfill 라이브러리라고 한다. 추가 조치 없이, 기본적인 구성은 아래와 같다.

/***************************************************************************************************
 * Zone JS is required by default for Angular itself.
 */
import 'zone.js/dist/zone';  // Included with Angular CLI.


4. /src/main.ts

main.ts의 경우, Angular가 App을 실행하는 entry point로써, bootstrap Module을 실행한다. 기본적으로 bootstrap Module은 app.module.ts이고, app.component.ts에서 시작된다. 또한 환경변수를 체크하여, production 모드일 경우, enableProdMode() 함수를 호출한다. 추가 조치 없이, 가장 기본적인 구성은 아래와 같다.

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));


5. /src/index.html && /src/index.prod.html

index.html 파일의 경우, Main index html의 역할을 하는 파일이다. 브라우저에 의해 가장 처음으로 로딩되는 페이지이며, 페이지의 최상단 제목, bootstrap.css와 같은 stylesheet를 global로 적용 가능하다. 보통은 추가적인 .css 파일을 적용하려면, node 모듈로 추가하고, angular.json 파일내 styles, scripts 속성에 참조할 경로를 입력한다. <app> 태그는 bootstrap Module인 app.module.ts 내의 bootstrap Component인 app.component.ts 상의 selector와 동일하다. 

"ng build --prod" 명령어를 통해 production 용도 앱을 build할 경우, output 파일 중에 index.html 파일이 index.prod.html 내용으로 대체된다.


<!doctype html>
<html lang="en">
  <meta charset="utf-8">
  <title>사이트 페이지 제목</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 추가적인 css 파일 지정 가능 -->
<link href="//netdna.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" />
</head>

<body>
  <app>
    <div class="spinner">
    </div>
Loading 메시지 또는 Spinner 같은 UI 요소
  </app>
</body>
</html>


6. /src/environments/environment.ts

개발시 사용되는 환경변수를 관리하는 파일이다. 개발, 운영에 따라 변하는 접속 url, 계정 정보 등을 관리한다. 평상시 개발 environment 환경변수 파일의 경우, 아래와 같은 형식을 취한다.

export const environment = {
  production: false,
  apiUrl: 'http://localhost:3000' 
};


7.  /src/environments/environment.prod.ts

"ng build --prod" 명령어를 통해 production 용도 앱을 build할 경우, output 파일 중에 environment.ts 파일이 environment.prod.ts 내용으로 대체된다. --prod 옵션을 붙일 경우, .prod.ts가 구현되어 있는 파일로 적용된다.

export const environment = {
  production: true,
  apiUrl: 'http://localhost:3000'
};


8. /src/app/app.module.ts

app.module.ts의 경우, App의 Root module이자, bootstrap module의 역할을 한다. app.module에 대한 metadata들을 포함하는데, imports 속성에는 해당 module에 포함되는 component가 사용할 module[component들의 그룹]들의 목록을 나열한다. module은 관련있는 component들의 그룹이므로, 묶을 component 목록을 나열해야하는데, app.module.ts의 declarations에는 의미상 AppComponent를 하나 정도만 넣어주고, 사실상 AppRoutingModule에서 route 경로에 따라 SubModule들을 import 하는 식[lazy Loading]으로 보통 진행 된다. 다른 모듈과 다르게 app.module.ts만 특별히 bootstrap 속성이 존재하고, 해당 값에는 bootstrap component인 AppComponent가 할당된다. 추가로 providers 속성의 경우 일정 형태의 json 형식으로 지정하거나, service 목록을 정의하면, 광역서비스 느낌으로, 다른 Component 상에서 추후 접근가능하다. Service와 Provider는 동의어로 봐도 무방하다. 추후 컴포넌트/서비스 상에서, 의존성주입[DI]를 통해, 사용된다.

import { NgModuleAPP_INITIALIZER } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientModuleHTTP_INTERCEPTORS } from '@angular/common/http';

// used to create fake backend
import { fakeBackendProvider } from './_helpers';

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

import { JwtInterceptorErrorInterceptorappInitializer } from './_helpers';
import { AuthenticationService } from './_services';

@NgModule({
    imports: [
        BrowserModule,
        ReactiveFormsModule,
        HttpClientModule,
        AppRoutingModule
    ],
    declarations: [
        AppComponent
    ],
    providers: [
        { provide: APP_INITIALIZERuseFactory: appInitializermulti: truedeps: [AuthenticationService] },
        { provide: HTTP_INTERCEPTORSuseClass: JwtInterceptormulti: true },
        { provide: HTTP_INTERCEPTORSuseClass: ErrorInterceptormulti: true },

// provider used to create fake backend

        fakeBackendProvider
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }


8-1. @injectable, token, provider의 개념에 대하여

Angular 상에서 의존성 주입은 아래와 같은 방법으로 진행된다.[constructor 내에 Service/Provider 타입정의]

import { Http } from '@angular/http';
@Component({ selector: 'example-component'template: '<div>I am a component</div>'})
class ExampleComponent {
    constructor(private httpHttp) {
      // `this.http` 로 Http 프로바이더에 접근 가능.
    }
}

@Injectable()은 컴포넌트나 서비스에 의존성 주입을 위해 필수적인 데코레이터라는 점은 잘못된 생각이고, 현재 이슈가 존재하여, @Injectable()을 필수로 사용할 뿐이고 변경될 가능성이 있다. @Injectable() 데코레이터를 사용할 경우, Angular가 해당 class에 대한 메타데이터를 보관하게 되는데, 여기에 의존성을 어떻게 찾고 주입할 것인가에 대해 포함하고 있으므로, 1개 이상의 의존성[constructor 내 다른 프로바이더]을 가진 service일 경우, 반드시 @Injectable() 데코레이터를 붙여주어야, 다른 컴포넌트/서비스 상에서 의존성 주입하여, 사용 및 접근 가능하다.

전형적인 서비스를 생성하는 방법은 아래와 같다.

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';

@Injectable()
@Injectable({ providedIn: 'root' }) //이렇게 사용할 경우, 등록까지 완료된다.
export class UserService {
  constructor(private httpHttp) {}
  isAuthenticated(): Observable<boolean> {
    return this.http.get('/api/user').map((res=> res.json());
  }
}

Module 내에 전형적인 서비스를 등록하는 과정은 아래와 같다.

import { NgModule } from '@angular/core';
import { AuthService } from './auth.service';

@NgModule({
  providers: [
    AuthService,
    // 위에 것과 동일한 문법
    {
        provide: AuthService//Token의 역할(class명이지만, string과 같은 역할)
        useClass: AuthService //실제 서비스명(@Injectable() 사용) 
    }
  ]
})
class ExampleModule {}

Angular는 App 내 컴포넌트/서비스들에게, Token - 서비스Class가 매핑된 리스트를 제공하고, 의존성 주입이 일어나는 내부 프로세스를 Token을 통해 필요한 서비스/프로바이더에 접근하는 것이다.


9. /src/app/app.component.ts

app.module.ts 내에 포함되는 bootstrap component 역할을 감당한다. 해당 component의 selector[기본값: app]는 매우 중요한 의미를 가지며, index.html 상에서 <body> 태그 안에 사용된다. 


import { Component } from '@angular/core';
import { AuthenticationService } from './_services';
import { User } from './_models';

@Component({ selector: 'app'templateUrl: 'app.component.html' })
export class AppComponent {
    userUser;
    constructor(private authenticationServiceAuthenticationService) {
        this.authenticationService.user.subscribe(x => this.user = x);
    }
    logout() {
        this.authenticationService.logout();
    }
}


10. /src/app/app.component.html

app.module.ts 내에 포함되는 bootstrap component의 템플릿 역할을 감당한다. 실질적으로 <body> 태그를 채우기 시작하는 html이다.

주로 body tag내의 Navbar 부분을 구현하고, content 부분만 비워두고, app.routing.module.ts이 제공하는 <routor-outlet></router-outlet>을 배치하여, route 경로에 따라, Sub Page Module을 import[lazy loading] 하는 방식으로 구성된다.

또는, 로딩처리만 해두고, 통으로 Main Page Module을 load하기도 한다.

<!-- nav -->
<nav class="navbar navbar-expand navbar-dark bg-dark" *ngIf="user">
    <div class="navbar-nav">
        <a class="nav-item nav-link" routerLink="/">Home</a>
        <a class="nav-item nav-link" (click)="logout()">Logout</a>
    </div>
</nav>

<!-- main app container -->
<div class="container">
    <router-outlet></router-outlet>
</div>


11. /src/app/app-routing.module.ts

Angular App의 Routing은 Routes의 배열로 구성된다. 각 Routes 컴포넌트는 path[메인], component[메인], redirecTo, pathMatch, loadChildren, canActivate, pathMatch와 같은 속성으로 구성되어 있다.

주로 path는 주소요청 경로를 의미하고, 해당 path와 Mapping된 단일 component가 존재해야만 한다.

가능한 Route 조합은 아래와 같다.

app-routing.module.ts의 경우, 외부 app.module.ts 상에서 사용될 목적으로 만들어졌기 때문에, exports 값 지정이 필수적이다.

import { NgModule } from '@angular/core';
import { RoutesRouterModule } from '@angular/router';

import { HomeComponent } from './home';
import { LoginComponent } from './login';
import { AuthGuard } from './_helpers';

const routesRoutes = [
    //1. 가장 기본적인 path - component 조합
    { path: 'login'component: LoginComponent },
    //2. Canactivate를 구현한 Guard Service를 추가하여, 접근 가능한지를 먼저 체크
    { path: ''component: HomeComponentcanActivate: [AuthGuard] },
    //3. App.Module.ts처럼 Routing.module이 import된 Module을 load하여,
    //(사용자정의앱모듈)-routing.module.ts 상에서 Routing 처리
    { 
      path: 'project',
      loadChildren: () => import('./project/project.module').then((m=> m.ProjectModule)
    },
    //4. 빈값 입력시, 다른 url로 다시 리다이렉팅 수행
    //pathMatch: full을 지정함으로, 반드시 정확하게 경로를 다입력해야함
    {
    path: '',
    redirectTo: 'project',
    pathMatch: 'full'
    }
    //5. 미리 지정된 경로 예외의 것은 모두 기본 경로로 이동
    { path: '**'redirectTo: '' }
];

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


12. /src/app/_helpers/auth.guard.ts

guard 라는 개념이 특별한 개념이라기 보다는, 일종의 서비스이면서, CanActivate라는 기능을 구현한 서비스일 뿐이다.

보통은, 특정 Route path에 대한 접근이 허용되는 조건을 명시하여, 가능할 경우 true, 불가능할 경우, 다른 조치 후, false를 반환하는 방식으로 구성된다.

 


import { Injectable } from '@angular/core';
import { RouterCanActivateActivatedRouteSnapshotRouterStateSnapshot } from '@angular/router';

import { AuthenticationService } from '@app/_services';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
    constructor(
        private routerRouter,
        private authenticationServiceAuthenticationService
    ) { }

    canActivate(routeActivatedRouteSnapshotstateRouterStateSnapshot) {
        const user = this.authenticationService.userValue;
        if (user) {
            // logged in so return true
            return true;
        } else {
            // not logged in so redirect to login page with the return url
            this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
            return false;
        }
    }
}


  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기