1. 위 이미지는 단순히 ngx store 구현 시 http 요청의 횟수가 확연히 줄어든다는 것을 나타내기 위한 그림입니다.
2. 이 부분이 핵심입니다.
4. 우측 상단에 서버가 보입니다. 사용자가 많을 경우 store 는 더욱 빛을 발합니다. 예를 들어 한 명의 고객이 정보를 수정했는데, 해당 정보를 다른 모든 고객에게 보여줘야 하는 상황이 발생한다면? 고객들은 각각 페이지 새로고침 및 서버 요청이 필요합니다. 하지만, store 경우 수정이 발생했을 때 변화를 감지하여 서버에 새로운 데이터 목록을 요청할 수도 있으며, 자동으로 클라이언트들 또는 컴포넌트에 데이터 갱신을 할 수 있습니다. 이론 설명은 여기까지 하고 실전으로 넘어갑시다.
yarn add @ngrx/store
1. 경로는 보통 app/store 에 생성합니다.
import { Injectable } from '@angular/core'; // Angular의 핵심 모듈 중 하나로, @Injectable 데코레이터를 사용하기 위해 필요합니다.
import { DTO } from '@DTO/interface'; // 사용자 정의 DTO
import { Action, NgxsOnInit, Selector, State, StateContext } from '@ngxs/store'; // Ngxs 라이브러리의 상태 관리를 위한 데코레이터와 인터페이스를 가져옵니다.
import { HttpService } from '../services/http.service'; // Http 통신을 위한 서비스를 가져옵니다.
export type ClientInformationStateModel = { // ClientState의 상태 모델을 정의합니다.
client: DTO; // client 정보를 담을 DTO 타입의 객체입니다.
};
export class FetchClient {
static readonly type = '[Client] Fetch Client'; // FetchClient 액션의 유형을 정의합니다.
}
@State({ // @State 데코레이터를 사용하여 상태 클래스를 정의합니다.
name: 'client', // 상태의 이름을 설정합니다.
defaults: { // 상태의 기본값을 설정합니다.
client: null, // client 정보는 초기에는 null로 설정합니다.
},
})
@Injectable() // 상태 클래스에 @Injectable 데코레이터를 사용하여 의존성 주입이 가능하도록 합니다.
export class ClientState implements NgxsOnInit { // NgxsOnInit 인터페이스를 구현합니다. 이는 상태 클래스의 초기화를 위한 라이프사이클 후크를 제공합니다.
constructor(private readonly httpService: HttpService) {} // HttpService를 주입합니다.
// NgxsOnInit 인터페이스의 메서드를 구현합니다.
ngxsOnInit(ctx: StateContext<ClientInformationStateModel>): void {
this.httpService
.get<DTO>(`client-info/${process.env['NX_PROJECT_ID']}`) // HTTP GET 요청을 보냅니다.
.subscribe({
next: (client) => {
// HTTP 요청의 결과값인 client 정보를 상태에 업데이트합니다.
ctx.setState({ client });
},
});
}
// 수동으로 FetchClient 액션을 호출할 때 사용되는 메서드입니다.
@Action(FetchClient)
async fetchClient(ctx: StateContext<ClientInformationStateModel>) {
this.httpService
.get<DTO>(`client-info/${process.env['NX_PROJECT_ID']}`) // HTTP GET 요청을 보냅니다.
.subscribe({
next: (client) => {
// HTTP 요청의 결과값인 client 정보를 상태에 업데이트합니다.
ctx.setState({ client });
},
});
}
@Selector() // Selector 데코레이터를 사용하여 상태의 일부를 선택할 수 있는 선택자 함수를 정의합니다.
static client(state: ClientInformationStateModel) {
return state.client; // 상태 모델에서 client 정보를 선택하여 반환합니다.
// static 키워드를 사용하는 이유는 선택자 메서드를 정적(static)으로 선언하여
// 외부에서 인스턴스화하지 않고도 호출할 수 있도록 하기 위함입니다.
// 정적 메서드는 클래스 자체에 속하며 클래스의 인스턴스를 생성하지 않고도 호출할 수 있습니다.
// 이렇게 하면 선택자 메서드를 간편하게 사용할 수 있고, 상태 클래스 외부에서도 접근할 수 있습니다.
// 따라서, static 키워드를 사용하여 선택자 메서드를 정적으로 선언한 이유는
// 선택자를 상태 클래스 외부에서 사용하기 쉽고 편리하게 하기 위해서입니다.
}
}
store 내부입니다. 상세한 설명은 주석을 참고해주세요.
이제 작성한 스토어를 기반으로 다른 컴포넌트에서 호출하여 사용해봅시다.
port class test {
@Select(ClientState.client) information!: Observable<DTO>;
}
@Select(ClientState.client): ClientState 상태 클래스의 client 속성을 선택하는 선택자 함수입니다.
스토어에서 작성한 대로 해당 정보를 리턴 받아 곧바로 할당합니다.
주의할 점은 반드시 옵저버블 타입이어야 합니다.
import { Store } from '@ngxs/store';
import { FetchClient } from '../../store/client-information.state';
constructor(private store: Store) {} // store 의존성 주입
this.store.dispatch(new FetchClient()); // @Action 사용 예시 -> 수동 호출
@Action 사용 예시입니다.
액션의 경우 의존성 주입이 필요합니다.
스토어에서 작성한 대로 리턴 받은 값을 바로 할당합니다.
이렇게 구현할 경우 유지보수도 용이할 뿐더러 http 요청을 줄여 성능 향상에 도움이 됩니다.
import { ApplicationConfig, importProvidersFrom } from '@angular/core';
import {
provideRouter,
withEnabledBlockingInitialNavigation,
RouteReuseStrategy,
} from '@angular/router';
import { appRoutes } from './app.routes';
import { BrowserModule } from '@angular/platform-browser';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
// Animation
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
// HTTP
import { HttpClientModule } from '@angular/common/http';
import { NgxsModule } from '@ngxs/store'; // store
import { ClientState } from './store/client-information.state'; //store
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(appRoutes, withEnabledBlockingInitialNavigation()),
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
importProvidersFrom(
BrowserModule,
IonicModule.forRoot(),
BrowserAnimationsModule,
HttpClientModule,
NgxsModule.forRoot([ClientState]) //store
),
],
};