NestJS에서 Optional 필드 처리 정리

ddoachi·2025년 6월 4일

TekaPicker

목록 보기
20/30

1. TypeScript에서의 ? 의미

coin?: number;
  • TypeScript에서는 해당 필드를 생략 가능하게 합니다 (undefined 허용).
  • 내부적으로는 coin: number | undefined와 동일합니다.

2. REST용 DTO에서의 Optional 필드 처리

export class CreateMenuDto {
  @IsOptional()
  @IsNumber()
  coin?: number;
}

동작 방식

클라이언트 요청 JSON서버 수신 값 (coin)유효성 검증 통과 여부
{}undefinedO
{ "coin": null }nullX (IsNumber() 실패)
{ "coin": 4 }4O

null 허용하고 싶을 경우

@IsOptional()
@Transform(({ value }) => value === null ? undefined : value)
@IsNumber()
coin?: number;

또는

@IsOptional()
@IsNumber()
@ValidateIf((_, v) => v !== null)
coin?: number;

3. GraphQL용 DTO에서의 Optional 필드 처리

@InputType()
export class CreateMenuInput {
  @Field(() => Int, { nullable: true })
  coin?: number;
}

동작 방식

클라이언트 요청 GraphQL Query서버 수신 값 (coin)
{} (생략)null
{ coin: null }null
{ coin: 4 }4
  • GraphQL에서는 nullable: true를 설정해야 클라이언트가 필드를 생략할 수 있음
  • 생략된 필드도 자동으로 null로 전달됨 (절대 undefined 아님)

4. gRPC(ts-proto)에서의 Optional 필드 처리

message CreateMenuRequest {
  string name = 1;
  optional int32 coin = 2;
}

→ ts-proto로 변환 시:

export interface CreateMenuRequest {
  name: string;
  coin?: number; // number | undefined
}

주의사항

  • null을 넘기면 타입 에러 발생
  • 반드시 undefined만 허용

안전한 호출 예시

await this.grpcService.createMenu({
  name: input.name,
  coin: input.coin ?? undefined,
});

또는 조건부 spread:

const grpcInput = {
  name: input.name,
  ...(input.coin != null && { coin: input.coin }),
};

5. 비교 표: REST vs GraphQL

항목REST DTOGraphQL DTO
생략 시undefined자동으로 null으로 들어옴
null 허용 여부기본적으로 허용하지 않음 (IsNumber 실패)nullable: true 설정 시 허용됨
?만 쓰면?OK (타입 및 validation 통과)❌ GraphQL 스키마엔 영향 없음 (nullable: false됨)
nullable 설정무의미필수 (nullable: true)로 생략 가능 설정 필요

6. 결론 정리

  • REST에서는 ? + @IsOptional()undefined만 허용하는 게 일반적
  • GraphQL에서는 nullable: true로 설정하고 내부에서 null → undefined로 변환 필요
  • gRPC는 null 절대 허용 안 됨 → 항상 undefined로 변환해서 넘겨야 함

7. 유틸 함수 예시

function convertNullsToUndefined<T extends object>(obj: T): T {
  return Object.fromEntries(
    Object.entries(obj).map(([k, v]) => [k, v === null ? undefined : v])
  ) as T;
}
await this.grpcService.createMenu(
  convertNullsToUndefined(input)
);

8. Entity (*.entity.ts)에서의 Optional 필드 처리

@Entity('menus')
export class Menu {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column({ nullable: true })
  coin?: number;

  @Column({ nullable: true })
  categoryId?: number;
}

정리

설정의미
coin?: numberTypeScript 상 optional (undefined 가능)
@Column({ nullable: true })DB에서 NULL 허용 (컬럼 자체가 null 가능)

저장 시 동작

DTO 값저장 결과
undefined해당 컬럼 생략 (쿼리에서 빠짐)
nullDB에 null 값으로 명시적 저장
숫자 (e.g. 4)해당 숫자 저장

nullable: true 없으면 null 저장 시 DB 에러 발생


profile
내일도 풀스택

0개의 댓글