디렉티브(Directive)
Angular에서 디렉티브는 DOM 요소의 동작이나 외형을 제어하는 데 사용되는 강력한 기능입니다.
Angular 애플리케이션의 UI 동작을 선언적(declarative)으로 처리할 수 있게 해주며, 재사용성과 모듈화를 높이는 데 크게 기여합니다.
Angular에서 디렉티브는 세 가지 주요 타입으로 구분됩니다
1. Component (컴포넌트)
- 실제로는 디렉티브의 한 종류이며, 템플릿을 갖고 있는 디렉티브입니다.
- @Component() 데코레이터로 정의됩니다.
- 대부분의 Angular 애플리케이션에서 중심이 되는 UI 단위입니다.
2. Structural Directive (구조 디렉티브)
- DOM의 구조 자체를 변경합니다.
- 예: *ngIf, *ngFor, *ngSwitch
- Angular 내부적으로 DOM을 추가하거나 제거합니다.
- 커스텀 구조 디렉티브도 구현 가능하며, 예를 들어 appPermission 같은 디렉티브를 만들어 권한 기반 DOM 제어를 할 수 있습니다.
3. Attribute Directive (속성 디렉티브)
- HTML 엘리먼트의 속성이나 외형을 변경하는 데 사용됩니다.
- 예: ngClass, ngStyle
- 커스텀 속성 디렉티브를 만들어 CSS 스타일, 클래스를 동적으로 제어하거나, 요소에 새로운 행동을 부여할 수 있습니다.
디렉티브를 써야 하는 순간
왜 컴포넌트로 안 하고 디렉티브로 만드는 걸까?
컴포넌트는 전체 템플릿을 가지므로 특정 UI 행위 하나만 바꾸기에는 무겁습니다.
반면 디렉티브는 기존 DOM 구조를 유지하면서 로직만 주입할 수 있어 유연합니다.
사용 예:
- input 포커스를 자동으로 설정 (autofocus)
- 클릭 외 영역 감지 후 닫기 (clickOutside)
- 특정 조건일 때만 DOM 노출 (appPermission)
- 로딩 상태일 때 버튼 비활성화 ([appLoadingButton])
- 애니메이션 효과 부여 ([appFadeInOnVisible])
이처럼 디렉티브는 UI에 미세한 행동이나 스타일을 추가하는 데 최적화되어 있습니다.
예시 : 마우스 오버 시 팝오버 표시 디렉티브
시나리오
- 특정 버튼에 마우스를 올리면 팝오버 컴포넌트를 동적으로 띄움
- 버튼마다 다른 컴포넌트를 바인딩 가능
- 위치는 offset 및 방향(side) 설정 가능
디렉티브 코드 (hover-popover.directive.ts)
@Directive({
selector: '[appHoverPopover]',
standalone: true,
})
export class HoverPopoverDirective implements OnDestroy {
@Input('appHoverPopover') component!: Type<any>;
@Input() popoverProps: PopoverProps = { width: 200, offsetX: 0, offsetY: 0, side: 'bottom' };
private componentRef?: ComponentRef<any>;
constructor(
private el: ElementRef,
private vcr: ViewContainerRef,
private injector: Injector
) {}
@HostListener('mouseenter')
onMouseEnter() {
if (this.componentRef) return;
const componentRef = this.vcr.createComponent(this.component, { injector: this.injector });
Object.assign(componentRef.instance, this.popoverProps.componentInput);
this.componentRef = componentRef;
this.setPosition(componentRef.location.nativeElement);
}
@HostListener('mouseleave')
onMouseLeave() {
this.componentRef?.destroy();
this.componentRef = undefined;
}
private setPosition(host: HTMLElement) {
const rect = this.el.nativeElement.getBoundingClientRect();
host.style.position = 'fixed';
host.style.left = `${rect.left + this.popoverProps.offsetX}px`;
host.style.top = `${rect.bottom + this.popoverProps.offsetY}px`;
}
ngOnDestroy() {
this.componentRef?.destroy();
}
}
사용 예시
<button
[appHoverPopover]="UserPopoverComponent"
[popoverProps]="{ offsetX: -50, offsetY: 10, componentInput: { userId: 123 } }"
>
Hover me
</button>
ViewContainerRef vs ApplicationRef vs ComponentFactoryResolver
Angular 14 이상에서는 createComponent() API 덕분에 동적 컴포넌트 생성이 간결해졌습니다.
- ViewContainerRef.createComponent() : 현재 뷰 트리 내부에 생성. 팝오버, 툴팁 등 지역 UI에 적합.
- ApplicationRef.attachView() : 전역 DOM에 붙이는 방식. 예: 모달, 전역 알림 등.
- ComponentFactoryResolver : Angular 13 이하 버전에서 사용하는 방식. 현재는 거의 사용하지 않음.
커스텀 Structural Directive 예시 (예: 권한 기반 노출)
@Directive({ selector: '[appHasRole]' })
export class HasRoleDirective {
constructor(private tpl: TemplateRef<any>, private vcr: ViewContainerRef, private auth: AuthService) {}
@Input() set appHasRole(role: string) {
if (this.auth.hasRole(role)) {
this.vcr.createEmbeddedView(this.tpl);
} else {
this.vcr.clear();
}
}
}
<!-- 관리자에게만 보이는 버튼 -->
<button *appHasRole="'admin'">관리자 메뉴</button>
'개발 공부 > Angular' 카테고리의 다른 글
| Angular computed() (0) | 2025.05.26 |
|---|---|
| @HostListener (0) | 2025.05.19 |
| Angular Route Guard (canActivateChild, canDeactivate, canLoad ) (0) | 2025.03.17 |
| Angular Resolve (0) | 2025.03.15 |
| Angular CanActivate (0) | 2025.03.13 |