Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 선형대수
- 친절한SQL튜닝
- d
- /etc/network/interfaces
- 데비안
- 페이징
- 코드로배우는스프링부트웹프로젝트
- resttemplate
- 구멍가게코딩단
- 티스토리 쿠키 삭제
- 자료구조와 함께 배우는 알고리즘 입문
- 스프링 시큐리티
- iterator
- Kernighan의 C언어 프로그래밍
- baeldung
- 자료구조와함께배우는알고리즘입문
- 자바편
- 이터레이터
- GIT
- 목록처리
- ㅒ
- 코드로배우는스프링웹프로젝트
- 서버설정
- network configuration
- 스프링부트핵심가이드
- 리눅스
- 네트워크 설정
- 알파회계
- 처음 만나는 AI 수학 with Python
- 처음 만나는 AI수학 with Python
Archives
- Today
- Total
bright jazz music
[nestjs] 스웨거 설치 및 DTO 에 적용 본문
1. 먼저 스웨거를 설치한다
pnpm add @nestjs/swagger swagger-ui-express
// npm install @nestjs/swagger swagger-ui-express
원래는 기본적인 dto와 컨트롤러, 서비스를 먼저 작성하였다. 그러나 그 과정을 기록해 놓지 않았기 때문에 현재 코드는 스웨거가 적용된 상태이다. 따라서 이해를 돕기 위해 스웨거를 먼저 설치하고 진행한다.
2. main.ts에 적용
설치를 완료했으면 main.ts에 적용해준다.
// src/main.ts
// 애플리케이션 진입점
// 스프링부트의 @SpringBootApplication가 붙어있는 파일(메인함수가 있는 파일)에 대응되는 파일
// 유효성 검사 파이프 추가
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
// Swagger 모듈 추가
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
console.log('DB Connection Info:', {
host: process.env.DB_HOST,
port: process.env.DB_PORT,
username: process.env.DB_USERNAME,
database: process.env.DB_DATABASE,
// password는 보안상 출력하지 않음
});
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({
whitelist: true, // DTO에 정의되지 않은 속성은 제거
forbidNonWhitelisted: true, // DTO에 정의되지 않은 속성이 있으면 요청 자체를 막음
transform: true, // 요청 데이터를 DTO 클래스의 인스턴스로 변환
}));
// CORS 허용
app.enableCors({
// origin: true로 설정하면 모든 도메인에서의 요청을 허용
// 실제 운영 환경에서는 특정 도메인만 허용하도록 설정해야 함
});
// 전역 경로 설정: 경로 앞에 /api/v1 붙이기
app.setGlobalPrefix('api/v1'); // /api/v1/members
// Swagger 설정
const config = new DocumentBuilder()
.setTitle('Members API') // 문서 제목
.setDescription('회원 관리 API 문서') // 문서 설명
.setVersion('1.0') // 문서 버전
.addBearerAuth( // Bearer 인증 토큰 추가
{
type: 'http',
scheme: 'bearer', // 인증 토큰 타입
bearerFormat: 'JWT', // 인증 토큰 형식
name: 'JWT', // 인증 토큰 이름
description: 'Enter JWT token', // 인증 토큰 설명
in: 'header', // 인증 토큰 위치
},
'access-token',
)
.addTag('auth', '인증 관련 API') // 태그 추가
.addTag('members', '회원 관리 API') // 태그 추가
.addTag('email', '이메일 관련 API') // 태그 추가
.build(); // 문서 빌드
const document = SwaggerModule.createDocument(app, config); // Swagger 문서 생성
SwaggerModule.setup('api-docs', app, document, { // Swagger 문서 설정
swaggerOptions: {
persistAuthorization: true, // 인증 토큰 유지
},
});
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
3. 디티오 작성
3.1. 멤버 생성 디티오
얜 일단 너무 많아서 아직 안 달았음
// src/members/dto/create-member.dto.ts
// 멤버 생성 DTO: 새로운 회원 가입 시 필요한 데이터 정의
// 필수 정보만을 포함하여 회원가입의 진입 장벽을 낮춤
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsString, MinLength, IsBoolean, IsEnum } from 'class-validator';
import { AuthProvider } from '@common/enums';
export class CreateMemberDto {
@ApiProperty({
example: 'user@example.com',
description: '사용자 이메일 (로그인 ID로 사용)',
required: true,
})
@IsEmail()
email: string;
@ApiProperty({
example: 'Password123!',
description: '비밀번호 (최소 8자)',
required: true,
})
@IsString()
@MinLength(8)
password: string;
@ApiProperty({
example: true,
description: '이용약관 동의',
required: true,
})
@IsBoolean()
termsAgreed: boolean;
@ApiProperty({
example: true,
description: '개인정보 처리방침 동의',
required: true,
})
@IsBoolean()
privacyAgreed: boolean;
@ApiProperty({
example: false,
description: '마케팅 수신 동의 (선택)',
required: false,
default: false,
})
@IsBoolean()
marketingAgreed: boolean = false;
@ApiProperty({
example: 'email',
description: '인증 제공자',
enum: AuthProvider,
default: AuthProvider.LOCAL,
})
@IsEnum(AuthProvider)
provider: AuthProvider = AuthProvider.LOCAL;
}
3.2. 멤버 업데이트 디티오
// src/members/dto/update-member.dto.ts
// 멤버 수정 DTO: 회원 정보 업데이트 시 사용되는 데이터 정의
// CreateMemberDto를 상속받아 모든 필드를 선택적으로 만듦
import { CreateMemberDto } from './create-member.dto';
import { ApiProperty,PartialType }from '@nestjs/swagger';
import { IsOptional, IsString, IsObject } from 'class-validator';
export class UpdateMemberDto extends PartialType(CreateMemberDto) {
// @IsOptional()
// @IsString()
// @ApiProperty({
// example: "새로운닉네임",
// description: "변경할 닉네임",
// required: false
// })
// nickname?: string;
@IsOptional()
@IsObject()
@ApiProperty({
example: {
"email": true,
"push": false,
"sms": true,
"marketing": false,
"inApp": true
},
description: "알림 설정",
required: false
})
notificationSettings?: {
email: boolean;
push: boolean;
sms: boolean;
marketing: boolean;
inApp: boolean;
};
@ApiProperty({
example: "newPassword123!",
description: "새 비밀번호 (로컬 로그인 사용자만)",
required: false
})
password?: string;
}
3.3. 응답 디티오
아직은 전체 상태를 반환하는 전체 필드 응답 디티오만 만들었음. 여긴 너무 많아서 스웨거 달지 않음. 나중에 추가 예정
// src/members/dto/member-response.dto.ts
// 멤버 응답 DTO: 멤버의 모든 공개 가능한 정보를 포함하는 응답 객체
import { MemberStatus } from "@common/enums";
export class MemberResponseDto {
uuid: string; // 외부 노출용 식별자
email: string; // 이메일
name?: string; // 이름 (선택)
nickname?: string; // 닉네임 (선택)
phoneNumber?: string; // 전화번호 (선택)
provider: string; // 인증 제공자 (kakao/google/email)
emailVerified: boolean; // 이메일 인증 여부
profileImage?: string; // 프로필 이미지 URL (선택)
status: MemberStatus; // 계정 상태
lastLoginAt?: Date; // 마지막 로그인 시간
// 약관 동의 정보
termsAgreed: boolean; // 이용약관 동의
termsAgreedAt?: Date; // 이용약관 동의 시간
marketingAgreed: boolean; // 마케팅 수신 동의
marketingAgreedAt?: Date; // 마케팅 수신 동의 시간
privacyAgreed: boolean; // 개인정보 수집 동의
privacyAgreedAt?: Date; // 개인정보 수집 동의 시간
// 알림 설정
notificationSettings: {
email: boolean; // 이메일 알림
push: boolean; // 푸시 알림
sms: boolean; // SMS 알림
marketing: boolean; // 마케팅 알림
};
// 사용자 설정
preferences: {
language: string; // 언어 설정
timezone: string; // 시간대 설정
theme: string; // 테마 설정
};
// 포인트 및 레벨 정보
points: {
total: number; // 총 포인트
purchase: number; // 구매 포인트
reward: number; // 리워드 포인트
};
levelInfo: {
level: number; // 현재 레벨
experience: number; // 경험치
};
role: string; // 사용자 역할 (ADMIN/USER/MANAGER)
// 시스템 정보
createdAt: Date; // 계정 생성 시간
updatedAt: Date; // 정보 수정 시간
}
4. 매퍼
매퍼도 변경했는지 기억나지 않으므로 혹시 몰라 첨부함.
// src/members/mappers/member.mapper.ts
// 멤버 매퍼: Entity와 DTO 간의 변환을 담당
// 데이터 계층과 표현 계층 사이의 데이터 변환을 중앙화하여 관리
import { Member } from '../entities/member.entity';
import { MemberResponseDto } from '../dto/member-response.dto';
import { CreateMemberDto } from '../dto/create-member.dto';
import { UpdateMemberDto } from '../dto/update-member.dto';
import { AuthProvider, MemberStatus } from '@common/enums';
export class MemberMapper {
// Entity를 ResponseDTO로 변환
static toDto(member: Member): MemberResponseDto {
const dto = new MemberResponseDto();
dto.uuid = member.uuid;
dto.email = member.email;
dto.name = member.name;
dto.nickname = member.nickname;
dto.phoneNumber = member.phoneNumber;
dto.provider = member.provider;
dto.emailVerified = member.emailVerified;
dto.profileImage = member.profileImage;
dto.status = member.status;
dto.lastLoginAt = member.lastLoginAt;
dto.termsAgreed = member.termsAgreed;
dto.termsAgreedAt = member.termsAgreedAt;
dto.marketingAgreed = member.marketingAgreed;
dto.marketingAgreedAt = member.marketingAgreedAt;
dto.notificationSettings = member.notificationSettings;
dto.preferences = member.preferences;
dto.createdAt = member.createdAt;
dto.updatedAt = member.updatedAt;
dto.points = member.points;
dto.levelInfo = member.levelInfo;
dto.role = member.role;
return dto;
}
// DTO 목록을 ResponseDTO 목록으로 변환
static toDtoList(members: Member[]): MemberResponseDto[] {
return members.map(member => this.toDto(member));
}
// DTO를 Entity로 변환 (생성/수정 시 사용)
static toEntity(dto: CreateMemberDto | UpdateMemberDto): Partial<Member> {
const entity = new Member();
if (dto.email) entity.email = dto.email;
if (dto.password) entity.password = dto.password;
// 약관 동의 처리
if ('termsAgreed' in dto) {
entity.termsAgreed = dto.termsAgreed;
entity.termsAgreedAt = new Date();
}
if ('marketingAgreed' in dto) {
entity.marketingAgreed = dto.marketingAgreed;
entity.marketingAgreedAt = new Date();
}
// 기본값 설정
entity.provider = AuthProvider.LOCAL;
entity.status = MemberStatus.ACTIVE;
return entity;
}
}
'Framework > NestJS' 카테고리의 다른 글
[nestjs] jest를 사용한 테스트 코드 작성(단위, e2e) (0) | 2024.12.26 |
---|---|
[nestjs] 멤버 컨트롤러, 서비스, 리포지토리 (0) | 2024.12.25 |
[nestjs] 회원 관련 엔티티와(member.entity.ts) 과 관련 파일 작성(enum등) (0) | 2024.12.23 |
[nestjs] TypeORM을 사용해 DB에 테이블 생성 (0) | 2024.12.21 |
[nestjs] Repository pattern으로 변경하기 (0) | 2024.12.21 |
Comments