๐ŸŽ‰ berenickt ๋ธ”๋กœ๊ทธ์— ์˜จ ๊ฑธ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค. ๐ŸŽ‰
Back
NestJs
19-Class Transformer

1. Class Transformer(๋ณ€ํ™˜)

1.1 Class Transformer ํŠน์„ฑ

  • TS Decorator๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํด๋ž˜์Šค๋ฅผ ๊ฒ€์ฆํ•œ๋‹ค (Validate)
  • ๋™๊ธฐ (Synchronous), ๋น„๋™๊ธฐ (Asynchronous) ๋ฐฉ์‹ ๋ชจ๋‘๋ฅผ ์ง€์›ํ•œ๋‹ค.
  • Class Validator ์ž์ฒด์ ์œผ๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” Validator๋“ค์„ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ปค์Šคํ…€ Validator๋ฅผ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
  • ์ปค์Šคํ…€ ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ๋ฐ˜ํ™˜ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Class Validator๋ฅผ ์ œ์ž‘ํ•œ ๊ฐœ๋ฐœ์ž๊ฐ€ ์‹œ์ž‘ํ•œ ํ”„๋กœ์ ํŠธ๋‹ค

1.2 Class Transformer ์„ค์น˜ ๋ฐ ์‚ฌ์šฉ๋ฒ•

(1) Class Transformer ์„ค์น˜

1
yarn add class-transformer

(2) Class Transformer ์‚ฌ์šฉ๋ฒ•

1
class User {
2
@Exclude()
3
name: string
4
5
@Transform(({ value }) => value.toUpperCase())
6
email: string
7
}

์–ด๋–ค Transformer๋“  ๊ฒ€์ฆํ•˜๊ณ  ์‹ถ์€ ํ”„๋กœํผํ‹ฐ์— Class Transformer์—์„œ ์ œ๊ณตํ•˜๋Š” Decorator๋ฅผ ๋ถ™์—ฌ์ฃผ๋ฉด ๋œ๋‹ค.

1
const plainUser = {
2
name: 'John',
3
email: 'John@example.com',
4
}
5
const user = plainToClass(User, plainUser)
6
7
// User { email: 'JOHN@EXAMPLE.COM' }
8
console.log(user)
9
10
const plain = classToPlain(user)
11
12
// { email: 'JOHN@EXAMPLE.COM' }
13
console.log(plain)

validate() ํ•จ์ˆ˜๋กœ ๊ฐ์ฒด๋ฅผ ๊ฒ€์ฆํ–ˆ์„๋•Œ, Class Validator์— ๋ถ€ํ•ฉํ•˜์ง€ ์•Š์€ ๊ฐ’์ด ์ž…๋ ฅ๋๋‹ค๋ฉด ํ•ด๋‹น๋˜๋Š” ์—๋Ÿฌ๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.


1.3 ์ค‘์ฒฉ ํด๋ž˜์Šค ๋ณ€ํ™˜

1
class Address {
2
city: string
3
country: string
4
}
5
6
class User {
7
@Exclude()
8
name: string
9
10
@Type(() => Address)
11
address: Address
12
}

์ค‘์ฒฉ๋œ ๊ฐ์ฒด ํƒ€์ž…์„ Type ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์— ์ œ๊ณตํ•ด์ค€๋‹ค.

1
const plainUser = {
2
name: 'John',
3
address: {
4
city: 'New York',
5
country: 'USA',
6
},
7
}
8
9
const user = plainToClass(User, plainUser)
10
11
// USer { address: Address { city: 'New York', country: 'USA' }}
12
console.log(user)

plainToClass๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์ค‘์ฒฉ๋œ ๊ฐ์ฒด๋„ Type ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์— ์ž…๋ ฅ๋œ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋กœ ๋ณ€ํ™˜๋œ๋‹ค


1.4 Custom Transformer

1
class User {
2
@Exclude()
3
name: string
4
5
@Transform(({ value }) => value.toLowerCase())
6
email: string
7
}

Transform ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ์ ์šฉ๋œ ํ”„๋กœํผํ‹ฐ์˜ ๊ฐ’์„ ์•„๊ทœ๋จผํŠธ๋กœ ์ œ๊ณตํ•ด์ฃผ๋ฉฐ ๋ณ€ํ™˜ ํ•˜๊ณ ์‹ถ์€ ํ˜•ํƒœ๋กœ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

1
const plainUser = {
2
name: 'John',
3
email: 'JOHN@EXAMPLE.COM',
4
}
5
6
const user = plainToClass(User, plainUser)
7
8
// john@example.com
9
console.log(user.email)

plainToClass๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์ค‘์ฒฉ๋œ ๊ฐ์ฒด๋„ Type ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์— ์ž…๋ ฅ๋œ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋กœ ๋ณ€ํ™˜๋œ๋‹ค


2. Exclude Annotation

๋ฏผ๊ฐํ•œ ๊ฐœ์ธ์ •๋ณด์™€ ๊ฐ™์ด ๋…ธ์ถœํ•˜๊ณ  ์‹ถ์ง€ ์•Š์€ ์ปฌ๋Ÿผ์„ ์ œ์™ธ(exclude)ํ•  ์ˆ˜ ์žˆ๋‹ค.

users.entity.ts
1
@Column()
2
@IsString({
3
message: stringValidationMessage,
4
})
5
@Length(3, 8, {
6
message: lengthValidationMessage,
7
})
8
@Exclude() // ์ถ”๊ฐ€
9
password: string

e.g. ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋…ธ์ถœํ•˜๊ณ  ์‹ถ์ง€ ์•Š์€ ๊ณณ์—์„œ interceptor๋ฅผ ๋“ฑ๋กํ•ด์ฃผ๋ฉด, API ์š”์ฒญ ์‹œ ํ•ด๋‹น ์ปฌ๋Ÿผ์ •๋ณด๊ฐ€ ์ œ์™ธ๋œ๋‹ค.

users.controller.ts
1
// ์ƒ๋žต
2
3
@Controller('users')
4
export class UsersController {
5
constructor(private readonly usersService: UsersService) {}
6
7
@Get()
8
/*** ClassSerializerInterceptor ๋œป
9
* Serialization(์ง๋ ฌํ™”)
10
* - ํ˜„์žฌ ์‹œ์Šคํ…œ์—์„œ ์‚ฌ์šฉ๋˜๋Š” (NestJS) ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ๋‹ค๋ฅธ ์‹œ์Šคํ…œ์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํฌ๋งท์œผ๋กœ ๋ณ€ํ™˜
11
* - class์˜ object์—์„œ JSON ํฌ๋งท์œผ๋กœ ๋ณ€ํ™˜
12
*
13
* Deserialization(์—ญ์ง๋ ฌํ™”) : ์ง๋ ฌํ™”์˜ ๋ฐ˜๋Œ€
14
*/
15
@UseInterceptors(ClassSerializerInterceptor)
16
getUsers() {
17
return this.usersService.getAllUsers()
18
}
19
20
// ์ƒ๋žต
21
}

3. Exclude Annotation ์˜ต์…˜ ํƒ๊ตฌ

users.entity.ts
1
@Column()
2
@IsString({
3
message: stringValidationMessage,
4
})
5
@Length(3, 8, {
6
message: lengthValidationMessage,
7
})
8
/*** ๐Ÿ“Œ toClassOnly์™€ toPlainOnly
9
* Request
10
* frontend -> backend
11
* plain object (JSON) -> class instance (dto)
12
*
13
* Response
14
* backend -> frontend
15
* class instance (dto) -> plain obejct (JSON)
16
*
17
* toClassOnly -> class Instance ๋ณ€ํ™˜๋  ๋–„๋งŒ (์š”์ฒญ์ผ ๋–„)
18
* toPlainOnly -> plain object๋กœ ๋ณ€ํ™˜๋  ๋–„๋งŒ (์‘๋‹ต์ผ ๋•Œ)
19
* ์‘๋‹ต์ด ๋‚˜๊ฐˆ ๋–„๋งŒ password๋ฅผ ์ œ์™ธ์‹œํ‚ค๊ณ  ์‹ถ์„ ๋–„
20
*/
21
@Exclude({ toPlainOnly: true })
22
password: string

4. ClassSerializer AppModule์— ์ ์šฉ

๊ทธ๋Ÿฐ๋ฐ ์ด๋ ‡๊ฒŒ ์ผ์ผ์ด ์ œ์™ธ ์–ด๋…ธํ…Œ์ด์…˜์„ ๋‹ค๋Š” ๊ฒƒ์„ ์‚ฌ๋žŒ์ด๊ธฐ์— ์‹ค์ˆ˜ํ•˜๋ฉด ๋น ์งˆ ์ˆ˜ ์žˆ๋‹ค.

  • ๊ทธ๋ž˜์„œ ์ž๋™์œผ๋กœ ๋ชจ๋“  API์— ๊ธฐ๋ณธ์ ์œผ๋กœ class-transformer ์• ๋…ธํ…Œ์ด์…˜์ด ๋‹ฌ๋ ค์žˆ์œผ๋ฉด, ๋ชจ๋‘ ๊ธฐ๋ณธ ์ ์šฉ๋œ๋‹ค.
  • ์ด๋ฅผ ์œ„ํ•ด ClassSerializer์„ App ๋ชจ๋“ˆ์— ์ ์šฉํ•˜๋ฉด ๋œ๋‹ค.
  • user ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ClassSerializerInterceptor๋ฅผ ์ง€์šด๋‹ค.
app.module.ts
1
// ์ƒ๋žต
2
3
@Module({
4
// ์ƒ๋žต
5
providers: [
6
AppService,
7
{
8
provide: APP_INTERCEPTOR,
9
useClass: ClassSerializerInterceptor,
10
},
11
],
12
})
13
export class AppModule {}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ชจ๋“  API์—์„œ Exclude ์–ด๋…ธํ…Œ์ด์…˜์„ ์ ์šฉ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.


5. Expose Annotation ์‚ฌ์šฉ

์˜ˆ์‹œ๋ฅผ ์œ„ํ•ด ๋‹‰๋„ค์ž„๊ณผ ์ด๋ฉ”์ผ์„ ์„ž์€ ํ”„๋กœํผํ‹ฐ๊ฐ€ ํฌํ•จ๋˜์–ด์•ผ ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

users.entity.ts
1
// users.entity.ts ์ƒ๋žต
2
@Expose()
3
get nicknameAndEmail() {
4
return this.nickname + '/' + this.email
5
}
  • ์ด๋Ÿฐ ์‹์œผ๋กœ ์‹ค์ œ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ expoese(๋…ธ์ถœ)์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
  • ๊ธฐ๋Šฅ๋งŒ ํ™•์ธํ•ด๋ณด๊ณ , ์ง€์›Œ์„œ ์›์ƒ๋ณต๊ท€์‹œํ‚จ๋‹ค.

6. Expose Annotation ํด๋ž˜์Šค์— ์ ์šฉ

๋„ˆ๋ฌด ๋ณด์•ˆ์„ฑ์ด ๊ฐ•ํ•œ ๊ฐ์ฒด๋ผ์„œ, ๊ธฐ๋ณธ์œผ๋กœ ๋‹ค ๋ณด์ด์ง€ ์•Š๊ฒŒํ•˜๊ณ , ๋ณด์ด๊ฒŒํ•  ํ”„๋กœํผํ‹ฐ๋งŒ ์ ์šฉ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

users.entity.ts
1
// ์ƒ๋žต
2
3
@Entity()
4
@Exclude() // ์ถ”๊ฐ€
5
export class UsersModel extends BaseModel {
6
// ์ƒ๋žต
7
@Expose()
8
nickname: string
9
10
// ์ƒ๋žต
11
@Expose()
12
email: string
13
14
// ์ƒ๋žต
15
}
  • ์ด๋Ÿฐ ์‹์œผ๋กœ ํด๋ž˜์Šค ์ž์ฒด์—๋‹ค๊ฐ€ Exlcude ์–ด๋…ธํ…Œ์ด์…˜์„ ์ ์šฉํ•˜๋ฉด ๋œ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  ๋…ธ์ถœ์‹œํ‚ฌ ํ”„๋กœํผํ‹ฐ์—๋งŒ Expose ์–ด๋…ธํ…Œ์ด์…˜์„ ๋ถ™์—ฌ์ฃผ๋ฉด ๋œ๋‹ค.
  • ๊ธฐ๋Šฅ๋งŒ ํ™•์ธํ•ด๋ณด๊ณ , ์ง€์›Œ์„œ ์›์ƒ๋ณต๊ท€์‹œํ‚จ๋‹ค.