icon

버전 관리와 API 진화 전략


 API 버전 관리는 기존 클라이언트를 방해하지 않으면서 API를 발전시키는 핵심 전략입니다.

 이는 API의 안정성을 유지하고 사용자 경험을 개선하는 데 중요한 역할을 합니다.

API 버전 관리 전략 비교

 1. URI 버저닝

  • 예 : /v1/users, /v2/users
  • 장점 : 구현이 간단하고 명확함
  • 단점 : URL 구조 변경, 캐싱 문제 가능성

 2. 헤더 버저닝

  • 예 : Accept-version: v1 또는 커스텀 헤더 사용
  • 장점 : URL 구조 유지, 더 RESTful한 접근
  • 단점 : 헤더 처리 로직 필요, 디버깅 어려움

 3. 미디어 타입 버저닝

  • 예 : Accept: application/vnd.myapp.v1+json
  • 장점 : 콘텐츠 협상 메커니즘 활용
  • 단점 : 복잡한 미디어 타입, 파싱 로직 필요

NestJS에서의 버전 관리 구현

  1. URI 버저닝
// main.ts
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api');
 
// users.controller.ts
@Controller('v1/users')
export class UsersV1Controller {
  @Get()
  findAll() {
    return 'This is V1 Users';
  }
}
 
@Controller('v2/users')
export class UsersV2Controller {
  @Get()
  findAll() {
    return 'This is V2 Users';
  }
}
  1. 헤더 버저닝
import { VersioningType } from '@nestjs/common';
 
// main.ts
const app = await NestFactory.create(AppModule);
app.enableVersioning({
  type: VersioningType.HEADER,
  header: 'Custom-API-Version',
});
 
// users.controller.ts
@Controller('users')
@Version('1')
export class UsersV1Controller {
  @Get()
  findAll() {
    return 'This is V1 Users';
  }
}
 
@Controller('users')
@Version('2')
export class UsersV2Controller {
  @Get()
  findAll() {
    return 'This is V2 Users';
  }
}
  1. 미디어 타입 버저닝
import { VersioningType } from '@nestjs/common';
 
// main.ts
const app = await NestFactory.create(AppModule);
app.enableVersioning({
  type: VersioningType.MEDIA_TYPE,
  key: 'v=',
});
 
// users.controller.ts
@Controller('users')
@Version('1')
export class UsersV1Controller {
  @Get()
  findAll() {
    return 'This is V1 Users';
  }
}
 
@Controller('users')
@Version('2')
export class UsersV2Controller {
  @Get()
  findAll() {
    return 'This is V2 Users';
  }
}

하위 호환성 유지

  1. 선택적 매개변수 사용
@Post()
create(@Body() createUserDto: CreateUserDtoV2) {
  if (createUserDto.newField === undefined) {
    // V1 로직 처리
  } else {
    // V2 로직 처리
  }
}
  1. 인터페이스 확장
interface UserV1 {
  id: number;
  name: string;
}
 
interface UserV2 extends UserV1 {
  email: string;
}

점진적 API 진화

 1. 필드 추가

  • 새 필드를 선택적으로 추가
  • 기본값 제공

 2. 필드 제거

  • 사용 중단(Deprecation) 공지
  • 일정 기간 동안 필드 유지 후 제거

 3. 엔드포인트 변경

  • 새 엔드포인트 추가
  • 기존 엔드포인트 유지 및 사용 중단 공지
@Get('old-endpoint')
@Deprecated()
oldEndpoint() {
  // 기존 로직
}
 
@Get('new-endpoint')
newEndpoint() {
  // 새로운 로직
}

버전 간 코드 공유 및 재사용

 NestJS 모듈 시스템 활용

@Module({
  imports: [SharedModule],
  controllers: [UsersV1Controller],
  providers: [UsersV1Service],
})
export class UsersV1Module {}
 
@Module({
  imports: [SharedModule],
  controllers: [UsersV2Controller],
  providers: [UsersV2Service],
})
export class UsersV2Module {}
 
@Module({
  providers: [SharedService],
  exports: [SharedService],
})
export class SharedModule {}

API 사용 중단(Deprecation) 프로세스

 1. 사용 중단 공지

  • API 응답 헤더 활용
  • 문서화 (Swagger)

 2. 로깅 및 모니터링

  • 사용 중단된 API 사용 트래킹

 3. 점진적 제거

  • 일정 기간 동안 유지 후 제거
@Get('deprecated-endpoint')
@Header('Warning', '299 - "This API version is deprecated"')
@ApiResponse({ status: 200, description: 'Deprecated: Will be removed in future versions' })
deprecatedEndpoint() {
  // 로직
}

다중 버전 테스트 전략

  1. 버전별 테스트 케이스 작성
describe('UsersController (V1)', () => {
  // V1 테스트 케이스
});
 
describe('UsersController (V2)', () => {
  // V2 테스트 케이스
});
  1. 공통 테스트 유틸리티 활용
function testUserEndpoint(version: string) {
  // 공통 테스트 로직
}
 
describe('Users API', () => {
  it('should work for V1', () => testUserEndpoint('v1'));
  it('should work for V2', () => testUserEndpoint('v2'));
});

API 버전 관리와 Swagger 연계

  1. 버전별 Swagger 문서 생성
const options = new DocumentBuilder()
  .setTitle('My API')
  .setVersion('1.0')
  .build();
const v1Document = SwaggerModule.createDocument(app, options, {
  include: [UsersV1Module],
});
SwaggerModule.setup('api/v1/docs', app, v1Document);
 
const v2Options = new DocumentBuilder()
  .setTitle('My API')
  .setVersion('2.0')
  .build();
const v2Document = SwaggerModule.createDocument(app, v2Options, {
  include: [UsersV2Module],
});
SwaggerModule.setup('api/v2/docs', app, v2Document);
  1. 버전별 태그 사용
@ApiTags('users-v1')
@Controller('v1/users')
export class UsersV1Controller {}
 
@ApiTags('users-v2')
@Controller('v2/users')
export class UsersV2Controller {}

Best Practices 및 주의사항

  1. 명확한 버전 관리 정책 수립
  2. 하위 호환성 유지 노력
  3. 점진적인 변경 적용
  4. 충분한 마이그레이션 기간 제공
  5. 명확한 문서화 및 변경 사항 공지
  6. 버전 간 일관된 동작 유지
  7. 성능 영향 최소화
  8. 보안 업데이트는 모든 지원 버전에 적용
  9. 클라이언트 사용 패턴 모니터링
  10. 정기적인 기술 부채 검토 및 해결

 NestJS에서 API 버전 관리와 진화 전략을 효과적으로 구현하는 것은 안정적이고 유연한 API를 제공하는 데 핵심적입니다.

 버전 관리 전략을 선택할 때는 프로젝트의 요구사항, 팀의 기술 스택, 그리고 클라이언트의 특성을 고려해야 합니다.

 NestJS의 모듈 시스템을 활용하면 버전 간 코드 공유 및 재사용을 효과적으로 구현할 수 있습니다.

 이는 코드 중복을 줄이고 유지보수성을 향상시키는 데 도움이 됩니다.