버전 관리와 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에서의 버전 관리 구현
- 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';
}
}
- 헤더 버저닝
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';
}
}
- 미디어 타입 버저닝
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';
}
}
하위 호환성 유지
- 선택적 매개변수 사용
@Post()
create(@Body() createUserDto: CreateUserDtoV2) {
if (createUserDto.newField === undefined) {
// V1 로직 처리
} else {
// V2 로직 처리
}
}
- 인터페이스 확장
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() {
// 로직
}
다중 버전 테스트 전략
- 버전별 테스트 케이스 작성
describe('UsersController (V1)', () => {
// V1 테스트 케이스
});
describe('UsersController (V2)', () => {
// V2 테스트 케이스
});
- 공통 테스트 유틸리티 활용
function testUserEndpoint(version: string) {
// 공통 테스트 로직
}
describe('Users API', () => {
it('should work for V1', () => testUserEndpoint('v1'));
it('should work for V2', () => testUserEndpoint('v2'));
});
API 버전 관리와 Swagger 연계
- 버전별 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);
- 버전별 태그 사용
@ApiTags('users-v1')
@Controller('v1/users')
export class UsersV1Controller {}
@ApiTags('users-v2')
@Controller('v2/users')
export class UsersV2Controller {}
Best Practices 및 주의사항
- 명확한 버전 관리 정책 수립
- 하위 호환성 유지 노력
- 점진적인 변경 적용
- 충분한 마이그레이션 기간 제공
- 명확한 문서화 및 변경 사항 공지
- 버전 간 일관된 동작 유지
- 성능 영향 최소화
- 보안 업데이트는 모든 지원 버전에 적용
- 클라이언트 사용 패턴 모니터링
- 정기적인 기술 부채 검토 및 해결
NestJS에서 API 버전 관리와 진화 전략을 효과적으로 구현하는 것은 안정적이고 유연한 API를 제공하는 데 핵심적입니다.
버전 관리 전략을 선택할 때는 프로젝트의 요구사항, 팀의 기술 스택, 그리고 클라이언트의 특성을 고려해야 합니다.
NestJS의 모듈 시스템을 활용하면 버전 간 코드 공유 및 재사용을 효과적으로 구현할 수 있습니다.
이는 코드 중복을 줄이고 유지보수성을 향상시키는 데 도움이 됩니다.