본문 바로가기

개발 공부/Angular

Angular Directive

디렉티브(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