최근 프로젝트에서 아래와 같은 요청을 처리해야 할 일이 있었습니다.
{
"templateProjects": ["proj_abc123", "proj_def456", "proj_ghi789"]
}
이 요청은 배열 형태로 전달된 templateProjects
를 기준으로 여러 데이터를 삭제하는 작업입니다. 처음에는 DELETE 메서드를 사용하려 했지만, 예상치 못한 문제가 발생했습니다. 요청이 NestJS 컨트롤러에 도달하지 못했던 것입니다.
1. DELETE로 배열 데이터 삭제 시 발생한 문제
DELETE 메서드를 사용해 데이터를 삭제하려면 아래와 같이 요청을 설계할 수 있습니다:
DELETE /api/projects
Content-Type: application/json
{
"templateProjects": ["proj_abc123", "proj_def456", "proj_ghi789"]
}
하지만, 이 방식에서는 다음과 같은 문제가 발생했습니다:
- NestJS 컨트롤러에 도달하지 못함
- DELETE 메서드는 본문(body)을 허용하지만, NestJS에서 DELETE 메서드의 본문 데이터 처리를 제대로 설정하지 않으면 컨트롤러에 도달하지 못할 수 있습니다.
- 컨트롤러에서 DTO(Data Transfer Object)를 사용하여 데이터를 검증하려 했지만, 요청 본문이 컨트롤러에 전달되기 전에 차단되었습니다.
- DELETE 본문 처리의 제약
- 일부 클라이언트(예: 브라우저 Fetch API)와 프록시 서버는 DELETE 본문 데이터를 처리하지 않거나 제한을 두는 경우가 있습니다.
- 이는 DELETE 메서드가 RESTful 설계에서 본문 없이 단일 리소스를 삭제하는 데 사용되는 경우가 많기 때문입니다.
- 디버깅의 어려움
- 요청이 차단되었을 때 터미널이나 로그에 아무런 에러가 표시되지 않아 원인을 파악하는 데 시간이 걸렸습니다.
2. DELETE 메서드로 배열 데이터 처리
DELETE 메서드를 사용하려면 본문을 사용하지 않고 쿼리 파라미터를 활용하거나, 본문 데이터를 처리할 수 있도록 추가 설정이 필요합니다.
(1) DELETE + 쿼리 파라미터
쿼리 파라미터를 사용하여 배열 데이터를 전달하면 컨트롤러가 데이터를 정상적으로 처리할 수 있습니다.
컨트롤러 코드
import { Controller, Delete, Query } from '@nestjs/common';
@Controller('api')
export class ProjectsController {
@Delete('/projects')
deleteByProjects(@Query('ids') ids: string) {
const projectIds = ids.split(',');
console.log('삭제 요청:', projectIds);
return this.service.execute(projectIds);
}
}
요청 예시
DELETE /api/projects?ids=proj_abc123,proj_def456,proj_ghi789
장점:
- 컨트롤러에 데이터를 안정적으로 전달 가능.
- RESTful 설계 원칙에 적합.
단점:
- 쿼리 파라미터에 배열 데이터를 전달하면 URL 길이 제한 문제가 발생할 수 있음.
(2) DELETE 본문 데이터 허용
DELETE 메서드로 배열 데이터를 본문(body)로 전달하려면 NestJS에서 본문 처리를 명시적으로 설정해야 합니다.
DTO 정의
import { IsArray, IsString } from 'class-validator';
export class DeleteProjectsDto {
@IsArray()
@IsString({ each: true })
templateProjects: string[];
}
컨트롤러 코드
import { Controller, Delete, Body } from '@nestjs/common';
@Controller('api')
export class ProjectsController {
@Delete('/projects')
deleteByProjects(@Body() body: DeleteProjectsDto) {
console.log('삭제 요청 본문:', body.templateProjects);
return this.service.execute(body.templateProjects);
}
}
요청 예시
DELETE /api/projects
Content-Type: application/json
{
"templateProjects": ["proj_abc123", "proj_def456", "proj_ghi789"]
}
주의 사항
- 클라이언트 지원 여부 확인
- DELETE 본문을 지원하지 않는 클라이언트에서는 요청이 실패할 수 있습니다.
- 게이트웨이/프록시 설정
- Nginx, AWS API Gateway 등에서 DELETE 본문 처리가 기본적으로 비활성화된 경우가 있으므로 설정을 확인해야 합니다.
3. POST로 배열 데이터 삭제
DELETE 메서드의 제한을 우회하려면 POST를 사용하는 것이 더 안정적이고 유연한 대안이 될 수 있습니다.
POST 설계 예시
POST 메서드는 본문 데이터를 안정적으로 처리하므로 대량의 배열 데이터를 전달하기 적합합니다.
컨트롤러 코드
import { Controller, Post, Body } from '@nestjs/common';
@Controller('api')
export class ProjectsController {
@Post('/delete-projects')
deleteByProjects(@Body('templateProjects') templateProjects: string[]) {
console.log('삭제 요청:', templateProjects);
return this.service.execute(templateProjects);
}
}
요청 예시
POST /api/delete-projects
Content-Type: application/json
{
"templateProjects": ["proj_abc123", "proj_def456", "proj_ghi789"]
}
장점:
- 본문 데이터를 안정적으로 처리 가능.
- 대량 데이터 처리에 적합.
단점:
- RESTful 설계 원칙에 어긋날 수 있음.
4. 대량 삭제를 위한 최적화
5분마다 반복적으로 대량 데이터를 삭제해야 한다면, 데이터베이스와 서버 부하를 줄이기 위해 최적화가 필요합니다.
(1) MongoDB 인덱스 최적화
templateProject
필드에 인덱스를 추가하면 검색 성능이 크게 향상됩니다.
db.projects.createIndex({ templateProject: 1 });
(2) Batch 처리
요청 데이터를 배치로 나눠 처리하여 서버 부하를 분산합니다.
async execute(templateProjects: string[]) {
const BATCH_SIZE = 100;
for (let i = 0; i < templateProjects.length; i += BATCH_SIZE) {
const batch = templateProjects.slice(i, i + BATCH_SIZE);
await this.projectsRepository.deleteManyWithCount({
templateProject: { $in: batch },
});
}
}
5. DELETE vs POST: 언제 사용할까?
메서드 | 사용 사례 | 장점 | 단점 |
---|---|---|---|
DELETE | 단일 또는 간단한 배열 데이터를 삭제할 때 | RESTful 설계 원칙에 적합 | 본문 데이터 처리 제약 |
POST | 복잡한 구조나 대량 데이터를 삭제할 때 | 본문 데이터를 유연하게 처리 가능 | RESTful 설계 원칙에 엄밀히 맞지 않음 |
정리
- DELETE 메서드는 RESTful 설계 원칙에 가장 적합하지만, 배열 데이터를 처리하거나 본문 데이터를 사용해야 할 때 기술적 제약이 발생할 수 있습니다.
- POST 메서드는 DELETE의 한계를 보완하며, 본문 데이터를 안정적으로 처리할 수 있어 대량 삭제 작업에 적합한 실용적인 대안입니다.
- 데이터베이스 최적화, 배치 처리, Bulk 처리, 캐싱을 활용하면 반복적인 대량 데이터 삭제 요청에서도 효율성을 유지할 수 있습니다.
RESTful 원칙과 실용성의 균형
이번 경험을 통해 RESTful 설계 원칙을 지키는 DELETE 메서드 외에도, 특정 상황에서는 POST를 사용하여 삭제 작업을 처리할 수도 있다는 점을 깨달았습니다. 특히, 다음과 같은 상황에서 POST가 유용할 수 있다는 점을 알게 되었습니다:
DELETE 본문 데이터를 지원하지 않는 환경:
- 클라이언트나 게이트웨이에서 DELETE 본문 처리를 제한할 경우 POST가 대안이 될 수 있습니다.
대량 데이터 삭제
- POST는 대량 데이터를 본문으로 안정적으로 전달할 수 있어, DELETE 메서드보다 실용적입니다.
복잡한 요청 데이터 구조
- 배열이나 중첩된 데이터를 처리할 때 POST는 데이터 전달 방식을 더 유연하게 지원합니다.
'개발일지 > 기타' 카테고리의 다른 글
포트 어댑터 패턴에서 사용하는 디렉토리 및 파일 구조와 주요 컴포넌트 (0) | 2024.11.11 |
---|---|
모니터링의 중요성 : 성능 비교의 기회 (0) | 2024.11.08 |
효율적인 Monorepo 브랜치 전략: 안정적인 배포를 위한 가이드 (6) | 2024.11.07 |
Node.js의 내부 작동 방식을 이해하기 (2) | 2024.08.17 |
Node.js의 내부 구조를 이해하기 (0) | 2024.08.16 |