낙서장이자 오답 노트이자 컨닝 페이퍼
패스트캠퍼스 환급챌린지 8일차 : 코드팩토리의 백엔드 아카데미 : 한 번에 끝내는 NestJS 패키지 - 기초부터 MSA까지 강의 후기 본문
패스트캠퍼스 환급챌린지 8일차 : 코드팩토리의 백엔드 아카데미 : 한 번에 끝내는 NestJS 패키지 - 기초부터 MSA까지 강의 후기
NangIn 2025. 4. 8. 20:08본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.
강의 내용 정리
Class Transformer
클래스 트랜스포머란?
- class-validator와 함께 자주 사용하는 형식 변환(Transformation) 라이브러리
- 주로 요청 데이터를 클래스 인스턴스로 바꾸고, 필요 시 값을 자동 가공(transform) 함
- 예: "123"(문자열) → 123(숫자), "EMAIL@EXAMPLE.COM" → "email@example.com" 등
주요 기능
| 기능 | 설명 |
| 값 변환 (@Transform) | 특정 필드의 값을 가공/수정할 수 있음 |
| 중첩 객체 변환 (@Type) | 클래스 안의 다른 클래스도 자동으로 인스턴스화 |
| 출력 제어 (@Exclude, @Expose) | @Exclude() → 해당 필드를 출력 결과에서 제외 @Expose() → @Expose()로 전체를 감춘 후, 노출할 필드만 선택 |
| 양방향 직렬화 지원 | plainToInstance, instanceToPlain로 일반 객체 ↔ 클래스 변환 가능 |
| 커스텀 처리 유연함 | 복잡한 변환도 사용자 정의 트랜스포머로 처리 가능 |
사용 예시
- 간단한 값 변환
-
import { Transform } from 'class-transformer'; class UserDto { @Transform(({ value }) => value.toLowerCase()) // 이메일 값을 항상 소문자로 변환 email: string; } - 중첩 클래스 변환
- plain 객체의 중첩 구조를 실제 클래스로 자동 변환하고 싶을 때 사용
-
import { Type } from 'class-transformer'; class Address { city: string; country: string; } class User { name: string; @Type(() => Address) // 중첩된 객체를 Address 인스턴스로 변환 address: Address; } - 변환: plain → class instance
-
const plainObject = { name: 'Alice', address: { city: 'New York', country: 'USA', }, }; const userInstance = plainToInstance(User, plainObject); // plainToInstance(User, plainObj) 호출 시 address는 자동으로 Address 인스턴스로 변환됨 console.log(userInstance instanceof User); // true console.log(userInstance.address instanceof Address); // true console.log(userInstance); /* User { name: 'Alice', address: Address { city: 'New York', country: 'USA' } } */
- 출력 제외/선택
-
import { Exclude, Expose } from 'class-transformer'; @Exclude() class User { @Expose() name: string; password: string; // 노출되지 않음 }
주요 함수
| 함수 | 역할 |
| plainToInstance(Class, obj) | 일반 JS 객체 → 클래스 인스턴스 |
| instanceToPlain(instance) | 클래스 인스턴스 → 일반 객체 (JSON 응답 직렬화 시 사용) |
- NestJS에서는 대부분 ValidationPipe가 이 과정 자동 처리 → 데코레이터만 잘 붙이면 끝!
클래스 트랜스포머 vs 클래스 밸리데이터
| 항목 | class-validator | class-transformer |
| 목적 | 유효성 검사 (Validation) | 값 변환 (Transformation) |
| 대표 데코레이터 | @IsString(), @IsEmail() 등 | @Transform(), @Type() 등 |
| 역할 | "이 값, 맞는 값이야?" | "이 값, 이렇게 바꿔줄게!" |
| 실행 시점 | 요청 받았을 때 검사 | 값을 받자마자 변환 |
Expose와 Exclude 적용해보기
클래스 트랜스포머는 클래스 인스턴스에만 적용됨
- 인터페이스에는 적용되지 않으므로, 반드시 클래스 형태의 엔티티를 사용해야 함.
- 예: interface 대신 movie.entity.ts에 Movie 클래스를 만들어 사용.
객체가 아닌 new Movie() 인스턴스를 사용해야 함
- 그냥 { title: '해리포터' } 이런 객체를 넣으면 트랜스포머가 작동하지 않음.
- new Movie()를 통해 명시적으로 인스턴스 생성해야 트랜스포머가 작동함.
-
private movies: Movie[] = []; constructor() { const movie1 = new Movie(); movie1.id = 1; movie1.title = '해리포터'; movie1.genre = 'fantasy'; const movie2 = new Movie(); movie2.id = 2; movie2.title = '반지의 제왕'; movie2.genre = 'action'; this.movies.push(movie1, movie2); }
@Exclude() & @Expose() 사용법
- @Exclude(): 해당 프로퍼티를 응답 결과에서 제외
- 예: @Exclude() genre: string; → genre는 응답에서 제외됨
- 전체 클래스에 @Exclude() 설정하면 모든 필드가 숨겨지고, @Expose()로 선택 노출 가능
- @Expose(): 제외된 필드 중에서 응답에 포함시킬 필드만 지정
- 보안이 중요한 경우 전체에 @Exclude() 적용 후, 필요한 필드만 @Expose()로 노출
-
@Exclude() export class Movie { @Expose() title: string; @Expose() id: number; genre: string; // 숨겨짐 }
클래스 트랜스포머가 동작하게 하려면?
- NestJS에서는 명시적으로 설정 필요:
-
@UseInterceptors(ClassSerializerInterceptor)
- 해당 데코레이터를 컨트롤러에 추가해야 @Expose, @Exclude가 작동함
추가 기능: Getter도 노출 가능
- 일반 프로퍼티가 아닌 getter 함수도 @Expose()로 응답에 포함 가능 (원래 응답에 getter는 포함 안됐음)
- 값에 접근할 때 getter 유용
-
@Expose() get description() { return `id: ${this.id} title: ${this.title}`; }
Custom Transformer 사용해보기
목적
- 클래스 프로퍼티의 값을 원하는 형태로 가공하여 반환하고 싶을 때 사용
- 예: 모든 문자열을 대문자로, 특정 문자열로 치환 등
기본 형식
import { Transform } from 'class-transformer';
class Movie {
@Transform(({ value }) => value.toUpperCase()) // 항상 대문자로 변환
genre: string;
}
- @Transform(({ value }) => ...) → 해당 프로퍼티의 원래 값에 가공 로직을 적용해서 반환
예시 1: 특정 문자열로 강제 고정
@Transform(({ value }) => '코드팩토리')
genre: string;
- → 어떤 값을 넣어도 genre는 항상 "코드팩토리"로 처리됨
예시 2: 대문자 변환
@Transform(({ value }) => value.toString().toUpperCase())
genre: string;
- → "fantasy" → "FANTASY"
- → "액션" → "액션" (영문자만 대문자화됨)
응용 아이디어
- 전화번호 마스킹: "01012345678" → "010****5678"
- 금액 쉼표 삽입: 1234567 → "1,234,567"
- 날짜 포맷 변환: "2024-07-21T10:00:00Z" → "2024년 7월 21일"
마무리 정리
- Transform()은 단순 출력 가공뿐 아니라 저장 전 값 변환, 응답 가공, 로깅 처리에도 활용 가능
- 불필요한 데이터 가공 로직을 DTO에서 처리할 수 있어 서비스 로직 분리 + 테스트 용이성 상승
- 실무에서도 입력 정제, 출력 포맷팅, 시큐리티 처리 등 다양하게 쓰임
Joi 이론
Joi란?
- JavaScript/TypeScript에서 사용하는 스키마 기반의 객체 검증 라이브러리
- class-validator와는 다르게 데코레이터가 아닌, 명시적인 스키마 정의를 통해 유효성 검사를 수행
- NestJS에서도 사용 가능하지만, 보통은 환경 변수 검증용으로만 주로 사용됨
Joi의 주요 특징
| 기능 | 설명 |
| 스키마 기반 구조 | Joi.object({})로 구조를 명시 |
| 타입 세이프 | 타입에 따라 자동 검사 (Joi.string(), Joi.number() 등) |
| 에러 메시지 설정 | .message()로 커스텀 메시지 지정 가능 |
| 커스텀 검증 로직 | .extend()로 직접 Validator 확장 가능 |
| NestJS에서 사용처 | 환경 변수 검증용으로 주로 사용됨 (ConfigModule) |
사용 예시
import * as Joi from 'joi';
const schema = Joi.object({
name: Joi.string().required(),
email: Joi.string().email().required(),
age: Joi.number().optional(),
});
const data = {
name: 'Alice',
email: 'alice@example.com',
};
const { error, value } = schema.validate(data);
if (error) {
console.log('유효성 실패:', error.message);
} else {
console.log('유효한 데이터:', value);
}
커스텀 메시지
Joi.string().required().messages({
'string.empty': '이름은 비워둘 수 없습니다.',
});
커스텀 확장 예시
const extendedJoi = Joi.extend((joi) => ({
type: 'string',
base: joi.string(),
messages: {
'string.capitalized': '{{#label}}는 첫 글자가 대문자여야 합니다.',
},
rules: {
capitalized: {
validate(value, helpers) {
if (value[0] !== value[0].toUpperCase()) {
return helpers.error('string.capitalized');
}
return value;
},
},
},
}));
extendedJoi.string().capitalized().validate('hello'); // ❌
NestJS에서의 실전 활용
- NestJS에서는 대부분 class-validator를 사용하지만,
- .env 파일의 환경 변수 검증에는 Joi를 사용함
- ConfigModule.forRoot({ validationSchema: Joi.object({...}) }) 형태로 사용
핵심 요약
- Joi는 객체 단위로 유효성을 정의하는 도구
- 실무에서는 주로 환경 변수 검증에 사용되고
- 일반적인 API 요청 검증은 class-validator가 훨씬 더 직관적이고 효율적임
- 단, 스키마 기반 검증의 유연성이 필요할 땐 Joi가 유리한 선택이 될 수도 있음
학습 후기
이번 강의를 통해 class-transformer가 단순한 값 변환 도구를 넘어, 응답 데이터의 형태를 명확히 통제할 수 있는 강력한 기능임을 알게 되었습니다. 특히 @Exclude()와 @Expose()를 통해 DTO의 출력 구조를 세밀하게 조정할 수 있다는 점이 인상 깊었고, 보안이나 응답 최적화 측면에서도 실무에서 꼭 활용해야겠다는 생각이 들었습니다. 단순히 값을 가공하는 @Transform() 기능도, 서비스 로직에서 처리하던 반복적인 작업을 DTO에서 깔끔하게 처리할 수 있어 생산성과 유지보수성이 높아진다는 장점이 느껴졌습니다.
중첩 객체를 자동으로 클래스 인스턴스로 변환해주는 @Type()의 사용도 매우 유용했습니다. 특히 복잡한 관계형 구조를 다루는 데 있어 이 기능은 실무에서 반복해서 마주치는 요구사항을 효과적으로 해결할 수 있는 방법이라는 생각이 들었습니다. 또한 NestJS에서 ClassSerializerInterceptor를 통해 해당 기능들을 컨트롤러에 적용하는 방식도 명확히 이해하게 되어, 실무에서 직접 활용하는 데 무리가 없겠다는 자신감도 생겼습니다.
Joi는 이전에 환경 변수 검증에서만 접했었지만, 이번 학습을 통해 단순한 데이터 유효성 검사 이상의 역할을 한다는 걸 알게 되었습니다. 객체 스키마 기반 구조를 명시적으로 설정할 수 있어서 복잡한 환경 구성을 검증할 때 매우 유용했고, class-validator와의 차이점도 명확히 구분할 수 있어 어떤 상황에 어떤 도구를 써야 하는지도 자연스럽게 체득되었습니다.
학습 인증샷




https://abit.ly/lisbva
Abit.ly 다운받기
abit.ly