createComponent로 툴팁 만들기

Adam Kim·2025년 10월 10일
0

angular

목록 보기
52/88

Angular의 동적 컴포넌트 생성 기능인 createComponent를 활용하여, 마우스를 올렸을 때 나타나는 동적 툴팁(Tooltip)을 만들어 봅시다.

기본 동작

  • 툴팁으로 사용될 컴포넌트(ATooltipComponent, BTooltipComponent)를 standalone으로 생성합니다.
  • 툴팁을 표시할 컨테이너 컴포넌트를 standalone으로 생성합니다.
  • mouseenter, mouseleave 이벤트를 감지하고, createComponent를 통해 툴팁 컴포넌트를 동적으로 생성하고 제거하는 standalone 디렉티브를 생성합니다.

컴포넌트 생성하기

컨테이너 컴포넌트

툴팁을 표시할 div 요소들을 만들고, 우리가 만들 tooltip 디렉티브를 적용합니다.

// src/app/container.component.ts
import { Component } from '@angular/core';
import { TooltipDirective } from './tooltip.directive';

@Component({
  selector: 'app-container',
  standalone: true,
  imports: [TooltipDirective], // 사용할 디렉티브를 import
  template: `
    <h2>Hover over these elements</h2>
    <div class="box" [tooltip]="'A'">Show Tooltip A</div>
    <div class="box" [tooltip]="'B'">Show Tooltip B</div>
  `,
  styles: `
    .box {
      border: 1px solid #ccc;
      padding: 20px;
      margin: 20px;
      cursor: pointer;
      width: 200px;
      text-align: center;
    }
  `
})
export class ContainerComponent {}

동적 툴팁 컴포넌트

화면에 표시될 두 개의 간단한 툴팁 컴포넌트를 작성합니다.

// src/app/a-tooltip.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'a-tooltip',
  standalone: true,
  template: `<div class="tooltip">Tooltip A: Hello World</div>`,
  styles: `.tooltip { background-color: lightblue; padding: 10px; border-radius: 5px; }`
})
export class ATooltipComponent {}
// src/app/b-tooltip.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'b-tooltip',
  standalone: true,
  template: `<div class="tooltip">Tooltip B: Goodbye</div>`,
  styles: `.tooltip { background-color: lightcoral; color: white; padding: 10px; border-radius: 5px; }`
})
export class BTooltipComponent {}

디렉티브 생성하기

이 예제의 핵심 로직입니다. 디렉티브에서 이벤트를 감지하고, 입력 값('A' 또는 'B')에 따라 적절한 툴팁 컴포넌트를 동적으로 생성하고 제거합니다.

// src/app/tooltip.directive.ts
import {
  Directive,
  Input,
  HostListener,
  ComponentRef,
  ViewContainerRef,
  inject,
  Type
} from '@angular/core';
import { ATooltipComponent } from './a-tooltip.component';
import { BTooltipComponent } from './b-tooltip.component';

@Directive({
  selector: '[tooltip]',
  standalone: true,
})
export class TooltipDirective {
  @Input('tooltip') type: 'A' | 'B' | string = '';

  private componentRef?: ComponentRef<any>;
  // inject 함수로 ViewContainerRef를 주입받습니다.
  private viewContainerRef = inject(ViewContainerRef);

  @HostListener('mouseenter')
  onMouseEnter(): void {
    if (this.componentRef) return; // 이미 툴팁이 있으면 생성하지 않음

    const componentToCreate = this.getComponentType(this.type);
    if (!componentToCreate) return;

    // ComponentFactoryResolver 없이 컴포넌트 클래스를 직접 전달
    this.componentRef = this.viewContainerRef.createComponent(componentToCreate);
  }

  @HostListener('mouseleave')
  onMouseLeave(): void {
    if (this.componentRef) {
      this.componentRef.destroy();
      this.componentRef = undefined;
    }
  }

  private getComponentType(type: string): Type<any> | null {
    switch (type) {
      case 'A':
        return ATooltipComponent;
      case 'B':
        return BTooltipComponent;
      default:
        return null;
    }
  }
}
  • inject(ViewContainerRef): 생성자 대신 inject 함수를 사용하여 의존성을 주입하는 최신 방식입니다.
  • createComponent(componentToCreate): v13 이후부터 ComponentFactoryResolver 없이 컴포넌트 클래스를 직접 전달하여 인스턴스를 생성할 수 있습니다. 코드가 훨씬 간결해졌습니다.
  • componentRef.destroy(): 생성된 컴포넌트 인스턴스를 안전하게 제거하고 메모리 누수를 방지합니다.

주의사항: Standalone 환경에서의 구성

NgModule을 사용하던 과거와 달리, Standalone 환경에서는 각 컴포넌트와 디렉티브가 필요한 의존성을 imports 배열에 직접 명시해야 합니다.

  • ContainerComponent: 자신의 템플릿에서 TooltipDirective를 사용하므로 imports: [TooltipDirective]를 추가해야 합니다.
  • TooltipDirective: 이 디렉티브는 동적으로 ATooltipComponent와 BTooltipComponent를 생성하지만, 템플릿에서 직접 사용하는 것이 아니므로 imports 배열에 추가할 필요는 없습니다. TypeScript의 import 구문으로 클래스 타입만 가져오면 됩니다.

이제 예제를 실행하고 각 div 요소 위로 마우스를 가져가면, 해당 타입에 맞는 툴팁이 동적으로 생성되고 사라지는 것을 확인할 수 있습니다.

profile
Angular2+ Developer

0개의 댓글