Angular 19로 빌드하는 반응형 UI: 신호(Signal)를 통한 실용적인 프로필 편집기
2025-04-17 15:23:00Angular 19로 빌드하는 반응형 UI: 신호(Signal)를 통한 프로필 편집기
현대의 웹 개발에서 반응형 UI를 구축하는 과정은 Angular와 같은 프레임워크를 통해 보다 직관적으로 접근하고 있습니다. 이번 글에서는 Angular 19에서 제공하는 Signal API를 통해 반응형 프로필 편집기를 예제로 다뤄보겠습니다. Signal은 Angular 16부터 도입된 새로운 반응성 모델로, 복잡한 상태 관리 도구에 의존하지 않고 효과적인 상태 관리를 가능하게 해 줍니다.
Angular Signal이란 무엇인가?
Signal은 Angular가 16버전부터 도입한 반응식 원시 타입(primitives)으로, 상태 관리에 정밀한 제어를 가능하게 합니다. Signal은 다음과 같은 특성을 가지고 있습니다:
- 구독 관리가 필요 없습니다.
- 뷰에서 자동으로 업데이트가 트리거됩니다.
- 성능 최적화가 되어 있습니다.
Signal API는 세 가지 핵심 기능을 제공합니다:
- signal(): 쓰기 가능한 반응식 값을 정의합니다.
- computed(): 하나 이상의 signal 기반으로 유도된 값을 생성합니다.
- effect(): Signal 변경 시 부수 효과를 실행합니다.
문자열과 객체와 함께 Signal 사용하기
이번 예제에서는 다음과 같은 데이터 타입으로 Signal을 활용합니다:
- 기본 문자열 타입 (firstName, lastName)
- 객체 타입인 UserProfile, 반응적으로 변형할 것입니다.
사용자 데이터 업데이트 및 표시
사용자 프로필 편집기를 구축하여 다음과 같은 반응을 보여주도록 하겠습니다:
- 이름 필드의 변경사항
- 이메일 업데이트
effect()로 특정 변경사항에 반응하기
Signal의 변화를 모니터링하고, 사용자가 이메일을 특정 도메인(e.g., @example.com)으로 설정할 때 경고를 출력하는 방법을 보여드립니다. 이 기술은 폼 검증이나 로깅, 분석 또는 외부 업데이트를 트리거하는 데 유용합니다.
computed()를 통해 전체 이름 유도하기
필드를 매번 조합하는 대신, computed()를 사용하여 전체 이름을 반응적으로 유도합니다. 이는 데이터의 일관성을 보장하고 논리 중복을 방지합니다.
객체 안전하게 업데이트하기
전체 객체를 교체하는 대신, update()를 통해 중첩된 속성을 제어된 방식으로 업데이트합니다. 이는 불필요한 객체 참조 및 재렌더링을 방지합니다.
고급 예제: user-profile.component.ts
Angular 컴포넌트의 코드 예제는 다음과 같습니다:
import { Component, signal, computed, effect, WritableSignal } from '@angular/core';
import { FormsModule } from '@angular/forms';
interface UserProfile {
firstName: string;
lastName: string;
email: string;
}
@Component({
selector: 'app-user-profile',
standalone: true,
imports: [FormsModule],
template: `
<div class="profile-card">
<h2>User Profile</h2>
<label>First Name: <input [ngModel]="firstName()" (ngModelChange)="updateFirstName($event)" /></label>
<label>Last Name: <input [ngModel]="lastName()" (ngModelChange)="updateLastName($event)" /></label>
<label>Email: <input [ngModel]="user().email" (ngModelChange)="updateEmail($event)" /></label>
<hr />
<p>Full Name: {{ fullName() }}</p>
<p>Email: {{ user().email }}</p>
</div>
`,
styles: [
`.profile-card {
background: #f5f5f5;
padding: 1rem;
border-radius: 8px;
width: 320px;
}`,
`label { display: block; margin-bottom: 0.5rem; }`,
`input { width: 100%; padding: 0.25rem; margin-top: 0.25rem; }`
]
})
export class UserProfileComponent {
firstName = signal('Ada');
lastName = signal('Lovelace');
user: WritableSignal<UserProfile> = signal({
firstName: 'Ada',
lastName: 'Lovelace',
email: 'ada@angular.dev'
});
fullName = computed(() => `${this.firstName()} ${this.lastName()}`);
constructor() {
effect(() => {
const currentEmail = this.user().email;
if (currentEmail.endsWith('@example.com')) {
console.warn(`Warning: Placeholder email detected: ${currentEmail}`);
}
});
}
updateFirstName(value: string) {
this.firstName.set(value);
this.user.update(current => ({
...current,
firstName: value
}));
}
updateLastName(value: string) {
this.lastName.set(value);
this.user.update(current => ({
...current,
lastName: value
}));
}
updateEmail(value: string) {
this.user.update(current => ({
...current,
email: value
}));
}
}
이 예제의 주요 개념
| 기능 | 목적 |
|---|---|
| signal() | 프리미티브와 복잡한 타입 모두를 위한 반응식 원시값 |
| computed() | 종속성이 변경될 때 값을 자동으로 유도 |
| effect() | 반응식 값 변화를 기반으로 로직 실행 |
| update() | 전체 신호 값을 교체하지 않고 객체의 일부를 업데이트 |
| ngModel + signals | Angular 폼과 신호의 통합 방식 예시 |
도전 과제
더 나은 학습과 완성을 위해 다음을 시도해 보세요:
- 폼의 유효성을 추적하는 신호 추가 (예: isValidName, isValidEmail).
- .set()을 사용해 원래 값으로 복원하는 reset() 메소드 생성.
- signal<string[]>을 사용하여 이름 변경 리스트 추가.
- 읽기 전용 필드를 가진 토글 모드 (isEditMode) 생성.
결론
Angular Signal은 RxJS의 오버헤드 없이 상태를 반응적으로 관리할 수 있는 강력하고 우아한 솔루션을 제공합니다. 최소한의 코드로:
- 실시간으로 변경 사항을 추적하고 표시합니다.
- 동적 유도 데이터를 생성합니다.
- 효과를 통해 선언적으로 변화에 반응합니다.
새로운 패러다임은 컴포넌트 로직 관리를 단순화하고 복잡한 애플리케이션의 유지 보수성을 향상시킵니다. Angular의 미래는 반응적이고, Signal을 새로운 상태 기본 요소로 받아들일 좋은 시점입니다.
백링크: