티스토리 뷰

반응형

📌 NestJS + Prisma + Next.js로 만드는 웹 애플리케이션 첫걸음 - 애자일 쇼핑몰 프로젝트 - 인증 및 보안

1. 웹 애플리케이션에서 인증과 보안이 중요한 이유

쇼핑몰 애플리케이션은 사용자 정보를 다루며 결제 시스템을 포함하기 때문에, 강력한 보안이 필수적입니다.
특히, **사용자 인증(Authentication) 및 권한 관리(Authorization)**를 제대로 구현해야 보안 취약점을 줄이고 데이터 보호가 가능합니다.

인증 및 보안의 주요 목표:

  • 사용자 데이터 보호 (비밀번호 암호화 및 안전한 로그인)
  • JWT 토큰 기반 인증 적용 (JSON Web Token)
  • 관리자와 일반 사용자 구분 (RBAC, Role-Based Access Control)
  • 보안 위협 차단 (SQL Injection, XSS, CSRF 방어)

2. JWT(JSON Web Token) 기반 인증 시스템

2.1. JWT란?

JWT(JSON Web Token)는 사용자 인증에 사용되는 토큰 방식의 인증 시스템입니다.

  • 사용자가 로그인하면 JWT 토큰을 발급받아 이후 요청 시 인증 수단으로 사용
  • 서버는 JWT를 검증하여 사용자의 신원을 확인
  • 세션 기반 인증보다 확장성과 보안성이 높음

💡 JWT의 구조 (Header + Payload + Signature)

{
  "alg": "HS256",
  "typ": "JWT"
}
{
  "id": 1,
  "email": "user@example.com",
  "role": "customer",
  "exp": 1712345678
}

JWT는 클라이언트가 보관하며, API 요청 시 Authorization: Bearer <TOKEN> 형태로 전달됩니다.


3. NestJS에서 JWT 기반 인증 구현

3.1. JWT 관련 패키지 설치

npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcryptjs
npm install --save-dev @types/bcryptjs

3.2. JWT 모듈 설정

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';

@Module({
  imports: [
    JwtModule.register({
      secret: process.env.JWT_SECRET, // .env 파일에서 가져오기
      signOptions: { expiresIn: '1h' },
    }),
  ],
  controllers: [AuthController],
  providers: [AuthService],
})
export class AuthModule {}

3.3. 사용자 로그인 및 JWT 발급

💡 로그인 시 비밀번호를 비교하고, JWT 토큰을 발급합니다.

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcryptjs';

@Injectable()
export class AuthService {
  constructor(private jwtService: JwtService) {}

  async validateUser(email: string, password: string) {
    const user = await this.findUserByEmail(email);
    if (user && bcrypt.compareSync(password, user.password)) {
      return this.generateToken(user);
    }
    return null;
  }

  async generateToken(user: any) {
    return this.jwtService.sign({
      id: user.id,
      email: user.email,
      role: user.role,
    });
  }
}

로그인 성공 시 JWT를 발급하며, 이후 요청에서 사용됩니다.


4. RBAC(역할 기반 접근 제어) 적용

반응형

4.1. 사용자 역할(Role) 설정

쇼핑몰 애플리케이션에서는 사용자의 역할을 기반으로 권한을 제한해야 합니다.

💡 예제: User 모델에서 역할 추가 (schema.prisma)

model User {
  id       Int    @id @default(autoincrement())
  email    String @unique
  password String
  role     String @default("customer") // customer, admin
}

4.2. 관리자 전용 API 보호 (Guard 적용)

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler());
    if (!requiredRoles) return true;

    const request = context.switchToHttp().getRequest();
    const user = request.user;
    
    return requiredRoles.includes(user.role);
  }
}

💡 이제 특정 API에 관리자만 접근하도록 설정할 수 있습니다.

import { Controller, Get, UseGuards } from '@nestjs/common';
import { RolesGuard } from './roles.guard';

@Controller('admin')
@UseGuards(RolesGuard)
export class AdminController {
  @Get('dashboard')
  getAdminDashboard() {
    return { message: '관리자 대시보드' };
  }
}

이제 GET /admin/dashboard API는 관리자만 접근 가능!


5. 보안 강화를 위한 추가 조치

5.1. 비밀번호 암호화 (Bcrypt 적용)

사용자 비밀번호를 암호화하여 저장해야 합니다.

💡 비밀번호 암호화 예제 (users.service.ts)

import * as bcrypt from 'bcryptjs';

async createUser(email: string, password: string) {
  const hashedPassword = await bcrypt.hash(password, 10);
  return this.prisma.user.create({
    data: { email, password: hashedPassword },
  });
}

이제 데이터베이스에 저장되는 비밀번호는 해싱되어 보호됩니다.


5.2. CORS 설정

CORS(Cross-Origin Resource Sharing)는 다른 도메인에서 API를 호출할 수 있도록 허용하는 설정입니다.

💡 NestJS의 main.ts에서 CORS 설정 추가

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors({
    origin: 'https://example.com', // 허용할 도메인 설정
    credentials: true,
  });
  await app.listen(3000);
}
bootstrap();

CORS를 설정하면 프론트엔드에서 API를 안전하게 호출할 수 있습니다.


6. 보안 위협 방지 (SQL Injection, XSS, CSRF 대응)

SQL Injection 방어

  • Prisma ORM을 사용하면 쿼리 빌더를 활용하여 SQL Injection을 방지할 수 있습니다.
  • findMany(), create() 등의 메서드를 사용하면 직접 SQL 쿼리를 실행하는 것보다 안전합니다.

XSS(Cross-Site Scripting) 방어

  • 입력값을 검증하여 악성 스크립트 삽입을 방지 (class-validator 활용)
  • escape-html 패키지를 사용하여 HTML 태그 제거

CSRF(Cross-Site Request Forgery) 방어

  • JWT를 사용하면 CSRF 공격 위험이 줄어듦
  • API 요청 시 CSRF 토큰 검증을 추가할 수도 있음

🎯 마무리하며

이번 챕터에서는 NestJS + Prisma를 활용하여 JWT 기반 인증 및 보안을 적용하는 방법을 살펴보았습니다.
다음 단계에서는 3.5. API 문서화 및 테스트를 다루겠습니다. 🚀


 

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/07   »
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
글 보관함
반응형