[Angular 마스터하기] Day 2 - 컴포넌트 기초, 나만의 첫 컴포넌트
이제와서 시작하는 Angular 마스터하기 - Day 2 “컴포넌트는 레고 블록입니다. 하나씩 만들어서 조립하면 멋진 앱이 완성돼요! 🧱”
오늘 배울 내용
- 컴포넌트가 무엇인지 이해하기
- Standalone Component 만들기 (최신 방식)
- 템플릿, 스타일, 로직의 3요소
- Angular CLI로 컴포넌트 자동 생성
- 실습: 프로필 카드 컴포넌트
1. 컴포넌트가 뭘까요?
웹 페이지의 레고 블록
컴포넌트는 재사용 가능한 UI 조각입니다.
실생활 예시:
1
2
3
4
5
6
7
8
9
유튜브 화면을 생각해보세요
┌─────────────────────────────┐
│ [헤더 컴포넌트] │
├─────────────────────────────┤
│ [동영상 카드] [동영상 카드] │
│ [동영상 카드] [동영상 카드] │ ← 같은 컴포넌트 반복!
├─────────────────────────────┤
│ [푸터 컴포넌트] │
└─────────────────────────────┘
graph TD
A[웹 애플리케이션] --> B[헤더 컴포넌트]
A --> C[메인 컴포넌트]
A --> D[푸터 컴포넌트]
C --> E[프로필 카드 컴포넌트]
C --> F[게시글 컴포넌트]
C --> G[댓글 컴포넌트]
style A fill:#dd0031,stroke:#fff,color:#fff
style C fill:#c3002f,stroke:#fff,color:#fff
컴포넌트의 3요소
모든 Angular 컴포넌트는 3가지로 구성됩니다:
| 요소 | 역할 | 파일 |
|---|---|---|
| 🎨 템플릿 (Template) | 화면 구조 (HTML) | .component.html |
| 💅 스타일 (Style) | 디자인 (CSS) | .component.css |
| 🧠 클래스 (Class) | 로직 (TypeScript) | .component.ts |
1
2
3
4
5
6
7
8
9
10
// 컴포넌트 = 템플릿 + 스타일 + 로직
@Component({
selector: 'app-profile', // HTML에서 사용할 태그명
templateUrl: './profile.component.html', // 템플릿
styleUrl: './profile.component.css' // 스타일
})
export class ProfileComponent {
// 로직 (데이터, 함수)
name = '홍길동';
}
2. 첫 컴포넌트 만들기
수동으로 만들기 (이해를 위해)
먼저 손으로 직접 만들어보면서 구조를 이해해봅시다!
Step 1: 폴더 생성
1
2
3
4
5
src/app/
└── greeting/
├── greeting.component.ts
├── greeting.component.html
└── greeting.component.css
Step 2: greeting.component.ts 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Component } from '@angular/core';
@Component({
selector: 'app-greeting', // <app-greeting></app-greeting>으로 사용
standalone: true, // ⭐ 최신 방식! (모듈 불필요)
templateUrl: './greeting.component.html',
styleUrl: './greeting.component.css'
})
export class GreetingComponent {
// 데이터
username = '방문자';
currentTime = new Date().toLocaleTimeString('ko-KR');
// 메서드
sayHello() {
alert(`안녕하세요, ${this.username}님!`);
}
}
Step 3: greeting.component.html 작성
1
2
3
4
5
<div class="greeting-card">
<h2>👋 환영합니다!</h2>
<p>{{ username }}님, 현재 시각은 {{ currentTime }}입니다.</p>
<button (click)="sayHello()">인사하기</button>
</div>
Step 4: greeting.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
.greeting-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
border-radius: 15px;
text-align: center;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
}
.greeting-card h2 {
font-size: 2em;
margin-bottom: 15px;
}
.greeting-card button {
background: white;
color: #667eea;
border: none;
padding: 12px 30px;
border-radius: 25px;
font-size: 1em;
font-weight: bold;
cursor: pointer;
transition: transform 0.2s;
}
.greeting-card button:hover {
transform: scale(1.05);
}
Step 5: app.component.ts에 등록
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Component } from '@angular/core';
import { GreetingComponent } from './greeting/greeting.component'; // 임포트!
@Component({
selector: 'app-root',
standalone: true,
imports: [GreetingComponent], // ⭐ 여기에 추가!
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
title = 'my-first-app';
}
Step 6: app.component.html에서 사용
1
2
3
4
5
6
<div style="padding: 50px;">
<h1>{{ title }}</h1>
<!-- 우리가 만든 컴포넌트 사용! -->
<app-greeting></app-greeting>
</div>
결과 확인: http://localhost:4200에서 확인! 🎉
3. CLI로 자동 생성하기 (실전 방식)
수동으로 만드니까 번거롭죠? Angular CLI가 자동으로 해줍니다!
명령어 한 줄이면 끝
1
2
3
4
ng generate component profile-card
# 축약형
ng g c profile-card
자동으로 생성되는 파일:
1
2
3
4
CREATE src/app/profile-card/profile-card.component.css
CREATE src/app/profile-card/profile-card.component.html
CREATE src/app/profile-card/profile-card.component.spec.ts # 테스트 파일
CREATE src/app/profile-card/profile-card.component.ts
생성된 코드:
1
2
3
4
5
6
7
8
9
10
11
12
import { Component } from '@angular/core';
@Component({
selector: 'app-profile-card',
standalone: true,
imports: [],
templateUrl: './profile-card.component.html',
styleUrl: './profile-card.component.css'
})
export class ProfileCardComponent {
// 여기에 코드 작성!
}
💡 꿀팁: CLI가 자동으로 standalone: true를 설정해줍니다!
4. 실전 예제: 프로필 카드 만들기
이제 진짜 쓸모있는 컴포넌트를 만들어봅시다!
요구사항
- 프로필 사진
- 이름, 직업
- 좋아요 버튼
- 좋아요 개수 표시
profile-card.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
import { Component } from '@angular/core';
@Component({
selector: 'app-profile-card',
standalone: true,
imports: [],
templateUrl: './profile-card.component.html',
styleUrl: './profile-card.component.css'
})
export class ProfileCardComponent {
// 프로필 데이터
name = '김개발';
job = 'Frontend Developer';
profileImage = 'https://i.pravatar.cc/150?img=12';
likes = 42;
isLiked = false;
// 좋아요 토글
toggleLike() {
if (this.isLiked) {
this.likes--;
this.isLiked = false;
} else {
this.likes++;
this.isLiked = true;
}
}
}
profile-card.component.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div class="profile-card">
<!-- 프로필 이미지 -->
<img [src]="profileImage" [alt]="name" class="profile-image">
<!-- 정보 -->
<h3 class="name">{{ name }}</h3>
<p class="job">{{ job }}</p>
<!-- 좋아요 버튼 -->
<div class="actions">
<button
(click)="toggleLike()"
[class.liked]="isLiked"
class="like-button">
{{ isLiked ? '❤️' : '🤍' }} {{ likes }}
</button>
</div>
</div>
profile-card.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
.profile-card {
max-width: 300px;
background: white;
border-radius: 20px;
padding: 30px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
text-align: center;
transition: transform 0.3s;
}
.profile-card:hover {
transform: translateY(-10px);
}
.profile-image {
width: 150px;
height: 150px;
border-radius: 50%;
object-fit: cover;
border: 5px solid #dd0031;
margin-bottom: 20px;
}
.name {
font-size: 1.8em;
margin: 10px 0;
color: #333;
}
.job {
color: #666;
font-size: 1.1em;
margin-bottom: 20px;
}
.like-button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 12px 30px;
border-radius: 25px;
font-size: 1.1em;
cursor: pointer;
transition: all 0.3s;
}
.like-button:hover {
transform: scale(1.05);
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
}
.like-button.liked {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
app.component.ts에 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Component } from '@angular/core';
import { ProfileCardComponent } from './profile-card/profile-card.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [ProfileCardComponent], // 추가!
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
title = '프로필 카드 갤러리';
}
app.component.html에서 사용
1
2
3
4
5
6
7
8
9
<div class="container">
<h1 style="text-align: center; color: #333;">{{ title }}</h1>
<div class="card-grid">
<app-profile-card></app-profile-card>
<app-profile-card></app-profile-card>
<app-profile-card></app-profile-card>
</div>
</div>
app.component.css
1
2
3
4
5
6
7
8
9
10
11
12
.container {
padding: 50px;
background: #f5f5f5;
min-height: 100vh;
}
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-top: 40px;
}
5. 컴포넌트 이해하기
Selector의 비밀
1
2
3
4
@Component({
selector: 'app-profile-card', // ← 이게 뭘까?
// ...
})
HTML에서 이렇게 사용:
1
<app-profile-card></app-profile-card>
💡 명명 규칙:
- 항상
app-으로 시작 (프로젝트 접두사) - 케밥-케이스 사용 (kebab-case)
- 의미있는 이름 사용
예시:
- ✅
app-user-profile - ✅
app-product-card - ❌
profile(너무 짧음) - ❌
appProfile(케밥-케이스 아님)
Standalone Components (⭐ 중요!)
과거 방식 (NgModule):
1
2
3
4
5
6
// 복잡했어요...
@NgModule({
declarations: [ProfileCardComponent],
imports: [CommonModule],
exports: [ProfileCardComponent]
})
최신 방식 (Standalone):
1
2
3
4
5
// 간단해요!
@Component({
standalone: true, // ← 이것만 추가!
imports: [] // 필요한 것만 여기에
})
장점:
- ✅ 더 간단한 구조
- ✅ 더 빠른 빌드
- ✅ 더 작은 번들 크기
- ✅ 더 쉬운 테스트
🧪 직접 해보기
실습 1: 상품 카드 컴포넌트
미션: 상품을 표시하는 카드를 만드세요!
요구사항:
- 상품 이미지
- 상품명
- 가격
- 장바구니 담기 버튼
시작하기:
1
ng g c product-card
힌트:
1
2
3
4
5
6
7
8
9
export class ProductCardComponent {
productName = '무선 이어폰';
price = 89000;
imageUrl = 'https://picsum.photos/200/200';
addToCart() {
alert(`${this.productName}을(를) 장바구니에 담았습니다!`);
}
}
실습 2: 여러 개 카드 표시하기
app.component.html:
1
2
3
4
5
6
<div class="product-grid">
<app-product-card></app-product-card>
<app-product-card></app-product-card>
<app-product-card></app-product-card>
<app-product-card></app-product-card>
</div>
app.component.css:
1
2
3
4
5
6
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
padding: 20px;
}
💡 자주 하는 실수
❌ 실수 1: imports에 추가 안 함
1
2
3
4
5
6
// 에러 발생!
@Component({
standalone: true,
imports: [], // ← ProfileCardComponent를 안 넣음
// ...
})
에러 메시지:
1
'app-profile-card' is not a known element
해결:
1
imports: [ProfileCardComponent] // ✅ 추가!
❌ 실수 2: selector 오타
1
selector: 'app-profle-card', // ← 'profile' 오타!
1
<app-profile-card></app-profile-card> <!-- 작동 안 함! -->
❌ 실수 3: 파일 경로 틀림
1
templateUrl: './profile.component.html', // ← 파일명 확인!
✅ 디버깅 팁
컴포넌트가 안 보일 때 체크리스트:
- 컴포넌트를 import 했나요?
- imports 배열에 추가했나요?
- selector가 맞나요?
- 파일 경로가 맞나요?
- 개발 서버가 실행 중인가요?
📝 정리
핵심 개념
mindmap
root((컴포넌트))
구조
템플릿 HTML
스타일 CSS
클래스 TS
생성 방법
수동 생성
CLI 자동 생성
사용 방법
import
imports 배열
selector 태그
특징
재사용 가능
독립적
조합 가능
오늘 배운 명령어
1
2
3
4
5
6
7
8
# 컴포넌트 생성
ng generate component 컴포넌트명
ng g c 컴포넌트명
# 예시
ng g c profile-card
ng g c product-card
ng g c user-list
체크리스트
- 컴포넌트의 개념을 이해했나요?
- CLI로 컴포넌트를 생성했나요?
- 프로필 카드를 만들어봤나요?
- 여러 개 카드를 표시해봤나요?
📚 다음 학습
다음 시간에는 템플릿 문법을 배웁니다!
지금은 간단한 {{ }} 문법만 사용했는데요, Angular 템플릿에는 더 강력한 기능이 많습니다:
- 속성 바인딩
[속성]="값" - 클래스/스타일 바인딩
-
다양한 데이터 표시 방법
- 이전: Day 1: Angular와의 첫 만남
- 다음: Day 3: 템플릿 문법 - 화면에 데이터 표시하기
- 전체 커리큘럼: Angular 마스터하기 시리즈
💬 마무리하며
“컴포넌트는 레고 블록입니다. 작은 조각들을 모아 멋진 작품을 만드세요! 🧱”
오늘은 컴포넌트의 기초를 배웠습니다. 어떤가요? 생각보다 어렵지 않죠?
내일은 템플릿에서 데이터를 더 멋지게 표시하는 방법을 배웁니다!
실습하다가 막히는 부분이 있으면 댓글로 질문해주세요! 🙋♂️
“천 리 길도 한 걸음부터. 오늘도 한 걸음 전진했습니다!” 🚀
