icon

프로젝트 요구사항 분석과 설계


 NestJS 프로젝트를 성공적으로 수행하기 위해서는 철저한 요구사항 분석과 설계가 필수적입니다.

 이 과정은 프로젝트의 방향을 설정하고 향후 개발 과정의 기반을 마련합니다.

요구사항 수집 및 분석

  1. 이해관계자 인터뷰 : 클라이언트, 최종 사용자, 도메인 전문가와의 대화
  2. 기존 시스템 분석 : 현재 시스템의 장단점 파악
  3. 사용자 스토리 작성 : 사용자 관점에서의 기능 요구사항 정의
  4. 비기능적 요구사항 식별 : 성능, 보안, 확장성 등

 요구사항 문서화 예시

# 기능 요구사항
1. 사용자 관리
   - 사용자 등록, 로그인, 프로필 수정 기능
   - 역할 기반 접근 제어 (RBAC)
 
2. 상품 관리
   - 상품 등록, 수정, 삭제, 조회 기능
   - 카테고리별 상품 분류
 
# 비기능적 요구사항
1. 성능
   - 페이지 로드 시간 3초 이내
   - 동시 접속자 1000명 지원
 
2. 보안
   - HTTPS 적용
   - 사용자 비밀번호 암호화 저장

도메인 모델링

 도메인 주도 설계(DDD) 원칙을 적용한 모델링

  1. 엔티티 식별
  2. 값 객체 정의
  3. 애그리게이트 경계 설정
  4. 도메인 이벤트 정의

 예시

// 엔티티
export class User {
  id: string;
  name: string;
  email: string;
  
  constructor(id: string, name: string, email: string) {
    this.id = id;
    this.name = name;
    this.email = email;
  }
  
  updateEmail(newEmail: string) {
    // 이메일 유효성 검사 로직
    this.email = newEmail;
  }
}
 
// 값 객체
export class Address {
  constructor(
    public readonly street: string,
    public readonly city: string,
    public readonly zipCode: string
  ) {}
}
 
// 애그리게이트 루트
export class Order {
  id: string;
  userId: string;
  items: OrderItem[];
  shippingAddress: Address;
  
  addItem(product: Product, quantity: number) {
    const item = new OrderItem(product, quantity);
    this.items.push(item);
  }
  
  calculateTotal(): number {
    return this.items.reduce((sum, item) => sum + item.subtotal(), 0);
  }
}

시스템 아키텍처 설계

 NestJS 특성을 고려한 아키텍처 결정

  1. 모듈화 : 기능별 모듈 분리
  2. 마이크로서비스 vs 모놀리식 : 프로젝트 규모와 복잡성 고려
  3. 데이터베이스 선택 : 관계형 DB vs NoSQL
  4. 캐싱 전략 : Redis 활용
  5. 메시지 큐 : RabbitMQ 또는 Apache Kafka 고려

 아키텍처 다이어그램 예시

[클라이언트] <--> [API Gateway]
                     |
     +---------------+---------------+
     |               |               |
[사용자 서비스] [상품 서비스] [주문 서비스]
     |               |               |
   [DB]            [DB]            [DB]

데이터베이스 스키마 설계

 TypeORM을 활용한 엔티티 정의

import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
 
@Entity()
export class User {
  @PrimaryGeneratedColumn('uuid')
  id: string;
 
  @Column()
  name: string;
 
  @Column({ unique: true })
  email: string;
 
  @OneToMany(() => Order, order => order.user)
  orders: Order[];
}
 
@Entity()
export class Order {
  @PrimaryGeneratedColumn('uuid')
  id: string;
 
  @ManyToOne(() => User, user => user.orders)
  user: User;
 
  @OneToMany(() => OrderItem, item => item.order)
  items: OrderItem[];
 
  @Column('jsonb')
  shippingAddress: Address;
}

API 설계

 RESTful API 설계 원칙

  1. 리소스 중심 URL 구조
  2. 적절한 HTTP 메서드 사용
  3. 명확한 에러 처리
  4. 버전 관리

 NestJS 컨트롤러 예시

@Controller('users')
export class UserController {
  @Get()
  findAll(): Promise<User[]> {
    // 사용자 목록 조회 로직
  }
 
  @Get(':id')
  findOne(@Param('id') id: string): Promise<User> {
    // 특정 사용자 조회 로직
  }
 
  @Post()
  create(@Body() createUserDto: CreateUserDto): Promise<User> {
    // 사용자 생성 로직
  }
}

 Swagger를 활용한 API 문서화

import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
 
@ApiTags('users')
@Controller('users')
export class UserController {
  @ApiOperation({ summary: '사용자 생성' })
  @ApiResponse({ status: 201, description: '사용자 생성 성공', type: User })
  @Post()
  create(@Body() createUserDto: CreateUserDto): Promise<User> {
    // 사용자 생성 로직
  }
}

보안 요구사항 분석 및 설계

  1. 인증 : JWT 기반 인증 시스템
  2. 인가 : RBAC(Role-Based Access Control) 구현
  3. 데이터 암호화 : 중요 정보 암호화 저장
  4. HTTPS 적용 : SSL/TLS 인증서 설정

 NestJS 인증 구현 예시

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
 
@Injectable()
export class AuthService {
  constructor(
    private usersService: UsersService,
    private jwtService: JwtService
  ) {}
 
  async validateUser(username: string, pass: string): Promise<any> {
    const user = await this.usersService.findOne(username);
    if (user && user.password === pass) {
      const { password, ...result } = user;
      return result;
    }
    return null;
  }
 
  async login(user: any) {
    const payload = { username: user.username, sub: user.userId };
    return {
      access_token: this.jwtService.sign(payload),
    };
  }
}

확장성 및 성능 고려

  1. 수평적 확장 : 마이크로서비스 아키텍처 채택
  2. 캐싱 전략 : Redis를 활용한 데이터 캐싱
  3. 데이터베이스 최적화 : 인덱싱, 쿼리 최적화
  4. 비동기 처리 : 이벤트 기반 아키텍처 구현

 NestJS 캐싱 예시

import { Injectable, CacheStore } from '@nestjs/common';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
 
@Injectable()
export class UserService {
  constructor(@Inject(CACHE_MANAGER) private cacheManager: CacheStore) {}
 
  async getUser(id: string): Promise<User> {
    const cachedUser = await this.cacheManager.get<User>(`user:${id}`);
    if (cachedUser) {
      return cachedUser;
    }
    const user = await this.userRepository.findOne(id);
    await this.cacheManager.set(`user:${id}`, user, { ttl: 3600 });
    return user;
  }
}

프로젝트 일정 수립 및 작업 분배

  1. 애자일 방법론 적용 : 스프린트 기반 개발
  2. 칸반 보드 활용 : Trello 또는 Jira 사용
  3. 지속적 통합/배포(CI/CD) 파이프라인 구축

Best Practices 및 주의사항

  1. 요구사항의 명확한 문서화 및 지속적인 업데이트
  2. 도메인 전문가와의 긴밀한 협력
  3. 확장성을 고려한 모듈화된 설계
  4. 보안을 처음부터 고려한 설계(Security by Design)
  5. 성능 요구사항의 구체적인 정의 및 벤치마킹
  6. 테스트 주도 개발(TDD) 적용
  7. 코드 품질 관리: 린트 도구, 코드 리뷰 프로세스
  8. 문서화 : API 문서, 아키텍처 문서 지속 관리
  9. 확장 가능한 데이터베이스 스키마 설계
  10. 정기적인 설계 리뷰 및 리팩토링