본문 바로가기

개발 공부/Angular

Angular / @ngrx / store

Angular NgRx Store:

NgRx Store는 Angular 애플리케이션을 위한 상태 관리 라이브러리로, 자바스크립트 애플리케이션의 인기 있는 상태 관리 라이브러리인 Redux에서 영감을 받았습니다. 이 라이브러리는 반응형이며 확장 가능한 방식으로 상태를 관리할 수 있는 예측 가능한 상태 컨테이너를 제공합니다.

NgRx Store의 주요 개념

  1. Store: 애플리케이션의 상태를 보관하는 곳입니다. Store는 단일 진실의 출처(single source of truth)로, 애플리케이션의 모든 상태를 중앙에서 관리합니다.
  2. State: 애플리케이션의 현재 상태를 나타냅니다. 상태는 일반적으로 여러 작은 상태 조각으로 나뉘어 관리됩니다.
  3. Actions: 상태 변화를 일으키는 이벤트입니다. Action은 type과 선택적으로 payload를 포함합니다.
  4. Reducers: Actions에 따라 상태를 변화시키는 순수 함수입니다. Reducer는 현재 상태와 Action을 입력으로 받아 새로운 상태를 반환합니다.
  5. Selectors: Store에서 상태를 추출하는 함수입니다. Selectors는 애플리케이션의 특정 부분에 접근할 수 있게 도와줍니다.
  6. Effects: 비동기 작업을 관리하기 위한 NgRx의 컴포넌트입니다. Effects는 Action을 관찰하고 비동기 작업(예: HTTP 요청)을 수행한 후 새로운 Action을 디스패치합니다.

 

전체적인 흐름

1. example.actions.ts

2. example.actions.ts

 

3. example.effects.ts

 

4. example.reducers.ts

 

 

example.selectors.ts

import { createFeatureSelector, createSelector } from '@ngrx/store';

export const selectExamplesState = createFeatureSelector<ExampleType[]>('examples');
export const selectExamples = createSelector(selectExamplesState, (examples: ExampleType[]) => examples);

 

  • import { createFeatureSelector } from '@ngrx/store';:
    • NgRx 패키지에서 createFeatureSelector 함수를 가져옵니다. 이 함수는 NgRx 스토어에서 특정 피처 상태를 선택하는 데 사용됩니다.
  • createFeatureSelector<ExampleType[]>('examples')
    • createFeatureSelector는 NgRx 스토어에서 특정 피처 상태 슬라이스를 선택하는 데 사용됩니다.
    • ExampleType[]는 'examples' 상태 슬라이스의 타입입니다.
    • 'examples'는 상태 슬라이스의 이름입니다. 이 이름은 보통 상태 모듈을 설정할 때 정의됩니다.
    • 결과적으로, selectExamplesState는 NgRx 스토어에서 'examples' 상태 슬라이스를 선택하는 선택자 함수가 됩니다.
  • createSelector(selectExamplesState, (examples: ExampleType[]) => examples)
    • createSelector는 입력 선택자들(selectExamplesState)과 프로젝션 함수((examples) => examples)를 인자로 받아 새로운 선택자를 만듭니다.
    • selectExamplesState는 첫 번째 선택자로, 'examples' 상태 슬라이스를 반환합니다.
    • 프로젝션 함수 (examples: ExampleType[]) => examples는 이 상태 슬라이스를 받아 그대로 반환합니다. 이는 선택자를 통해 'examples' 상태 슬라이스를 그대로 가져오는 역할을 합니다.
    • 결과적으로, selectExamples는 selectExamplesState와 같은 역할을 하지만, 이를 통해 추가적인 프로젝션이나 변환을 적용할 수 있는 구조를 제공합니다.

Selector 사용법

import { selectExamplesState } from 'path-to-selectors-file';
import { Store } from '@ngrx/store';

@Component({
  // Component metadata here
})
export class SomeComponent implements OnInit {
  examples$: Observable<ExampleType[]>;

  constructor(private store: Store) {}

  ngOnInit() {
    this.examples$ = this.store.select(selectExamplesState);
  }
}

 

 

Selector 후 전체 동작 흐름

  1. 액션 생성:
    • createExample 함수가 호출되어 { viewMode: this.viewMode } 페이로드를 포함한 액션 객체를 만듭니다.
    • 생성된 액션 객체는 다음과 같을 수 있습니다:
    • 액션 디스패치:
      • this.store.dispatch(createExample({ viewMode: this.viewMode }))가 호출됩니다.
  2. 이펙트 처리:
    • ExampleEffects에서 createExample 액션을 포착합니다.
    • ExampleService를 사용하여 비동기 작업(API 호출)을 수행합니다.
    • 작업이 성공하면 createExampleSuccess 액션을, 실패하면 createExampleFailure 액션을 디스패치합니다.
  3. 리듀서 처리:
    • 스토어는 디스패치된 액션을 받아 리듀서로 전달합니다.
    • 성공 액션(createExampleSuccess)이 리듀서에 전달되면, 새로운 보드가 상태에 추가됩니다.
    • 실패 액션(createExampleFailure)이 리듀서에 전달되면, 오류 상태가 업데이트됩니다.

 

 

1. 액션 (example.actions.ts)

import { createAction, props } from '@ngrx/store';

export const createExample = createAction(
  '[Example] Create Example',
  props<{ viewMode: string }>()
);

export const createExampleSuccess = createAction(
  '[Example] Create Example Success',
  props<{ example: ExampleType }>()
);

export const createExampleFailure = createAction(
  '[Example] Create Example Failure',
  props<{ error: any }>()
);

this.store.dispatch

  • store.dispatch는 NgRx 스토어의 메서드로, 액션을 스토어에 전달하여 상태 변화를 유도합니다.
this.store.dispatch(createExample({ viewMode: this.viewMode }));
  • dispatch 메서드:
    • 스토어의 dispatch 메서드는 액션 객체를 스토어에 전달하여 리듀서가 이를 처리하고 상태를 변경하도록 합니다.
  • createExample 액션 생성자:
    • 이 함수는 액션 객체를 반환합니다. 예를 들어, 다음과 같이 정의될 수 있습니다:
    import { createAction, props } from '@ngrx/store';
    
    export const createExample = createAction('[Examples] Create Example', props<{ viewMode: ExampleFilterTypes }>());
  • { viewMode: this.viewMode }:
    • createExample 액션 생성자 함수에 전달되는 페이로드입니다. this.viewMode는 현재 클래스의 viewMode 속성으로, Example 생성 시 필요한 상태나 모드를 나타냅니다.
    • 예를 들어, this.viewMode가 'ExampleFilterTypes'라면, 이 호출은 createExample 액션을 { viewMode: ExampleFilterTypes } 페이로드와 함께 디스패치합니다.

요약

  • this.store.dispatch(createExample({ viewMode: this.viewMode })):
    • createExample 액션을 디스패치하여 새로운 Example를 생성하고, 이 보드의 viewMode는 this.viewMode에서 가져온 값을 사용합니다.
    • 이를 통해 상태가 변경되고, 새로운 Example이 NgRx 스토어에 추가됩니다.

 

2. 이펙트 (example.effect.ts)

import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { ExampleService } from './example.service';
import { createExample, createExampleSuccess, createExampleFailure } from './example.actions';

@Injectable()
export class ExampleEffects {
  constructor(
    private actions$: Actions,
    private exampleService: ExampleService
  ) {}

  createExample$ = createEffect(() =>
    this.actions$.pipe(
      ofType(Actions.createExample),
      switchMap(({ viewMode }) =>
        from(this.apiCreateExample()).pipe(
          switchMap(({ result, errorCode, errorMessage, data }) => {
            if (result) {
              return of(ExampleActions.createExampleSuccess({ data, viewMode }));
            } else {
              return of(ExampleActions.fromServerFailure({ error: { errorCode, errorMessage } }));
            }
          }),
          catchError((error) => of(ExampleActions.genericFailure({ error: error.message })))
        )
      )
    )
  );
}
  • createEffect:
    • NgRx 이펙트를 정의하는 함수입니다. 이펙트는 액션 스트림을 관찰하고, 특정 액션에 반응하여 비동기 작업을 수행합니다.
  • this.actions$.pipe:
    • this.actions$는 NgRx에서 제공하는 액션 스트림입니다. 모든 디스패치된 액션이 이 스트림을 통해 전달됩니다.
    • pipe 메서드를 사용하여 액션 스트림을 조작합니다.
  • ofType(Actions.createExample):
    • ofType는 특정 타입의 액션을 필터링합니다. 여기서는 createExample 액션이 디스패치될 때만 이펙트가 실행되도록 합니다.
  • switchMap(({ viewMode }) =>:
    • switchMap은 새로운 Observable을 생성하며, 이전 Observable을 취소하고 새로운 Observable을 구독합니다.
    • 여기서는 createExample 액션의 페이로드인 viewMode를 구조 분해하여 사용합니다.
  • from(this.createExample()).pipe:
    • from 함수는 프로미스를 Observable로 변환합니다. this.createExample()는 보드 생성 API 호출을 나타냅니다.
  • switchMap(({ result, errorCode, errorMessage, data }) =>:
    • API 호출의 응답을 처리합니다. 응답 객체를 구조 분해하여 result, errorCode, errorMessage, data를 사용합니다.
  • 성공 처리:
    • if (result) { ... } 블록은 API 호출이 성공했을 때 실행됩니다.
    • Actions.createExampleSuccess({ data, viewMode }) 액션을 디스패치하여 성공 상태를 반영합니다.
  • 실패 처리:
    • else { ... } 블록은 API 호출이 실패했을 때 실행됩니다.
    • 오류가 발생하면, ExampleActions.fromServerFailure({ error: { errorCode, errorMessage } }) 액션을 디스패치합니다.
  • catchError:
    • API 호출 도중 발생한 에러를 처리합니다.
    • ExampleActions.genericFailure({ error: error.message }) 액션을 디스패치하여 에러 상태를 반영합니다.

 

 

3. 리듀서 (example.reducer.ts)

import { createReducer, on } from '@ngrx/store';
import { createExampleSuccess, createExampleFailure } from './example.actions';

export const initialState = {
  examples: [],
  error: null
};

const exampleReducer = createReducer(
  initialState,
  on(createExampleSuccess, (state, { example }) => ({
    ...state,
    examples: [...state.examples, example],
    error: null
  })),
  on(createExampleFailure, (state, { error }) => ({
    ...state,
    error
  }))
);

export function reducer(state, action) {
  return exampleReducer(state, action);
}

 

  • NgRx를 사용하여 보드 관련 상태를 관리하는 리듀서를 정의합니다.
  • 리듀서는 특정 액션에 따라 상태를 변경하는 역할을 합니다.
  • 여기서 examplesReducer는 createExampleSuccess 액션이 디스패치될 때 상태를 업데이트하는 방법을 정의합니다.

'개발 공부 > Angular' 카테고리의 다른 글

Angular LifeCycle  (1) 2024.11.02
Angular Standalone  (2) 2024.10.27
Angular 란  (0) 2024.07.21
Angular / RxJS  (0) 2024.07.16
Service Workers  (1) 2024.01.24