본문 바로가기

Web + APP/Angular

NGRX - Reducers

반응형
SMALL

https://ggodong.tistory.com/307

 

NGRX - Actions

안녕하세요 ! 꼬동입니다. 그.... 이번에 한 번 진짜 ! 각 잡고 ! NGRX 훑어보기 할려고 합니다. 이번엔 진심 !! 꾸준히 !! 할거야 !! 우선 유튜브 하나만 보고 NGRX는 Redux 패턴의 상태관리 시스템입니

ggodong.tistory.com

 

진짜 훑어 본다고 했죠 !?

 

너무 졸리지만,,, 그래도 공부해야하니까 !!

 


NgRx의 Reducers는 앱에서 하나의 state에서 다음 state로 바뀔 때, 다루는 책임을 가지고 있습니다.

 

어떤 action의 타입인지에 따라, Reducers는 때에 따른 트랜지션을 다룹니다.

 

위의 그림이 더 쉽게 이해 됐겠죠 ?

 

Reducer는 같은 input에 대해 같은 output을 뱉어내는 순수 함수입니다.

 

Side Effect가 없고, 각 state를 동기적으로 다룹니다.

 

각 Reducer 함수는 action을 받고, 현재의 state를 받고, 새로운 state를 리턴하는 역할을 합니다.

 

아래 가이드는 어떻게 리듀서 함수를 작성하는 지와 Store에 등록하는 법과 구성하는 법을 알아볼 것입니답.


Reducer Function

Reducer에 의해 관리되는 모든 state는 일관된 부분들이 있습니다.

  • state의 모양을 정의하는 interface 또는 type
  • arguments 들이 initial state 또는 현재 state와 현재 액션을 포함하고 있습니다.
  • 액션과 관련된 상태 변화를 다루는 함수입니다.

예제를 위해서 액션을 먼저 정의해보도록 합시다.

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

export const homeScore = createAction('[Scoreboard Page] Home Score');
export const awayScore = createAction('[Scoreboard Page] Away Score');
export const resetScore = createAction('[Scoreboard Page] Score Reset');
export const setScores = createAction('[Scoreboard Page] Set Scores', props<{game: Game}>());

자 그 다음, 위 actions을 import 하고, state를 정의해서 reducer를 만들어보도록 합시다.


State Shape 정의하기

각 Reducer 함수는 action의 관찰자입니다. scoreboard action은 위에서 정의가 되었는데요. 이는, reducer에 의해 트랜지션될 수 있습니다.

 

reducer 파일 안에 state 트랜지션을 다루기 위해서 actions을 import 하고 여라가지 sets를 import 하여 사용해봅시다.

import { Action, createReducer, on } from '@ngrx/store';
import * as ScoreboardPageActions from '../actions/scoreboard-page.actions';

export interface State {
	home: number;
    away: number;
}

그리고 state interface를 정의해보았습니다.


Initial State Setting

initial state는 initial value를 state에게 줍니다. 또는, 만약 현재 state가 undefined라면 값을 부여해줍니다.

export const initialState: State = {
	home: 0,
    away: 0,
}

요런식으로 initial state를 정의해보았습니다.


Reducer Function 만들기

드디어 진짜 Reducer Function 만들어봅시다.

 

이제 게살버거 만들어도 돼요 ?

 

Reducer Function의 책임은 state 트랜지션을 immutable하게 다루는 것입니다.

immutable object : 불변 객체, 불변 객체는 생성 후 그 상태를 바꿀 수 없는 객체

state를 관리하기 위해서 createReducer를 통해서, Reducer Function을 만들어 봅시다.

export const scoreboardReducer = createReducer(
	initialState,
    on(ScoreboardPageActions.homeScore, state => ({ ...state, home: state.home + 1})),
    on(ScoreboardPageActions.awayScore, state => ({ ...state, away: state.away + 1})),
    on(ScoreboardPageActions.resetScore, state => ({ home: 0, away: 0})),
    on(ScoreboardPageActions.setScores, (state, { game }) => ({ home: game:home, away: game:away })),
);
만약 default Ivy AOT 컴파일러 (또는 JIT)를 쓰면, Exported Reducer Function은 더 이상 필요하지 않습니다. View Engine AOT compiler 일 때만 필요합니답.

위의 예제를 보면 총 4개의 actions을 다루는 reducer를 만들어 보았습니다. 각 action은 새로운 객체를 리턴합니다. 즉, immutable 한 값을 책임진다는 뜻인데, 기존의 state를 수정하지 않는다는 의미가 될 수 있죠.

전개 연산자는 얕은 복사만 합니답. 깊은 복사를 원하시면, lodash / immer를 이용해보세요.

action이 dispatch 될 때, 등록된 reducer들이 이를 받습니다. action을 다루게 되면, on function을 통해서 state를 변화를 주게 됩니다.

버전 7.x 이전엔 Switch 문으로 Reducer를 작성할 수 있다곤 하는데, 보니까 on이 더 편한거 같으니, 그냥 on 쓰시는게 ㅎㅅㅎ;;

Root State를 설정

애플리케이션의 state는 하나의 큰 Object로 정의됩니다. 등록된 리듀서는 오직, 해당 state를 key / value를 관리하는 역할을 할 뿐입니다.

 

애플리케이션안에 전역 store를 등록하기 위해서 StoreModule.forRoot() 메소드를 사용하여, key / value 짝을 state에 정의를 하는데, StoreModule.forRoot()가 state의 일 부분을 선택하거나, action을 dispatch 하기 위해 component와 servicestore service를 주입하여, 전역 providers를 애플리케이션에 등록하게 됩니다.

 

import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import * as fromScoreboard from './reducers/scoreboard.reducer';

@NgModule({
	imports: [
    	StoreModule.forRoot({ game: fromScoreboard.reducer })
	],
})
export class AppModule {}

 

StoreModule.forRoot()로 등록된 상태들은 애플리케이션이 시작하자마자 등록됩니다. 일반적으로, root state들은 항상 모든 곳에서 즉시 접근이 가능합니다.


Feature State를 설정

Feature state들은 root state 들이 한거 처럼 똑같이 행동하는데, 애플리케이션 특정 feature 영역에서만 정의되는 것을 허락합니다. state가 하나 크게 객체가 있고, feature state들은 추가로 key value 객체로 안에 존재하게 될 겁니다.

 

예제를 보시면, feature state가 어떻게 설계되는지를 알 수 있을건데, 일단 빈 state 객체부터 시작해봅시다.

 

import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';

@NgModule({
	imports: [
    	StoreModule.forRoot({})
	],
})
export class AppModule {}

forRoot 안에 빈 객체를 넣어서 등록을 해보았는데, 이제 ScoreboardModule이라고 이름 붙혀진 NgModule에 scoreboard 리듀서를 사용해서 추가로 state를 등록해봅시다.

// scoreboard.reducer.ts
export const scoreboardFeatureKey = 'game';
// scoreboard.module.ts
import { NgModule } from '@angular/core';
improt { StoreModule } from '@ngrx/store';
import * as fromScoreboard from './reducers/scoreboard.reducer';

@NgModule({
	import: [
    	StoreModule.forFeature(fromScoreboard.scoreboardFeatureKey, fromScoreboard.reducer)
	],
})
export class ScoreboardModule {}
createFeatureSelector라고 불리는 가상 feature key string을 만들어서, 등록하는 것을 추천합니다. 안그럼 하드코딩 string을 넣어야하니까요.

그런 다음 ScoreboardModule을 AppModule에 등록합시다.

 

이렇게 되면, state가 eager loading이 되게됩니다.

Eager Loading: 애플리케이션이 시작되면서, 바로 로드 되는 것
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { ScoreboardModule } from './scoreboard/scoreboard.module';

@NgModule({
	imports: [
    	StoreModule.forRoot({}),
        ScoreboardModule
	],
})
export class AppModule {}

일단 ScoreboardModule이 로드되면, game 키가 객체의 property로 되면서, state로 관리되게 됩니다.

 

방금 egaer loading으로 했는데, lazy loading으로도 충분히 사용될 수가 있습니다.

 

여러가지 state를 만들 수가 있겠죠 ?


Actions과 Reducer를 알아봤습니다.

 

Reducer는 action이 이뤄졌을 때 결정을 state transition을 해주는 역할을 하는 애다. 요거만 기억하시면 됩니다.

 

만약, 추가적으로 뭔가 불러오면서, 더 로직을 넣고 싶다 하면 Effects를 쓰는데, 이는 추후에 알아보도록 하겠습니다.


이상 NGRX - Reducers였습니다. ^_^

반응형
LIST

'Web + APP > Angular' 카테고리의 다른 글

NGRX - Selectors  (0) 2021.11.26
Normalizing State Shape  (0) 2021.11.08
NGRX - Actions  (0) 2021.10.24
Angular : 같은 URL에서 Refetch data  (2) 2021.10.17
Renderer2 in Angular 12 (Why not ElementRef ?)  (0) 2021.09.25