1. Swagger 패키지 설치
npm install --save @nestjs/swagger
2. Swagger 실행하기
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('Cats example')
.setDescription('The cats API description')
.setVersion('1.0')
.addTag('cats')
.build();
const documentFactory = () => SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, documentFactory);
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
위와 같이 DocumentBuilder로 config를 생성하고 SwaggerModule.setup을 하면 Swagger를 실행할 수 있다.
서버를 실행하고 'localhost:3000/api' 로 접속하면 확인할 수 있게 된다.
3. Schema 속성 보이도록 설정
기본적으로 TypeScript 에선 메타데이터 리플렉션 시스템에는 몇 가지 제약사항이 있어 클래스가 어떤 속성으로 구성되어 있는지, 해당 속성이 필수 사항인지 선택 사항인지 등을 인식할 수 없다.
그렇기 때문에 Swagger에서도 Schema(모델 및 DTO)와 속성 등이 제대로 보이지 않는다.
"compilerOptions": {
"deleteOutDir": true,
"plugins": ["@nestjs/swagger"]
}
이것들을 제대로 보이게 하기 위해선 위와 같이 nest-cli.json 에서 plugins 부분을 추가한다.
설정 후엔 위와 같이 모든 Schema들의 속성이 보이게 된다. (참고로 CLI 플러그인으로 자동으로 스키마들이 보이게 하려면, .dto.ts 나 .entity.ts 이름으로 작성되어야 한다.)
3.1. 만약 CLI 플러그인을 사용하지 않고 속성들이 보이게 하고 싶다면?
@ApiProperty()
같은 데코레이터를 일일이 붙여주어야 한다.
4. Swagger 보안 설정
실제 서비스에 들어가면 Swagger에 대한 보안도 강화해야한다.
만약 누구나 볼 수 있다면 해커들이 API들을 보고 유추할 수 있게 되기 때문에 보안상 이슈가 생기게 된다.
4.1. express-basic-auth 설치
npm i express-basic-auth
4.2. express-basic-auth 설정
app.use(
'/api',
expressBasicAuth({
challenge: true,
users: {
[swaggerUser]: swaggerPassword, // 지정된 ID/비밀번호
},
}),
);
main.ts 의 bootstrap() 내에 위와 같이 작성한 후 서버를 실행하면 사용자 아이디와 비밀번호를 입력하여야만 Swagger에 접속할 수 있게 된다.
5. Authorization 관련 설정
Swagger에서도 JWT 를 사용하여 AccessToken과 RefreshToken을 발급하여 각종 API들을 사용할 수 있도록 하여야 한다.
5.1. DocumentBuilder() 옵션 추가 (.addBearerAuth())
const config = new DocumentBuilder()
.setTitle('NestJS SNS 프로젝트')
.setDescription('NestJS SNS API description')
.setVersion('0.0.1')
// JWT Bearer 보안 스키마 추가
.addBearerAuth()
.build();
const documentFactory = () => SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, documentFactory, {
swaggerOptions: {
persistAuthorization: true, // Swagger에서 저장된 Bearer Token이 날아가지 않게 해줌(편의성)
}
});
bootstrap() 내에 작성된 DocumentBuilder() 에서 .addBearerAuth()를 추가하게 되면,
Swagger 오른쪽 상단에 Authorize 버튼이 나타나며, 이것을 누르면 AccessToken을 입력하는 팝업이 나타난다.
입력하고 Authorize 를 누르면, Swagger에서 AccessToken을 계속해서 유지되게 되므로 각종 API들을 사용할 수 있다.
5.2. LoginDto 구현
Postman에선 Authorization 탭에서 Auth Type과 Username, Password를 통해서 로그인(AccessToken, RefreshToken 발급)이 가능하도록 구현했다.
import { ApiProperty } from "@nestjs/swagger";
import { IsString } from "class-validator";
export class LoginDto {
@IsString()
@ApiProperty({ description: '사용자 이메일', example: '' })
email: string;
@IsString()
@ApiProperty({ description: '사용자 비밀번호', example: '' })
password: string;
}
Swagger에서도 Username(Email)과 Password를 받을 수 있도록 LoginDto를 구현해야 한다.
5.3. 로그인 라우터에 LoginDto 적용 및 결과
@Post('login/email')
@ApiConsumes('application/x-www-form-urlencoded')
@ApiBody({ type: LoginDto })
@ApiOperation({
summary: '로그인',
description: '사용자가 이메일, 비밀번호로 로그인합니다. AccessToken과 RefreshToken이 반환됩니다.'
})
@IsPublic(IsPublicEnum.IS_PUBLIC)
postLoginEmail(@Body() loginDto: LoginDto) {
return this.authService.loginWithEmail(loginDto);
}
5.3.1. @ApiConsumes
@ApiConsumes은 어떤 MIME 타입의 데이터를 소비하는지를 Swagger에 명시하기 위한 데코레이터이다. 위에선 form타입을 지정하도록 하였다.
5.3.2. @ApiOperation
@ApiOperation 는 Swagger에서 엔드포인트 오른쪽에 나타나는 타이틀과 그 아래에 나타나는 설명을 보이도록 한다.
5.3.3. @ApiBody
@ApiBody는 body에 어떤 값이 들어갈지를 명시한다. LoginDto를 입력하였으므로 위와 같이 form의 형태로 email, password를 입력할 수 있는 란이 나타난다.
5.4. 로그인 엔드포인트 실행결과
위와 같이 이미 DB에 회원가입된 유저가 있다고 가정한 후 로그인을 하면,
Response body에 AccessToken과 RefreshToken이 발급되는 것을 볼 수 있다.
5.5. RegisterUserDto 구현
import { IsString } from "class-validator";
import { LoginDto } from "./login.dto";
import { ApiProperty } from "@nestjs/swagger";
export class RegisterUserDto extends LoginDto {
@IsString()
@ApiProperty({ description: '닉네임', example: ''})
nickname: string;
}
RegisterUserDto는 LoginDto를 받으며 추가적으로 nickname을 받도록 하여 회원가입할 수 있도록 한다.
@Post('register/email')
@ApiConsumes('application/x-www-form-urlencoded')
@ApiBody({ type: RegisterUserDto })
@ApiOperation({
summary: '회원가입',
description: '사용자가 이메일, 비밀번호, 닉네임을 입력하여 회원가입합니다. AccessToken과 RefreshToken이 반환됩니다.'
})
@IsPublic(IsPublicEnum.IS_PUBLIC)
postRegisterEmail(
@Body() body: RegisterUserDto,
) {
return this.authService.registerWithEmail(body);
}
엔드포인트는 로그인과 거의 동일하게 구현하면 된다.
5.6. 회원가입 엔드포인트 실행결과
회원가입 시 AccessToken과 RefreshToken을 발급하며, DB에도 유저가 생성되는 것을 볼 수 있다.
6. API 실행하기
@ApiBearerAuth()
@Get()
@Roles(RoleEnum.ADMIN)
getUsers() {
return this.usersService.getAllUsers();
}
Bearer를 사용하는 API의 경우 @ApiBearerAuth() 데코레이터를 사용하여야 한다.
위의 경우 모든 유저들을 가져오는 API를 예로 들어보자.
@ApiBearerAuth() 데코레이터를 붙이면 Swagger에서 해당 엔드포인트 오른쪽 끝에 자물쇠 모양이 생성된다.
이것을 누르면 AccessToken을 입력하는 Bearer 팝업이 활성화된다.
만약 그냥 엔드포인트를 실행하면 Response는 위와 같이 잘못된 토큰이라는 Error를 발생한다. (잘못된 토큰입니다는 개인적으로 구현한 BearerGuard에 의해 실행)
위와 같이 AccessToken을 입력하면
자물쇠가 잠기는 표시로 변하며,
정상적인 값을 Response 하게 된다.
'NestJS' 카테고리의 다른 글
[NestJS] Kakao OAuth2.0 Passport (0) | 2025.04.14 |
---|---|
[NestJS] Google Oauth2.0 Passport (0) | 2025.04.12 |
[NestJS] process.env 사용 및 분기처리 (0) | 2025.04.05 |
[NestJS] Authorization Guard (0) | 2025.03.14 |
[NestJS] RBAC (Role Base Access Controll) (0) | 2025.03.13 |