Handling CRUD Operations#
CRUD는 데이터 중심 애플리케이션에서 가장 기본적인 네 가지 작업인 생성(Create), 읽기(Read), 업데이트(Update), 삭제(Delete)를 의미한다.
이러한 작업은 데이터베이스, API 디자인, 애플리케이션 개발 등 다양한 분야에서 핵심적인 역할을 한다.
RESTful API에서 CRUD 작업 처리는 API 설계의 핵심이다. 표준 HTTP 메서드와 상태 코드를 활용하고, 자원 모델링, 엔드포인트 설계, 보안, 에러 핸들링 등 다양한 요소를 고려하여 효율적이고 안정적인 API를 구축해야 한다. 또한, 전통적인 CRUD 방식의 문제점을 인식하고, Get-and-Set, Get-and-Patch, Timestamp-Checked와 같은 대안을 고려하여 API의 유연성과 확장성을 높일 수 있다.
CRUD 작업과 HTTP 메서드 매핑#
RESTful API에서는 CRUD 작업을 표준 HTTP 메서드와 매핑하여 구현한다.
CRUD 작업 | HTTP 메서드 | 설명 |
---|
Create | POST | 새로운 리소스를 생성합니다. |
Read | GET | 리소스의 현재 상태를 조회합니다. |
Update | PUT/PATCH | 리소스의 전체 또는 일부를 업데이트합니다. |
Delete | DELETE | 리소스를 삭제합니다. |
CRUD API의 중요성#
- 일관성: 표준화된 방식으로 데이터를 관리하고 접근할 수 있도록 한다.
- 확장성: 애플리케이션의 규모가 커짐에 따라 효율적으로 데이터를 처리할 수 있도록 지원한다.
- 유연성: 다양한 클라이언트가 API를 통해 데이터에 접근하고 수정할 수 있도록 한다.
CRUD API 설계 시 고려사항#
자원 모델링:
- API의 핵심 자원을 식별하고, 각 자원에 대한 명확한 정의를 내린다.
- 자원 간의 관계를 설정하여 API 엔드포인트를 설계한다.
엔드포인트 설계:
- 각 자원에 대한 CRUD 작업을 수행할 수 있는 엔드포인트를 정의한다.
- 일관성 있는 명명 규칙을 사용하여 엔드포인트를 설계한다. (예:
/users
, /users/{id}
)
요청 및 응답 형식:
- JSON 또는 XML과 같은 표준 데이터 형식을 사용하여 요청과 응답을 구성한다.
- API 요청에 필요한 파라미터와 응답으로 반환되는 데이터 구조를 명확히 정의한다.
상태 코드:
- 각 API 요청의 성공 또는 실패를 나타내는 적절한 HTTP 상태 코드를 사용한다.
- 에러 발생 시, 상세한 에러 메시지를 응답에 포함하여 클라이언트가 문제를 해결할 수 있도록 돕는다.
보안:
- API에 대한 접근을 제어하기 위해 인증(Authentication) 및 권한 부여(Authorization) 메커니즘을 구현한다.
- OAuth 2.0, JWT(JSON Web Token) 등의 표준 인증 방식을 활용한다.
버전 관리:
- API의 변경 사항을 관리하기 위해 버전 관리 전략을 수립한다.
- URL 기반, 헤더 기반, 쿼리 파라미터 기반 등 다양한 버전 관리 방식을 고려한다.
에러 핸들링:
- 예외적인 상황에 대한 처리를 구현하고, 사용자에게 유용한 피드백을 제공한다.
- RFC 7807 또는 RFC 9457과 같은 표준을 준수하여 일관된 에러 응답 형식을 제공한다.
CRUD API 설계 시 문제점 및 대안#
불필요한 Create/Update/Delete 구분:
- 많은 클라이언트가 “존재하지 않으면 생성, 존재하면 업데이트, 값이 없으면 삭제"와 같은 “Set” 연산을 필요로 한다.
- 대안: Get-and-Set 패턴을 사용하여 하나의 연산으로 모든 경우를 처리한다.
부분 업데이트의 어려움:
- CRUD는 리소스의 전체를 읽고 써야 하므로, 부분적인 업데이트가 어렵고 비효율적이다.
- 대안: Get-and-Patch 패턴을 사용하여 리소스의 특정 필드만 업데이트할 수 있도록 한다.
동시성 문제:
- 여러 클라이언트가 동시에 리소스를 수정할 때, 데이터 일관성 문제가 발생할 수 있다.
- 대안: Timestamp-Checked 패턴을 사용하여 낙관적 동시성 제어를 구현한다.
검색 및 보안 변경 누락:
- CRUD API 설계 시 검색 및 보안 관련 기능이 누락되는 경우가 많다.
- 대안: API 설계 초기 단계에서 검색 및 보안 요구사항을 고려한다.
추가적인 고려사항#
- HATEOAS (Hypermedia as the Engine of Application State): API 응답에 하이퍼링크를 포함하여 클라이언트가 API를 동적으로 탐색할 수 있도록 한다.
- API 문서화: Swagger/OpenAPI와 같은 도구를 사용하여 API 문서를 자동 생성하고, API 사용법을 명확히 설명한다.
- 성능 최적화: 캐싱, 페이징, 압축 등을 통해 API 응답 시간을 최소화한다.
용어 정리#
참고 및 출처#
Get-and-Patch “Get-and-Patch"는 리소스의 부분적 업데이트를 효율적으로 처리하기 위한 REST API 디자인 패턴으로, 기존 CRUD(Create, Read, Update, Delete) 방식의 한계를 보완한다.
이 패턴은 GET과 PATCH 메서드 조합을 통해 리소스의 전체 상태를 검색하지 않고도 특정 필드만 업데이트할 수 있도록 설계되었다.
핵심 개념 두 단계 프로세스 Get: 리소스의 현재 상태 조회 (필요한 필드 확인) Patch: 변경된 필드만 서버에 전송하여 부분 업데이트 HTTP 메서드 활용 단계 HTTP 메서드 설명 Get GET 리소스의 전체/일부 상태 조회 Patch PATCH 식별된 필드만 부분적으로 업데이트 CRUD vs. Get-and-Patch 구분 CRUD (PUT) Get-and-Patch (PATCH) 업데이트 범위 전체 리소스 교체 특정 필드만 수정 네트워크 효율성 모든 필드 전송 필요 변경된 필드만 전송 멱등성 보장됨 조건부 보장 (구현 방식에 따라 다름) 동시성 제어 전체 리소스 버전 관리 필드 단위 낙관적 잠금 가능 사용 사례 단순 리소스 교체 대규모 객체의 일부 수정 작동 원리 Get 단계 클라이언트가 리소스의 현재 상태를 조회한다.
...
Get-and-Set “Get-and-Set"은 전통적인 CRUD(Create, Read, Update, Delete) 방식을 개선한 REST API 디자인 패턴으로, 리소스의 존재 여부와 관계없이 단순화된 작업 흐름을 제공한다.
기본 개념 두 가지 핵심 연산 Get: 리소스의 현재 상태 조회 (CRUD의 Read와 동일) Set: Create/Update: 리소스 존재 여부와 무관하게 값을 설정 (Last-Write-Wins 정책) Delete: null 값을 전달하여 리소스 삭제 동작 원리 1 2 3 4 [클라이언트] [서버] Get 요청 → 리소스 상태 확인 Set 요청 → 값 설정/삭제 ← 최종 상태 반환 (옵션: 이전 값 포함) CRUD와의 차이점 기능 CRUD API Get-and-Set API 생성/수정 POST/PUT/PATCH 분리 단일 Set 연산으로 통합 삭제 DELETE 메서드 사용 Set(null)으로 처리 동시성 제어 복잡한 버전 관리 필요 Last-Write-Wins 기본 적용 에러 처리 상태 코드 404/409 등 다양 단순화된 200/400/500 사용 사례 복잡한 비즈니스 로직 단순 리소스 관리 시스템 작동 원리 상세 Set 연산의 3가지 시나리오 리소스 없음 + 값 전달: 새 리소스 생성 (201 Created) 리소스 존재 + 값 전달: 기존 리소스 덮어쓰기 (200 OK) 리소스 존재 + null 전달: 리소스 삭제 (204 No Content) Last-Write-Wins 동시성 제어 타임스탬프 기반: 최종 쓰기 요청이 우선 적용
...
Timestamp-Checked Timestamp-Checked 방식은 동시성 제어를 위한 중요한 기법 중 하나로, 주로 낙관적 동시성 제어(Optimistic Concurrency Control)의 맥락에서 사용된다.
기본 원리 타임스탬프 할당: 각 트랜잭션에 고유한 타임스탬프를 부여한다. 이는 주로 트랜잭션이 시작될 때 시스템 시간이나 논리적 카운터를 사용하여 생성된다. 읽기-검증-쓰기 단계: 트랜잭션은 다음 세 단계로 실행된다. 읽기 단계: 데이터를 읽고 로컬에서 작업을 수행한다. 검증 단계: 다른 트랜잭션과의 충돌을 검사한다. 쓰기 단계: 충돌이 없다면 변경사항을 데이터베이스에 반영한다. 충돌 감지: 트랜잭션이 커밋하려 할 때, 자신이 읽은 데이터가 다른 트랜잭션에 의해 변경되었는지 확인한다. 작동 방식 각 데이터 항목에는 두 가지 타임스탬프가 유지된다: 읽기 타임스탬프(R-timestamp): 해당 데이터를 성공적으로 읽은 트랜잭션 중 가장 큰 타임스탬프 쓰기 타임스탬프(W-timestamp): 해당 데이터를 성공적으로 수정한 트랜잭션 중 가장 큰 타임스탬프 트랜잭션이 데이터를 읽거나 쓰려고 할 때, 다음과 같은 규칙이 적용된다: 읽기 연산: 트랜잭션의 타임스탬프가 데이터의 쓰기 타임스탬프보다 작으면 연산이 거부되고 트랜잭션은 롤백된다. 쓰기 연산: 트랜잭션의 타임스탬프가 데이터의 읽기 또는 쓰기 타임스탬프보다 작으면 연산이 거부되고 트랜잭션은 롤백된다. 장점 교착 상태(Deadlock) 방지: 락을 사용하지 않기 때문에 교착 상태가 발생하지 않는다. 대기 시간 감소: 트랜잭션이 다른 트랜잭션을 기다리지 않고 바로 실행된다. 높은 동시성: 여러 트랜잭션이 동시에 실행될 수 있어 시스템의 처리량이 향상된다. 단점 롤백 가능성 증가: 충돌이 감지되면 트랜잭션이 롤백되어야 하므로, 시스템 부하가 높을 때 롤백 빈도가 증가할 수 있다. 연쇄 롤백: 하나의 트랜잭션 롤백이 다른 트랜잭션의 롤백을 유발할 수 있다. 오버헤드: 각 데이터 항목에 대해 타임스탬프를 유지하고 관리해야 하므로 추가적인 저장 공간과 처리 시간이 필요하다. Timestamp-Checked 방식은 특히 읽기 작업이 많고 쓰기 충돌이 적은 환경에서 효과적이다. 그러나 높은 동시성 환경에서는 롤백으로 인한 성능 저하를 주의해야 한다. 따라서 시스템의 특성과 요구사항을 고려하여 적절한 동시성 제어 방식을 선택해야 한다.
...