Angular 코드에서 어떤 상태를 저장하려면 signal
함수를 사용하면 됩니다.
때로는 다른 상태에 종속된 상태를 관리해야 할 수도 있습니다.
사용자가 배송 방식을 선택하는 컴포넌트를 예로 들어 봅시다:
@Component({/* ... */})export class ShippingMethodPicker { shippingOptions: Signal<ShippingMethod[]> = getShippingOptions(); // 기본 배송 방법을 지정합니다. selectedOption = signal(this.shippingOptions()[0]); changeShipping(newOptionIndex: number) { this.selectedOption.set(this.shippingOptions()[newOptionIndex]); }}
이 예제에서 selectedOption
은 기본값이 설정되어 있으며, 사용자의 선택에 따라 값이 변경됩니다.
그런데 shippingOptions
는 시그널이기 떄문에 값이 변경될 수 있습니다!
shippingOptions
값이 변경되면 selectedOption
값은 더이상 유효하지 않습니다.
이 때 linkedSignal
함수를 사용하면, 다른 상태와 직접 연결되는 시그널을 생성할 수 있습니다.
위 예제 코드에서 signal
을 linkedSignal
로 바꿔 보세요:
@Component({/* ... */})export class ShippingMethodPicker { shippingOptions: Signal<ShippingMethod[]> = getShippingOptions(); // 기본 배송방법을 지정합니다. selectedOption = linkedSignal(() => this.shippingOptions()[0]); changeShipping(index: number) { this.selectedOption.set(this.shippingOptions()[index]); }}
linkedSignal
은 signal
과 비슷하지만 다른 점이 하나 있습니다.
linkedSignal
은 기본값을 직접 받지 않고 computed
시그널처럼 계산 함수 를 통해 전달 받습니다.
그리고 계산 값이 변경되면 linkedSignal
의 값도 새로 계산된 값으로 변경됩니다.
이제 linkedSignal
의 값은 언제나 유효한 값을 유지합니다.
아래 예제를 보며 linkedSignal
값이 연결 상태에 따라 어떻게 변경되는지 확인해 보세요:
const shippingOptions = signal(['Ground', 'Air', 'Sea']);const selectedOption = linkedSignal(() => shippingOptions()[0]);console.log(selectedOption()); // 'Ground'selectedOption.set(shippingOptions()[2]);console.log(selectedOption()); // 'Sea'shippingOptions.set(['Email', 'Will Call', 'Postal service']);console.log(selectedOption()); // 'Email'
이전 상태 참조하기
경우에 따라 linkedSignal
가 이전 값을 참조해야 하는 경우가 있습니다.
위 예제에서 shippingOptions
목록이 변경되면 selectedOption
의 값은 언제나 첫번째 항목으로 변경됩니다.
하지만 사용자가 선택한 옵션이 어딘가에 사용중이라 이전 상태를 유지해야 한다고 합시다.
그러면 linkedSignal
을 생성할 때 소스(source) 와 연산(computation) 을 구분하는 방식을 사용하면 됩니다:
interface ShippingMethod { id: number; name: string;}@Component({/* ... */})export class ShippingMethodPicker { constructor() { this.changeShipping(2); this.changeShippingOptions(); console.log(this.selectedOption()); // {"id":2,"name":"Postal Service"} } shippingOptions = signal<ShippingMethod[]>([ { id: 0, name: 'Ground' }, { id: 1, name: 'Air' }, { id: 2, name: 'Sea' }, ]); selectedOption = linkedSignal<ShippingMethod[], ShippingMethod>({ // `selectedOption`의 값은 `source`가 바뀌었을 때 `computation` 결과 값으로 할당됩니다. source: this.shippingOptions, computation: (newOptions, previous) => { // newOptions에 이전 값이 전달되면 이전 값을 유지합니다. // 아니면 첫번째 옵션 값을 선택합니다. return ( newOptions.find((opt) => opt.id === previous?.value.id) ?? newOptions[0] ); }, }); changeShipping(index: number) { this.selectedOption.set(this.shippingOptions()[index]); } changeShippingOptions() { this.shippingOptions.set([ { id: 0, name: 'Email' }, { id: 1, name: 'Sea' }, { id: 2, name: 'Postal Service' }, ]); }}
linkedSignal
을 생성할 때 연산 함수를 전달하는 대신, 객체를 전달하면서 이 객체의 프로퍼티로 source
와 computation
옵션을 전달할 수 있습니다.
source
는 시그널을 지정합니다.
computed
시그널이거나 컴포넌트의 input
시그널도 가능합니다.
source
시그널의 값이 변경되면 linkedSignal
은 computation
실행 결과로 시그널의 값을 할당합니다.
그리고 computation
은 source
에서 새 값을 받으면서 previous
객체를 받는 함수입니다.
이 때 previous
객체에는 source
프로퍼티와 value
프로퍼티가 있습니다.
previous.source
프로퍼티에는 이전 source
값이 전달되며, previous.value
에는 이전 computation
연산 결과가 전달됩니다.
연산 함수는 이 값들을 활용해서 새 값을 만들어 내면 됩니다.
도움말: previous
인자를 사용할 때는 linkedSignal
의 제네릭 타입을 명시적으로 지정해야 합니다.
제네릭의 첫 번째 타입은source
로 전달되는 타입이며, 두 번째 타입은 computation
연산 결과의 타입입니다.
커스텀 동일성 평가 함수
linkedSignal
은 다른 시그널과 마찬가지로 커스텀 동일성 평가 함수를 지정할 수 있습니다.
이 함수는 linkedSignal
값이 변경되었는지 판단하는 용도로 사용됩니다:
const activeUser = signal({id: 123, name: 'Morgan', isAdmin: true});const activeUserEditCopy = linkedSignal(() => activeUser(), { // 사용자의 id가 같으면 같은 사용자로 판단합니다. equal: (a, b) => a.id === b.id,});// `source`와 `computation`을 각각 지정합니다.const activeUserEditCopy = linkedSignal({ source: activeUser, computation: user => user, equal: (a, b) => a.id === b.id,});