#angular

angular에서 bootstrap 활용을 위한 ng-bootstrap 패키지를 활용하여 모달을 사용한다.

ModalService의 준비

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { Injectable } from '@angular/core';
// 모달을 사용하기 위한 import
import {NgbModal, NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';
import { LoginModalComponent } from '../_components/login-modal/login-modal.component';
import { ApplyCheckModalComponent } from '../_components/apply-check-modal/apply-check-modal.component';
@Injectable()
export class ModalService {
constructor(private modalService: NgbModal) {
}
openLoginModal(){
const modalRef = this.modalService.open(LoginModalComponent);
modalRef.componentInstance.name = 'World';
}

// open apply checking modal
openApplyCheckModal(){
const modalRef = this.modalService.open(ApplyCheckModalComponent);

//필요한 데이터를 전달
modalRef.componentInstance.name = 'World';
}
}

원래는 NgbModal 모듈을 이용하여 바로 특정 모달 컴포넌트를 여는 방식이지만
여러 페이지에서 다양한 모달을 쉽게 열고 닫기 위해서 새로운 ModalService 서비스를 만들었다.

이 서비스에서 열고자 하는 다양한 modal component를 import하여 쉽게 열 수 있다.

또한 MgbModal을 통해 open 한 modal에 componentInstance를 통해 data를 전달할 수 있다.

Modal Component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { Component, OnInit } from '@angular/core';
import {NgbModal, NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';
import { Router, ActivatedRoute } from '@angular/router';

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

constructor(public activeModal: NgbActiveModal) {

}

ngOnInit() {
// reset login status
this.authenticationService.logout();

}
close(){
this.activeModal.close();
}
}

모달 내에서 모달을 닫기 위해 NgbActiveModal 모듈을 사용하였다.

Modal Component.html

1
2
3
4
5
6
7
8
9
10
11
12
<div>
<div class="modal-header">
</div>
<div class="modal-body">
<div class="" style="text-align:center;">
<span style="font-weight:bold; margin-right:1rem;">아이디가 없으신가요?</span>
<span><a [routerLink]="['/register/selectTypePage']" style="color:#65b8b4; font-weight:bold;" (click)="close()">회원가입</a></span>
</div>
</div>
<div class="modal-footer">
</div>
</div>

모달을 한번 감싸주고
그 안에서 modal-header, modal-body, modal-footer class 들을 사용하여 안을 꾸며준다.

Module.ts 에 등록

1
2
3
4
5
6
7
8
9
10
@NgModule({
declarations: [
],
imports: [
],
providers: [
],
bootstrap: [],
entryComponents: [EmailCheckModalComponent]
})

app.module의 entryComponents에 등록하여 미리 컴포넌트를 로드시켜 놓는다.

참조하기

알고리즘 pseudo code

  1. 페이지 로딩 시점에 get(9)
  2. 스크롤바가 하단에 가까우면 받아오고, 받아오기 인덱스 true
    if(height<200px)
    get(9)
    getIndex=true

  3. 받아오는 동안에는 요청 안하기
    if(getIndex==true)
    return 0

  4. 받아온 값이 없을 경우에는 return 0하고 searchEndIndex를 true로 하기
    if(res.length==0)
    return 0
    searchEndIndex=true

HostListener Decorator Component 작성

아래와 같이 호스트의 이벤트를 감지한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { HostListener, Inject} from "@angular/core";
import { DOCUMENT } from "@angular/common";

export class LayoutNavComponent implements OnInit {
constructor(@Inject(DOCUMENT) private document: Document) { }
}
@HostListener("window:scroll", ['$event.target'])
onWindowScroll() {
//we'll do some stuff here when the window is scrolled
console.log(this.document.body.scrollHeight);
console.log("scrollTop:",this.document.documentElement.scrollTop);
let scrollHeight=this.document.documentElement.scrollHeight;
let readHeight=this.document.documentElement.scrollTop+this.document.documentElement.clientHeight;

if((-10<=(readHeight-scrollHeight))&&((readHeight-scrollHeight)<=10)){
this.groupService.getGroupsBySearchKey(this.locationService.locationDepth1, this.locationService.locationDepth2, this.groupService.searchKey);
}
}

scroll event의 성능향상을 위한 Tip

모든 이벤트 마다 onScroll 함수를 발생시키면 CPU의 성능을 많이 차지하므로
다음과 같이 함수에 delay를 주어 이를 해결할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
function () {
if (waiting) {
return;
}
waiting = true;

scroll();

setTimeout(function () {
waiting = false;
}, 100);
}

하드웨어 가속시키기

하드웨어 가속이란 그래픽 처리 장치를 이용하여 중앙처리 장치의 처리량을 줄이고, 브라우저의 렌더링을 효율화하는 것을 말한다.
css 작업에 하드웨어 가속을 활성화하면, 작업처리가 빨라져서 웹페이지의 렌더링을 보다 빠르게 할 수 있다.

특정 엘레멘트에 어떤 지시를 내리면 엘리먼트는 자신의 레이어에 분류되고 페이지에 있는 다른 엘리멘트와 독립되어 렌더링 된다.
결론만 말하면 css에 다음과 같은 속성을 붙이면 하드웨어를 가속화 하여 빨리 렌더링이 가능하다.

1
transform: 3dtransforms(0,0,0)

위 명령어는 3d가 아닌 엘레먼트에 가짜로 3d속성을 주므로써 해당 element의 렌더링을 cpu가 아닌 gpu가 담당하게 하여 렌더링 속도를 높이며, will-change 명령어는 엘레먼트에 어떤 변경을 할 것인지 브라우저에게 미리 알려주므로써 최적화 하는 것이다. 페이지는 순식간에 갱신돼 부드러운 화면처리가 가능하게 된다.

참조


html 상에서 input (type=file) 만들기

1
<input type="file" (change)="onChange($event)"></input>

onChange로 file 추출 및 저장하기

1
2
3
onChange(event){
let file = event.srcElement.files[0];
}

formData에 key, value 매핑하기

1
2
3
4
5
6
7
8
9
10
11
12

console.log(file);

폼데이터를 선언한다.
let formData:FormData = new FormData();

formData에 key와 value를 메겨준다.
formData.append('fileName',this.fileName+'.'+file.name.split('.')[1]);

여기서 file.name.split('.')[1]는 확장자를 찾아주는 것이다.

formData.append('imageFile', file, file.name);

참조자료

라우터 및 라우팅 파라미터 설정

Route를 활용하여 라우팅을 할 경로와 매칭하는 컴포넌트를 선언한다.

Example

1
2
3
4
5
export const routes: Routes = [
{ path: '', redirectTo: 'product-list', pathMatch: 'full' },
{ path: 'product-list', component: ProductList },
{ path: 'product-details/:id', component: ProductDetails }
];

여기서 :id는 해당 경로의 라우팅 할때의 파라미터 값이 들어간다.

특정 라우팅을 수행하는 링크 걸기

html태그에서 해당 라우팅으로 이동할 수 있도록 링크를 걸 수 있다.

Example - html 코드 내에서 라우팅 시키는 경우
undefined

example - 컴포넌트 상에서 라우팅 시키는 경우

1
2
3
goToProductDetails(id) {
this.router.navigate(['/product-details', id]);
}

참고자료

라우팅 이벤트를 감지하여 함수 실행시키기

라우팅이 일어날 때마다 해당 이벤트를 감지하여 함수를 실행시킨다.

Example

1
2
3
4
5
6
7
8
9
10
constructor(router:Router) {
router.events.subscribe(event:Event => {
if(event instanceof NavigationStart) {
}
// NavigationEnd
// NavigationCancel
// NavigationError
// RoutesRecognized
});
}

현 페이지의 라우터 전달값을 받기

1
2
3
4
5
this.route.params.subscribe(
params=>{
this.sectionIndex=params['pageIndex'];
}
)

structural Directive

ngFor

어레이를 돌면서 반복적으로 나타내기 위한 요소이다.
흔히 리스트를 출력할 때 사용한다.

ngFor 내에서 인덱스를 붙이는 경우의 활용

1
<li *ngFor="let item of items; index as i; trackBy: trackByFn">...</li>

ngFor 안에서 ngFor를 사용하는 경우의 활용

1
*ngFor="let subMenuName of menuName['submenu']; let idx = index;"

ngIf

Template And Data Binding in Angular를

Angular에서는 다양한 종류의 Binding을 제공한다.
binding의 종류에 따라

  1. (element / component / directive) property
  2. (element / component / directive) event
  3. (rarely) an attribute name.

의 세가지 경우가 있다.

property binding

html 태그의 다양한 속성을 바인딩 해준다.

대상
Element property
Component property
Directive property

example1
예제에서 보여지듯이 다양한 종류의 속성을 바인딩 할 수 있다.
이미지를 불러오는 경로인 (src) 컴포넌트의 입력값을 주는 [hero] 바인딩, class 속성을 가르쳐주는 [ngClass] 등의
바인딩 사례가 있다.

1
2
3
<img [src]="heroImageUrl">
<app-hero-detail [hero]="currentHero"></app-hero-detail>
<div [ngClass]="{'special': isSpecial}"></div>

example2
[ngStyle]을 활용하여 동적으로 background-image 할당.

1
<div class="profile-image" [ngStyle]="{ 'background-image': 'url(' + image + ')'}">

Event Binding

html 태그에서 발생하는 다양한 종류의 event에 대한 binding이다.

대상
Element event
Component event
Directive event

example

1
2
3
<button (click)="onSave()">Save</button>
<app-hero-detail (deleteRequest)="deleteHero()"></app-hero-detail>
<div (myClick)="clicked=$event" clickable>click me</div>

(click)을 통해 클릭을 하는 시점에 함수를 발동하는 등의 역할을 수행한다.

(focus) and (focusout)

input 등의 경우 입력모드가 발동된 시점이나 특정 element가 focus 된 경우 동작을 나타낸다.

example

1
<input name="date" type="text" (focus)="focusFunction()" (focusout)="focusOutFunction()">

(ngModelChange)

해당 모델의 데이터 값이 바뀔 때마다 특정 함수를 실행할 수 있다.

Example
undefined

참고자료

Two Way Binding을

이벤트와 속성등을 양방향으로 연결하는 바인딩이다.

대상
Event and property

example

1
<input [(ngModel)]="name">

Attribute Binding

대상
Attribute

example

1
<button [attr.aria-label]="help">help</button>

다음과 같이 value, selected를 바인딩 하는 데에도 사용 가능하다.
참조
undefined

class binding

특정 조건에서 class를 바인딩 해준다.

example

1
<div [class.special]="isSpecial">Special</div>

style binding

스타일 속성을 바인딩한다.

example

1
<button [style.color]="isSpecial ? 'red' : 'green'">

너비, 높이 등을 유동적으로 세팅하기 위해 다음과 같은 코드를 흔히 사용한다.

1
2
3
<div class="home-component"
[style.width.px]="width"
[style.height.px]="height">Some stuff in this div</div>

사이즈 조절을 위한 style binding

Structural Directive

HTML Layout에 영향을 주는 요소들이다.
대표적으로
ngIf ngFor 등이 있다.

PagerService 준비하기

출력하고자 하는 데이터를 페이지에 담기 위한 PagerService를 준비한다.

PagerService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import * as _ from 'underscore';

export class PagerService {
getPager(totalItems: number, currentPage: number = 1, pageSize: number = 10) {
// calculate total pages
let totalPages = Math.ceil(totalItems / pageSize);

let startPage: number, endPage: number;
if (totalPages <= 10) {
// less than 10 total pages so show all
startPage = 1;
endPage = totalPages;
} else {
// more than 10 total pages so calculate start and end pages
if (currentPage <= 6) {
startPage = 1;
endPage = 10;
} else if (currentPage + 4 >= totalPages) {
startPage = totalPages - 9;
endPage = totalPages;
} else {
startPage = currentPage - 5;
endPage = currentPage + 4;
}
}

// calculate start and end item indexes
let startIndex = (currentPage - 1) * pageSize;
let endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1);

// create an array of pages to ng-repeat in the pager control
let pages = _.range(startPage, endPage + 1);

// return object with all pager properties required by the view
return {
totalItems: totalItems,
currentPage: currentPage,
pageSize: pageSize,
totalPages: totalPages,
startPage: startPage,
endPage: endPage,
startIndex: startIndex,
endIndex: endIndex,
pages: pages
};
}
}

component.ts 혹은 데이터를 총괄하는 service.ts

컴포넌트에서 사용할 두 변수를 선언한다

1
2
3
4
// pager object
pager: any = {};
// paged items
pagedItems: any[];

컴포넌트에서 pagerService를 import하고 특정 페이지로 이동하기 위한 함수인 setPage()함수를 정의한다.

1
2
3
4
5
6
7
8
9
10
setPage(page: number) {
if (page < 1 || page > this.pager.totalPages) {
return;
}
// get pager object from service
this.pager = this.pagerService.getPager(this.contents.length, page,6);

// get current page of items
this.pagedItems = this.contents.slice(this.pager.startIndex, this.pager.endIndex + 1);
}

pagedItems에는 받아온 데이터를 pager의 startIndex와 endIndex+1까지로 잘라 담아준다.

component.html

실제 pagination을 입혀준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<div class="row-wrapper">
<nav class="" style="display:inline-block">
<!-- pager -->
<ul *ngIf="paymentService.pager.pages && paymentService.pager.pages.length" class="pagination">
<li [ngClass]="{disabled:paymentService.pager.currentPage === 1}" class="page-item">
<a class="page-link direction first" (click)="paymentService.setPage(1)" aria-label="First">First</a>
</li>
<li [ngClass]="{disabled:paymentService.pager.currentPage === 1}" class="page-item">
<a class="page-link direction previous" (click)="paymentService.setPage(paymentService.pager.currentPage - 1)" aria-label="Previous">Prev</a>
</li>
<li *ngFor="let page of paymentService.pager.pages" [ngClass]="{active:paymentService.pager.currentPage === page}" class="page-item">
{% raw %}
<a class="page-link" (click)="paymentService.setPage(page)">{{page}}</a>
{% endraw %}
</li>
<li [ngClass]="{disabled:paymentService.pager.currentPage === paymentService.pager.totalPages}" class="page-item">
<a class="page-link direction next" (click)="paymentService.setPage(paymentService.pager.currentPage + 1)" aria-label="Next">Next</a>
</li>
<li [ngClass]="{disabled:paymentService.pager.currentPage === paymentService.pager.totalPages}" class="page-item">
<a class="page-link direction last" (click)="paymentService.setPage(paymentService.pager.totalPages)" aria-label="Last">Last</a>
</li>
</ul>
</nav>
</div>

다음을 클릭할때에는 service.ts의 setPage()를 호출하여 pagedItems를 바꾸어 준다.
만약에 데이터를 index를 통해 나누어 받아오고 싶은 경우에는 setPage()의 if 문에서 범위를 넘는 페이지의 요청에 대해 서버에 요청을 날려 다음 데이터들을 받아올 수 있다.

기본 명령어

새 프로젝트 만들기

ng new [project name]

빌드 및 실행

$ ng build
$ node server.js

참고 페이지

Component

컴포넌트는 어플리케이션을 구성하는 작은 조각이다.

@Component 를 클래스 위에 선언하여 컴포넌트를 꾸며주고 필요한 정보를 명시한다.

Selector

selector는 css선택자이며, 태그의 이름이다.

template

우리가 로드하려고 하는 템플릿을 작성한다.

브래킷인 `안에 작성한다.`

각컴포넌트는 html, css, class를 가진다.

컴포넌트는 template을 가진 일종의 directive이다.

ng generate component [componentname]
을 통해 아주 간편하게 컴포넌트를 생성할 수 있다.

Module

모듈은 우리의 어플리케이션을 angulr하게 조직하기 위해 필요한 것이다.

모든 어플리케이션은 앱을 lauch하기 위한 적어도 하나의 root module을 가진다.

모듈은 기능적으로 관력이 있는 컴포넌트들을 묶어준다.

declaration

컴포넌트들을 선언한다.

imports

브라우저에서 앱을 런칭하기 위해 필요한 디펜던시를 인젝트해준다.

bootstrap

root component를 명시하여 모듈이 bootstrap될 때 render하도록 한다.

Service

컴포넌트 사이에 중복된 코드와 데이터를 교환하는데 사용된다.

WorkFlow

  1. Service에 @Injectable()을 추가한다.

뿐만 아니라 import{Injactable} from “@angular/core”도 해주어야 한다.

  1. Injector 즉, Module에게 Providers를 제공하여 서비스에 대해 알게 해야 한다.

providers:[Service]

  1. 컴포넌트에 Dependency를 Inject 해준다.
    • ngOnInit() :
    • constructor(pricate service:Service){}

main.ts

필요한 라이브러리를 import해준다.

BrowserModule

앵귤러 웹사이트를 동작하기 위한 라이브러리이다.

platformBrowserDynamic

웹사이트를 render하기 위한 앵귤러의 라이브러리이다.

라이브러리

Express

Express는 자체적인 최소한의 기능을 갖춘 라우팅 및 미들웨어 웹 프레임워크이며, Express 애플리케이션은 기본적으로 일련의 미들웨어 함수 호출입니다.

app.use() 및 app.METHOD() 함수를 이용해 애플리케이션 미들웨어를 앱 오브젝트의 인스턴스에 바인드하십시오.

이때 METHOD는 미들웨어 함수가 처리하는 요청(GET, PUT 또는 POST 등)의 소문자로 된 HTTP 메소드입니다.

다음 예에는 마운트 경로가 없는 미들웨어 함수가 표시되어 있습니다. 이 함수는 앱이 요청을 수신할 때마다 실행됩니다.

Example

1
2
3
4
5
var app = express();
app.use(function (req, res, next) {
console.log('Time:', Date.now());
next();
});

활용법

Structural Deretive

html element들을 추가 제거 대체하여 레이아웃을 변형한다.

*ngFor = "let carPart of catParts"

어레이를 돌며 객체를 꺼낼때 사용한다.

*ngIf="carPart.inStock>0"

if문을 사용한다.

Pipe

template output을 변형하기 위해 쓰인다.

Method in Components

template output을 변형하기 위해 쓰이며, 선언없이 바로 정의한다.

Model

특정 객체를 선언하도록 ts에서 선언한다.
export const
export const를 통해 상수 객체를 선언하고 가져다 쓸 수 있따.

Property Binding

프로퍼티 바인딩은 컴포넌트의 property를 any Dom element property로 지정할 수 있게 한다.
여기서 컴포넌트의 property가 업데이트 되면 DOM도 자동으로 업데이트 된다.
하지만 DOM에서 업데이트 되었다고 컴포넌트의 property가 업데이트 되지는 않는다.

Class Binding

[class.name]="property"를 통해 property가 참이면 클래스에 name을 추가할 수 있다.

Event Binding

DOM에서 발생하는 다양한 이벤트들을 처리할 수 있다.

(click)=”func()”

클릭이벤가 발생하면 컴포넌트의 func()를 실행시킨다.

Two Way Binding

[(ngModel)}="property"

이를 통해 two way data binding이 가능하다.
이를 쓰기 위해서는 FormModule을 Import해야 한다.

Routes

Angular 내에서 url을 부여하여 옮겨다니기 위함이다.

  1. RouterModule을 Import 해준다.

    import{RouterModule} from "@angular/router"
  2. RouterModule을 imports에 추가해 준다.

    .forRoot([{path:"/race",component:raceComponent},{}])
  3. 링크를 걸어준다.

    a [routerLink]="['/races']"
  4. router-outlet 태그를 먹여준다.

    <router-outlet></router-outlet>

Http Library

  1. json파일을 만든다.
  2. HttpModule을 Import 해준다.
    Http를 위한 library를 추가해야 하지만 HttpModule 내에서 이미 서비스를 provide하므로 할필요 없다.
  3. injector에 httprovider를 추가해준다.
  4. 서비스에 http를 dependency inject해준다. 그리고 get request를 한다.
  5. req에 의해 반환되는 data를 listen한다.

언디파인 방지

if(Array.isArray(this.carParts){}로 감싸 this.carParts가 undefined된 경우를 처리할 수 있다.


설치하기

앵귤러는 npm을 사용하여 다음과 같이 설치한다.
이때 반드시 -g 옵션을 사용하여 global하게 설치를 해 주어야 ng 명령어가 등록이 되며, 앵귤러 cli를 설치하기에 앞서서 typescript기반인 angular를 동작시키기 위한 typescript를 설치해 주어야 한다.

1
2
3
sudo npm install -g typescript
sudo npm install -g @angular/cli@latest
sudo npm install --save-dev @angular/cli@latest

만약 aws 환경에서 위 코드를 실행하면 root 권한으로 인한 많은 문제가 발생한다 이때는 뒤에 –unsafe-perm을 붙여서 설치해 준다.

1
2
sudo npm install -g @angular/cli@latest --unsafe-perm
sudo npm install --save-dev @angular/cli@latest --unsafe-perm

앵귤러를 설치한 후에는 앵귤러에 필요한 각종 모듈을 설치해 주면 되며 package.json을 설정하며 필요한 모듈을 설치한다.

1
npm install

도 추가적으로 설치해주어야 하는 기본적인 node-module이다.

1
sudo npm install underscore

angular-cli 에서 scss 사용하기

sass는 루비라는 언어로 동작하기 때문에 먼저 루비를 설치해 줍니다.
리눅스와 맥의 경우는 루비가 기본으로 설치되어 있기 때문에 window의 경우만 설치해 줍니다.

다음의 링크를 통해 설치를 진행합니다.
다운로드

반드시 64비트인 x64로 설치해 줍니다.

ubuntu의 경우는 apt-get을 통해 설치하며 모든 설치 이후 다음 ruby-dev를 설치해주어야 한다.

1
apt-get install ruby-dev

루비 설치가 완료되었으면 다음 명령어를 통해 sass를 설치해 줍니다.

1
gem install sass

sass를 설치하였으면 angular-cli를 활용하여 sass 기반의 프로젝트를 만듭니다.

1
ng new 프로젝트 이름 --style=sass

만약 기존의 프로젝트가 있는 경우라면 다음과 같이 css 기반 프로젝트를 sass기반 프로젝트로 바꾸어 줍니다.

1
ng set defaults.styleExt scss

angualr-cli.json 에 다음의 내용을 추가해 줍니다.

1
2
3
4
5
{
"defaults": {
"styleExt": "scss"
}
}

angular migration

참조

Module

entryComponents

module.ts의 entryComponents는 html내에서 확인되는 태그가 아닌 동적으로만 생성되는 태그를 앵귤러가 알 수 있게 해준다.
앵귤러는 이를 통해 태그를 찾고 컴파일을 수행한다.
만약 html 내에서 확인되는 다른 많은 태그들은 단순히 declarations에만 있으면 된다.

Component

컴포넌트 안에 input 주기

example

undefined

[readonly]=”true”

인풋이 입력되지 않도록 읽기전용으로 바꾼다.

Library

Angular bootstrap

npm install --save @ng-bootstrap/ng-bootstrap

Angular material

code flow

1
2
3
4
5
6
7
8
9
10
11
//앵귤러 매터리얼 설치
npm install --save @angular/material @angular/cdk
//매터리얼에 필요한 애니메이션 설치
npm install --save @angular/animations
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
imports: [BrowserAnimationsModule],
//달력입력박스를 위한 module.ts에서 임포트
import {MdButtonModule, MdCheckboxModule} from '@angular/material';

// css 파일에서 추가시켜줌
@import "~@angular/material/prebuilt-themes/indigo-pink.css";

Angular Material 시작하기

Promise

세팅

1
2
npm install promise
var Promise = require('promise');

로그인 구현

Work Flow

  1. _guards디렉토리 생성
  2. alert component 생성
  3. alert service 생성
  4. auth guard생성
  5. authentication service 생성

jasonwatmore의 Angular를 이용한 로그인 구현 포스팅

ETC

주의 사항

  1. 임포트시 양방향으로 서로서로 import를 하는 circulr 임포트를 하면 안된다.

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×