[Angular 마스터하기] Day 5 - 조건과 반복, 동적인 화면 만들기
[Angular 마스터하기] Day 5 - 조건과 반복, 동적인 화면 만들기
이제와서 시작하는 Angular 마스터하기 - Day 5 “조건과 반복으로 진짜 동적인 앱을 만들어봅시다! 🔄”
오늘 배울 내용
- 새로운 제어 흐름:
@if,@else - 반복문:
@for - 조건 분기:
@switch - 실습: Todo 리스트 기본 화면 만들기
1. 조건부 렌더링 - @if
기본 문법 (⭐ Angular 17+ 최신 문법!)
Angular 17부터는 더 간단하고 직관적인 @if 문법을 사용합니다!
graph TD
A[조건 검사] -->|true| B[내용 표시]
A -->|false| C[숨김 또는 else 블록]
style B fill:#4CAF50,stroke:#fff,color:#fff
style C fill:#f44336,stroke:#fff,color:#fff
간단한 예제
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
import { Component } from '@angular/core';
@Component({
selector: 'app-welcome',
standalone: true,
template: `
<div class="welcome">
<h1>환영합니다!</h1>
@if (isLoggedIn) {
<p>{{ username }}님, 반갑습니다! 👋</p>
<button (click)="logout()">로그아웃</button>
}
@if (!isLoggedIn) {
<p>로그인이 필요합니다.</p>
<button (click)="login()">로그인</button>
}
</div>
`
})
export class WelcomeComponent {
isLoggedIn = false;
username = '홍길동';
login() {
this.isLoggedIn = true;
}
logout() {
this.isLoggedIn = false;
}
}
@else 사용하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Component({
selector: 'app-status',
standalone: true,
template: `
<div class="status-box">
@if (isLoading) {
<div class="spinner">⏳ 로딩 중...</div>
} @else {
<div class="content">✅ 로딩 완료!</div>
}
</div>
`
})
export class StatusComponent {
isLoading = true;
ngOnInit() {
// 3초 후 로딩 완료
setTimeout(() => {
this.isLoading = false;
}, 3000);
}
}
@else if 체인
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
@Component({
selector: 'app-grade',
standalone: true,
template: `
<div class="grade-display">
<h2>점수: 점</h2>
@if (score >= 90) {
<p class="grade-a">🏆 A등급 - 우수합니다!</p>
} @else if (score >= 80) {
<p class="grade-b">🥈 B등급 - 잘했습니다!</p>
} @else if (score >= 70) {
<p class="grade-c">🥉 C등급 - 보통입니다.</p>
} @else if (score >= 60) {
<p class="grade-d">📝 D등급 - 노력이 필요합니다.</p>
} @else {
<p class="grade-f">❌ F등급 - 재시험이 필요합니다.</p>
}
</div>
`,
styles: [`
.grade-a { color: #4CAF50; font-size: 1.5em; }
.grade-b { color: #2196F3; font-size: 1.3em; }
.grade-c { color: #ff9800; font-size: 1.2em; }
.grade-d { color: #ff5722; font-size: 1.1em; }
.grade-f { color: #f44336; font-size: 1.1em; }
`]
})
export class GradeComponent {
score = 85;
}
복잡한 조건
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
@Component({
selector: 'app-access-control',
standalone: true,
template: `
<div class="access-panel">
@if (user && user.isActive && !user.isBanned) {
<div class="granted">
<h3>✅ 접근 허용</h3>
<p>님, 환영합니다!</p>
@if (user.role === 'admin') {
<button class="admin-btn">관리자 패널</button>
}
</div>
} @else {
<div class="denied">
<h3>❌ 접근 거부</h3>
@if (!user) {
<p>사용자 정보가 없습니다.</p>
} @else if (!user.isActive) {
<p>비활성화된 계정입니다.</p>
} @else if (user.isBanned) {
<p>정지된 계정입니다.</p>
}
</div>
}
</div>
`
})
export class AccessControlComponent {
user = {
name: '김개발',
isActive: true,
isBanned: false,
role: 'admin'
};
}
2. 반복문 - @for
기본 사용법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component({
selector: 'app-fruit-list',
standalone: true,
template: `
<div class="fruit-list">
<h2>과일 목록</h2>
<ul>
@for (fruit of fruits; track fruit) {
<li></li>
}
</ul>
</div>
`
})
export class FruitListComponent {
fruits = ['사과', '바나나', '오렌지', '포도', '수박'];
}
💡 중요: track은 필수입니다! Angular가 항목을 추적하는 데 사용해요.
track의 다양한 방법
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
@Component({
selector: 'app-tracking-demo',
standalone: true,
template: `
<div>
<!-- 1. 값으로 추적 (간단한 경우) -->
@for (item of items; track item) {
<p>{{ item }}</p>
}
<!-- 2. 인덱스로 추적 -->
@for (item of items; track $index) {
<p>{{ $index }}: {{ item }}</p>
}
<!-- 3. ID로 추적 (객체 배열) -->
@for (user of users; track user.id) {
<div class="user-card">
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
</div>
}
</div>
`
})
export class TrackingDemoComponent {
items = ['A', 'B', 'C'];
users = [
{ id: 1, name: '홍길동', email: 'hong@example.com' },
{ id: 2, name: '김철수', email: 'kim@example.com' },
{ id: 3, name: '이영희', email: 'lee@example.com' }
];
}
반복문 변수들
@for는 유용한 변수들을 제공합니다:
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
@Component({
selector: 'app-loop-variables',
standalone: true,
template: `
<div class="items">
@for (item of items; track item; let i = $index; let f = $first; let l = $last) {
<div
class="item"
[class.first]="f"
[class.last]="l">
<span class="number">{{ i + 1 }}</span>
<span class="name">{{ item }}</span>
@if (f) {
<span class="badge">첫 번째</span>
}
@if (l) {
<span class="badge">마지막</span>
}
</div>
}
</div>
`,
styles: [`
.item {
padding: 15px;
margin: 10px 0;
background: #f5f5f5;
border-radius: 8px;
display: flex;
align-items: center;
gap: 15px;
}
.item.first {
background: #e3f2fd;
border-left: 4px solid #2196F3;
}
.item.last {
background: #fce4ec;
border-left: 4px solid #e91e63;
}
.number {
background: #667eea;
color: white;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
}
.badge {
background: #ff9800;
color: white;
padding: 4px 10px;
border-radius: 12px;
font-size: 0.8em;
margin-left: auto;
}
`]
})
export class LoopVariablesComponent {
items = ['첫째', '둘째', '셋째', '넷째', '다섯째'];
}
사용 가능한 변수들:
$index- 현재 인덱스 (0부터 시작)$first- 첫 번째 항목인지 (boolean)$last- 마지막 항목인지 (boolean)$even- 짝수 인덱스인지 (boolean)$odd- 홀수 인덱스인지 (boolean)$count- 전체 항목 수
@empty - 빈 배열 처리
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
@Component({
selector: 'app-product-list',
standalone: true,
template: `
<div class="product-list">
<h2>상품 목록</h2>
@for (product of products; track product.id) {
<div class="product-card">
<h3></h3>
<p>원</p>
</div>
} @empty {
<div class="empty-state">
<p>📦 상품이 없습니다.</p>
<button (click)="loadProducts()">상품 불러오기</button>
</div>
}
</div>
`,
styles: [`
.empty-state {
text-align: center;
padding: 60px;
background: #f5f5f5;
border-radius: 10px;
color: #666;
}
.product-card {
padding: 20px;
background: white;
margin: 10px 0;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
`]
})
export class ProductListComponent {
products: any[] = [];
loadProducts() {
this.products = [
{ id: 1, name: '노트북', price: 1500000 },
{ id: 2, name: '마우스', price: 50000 },
{ id: 3, name: '키보드', price: 80000 }
];
}
}
3. 조건 분기 - @switch
기본 사용법
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
@Component({
selector: 'app-day-message',
standalone: true,
template: `
<div class="day-card">
<h2>오늘은 요일</h2>
@switch (dayOfWeek) {
@case ('월') {
<p>😫 한 주의 시작입니다. 화이팅!</p>
}
@case ('화') {
@case ('수') {
@case ('목') {
<p>💪 열심히 달려봅시다!</p>
}
@case ('금') {
<p>🎉 불금이에요! 조금만 더!</p>
}
@case ('토') {
@case ('일') {
<p>😴 주말이에요! 푹 쉬세요!</p>
}
@default {
<p>❓ 알 수 없는 요일입니다.</p>
}
}
<select (change)="onDayChange($event)">
<option>월</option>
<option>화</option>
<option>수</option>
<option>목</option>
<option>금</option>
<option>토</option>
<option>일</option>
</select>
</div>
`,
styles: [`
.day-card {
padding: 40px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 15px;
text-align: center;
}
select {
margin-top: 20px;
padding: 10px 20px;
font-size: 1em;
border-radius: 5px;
border: none;
}
`]
})
export class DayMessageComponent {
dayOfWeek = '금';
onDayChange(event: Event) {
const target = event.target as HTMLSelectElement;
this.dayOfWeek = target.value;
}
}
실전 활용: 사용자 타입별 UI
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
@Component({
selector: 'app-user-dashboard',
standalone: true,
template: `
<div class="dashboard">
@switch (userType) {
@case ('admin') {
<div class="admin-panel">
<h2>🔐 관리자 대시보드</h2>
<button>사용자 관리</button>
<button>시스템 설정</button>
<button>통계 보기</button>
</div>
}
@case ('editor') {
<div class="editor-panel">
<h2>✏️ 편집자 대시보드</h2>
<button>글 작성</button>
<button>글 수정</button>
<button>댓글 관리</button>
</div>
}
@case ('user') {
<div class="user-panel">
<h2>👤 사용자 대시보드</h2>
<button>프로필 보기</button>
<button>설정</button>
</div>
}
@default {
<div class="guest-panel">
<h2>👋 게스트</h2>
<p>로그인이 필요합니다.</p>
<button>로그인</button>
</div>
}
}
</div>
`
})
export class UserDashboardComponent {
userType: 'admin' | 'editor' | 'user' | 'guest' = 'user';
}
4. 실전 예제: Todo 리스트 UI
모든 개념을 활용한 완전한 예제입니다!
todo-list.component.ts
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
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
interface Todo {
id: number;
text: string;
completed: boolean;
priority: 'low' | 'medium' | 'high';
}
@Component({
selector: 'app-todo-list',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './todo-list.component.html',
styleUrl: './todo-list.component.css'
})
export class TodoListComponent {
todos: Todo[] = [
{ id: 1, text: 'Angular 공부하기', completed: false, priority: 'high' },
{ id: 2, text: '점심 먹기', completed: true, priority: 'medium' },
{ id: 3, text: '운동하기', completed: false, priority: 'low' }
];
newTodoText = '';
filter: 'all' | 'active' | 'completed' = 'all';
addTodo() {
if (this.newTodoText.trim()) {
const newTodo: Todo = {
id: Date.now(),
text: this.newTodoText,
completed: false,
priority: 'medium'
};
this.todos.push(newTodo);
this.newTodoText = '';
}
}
toggleTodo(todo: Todo) {
todo.completed = !todo.completed;
}
deleteTodo(id: number) {
this.todos = this.todos.filter(todo => todo.id !== id);
}
get filteredTodos(): Todo[] {
switch (this.filter) {
case 'active':
return this.todos.filter(todo => !todo.completed);
case 'completed':
return this.todos.filter(todo => todo.completed);
default:
return this.todos;
}
}
get activeCount(): number {
return this.todos.filter(todo => !todo.completed).length;
}
get completedCount(): number {
return this.todos.filter(todo => todo.completed).length;
}
}
todo-list.component.html
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<div class="todo-app">
<header class="app-header">
<h1>📝 Todo 리스트</h1>
<p class="stats">
전체: {{ todos.length }} |
진행 중: {{ activeCount }} |
완료: {{ completedCount }}
</p>
</header>
<!-- 입력 폼 -->
<div class="input-section">
<input
type="text"
[(ngModel)]="newTodoText"
(keyup.enter)="addTodo()"
placeholder="할 일을 입력하세요..."
class="todo-input">
<button (click)="addTodo()" class="add-btn">추가</button>
</div>
<!-- 필터 버튼 -->
<div class="filter-buttons">
<button
(click)="filter = 'all'"
[class.active]="filter === 'all'">
전체
</button>
<button
(click)="filter = 'active'"
[class.active]="filter === 'active'">
진행 중
</button>
<button
(click)="filter = 'completed'"
[class.active]="filter === 'completed'">
완료
</button>
</div>
<!-- Todo 목록 -->
<div class="todo-list">
@for (todo of filteredTodos; track todo.id; let i = $index) {
<div
class="todo-item"
[class.completed]="todo.completed"
[class.priority-high]="todo.priority === 'high'"
[class.priority-medium]="todo.priority === 'medium'"
[class.priority-low]="todo.priority === 'low'">
<!-- 번호 -->
<span class="todo-number">{{ i + 1 }}</span>
<!-- 체크박스 -->
<input
type="checkbox"
[checked]="todo.completed"
(change)="toggleTodo(todo)"
class="todo-checkbox">
<!-- 텍스트 -->
<span class="todo-text">{{ todo.text }}</span>
<!-- 우선순위 배지 -->
@switch (todo.priority) {
@case ('high') {
<span class="priority-badge high">높음</span>
}
@case ('medium') {
<span class="priority-badge medium">보통</span>
}
@case ('low') {
<span class="priority-badge low">낮음</span>
}
}
<!-- 상태 -->
@if (todo.completed) {
<span class="status-badge">✅</span>
} @else {
<span class="status-badge">⏳</span>
}
<!-- 삭제 버튼 -->
<button
(click)="deleteTodo(todo.id)"
class="delete-btn">
🗑️
</button>
</div>
} @empty {
<div class="empty-state">
@switch (filter) {
@case ('active') {
<p>🎉 진행 중인 할 일이 없습니다!</p>
}
@case ('completed') {
<p>📝 완료된 할 일이 없습니다.</p>
}
@default {
<p>✨ 새로운 할 일을 추가해보세요!</p>
}
}
</div>
}
</div>
</div>
todo-list.component.css
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
.todo-app {
max-width: 600px;
margin: 50px auto;
padding: 30px;
background: white;
border-radius: 20px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
}
.app-header {
text-align: center;
margin-bottom: 30px;
}
.app-header h1 {
margin: 0;
color: #333;
}
.stats {
color: #666;
font-size: 0.9em;
margin-top: 10px;
}
/* 입력 섹션 */
.input-section {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.todo-input {
flex: 1;
padding: 15px;
font-size: 1em;
border: 2px solid #ddd;
border-radius: 10px;
}
.todo-input:focus {
outline: none;
border-color: #667eea;
}
.add-btn {
padding: 15px 30px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 10px;
font-weight: bold;
cursor: pointer;
}
.add-btn:hover {
transform: scale(1.05);
}
/* 필터 버튼 */
.filter-buttons {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.filter-buttons button {
flex: 1;
padding: 10px;
background: #f5f5f5;
border: 2px solid transparent;
border-radius: 8px;
cursor: pointer;
font-weight: 500;
}
.filter-buttons button.active {
background: #667eea;
color: white;
border-color: #667eea;
}
/* Todo 아이템 */
.todo-item {
display: flex;
align-items: center;
gap: 15px;
padding: 15px;
margin-bottom: 10px;
background: #f9f9f9;
border-radius: 10px;
border-left: 4px solid transparent;
transition: all 0.3s;
}
.todo-item:hover {
background: #f0f0f0;
transform: translateX(5px);
}
.todo-item.completed {
opacity: 0.6;
}
.todo-item.completed .todo-text {
text-decoration: line-through;
}
.todo-item.priority-high {
border-left-color: #f44336;
}
.todo-item.priority-medium {
border-left-color: #ff9800;
}
.todo-item.priority-low {
border-left-color: #4CAF50;
}
.todo-number {
background: #667eea;
color: white;
width: 25px;
height: 25px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9em;
font-weight: bold;
}
.todo-checkbox {
width: 20px;
height: 20px;
cursor: pointer;
}
.todo-text {
flex: 1;
font-size: 1em;
color: #333;
}
.priority-badge {
padding: 4px 10px;
border-radius: 12px;
font-size: 0.8em;
font-weight: bold;
}
.priority-badge.high {
background: #ffebee;
color: #f44336;
}
.priority-badge.medium {
background: #fff3e0;
color: #ff9800;
}
.priority-badge.low {
background: #e8f5e9;
color: #4CAF50;
}
.status-badge {
font-size: 1.2em;
}
.delete-btn {
background: none;
border: none;
font-size: 1.2em;
cursor: pointer;
opacity: 0.5;
transition: opacity 0.3s;
}
.delete-btn:hover {
opacity: 1;
}
/* 빈 상태 */
.empty-state {
text-align: center;
padding: 60px 20px;
color: #999;
font-size: 1.1em;
}
🧪 직접 해보기
실습 1: 사용자 리스트 필터링
미션: 나이로 필터링되는 사용자 리스트를 만드세요!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
export class UserFilterComponent {
users = [
{ name: '홍길동', age: 25 },
{ name: '김철수', age: 30 },
{ name: '이영희', age: 22 },
{ name: '박민수', age: 35 }
];
ageFilter: 'all' | 'young' | 'adult' = 'all';
get filteredUsers() {
switch (this.ageFilter) {
case 'young':
return this.users.filter(u => u.age < 30);
case 'adult':
return this.users.filter(u => u.age >= 30);
default:
return this.users;
}
}
}
실습 2: 조건부 스타일 카드
미션: 상태에 따라 다른 색상의 카드를 만드세요!
1
2
3
4
5
6
7
8
9
@for (item of items; track item.id) {
<div
class="card"
[class.success]="item.status === 'success'"
[class.warning]="item.status === 'warning'"
[class.error]="item.status === 'error'">
{{ item.message }}
</div>
}
💡 자주 하는 실수
❌ 실수 1: track 빼먹기
1
2
3
4
5
6
7
8
9
<!-- ❌ 에러 발생! -->
@for (item of items) {
<div></div>
}
<!-- ✅ 올바른 예 -->
@for (item of items; track item) {
<div></div>
}
❌ 실수 2: 중괄호 빼먹기
1
2
3
4
5
6
7
8
<!-- ❌ 틀린 예 -->
@if (isVisible)
<p>보임</p>
<!-- ✅ 올바른 예 -->
@if (isVisible) {
<p>보임</p>
}
❌ 실수 3: @empty 위치 잘못
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- ❌ 틀린 예 -->
@for (item of items; track item) {
<div></div>
}
@if (items.length === 0) {
<p>비어있음</p>
}
<!-- ✅ 올바른 예 -->
@for (item of items; track item) {
<div></div>
} @empty {
<p>비어있음</p>
}
📝 정리
제어 흐름 문법 요약
| 문법 | 용도 | 예시 |
|---|---|---|
@if | 조건부 렌더링 | @if (condition) { } |
@else | 거짓일 때 | @if (...) { } @else { } |
@else if | 다중 조건 | @if (...) { } @else if (...) { } |
@for | 반복 | @for (item of list; track item) { } |
@empty | 빈 배열 | @for (...) { } @empty { } |
@switch | 다중 분기 | @switch (value) { @case (1) { } } |
핵심 개념
mindmap
root((제어 흐름))
조건
if/else
else if
복잡한 조건
반복
for 루프
track 키
empty 처리
분기
switch/case
default
다중 case
Phase 1 완료 체크리스트
@if로 조건부 렌더링을 할 수 있나요?@for로 리스트를 표시할 수 있나요?@switch로 다중 분기를 만들 수 있나요?- Todo 리스트를 만들어봤나요?
- Phase 1 전체 내용을 이해했나요? 🎉
📚 다음 학습
축하합니다! Phase 1 완료! 🎉
이제 Angular의 기초를 모두 배웠습니다:
- ✅ 환경 설정
- ✅ 컴포넌트 만들기
- ✅ 템플릿 문법
- ✅ 이벤트 처리
- ✅ 조건과 반복
다음 Phase 2에서는 핵심 개념을 배웁니다:
- Signal 상태 관리
- 컴포넌트 간 통신
- 서비스와 의존성 주입
- 라우팅
-
HTTP 통신
- 이전: Day 4: 이벤트 처리
- 다음: Day 6: Signal 상태 관리 - 반응형 데이터의 시작
- 전체 커리큘럼: Angular 마스터하기 시리즈
💬 마무리하며
“조건과 반복, 이 두 가지만 있으면 어떤 복잡한 UI도 만들 수 있어요! Phase 1을 완료한 여러분, 정말 대단해요! 🏆”
Phase 1을 마무리했습니다! 기초를 탄탄히 다진 지금, 이제 더 멋진 기능들을 배울 준비가 되었어요!
Phase 2부터는 본격적으로 실전 앱을 만들 수 있는 기능들을 배웁니다! 🚀
여기까지 오시느라 수고하셨습니다! 잠깐 쉬었다가 Phase 2로 Go! 💪
“작은 성공들이 모여 큰 성취를 만듭니다. Phase 1 완주, 축하합니다!” 🎊
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.
