앵귤러 기초 강좌 #2 컴포넌트와 템플릿 문법
지난 시간에는 앵귤러가 무엇이고 어떤 강점을 가지고 있는지 살펴봤습니다. 이번 시간부터는 본격적으로 코드를 만져봅니다. 앵귤러 학습은 결국 **컴포넌트(Component)**에서 시작해서 컴포넌트로 끝난다고 해도 과언이 아닐 만큼, 컴포넌트가 모든 화면의 기본 단위입니다.
이번 글에서 할 일은 크게 네 가지입니다.
- Node.js와 Angular CLI 설치
ng new로 첫 프로젝트 생성- 컴포넌트 한 조각의 구조 파악 (
@Component데코레이터) - 새 컴포넌트를 만들어 부모 컴포넌트에 끼워 넣기
Node.js와 Angular CLI 설치 #
앵귤러로 개발하기 위해서는 먼저 Node.js가 필요합니다. 앵귤러 CLI 자체가 Node.js 위에서 동작하고, 빌드,테스트 도구들도 모두 Node 환경을 사용하기 때문입니다.
nodejs.org에 접속해서 LTS(Long Term Support) 버전을 받아 설치합니다. 이 글을 쓰는 시점의 LTS는 Node 22 라인입니다. Mac에서는 Homebrew로 설치해도 됩니다.
brew install node설치가 끝나면 터미널을 새로 열고 버전을 확인합니다.
node -v
npm -vNode와 npm 버전이 출력되면 정상입니다. 이제 앵귤러의 표준 도구인 Angular CLI를 전역으로 설치합니다.
npm install -g @angular/cli-g는 “global"의 약자로, 이 패키지를 시스템 전역에 설치하여 어느 폴더에서든 ng 명령어를 쓸 수 있게 해달라는 뜻입니다. 설치가 끝나면 다음 명령어로 확인합니다.
ng versionAngular CLI 버전과 함께 ASCII 아트로 그려진 앵귤러 로고가 출력되면 성공입니다. 이 강좌는 Angular v17 이상(Standalone Components가 기본인 시점)을 기준으로 합니다.
ng version 실행 시 권한 오류가 나는 분은 npm 글로벌 경로 설정에 문제가 있을 수 있습니다. 이런 경우에는 nvm이나 Homebrew를 통해 Node를 설치하면 대부분 해결됩니다.첫 프로젝트 만들기 #
원하는 위치(예: ~/projects 또는 Desktop)로 이동한 뒤 다음 명령어를 실행합니다.
ng new my-appmy-app은 프로젝트 폴더 이름이니 원하는 이름으로 바꿔도 됩니다. 명령어를 실행하면 몇 가지 질문을 받습니다.
- Which stylesheet format would you like to use? — 스타일 형식을 묻습니다.
CSS를 선택합니다 (입문자에게는 가장 무난합니다. SCSS,Sass,Less에 익숙한 분이라면 그쪽을 골라도 됩니다). - Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)? — SSR/SSG를 활성화할지 묻습니다. 기초 학습 단계에서는
No로 답해두세요. 나중에 필요해지면 옵션으로 추가할 수 있습니다.
질문이 끝나면 Angular CLI가 폴더를 만들고 의존성까지 자동으로 설치해줍니다. 시간이 좀 걸릴 수 있으니 차 한잔 하고 오셔도 됩니다.
설치가 끝나면 만들어진 폴더 구조를 살펴봅니다.
my-app/
├── node_modules/ ← 설치된 라이브러리
├── public/ ← 정적 파일 (favicon 등)
├── src/ ← 우리가 작성할 코드
│ ├── app/
│ │ ├── app.component.ts ← 루트 컴포넌트 (TS)
│ │ ├── app.component.html ← 루트 컴포넌트 (템플릿)
│ │ ├── app.component.css ← 루트 컴포넌트 (스타일)
│ │ ├── app.config.ts ← 앱 설정 (라우팅, provider 등)
│ │ └── app.routes.ts ← 라우팅 정의
│ ├── index.html ← 앱이 주입될 HTML
│ ├── main.ts ← 앱 진입점 (bootstrap)
│ └── styles.css ← 전역 스타일
├── angular.json ← Angular CLI 설정
├── package.json ← 프로젝트 정보와 의존성 목록
└── tsconfig.json ← TypeScript 설정지금 단계에서 가장 중요한 폴더는 src/app/입니다. 우리가 만들 컴포넌트들이 모두 이 안에 들어갑니다.
개발 서버 실행 #
이제 프로젝트 폴더로 들어가서 개발 서버를 띄워봅니다.
cd my-app
ng serve다음과 비슷한 출력이 보이면 성공입니다.
➜ Local: http://localhost:4200/
➜ press h + enter to show help브라우저를 열고 http://localhost:4200에 접속해봅니다. 앵귤러 로고와 함께 환영 화면이 보이면 모든 것이 정상입니다.
이 상태에서 src/app/app.component.html의 텍스트를 살짝 바꾸고 저장해보세요. 새로고침하지 않아도 브라우저 화면이 즉시 갱신됩니다. 이게 바로 HMR(Hot Module Replacement) — 변경된 부분만 실시간으로 다시 그려주는 개발 기능입니다.
Ctrl + C를 누릅니다. 다시 시작할 때는 같은 폴더에서 ng serve를 실행하면 됩니다. 포트를 바꾸고 싶다면 ng serve --port 4300처럼 옵션을 주면 됩니다.컴포넌트 한 조각의 모양 #
이제 진짜 본론입니다. 앵귤러 컴포넌트는 보통 세 개의 파일이 한 세트로 움직입니다.
app.component.ts— 컴포넌트의 클래스 (TypeScript)app.component.html— 화면에 그릴 템플릿app.component.css— 이 컴포넌트에만 적용되는 스타일
src/app/app.component.ts 파일을 열어보면 다음과 비슷한 코드가 보일 것입니다.
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
templateUrl: './app.component.html',
styleUrl: './app.component.css',
})
export class AppComponent {
title = 'my-app';
}처음 보면 낯설 수 있지만, 한 줄씩 뜯어보면 의외로 단순합니다.
@Component({...})— 이 클래스를 “앵귤러 컴포넌트로 만들겠다"고 선언하는 **데코레이터(decorator)**입니다. 그 아래의AppComponent클래스에 메타데이터를 붙이는 역할을 합니다.selector: 'app-root'— 이 컴포넌트를 HTML에서 부를 때 쓸 태그 이름입니다.<app-root></app-root>로 사용하게 됩니다.standalone: true— 이 컴포넌트가 Standalone Component라는 뜻입니다. 예전에는 컴포넌트를 NgModule로 묶어야 했지만, 모던 앵귤러에서는 모듈 없이도 컴포넌트가 독립적으로 동작합니다.imports: [...]— 이 컴포넌트의 템플릿에서 사용할 다른 컴포넌트나 디렉티브를 미리 등록해두는 곳입니다. 위 예제에서는 라우팅에 쓰는RouterOutlet을 등록해뒀습니다.templateUrl/styleUrl— 템플릿과 스타일 파일의 경로입니다. 짧은 컴포넌트라면 별도 파일 없이template: '<h1>...</h1>'처럼 인라인으로 적을 수도 있습니다.export class AppComponent— 실제 컴포넌트의 본체. 데이터(클래스 필드)와 동작(메서드)이 들어가는 곳입니다.
쉽게 비유하자면 @Component는 “이 클래스가 어떤 화면 조각인지를 설명하는 라벨”, 그 아래 클래스는 **“그 조각의 두뇌”**입니다.
보간법 (Interpolation) #
클래스에 정의한 데이터를 템플릿에 표시하려면 **보간법(interpolation)**을 사용합니다. 위 코드에서 title = 'my-app'이라는 필드를 만들어둔 게 보이실 겁니다. 이 값을 화면에 표시해봅니다.
src/app/app.component.html 파일을 열고 내용을 모두 지운 뒤, 다음과 같이 작성합니다.
<h1>안녕하세요, {{ title }}!</h1>
<p>제 첫 앵귤러 앱입니다.</p>저장하면 브라우저에 “안녕하세요, my-app!“이 표시됩니다. 이중 중괄호({{ }}) 안에 컴포넌트 클래스의 속성을 적으면 그 값이 화면에 출력됩니다. 이것이 보간법입니다.
중괄호 안에는 단순한 속성뿐 아니라 간단한 표현식도 들어갈 수 있습니다.
<h1>안녕하세요, {{ title.toUpperCase() }}!</h1>
<p>1 + 2 = {{ 1 + 2 }}</p>다만 템플릿 표현식 안에서는 너무 복잡한 로직을 넣지 않는 것이 좋습니다. 복잡한 계산은 클래스의 메서드나 게터(getter)로 빼두고 템플릿에서는 그 결과만 호출하는 식으로 작성하는 것이 유지보수에 좋습니다.
새 컴포넌트 만들기 #
루트 컴포넌트(AppComponent) 하나만으로 전체 앱을 만들 수는 없습니다. 화면을 작은 조각으로 쪼개고, 각 조각을 별도의 컴포넌트로 만들어 조립하는 것이 앵귤러(그리고 모든 컴포넌트 기반 프레임워크)의 기본 사고방식입니다.
새 컴포넌트는 Angular CLI로 만듭니다.
ng generate component user-card길게 쓰는 게 귀찮다면 단축형도 있습니다.
ng g c user-card명령어를 실행하면 src/app/user-card/ 폴더 안에 다음 파일들이 자동으로 만들어집니다.
src/app/user-card/
├── user-card.component.ts
├── user-card.component.html
├── user-card.component.css
└── user-card.component.spec.ts ← 테스트 파일user-card.component.ts를 열어보면 우리가 앞서 본 AppComponent와 거의 같은 모양의 코드가 들어 있는 것을 볼 수 있습니다.
import { Component } from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
imports: [],
templateUrl: './user-card.component.html',
styleUrl: './user-card.component.css',
})
export class UserCardComponent {
name = '커티스';
role = '프론트엔드 개발자';
}직접 name과 role 필드를 추가해봤습니다. 이제 템플릿을 작성합니다.
<div class="card">
<h2>{{ name }}</h2>
<p>{{ role }}</p>
</div>스타일도 살짝 입혀봅니다.
.card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
width: 240px;
}이 컴포넌트의 selector는 app-user-card입니다. 즉, HTML 어딘가에 <app-user-card></app-user-card> 태그를 넣으면 이 컴포넌트가 그 위치에 그려집니다.
컴포넌트 조립 #
이제 만들어둔 UserCardComponent를 루트 컴포넌트인 AppComponent에 끼워넣어 봅시다. 두 단계가 필요합니다.
먼저 부모 컴포넌트의 imports 배열에 자식 컴포넌트를 등록합니다. Standalone 컴포넌트는 NgModule이 없는 대신, 사용할 컴포넌트를 명시적으로 import 해줘야 합니다.
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { UserCardComponent } from './user-card/user-card.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, UserCardComponent],
templateUrl: './app.component.html',
styleUrl: './app.component.css',
})
export class AppComponent {
title = 'my-app';
}그 다음 부모 템플릿에서 자식의 selector를 사용합니다.
<h1>안녕하세요, {{ title }}!</h1>
<app-user-card></app-user-card>
<app-user-card></app-user-card>저장하면 UserCard 카드 두 개가 화면에 그려지는 것을 볼 수 있습니다. 이렇게 컴포넌트는 마치 레고 블록처럼 다른 컴포넌트의 템플릿 안으로 끼워 넣을 수 있고, 자기 자신을 다른 곳에서 여러 번 재사용할 수 있습니다.
imports에 등록을 해야 하지?“라는 의문이 들 수 있습니다. Standalone 컴포넌트는 NgModule처럼 거대한 등록부가 없기 때문에, 각 컴포넌트가 자기가 필요한 것만 명시적으로 가져오는 구조입니다. 처음에는 번거로워 보이지만, 컴포넌트 단위로 의존성이 명확해져서 트리 셰이킹과 번들 최적화에 큰 이점이 있습니다.물론 지금 두 카드가 똑같은 데이터를 보여준다는 한계가 있습니다. 카드마다 다른 사람의 정보를 표시하려면 부모에서 자식으로 데이터를 전달할 수 있어야 하는데, 이 부분은 다음 글에서 다룹니다.
마무리 #
이번 글에서는 Node.js와 Angular CLI를 설치하고, ng new로 첫 프로젝트를 만들고, @Component 데코레이터의 구조를 살펴봤습니다. 그리고 보간법({{ }})으로 클래스 데이터를 화면에 표시하고, ng generate component로 새 컴포넌트를 만들어 부모 템플릿에 끼워 넣는 것까지 해봤습니다.
지금까지의 내용만으로도 앵귤러 앱이 어떻게 컴포넌트 단위로 조립되는지 큰 그림이 잡혔을 겁니다. 다음 글인 “앵귤러 기초 강좌 #3 데이터 바인딩과 이벤트"에서는 부모에서 자식으로 데이터를 내려주는 **속성 바인딩([name]="...")**과, 사용자 클릭 같은 이벤트를 처리하는 **이벤트 바인딩((click)="...")**을 본격적으로 다뤄보겠습니다.