[Angular 마스터하기] Day 15 - 에러 처리와 로딩 상태
[Angular 마스터하기] Day 15 - 에러 처리와 로딩 상태
이제와서 시작하는 Angular 마스터하기 - Day 15 “안정적인 앱을 위한 에러 처리와 로딩 상태 관리! 🛡️”
오늘 배울 내용
- HTTP 에러 처리
- 로딩 상태 관리
- try-catch와 catchError
- 실전: 안전한 데이터 페칭
1. 로딩 상태 관리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import { Component, signal } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-data-loader',
template: `
<div class="container">
<button (click)="loadData()">데이터 로드</button>
@if (isLoading()) {
<div class="loading">⏳ 로딩 중...</div>
} @else if (error()) {
<div class="error">❌ 에러: {{ error() }}</div>
} @else if (data()) {
<div class="success">✅ 데이터: {{ data() | json }}</div>
}
</div>
`,
styles: [`
.loading { color: #2196F3; }
.error { color: #f44336; }
.success { color: #4CAF50; }
`]
})
export class DataLoaderComponent {
private http = inject(HttpClient);
data = signal<any>(null);
isLoading = signal(false);
error = signal<string | null>(null);
loadData() {
this.isLoading.set(true);
this.error.set(null);
this.http.get('https://jsonplaceholder.typicode.com/posts/1')
.subscribe({
next: (response) => {
this.data.set(response);
this.isLoading.set(false);
},
error: (err) => {
this.error.set(err.message);
this.isLoading.set(false);
}
});
}
}
2. catchError 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { catchError, of } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class SafeDataService {
private http = inject(HttpClient);
getData() {
return this.http.get('https://api.example.com/data').pipe(
catchError(error => {
console.error('에러 발생:', error);
return of({ error: true, message: error.message });
})
);
}
}
3. 재시도 로직
1
2
3
4
5
6
7
8
9
10
import { retry, delay } from 'rxjs';
loadDataWithRetry() {
this.http.get('https://api.example.com/data').pipe(
retry({ count: 3, delay: 1000 }) // 3번 재시도, 1초 대기
).subscribe({
next: (data) => console.log('성공:', data),
error: (err) => console.error('3번 시도 후 실패:', err)
});
}
4. 실전 예제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
@Component({
selector: 'app-user-list',
template: `
<div class="user-list">
<h1>사용자 목록</h1>
<button (click)="loadUsers()" [disabled]="isLoading()">
{{ isLoading() ? '로딩 중...' : '새로고침' }}
</button>
@if (isLoading()) {
<div class="spinner">
<div class="loader"></div>
<p>데이터를 불러오는 중...</p>
</div>
} @else if (error()) {
<div class="error-box">
<h3>😢 오류가 발생했습니다</h3>
<p>{{ error() }}</p>
<button (click)="loadUsers()">다시 시도</button>
</div>
} @else {
<div class="users">
@for (user of users(); track user.id) {
<div class="user-card">
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
</div>
}
</div>
}
</div>
`,
styles: [`
.spinner {
text-align: center;
padding: 40px;
}
.loader {
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error-box {
background: #ffebee;
padding: 30px;
border-radius: 10px;
text-align: center;
}
`]
})
export class UserListComponent {
private http = inject(HttpClient);
users = signal<any[]>([]);
isLoading = signal(false);
error = signal<string | null>(null);
ngOnInit() {
this.loadUsers();
}
loadUsers() {
this.isLoading.set(true);
this.error.set(null);
this.http.get<any[]>('https://jsonplaceholder.typicode.com/users')
.pipe(
retry({ count: 2, delay: 1000 }),
catchError(err => {
this.error.set(err.message || '알 수 없는 오류');
this.isLoading.set(false);
return of([]);
})
)
.subscribe(data => {
this.users.set(data);
this.isLoading.set(false);
});
}
}
📝 정리
Phase 3 완료! 🎉
축하합니다! Phase 3를 모두 완료했습니다:
- ✅ Day 11: 폼 다루기
- ✅ Day 12: Reactive Forms
- ✅ Day 13: 파이프
- ✅ Day 14: 컴포넌트 생명주기
- ✅ Day 15: 에러 처리
체크리스트
- 로딩 상태를 관리할 수 있나요?
- 에러를 처리할 수 있나요?
- 재시도 로직을 구현할 수 있나요?
📚 다음 학습
Phase 4 시작!
- 이전: Day 14: 컴포넌트 생명주기
- 다음: Day 16: Signal 고급
“안정적인 앱은 에러 처리에서 시작됩니다!” 🛡️
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.