파이프란
보통 애플리케이션이 관리하는 데이터 또는 서버에서 받아오는 데이터는 사용자들에게 실생활에 익숙한 형태가 아닐 수 있다.
예를 들면, 날짜 데이터 같은 경우 Date 함수가 리턴하는 인스턴스를 문자열화하면 아래와 같다.
const today = new Date();
console.log(today.toString()); // Thu May 25 2023 14:00:00 GMT+0900 (KST)
딱보기에도 사용자가 읽기 쉬운 형태는 아니다.
이 때 데이터 자체를 변경하는 것은 다른 side effect가 있을 수 있으므로 화면에 표시하는 형식만 변경하고 싶을 때 사용하는 것이 Pipe 이다. 파이프를 사용하면 마치 스타일 정의하듯 HTML에서 바로 출력 변환이 가능하다.
사용법
사용법은 간단하다.
import { Component } from ‘@angular/core’;
@Component({
selector: ‘app-today’,
template: `
<p>Today is {{ today | date }}</p>
<p>Today is {{ today | date: 'y년 MM월 dd일' }}</p>
`
})
export class TodayComponent {
today = new Date();
}
date 파이프를 사용하면 다음과 같이 출력된다.
Today is May 25, 2023
Today is 2023년 05월 25일
대상 값 뒤에 연산자 | 를 붙인 후 원하는 파이프를 지정한다. 예시에서 처럼 콜론(:)을 이용하여 옵션을 지정할 수도 있다.
파이프는 템플릿 내에서만 원하는 형식으로 데이터를 변환하기 때문에 원본데이터 자체는 변경되지 않는다.
빌트인 파이프 Built-in Pipe
앵귤러에서는 위에서 쓴 date 파이프와 같은 기본 파이프를 제공한다.
- DatePipe: 날짜를 원하는 형식으로 변환
- UpperCasePipe: 문자열을 대문자로 변경
- LowerCasePipe: 문자열을 소문자로 변경
이 외에도 많은 파이프들이 있는데 자세한 내용과 사용법은 아래 앵귤러 공식문서를 참조하는 게 좋다.
체이닝 파이프
여러 개의 파이프를 조합할 수도 있다.
import { Component } from ‘@angular/core’;
@Component({
selector: ‘app-my’,
template: `
<p>SlicePipe + UpperCasePipe</p>
<p>{{ name | slice: 4 | uppercase }}</p>
`
})
export class MyComponent {
name = 'hee tistory';
}
빌트인 파이프 중 슬라이스 파이프와 대문자 파이프를 체이닝한 결과이다.
SlicePipe + UpperCasePipe
TISTORY
커스텀 파이프
개발자가 직접 파이프를 만들 수도 있다.
- 파이프 클래스를 생성한다.
- 생성한 파이프 클래스에는 @Pipe 데코레이터 장식이 필요하며, name 프로퍼티에 식별자를 지정한다.
- PipeTransform 인터페이스의 추상메소드 transform 안에 원하는 내용을 구현하면 된다.
- 파이프를 module의 declarations에 등록한다.
아래와 같이 pipe 파일을 생성해주고
ng generate pipe file-size
나는 숫자로만 입력된 파일사이즈를 예쁘게 바꿔주는 파이프를 만들었다.
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'fileSize'
})
export class FileSizePipe implements PipeTransform {
transform(value, option?: object) {
if(isNaN(value)) return value;
if(typeof value !== 'number') value = Number(value);
if(value < 0) return value;
const oneMBToByte = 1024 * 1024;
const oneKBToByte = 1024;
const isGreaterOneMBToByte = value > oneMBToByte;
let unit = isGreaterOneMBToByte ? 'MB' : 'KB';
if (isGreaterOneMBToByte) {
value = (value / oneMBToByte).toFixed(1)
} else {
value = (value / oneKBToByte).toFixed(1)
}
return value + ' ' + unit;
}
}
- transform()
transform(value: any, ...args: any[]): any
- transform 메서드는 변환 대상 값 value와 옵션 args 들을 인자로 받는데 옵션(args)는 Rest 파라미터로 정의되어 있어서 여러 개의 옵션을 파라미터로 전달할 수 있다.
이렇게 만든 커스텀 파이프를 이제 모듈의 declarations에 등록하면 완료다. 나는 pipe만 모아놓은 모듈을 따로 만들었다.
//pipes.modules.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FileSizePipe } from './file-size.pipe';
const PIPES = [
FileSizePipe,
];
@NgModule({
declarations: [
...PIPES,
],
imports: [
CommonModule
],
exports: [
...PIPES
]
})
export class PipesModule {}
적용은 빌트인 파이프와 같이 동일하게 적용하면 되고 실행결과는 다음과 같다.
import { Component } from ‘@angular/core’;
@Component({
selector: ‘app-my’,
template: `
<p> Size {{size1}} is {{ size1 | fileSize }}</p>
<p> Size {{size2}} is {{ size2 | fileSize }}</p>
`
})
export class MyComponent {
size1: number = 20230525;
size2: number = 202305;
}
Size 20230525 is 19.28MB
Size 202305 is 197.6KB
[참고]
- Angular Essentials (루비페이퍼; 이웅모 저)