interface EmailOptions {
to: string;
subject: string;
html: string;
}
// ....
const mailOptions: EmailOptions = {
to: emailAddress,
subject: '가입 인증 메일',
html: `
가입확인 버튼를 누르시면 가입 인증이 완료됩니다.<br/>
<form action="${url}" method="POST">
<button>가입확인</button>
</form>
`
}
해당하는부분에서 from 속성이 빠져있어서 메일을 보낼따마다 error가 발생했다
from : "보내는사람이메일"
을 추가하면 오류해결
가입 확인을 누르면
/email-verify
엔드포인트로 요청이와 log가 출력이된다
해당 엔드포인트의 로직도 Service로 위임한다
컨트롤러는 요청과 응답처리만
실질적인 로직은 Service로
@Post('/login')
async login(@Body() dto: UserLoginDto): Promise<string> {
const { email, password } = dto;
return await this.usersService.login(email, password);
}
async login(email: string, password: string): Promise<string> {
// todos
// 1. email, password 가진 유저가 db에있다면 처리, 없으면 에러
// 2. JWT를 발급해 전달
throw new Error('method not implemented');
}
위와 마찬가지로 로직을 분리한다
@Get('/:id')
async getUserInfo(@Param('id') userId: string): Promise<UserInfo> {
return await this.usersService.getUserInfo(userId)
}
async getUserInfo(userId: string): Promise<string> {
// todos
// 1. userId를 DB에서 조회해 없다면 에러
// 2. 조회된 데이터를 UserInfo 타입으로 전달
throw new Error('Method not implemented');
}
일반적으로 사용한다면 문제가 생기지 않지만, 테스트코드에 MOCK을 사용하게 된다면 커스텀프로바이더를 사용해야한다.
- Nest 프레임워크가 만들어주는 인스턴스 대신 직접 생성을 하고 싶은 경우
- 이미 존재하는 클래스를 재사용 하고자 할때.
- 테스트를위해 MOCK버전으로 프로바이더를 재정의하려는 경우
Nest에서 모듈은 App이 실행되기 위해 Root모듈이 존재하며 이 RootModule에서 다른 모듈로 구성을해 각각 맡은 로직을 나눈다
export declare function Module(metadata: ModuleMetadata): ClassDecorator;
export interface ModuleMetadata {
imports?: Array<Type<any> | DynamicModule | Promise<DynamicModule> | ForwardReference>;
controllers?: Type<any>[];
providers?: Provider[];
exports?: Array<DynamicModule | Promise<DynamicModule> | string | symbol | Provider | ForwardReference | Abstract<any> | Function>;
}
import
: 이 모듈에서 사용하기 위한 프로바이더를 가지고있는 다른 모듈을 불러온다.
controllers
/providers
: 모듈 전반에서 컨트롤러와 프로바이더를 사용할 수 있도록 Nest가 객체를 생성하고 주입할 수있도록 해준다.
export
: 이 모듈에서 사용하는 Component를 다른 모듈에서 import에서 사용하려면, export를 시켜야한다.
예시 : 모듈
A
,B
,C
A
에서B
를 가져오고C
에서A
를 가져온다면C
모듈이B
모듈을 가져갈수 있도록export
해야한다.
앱모듈에서 유저 관리 기능을 유저모듈로 분리한다.
nest g mo Users
users.module.ts
import { Module } from '@nestjs/common';
import { EmailService } from 'src/email/email.service';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
@Module({
imports: [],
controllers: [UsersController],
providers: [UsersService, EmailService],
})
export class UsersModule {}
app.module.ts
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module';
@Module({
imports: [UsersModule],
controllers: [],
providers: [],
})
export class AppModule {}
이메일 기능또한 모듈로 분리하여 관리하도록 생성한다
nest g mo Email
email.module.ts
import { Module } from '@nestjs/common';
import { EmailService } from './email.service';
@Module({
providers: [EmailService],
exports: [EmailService],
})
export class EmailModule {}
users.module.ts
import { Module } from '@nestjs/common';
import { EmailModule } from 'src/email/email.module';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
@Module({
imports: [EmailModule],
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
환경변수패키지와 유효성 검사를위한 라이브러리부터 설치한다
yarn add @nestjs/config
yarn add joi
emailConfig.ts를
src/config
에 작성한다
import { registerAs } from '@nestjs/config';
export default registerAs('email', () => ({
service: process.env.EMAIL_SERVICE,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASSWORD,
},
baseUrl: process.env.EMAIL_URL,
}));
.env파일을 dist에 복사할 수 있도록 nest-cli.json에서 옵션을 바꿔준다
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"assets": [
{
"include": "./config/env/*.env",
"outDir": "./dist"
}
]
}
}
AppModule에 ConfigModule을 동적모듈로 등록한다
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module';
import { EmailModule } from './email/email.module';
import { ConfigModule } from '@nestjs/config';
import emailConfig from './config/emailConfig';
@Module({
imports: [
UsersModule,
EmailModule,
ConfigModule.forRoot({
envFilePath: [`${__dirname}/config/env/.${process.env.NODE_ENV}.env`],
load: [emailConfig],
isGlobal: true,
}),
],
controllers: [],
providers: [],
})
export class AppModule {}
envFilePath
: NODE_ENV의 값이 stage라면 dist파일아래의 .stage.env
의 절대 경로를 가지게된다
load
: 앞에 구성해둔 ConfigFactory를 지정한다.
isGlobal
: 전역 모듈로 동작하게 만들어 어느 모듈에도 사용하도록 지정한다.
emailService에 등록해준다
// 생성자
constructor(
@Inject(emailConfig.KEY) private config: ConfigType<typeof emailConfig>,
) {
this.transporter = nodemailer.createTransport({
service: config.service,
host: 'smtp.naver.com',
port: 587,
auth: {
user: config.auth.user,
pass: config.auth.pass,
},
});
}
@Inject()
: 앞서만든 환경변수의 key인 email
을 문자열로 넣어주면된다.
이후 원래 하드코딩돼있던 변수들을 환경변수로 교체해준다.
글이 너무길어져서 (2)로 이어짐.