TIL - 20260303

juni·2026년 3월 3일

TIL

목록 보기
283/316

0303 NestJS 심화 (3/N): 파일 업로드와 이메일 발송


✅ 1. 파일 업로드 처리

  • NestJS에서 클라이언트가 업로드하는 파일(이미지, 문서 등)을 처리하는 방법은 크게 두 가지로 나뉩니다: 로컬 서버에 저장하는 방식과, 클라우드 스토리지(AWS S3 등)에 저장하는 방식.

➕ 1-1. 기본 설정: Multer 미들웨어

  • NestJS는 Express.js에서 널리 사용되는 Multer 미들웨어를 파일 업로드 처리를 위한 기본 솔루션으로 채택하고 있습니다.
  • Multermultipart/form-data 형식으로 들어오는 요청을 파싱하여, 파일과 텍스트 필드를 쉽게 다룰 수 있도록 해줍니다.

➕ 1-2. 로컬 서버에 파일 저장하기

  • 가장 간단한 방법으로, 업로드된 파일을 서버의 특정 디렉토리에 저장합니다.
  1. FileInterceptor 사용:

    • 파일을 하나만 업로드하는 경우, 컨트롤러의 라우트 핸들러에 @UseInterceptors(FileInterceptor('fieldName')) 데코레이터를 적용합니다.
    • 'fieldName'은 클라이언트가 파일을 보낼 때 사용한 form-data의 필드 이름입니다.
  2. @UploadedFile 데코레이터:

    • 핸들러의 파라미터에 @UploadedFile()을 사용하여, 업로드된 파일 객체(Express.Multer.File)를 주입받을 수 있습니다.
  3. 정적 파일 서빙 설정:

    • 업로드된 파일을 외부에서 URL로 접근할 수 있게 하려면, main.ts에서 app.useStaticAssets()를 사용하여 해당 디렉토리를 정적 파일 제공 경로로 설정해야 합니다.
    // app.controller.ts
    @Post('upload')
    @UseInterceptors(FileInterceptor('file')) // 'file'이라는 필드명의 파일을 가로챔
    uploadFile(@UploadedFile() file: Express.Multer.File) {
      console.log(file); // 파일 정보 (원본 이름, 저장된 경로, 크기 등)
      return { filePath: file.path };
    }
    
    // main.ts
    app.useStaticAssets(join(__dirname, '..', 'uploads'), {
      prefix: '/uploads/', // URL 경로에 /uploads/ 프리픽스 추가
    });
  • 단점: 이 방식은 서버의 로컬 디스크를 사용하므로, 서버를 여러 대로 확장(Scale-out)할 경우 파일 동기화 문제가 발생하고, 서버 장애 시 파일이 유실될 수 있습니다.

➕ 1-3. AWS S3에 파일 저장하기 (권장)

  • 운영 환경에서는 확장성과 안정성을 위해, 파일을 AWS S3와 같은 객체 스토리지에 저장하는 것이 표준적인 방법입니다.

  • 구현 흐름:

    1. AWS SDK 설치: npm install @aws-sdk/client-s3
    2. S3 서비스 구현: S3와의 통신을 담당하는 별도의 S3Service를 만듭니다. 이 서비스는 S3Client를 사용하여 파일을 S3 버킷에 업로드하는 로직을 캡슐화합니다.
    3. 컨트롤러 로직: 컨트롤러는 FileInterceptor를 통해 파일을 받은 후, 이 파일을 S3Service에 전달하여 업로드를 위임합니다.
    4. 결과 처리: S3Service는 업로드 성공 후, 해당 파일에 접근할 수 있는 S3 URL을 반환합니다. 이 URL을 데이터베이스에 저장하여 관리합니다.

✅ 2. 이메일 발송

  • 회원가입 인증, 비밀번호 찾기, 알림 등 다양한 기능에서 이메일 발송이 필요합니다. NestJS에서는 @nestjs-modules/mailer 패키지를 사용하여 이메일 발송 기능을 쉽게 구현할 수 있습니다.

➕ 2-1. mailer 모듈 설정

  1. 필요한 패키지 설치:

    npm install @nestjs-modules/mailer nodemailer
  2. 루트 모듈(app.module.ts)에 MailerModule 등록:

    • MailerModule.forRoot()를 사용하여, 사용할 이메일 서비스(e.g., Gmail, SendGrid)의 SMTP 서버 정보를 설정합니다.
    • 보안: SMTP 계정 정보는 반드시 환경 변수(ConfigService)를 통해 안전하게 관리해야 합니다.
    • 템플릿 엔진: Pug, EJS 등의 템플릿 엔진을 사용하여 동적인 HTML 이메일을 쉽게 만들 수 있습니다.
    // app.module.ts
    import { MailerModule } from '@nestjs-modules/mailer';
    
    @Module({
      imports: [
        MailerModule.forRoot({
          transport: {
            host: 'smtp.gmail.com',
            port: 587,
            secure: false, // true for 465, false for other ports
            auth: {
              user: 'your-email@gmail.com', // 환경 변수로 관리
              pass: 'your-password',      // 환경 변수로 관리
            },
          },
          // ... 템플릿 설정 등 ...
        }),
      ],
    })
    export class AppModule {}

➕ 2-2. 이메일 발송 서비스 구현

  • MailerService를 주입받아, sendMail() 메서드를 호출하여 이메일을 발송합니다.
// email.service.ts
import { MailerService } from '@nestjs-modules/mailer';
import { Injectable } from '@nestjs/common';

@Injectable()
export class EmailService {
  constructor(private readonly mailerService: MailerService) {}

  async sendVerificationEmail(email: string, code: string): Promise<void> {
    await this.mailerService.sendMail({
      to: email,
      subject: '회원가입 인증 코드입니다.',
      // 템플릿을 사용하는 경우
      template: './verification', // templates/verification.pug
      context: {
        code, // 템플릿에 전달할 변수
      },
      // 템플릿을 사용하지 않는 경우
      // html: `인증 코드: <b>${code}</b>`,
    });
  }
}```

*   **비동기 처리**: 이메일 발송은 외부 SMTP 서버와의 통신으로 인해 시간이 걸릴 수 있는 작업이므로, 실제 서비스에서는 **Queue**와 같은 메시징 시스템과 연동하여 **비동기적으로 처리**하는 것이 시스템 전체의 응답성을 높이는 좋은 방법입니다.

---

### 📌 요약

*   NestJS에서의 파일 업로드는 **`Multer`** 기반의 **`FileInterceptor`**를 사용하여 처리합니다.
*   개발 환경에서는 **로컬 서버**에 파일을 저장할 수 있지만, 운영 환경에서는 확장성과 안정성을 위해 **AWS S3**와 같은 **객체 스토리지**를 사용하는 것이 표준입니다.
*   이메일 발송은 **`@nestjs-modules/mailer`** 패키지를 통해 쉽게 구현할 수 있으며, SMTP 접속 정보와 같은 민감한 데이터는 반드시 **환경 변수**로 관리해야 합니다.
*   이메일 발송과 같은 시간이 오래 걸리는 작업은, 사용자 경험 향상을 위해 **비동기 방식(e.g., Queue)**으로 처리하는 것을 고려해야 합니다.

0개의 댓글