Angular의 새로운 상태관리: Signals
Table of contents
Signals란?
Angular 17부터 정식 기능으로 포함되었다.
값이 변경될 때 자동으로 관련 부분을 업데이트하는 간단한 상태 관리 솔루션.
추가 라이브러리가 아니고 Angular 코어에 내장되어 있다.
Signals의 주요 개념 및 특징
특징
복잡한 Observable 패턴 없이 간단하게 사용할 수 있는 API이다.
Zone.js에 의존하지 않는 새로운 변화 감지 전략을 지원한다.
Signals 이전의 방식: dirty checking으로, Angular의 기본 변경 감지 메커니즘인 Zone.js에 의존하며 전체 컴포넌트 트리의 모든 바인딩을 확인하여 변경 사항을 감지한다. 따라서 불필요한 검사로 인한 성능 저하의 가능성이 있다.
Signals의 경우 변경된 Signal에 직접 연결된 부분만 업데이트하여 성능상 이점이 있다.
Angular의 변화 감지 메커니즘과 긴밀히 통합된다.
성능상 이점
렌더링 최적화: 필요한 부분만 정확하게 리렌더링되어 전반적인 애플리케이션 성능이 향상된다.
메모리 사용 개선: 불필요한 객체 생성이 줄어들어 메모리 사용이 효율적.
CPU 사용 효율화: 불필요한 계산과 검사가 줄어들어 CPU 사용이 최적화 된다.
반응성 향상: 더 빠른 UI 업데이트.
개념
Signal: 시간에 따라 변할 수 있는 값.
const count = signal(0)
Computed Signal: 다른 Signal들을 기반으로 계산되는 Signal.
const doubleCount = computed(() => count() * 2)
this.count() * 2
이렇게 사용할 경우에 변화 감지 주기마다 재계산될 수 있어, 성능 저하의 원인이 될 수 있다.Computed Signal을 사용하면 변화 감지와 최적화가 자동으로 이루어진다.
Effect: Signal들의 변화에 반응하여 부수효과를 실행한다.
effect(() => console.log('Count changed!:', count()))
불변성: Signal의 값은 직접 수정할 수 없고, 새로운 값으로 대체해야 한다.
set
:count.set(count() + 1)
update
:count.update(prevCount => prevCount + 1)
set
은 React에서setCount(2)
처럼 직접 값을 설정하는 것에 대응.update
는 React에서 이전 상태를 기반으로 새 상태를 계산하는 것에 대응.setCount(prev => prev + 1)
SignalStore
앱의 상태를 한 곳에서 관리할 수 있다.
NgRx의 개념을 Signals에 적용한 것이라고 할 수 있다.
import { signalStore, withState, withComputed, withMethods } from '@ngrx/signals';
export const CounterStore = signalStore(
{ providedIn: 'root' },
withState({ count: 0 }),
withComputed(({ count }) => ({
doubleCount: computed(() => count() * 2)
})),
withMethods(({ count, ...store }) => ({
increment() {
count.update(c => c + 1);
},
decrement() {
count.update(c => c - 1);
}
}))
withHooks({
onInit: (store) => {
// 스토어 초기화 로직
},
onDestroy: (store) => {
// 정리 작업
}
})
);
withState
: 초기상태 정의. 객체 내의 각 속성은 자동으로 Signal로 변환된다.withComputed
: 기존 상태를 기반으로 계산된 값을 정의한다.withMethods
: 상태를 변경하는 메서드를 정의한다.withHooks
: 스토어의 생명주기 훅을 정의한다.스토어의 생명주기?
스토어가 생성되고, 사용되며, 최종적으로 제거되는 과정.
초기화
onInit
시점: 스토어가 처음 생성되거나 주입되거나 사용될 때
용도: 초기 설정, 데이터 로딩, 구독 설정 등
활성: 특별한 훅은 없지만, 스토어의 메서드와 상태 변경은 이 단계에서 주로 발생.
시점: 초기화 이후부터 제거 전까지
특징: 상태 변경, 계산된 값 업데이트, 메서드 호출 등이 발생
제거
onDestroy
시점: 스토어가 더 이상 필요하지 않을 때 (예: 애플리케이션 종료, 라우트 변경 등)
용도: 리소스 정리, 구독 해제, 메모리 누수 방지 등
NgRx와의 비교
Signals
간단하고 직관적이며, 빠른 학습 곡선을 가지고 있다.
작은 규모 ~ 중간 규모의 앱이나 빠른 개발이 필요한 프로젝트일 경우.
컴포넌트 수준의 상태 관리가 주로 필요한 경우.
NgRx
강력한 상태 관리 기능.
대규모의 복잡한 애플리케이션에서 사용한다.
높은 학습 곡선, 보일러플레이트 코드가 많다.
초기 설정이 복잡하고 번들 크기가 커질 수 있다.
내부적으로 RxJS의 Observable을 사용하여 상태 변화를 관리한다.
React Hooks와의 비교
Signals의 signal은 React의 useState
에 대응하고, effect는 useEffect
에 대응한다고 이해해도 무방할 듯하다. computed signals의 경우 useMemo
에 대응된다고 할 수 있다.
다만 차이가 있다면,
signal은 클래스 프로퍼티로 정의된다. 또한 함수형 컴포넌트 외부에서도 사용이 가능하며 signal의 값에 접근하려면 함수를 호출해야 한다.
count()
effect는
useEffect
와는 달리 의존성 배열을 명시적으로 지정하지 않고 자동으로 의존성을 추적한다. 또한 컴포넌트가 제거될 때 자동으로 정리되어 cleanup 함수를 반환할 필요도 없다. 그리고 effect는 보통 컴포넌트 생성자나 ngOnInit에서 설정한다.React에서는 useCallback, useMemo 등을 사용하여 명시적으로 최적화해야 하지만, Angular Signals는 자체적으로 최적화되어 있어, 추가적인 최적화가 필요없다.
마무리
React에 익숙해서 그런지, Signals의 개념을 이해할 때도 React의 어떤 훅에 대응하는지 생각하면서 이해하니 훨씬 쉽게 접근할 수 있었다. 그리고 이해하고 나니 오히려 React Hooks 보다 간단하다고 느껴졌다.
이렇게
Angular 기초와 핵심 개념
RxJS와 반응형 프로그래밍
Angular 상태관리: NgRx 사용법 가이드
Angular의 새로운 상태관리: Signals
까지 총 4개의 Angular 이해하기 포스트를 발행 완료했다! 👏
Subscribe to my newsletter
Read articles from yerin directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by