생성되는 API 와 오류 응답
@expose 엔티티의 API
기본 경로는 /api/<도메인>/<엔티티> - 엔티티 이름의 첫 글자를 소문자로(PurchaseOrder 는 /api/shop/purchaseOrder).
env.yml 의 apiPath 가 /api 를 정합니다.
| 요청 | 하는 일 |
|---|---|
POST / 필드와 함께 | 생성. 키가 채워진 행을 답함 |
GET /{key} | 한 건 조회. 없으면 404 |
PUT / 키는 본문에 | 수정 (@editable 루트나 필드) |
DELETE /{key} | 삭제. true 를 답함 |
POST /search {필드: 값, "pageNumber": 0, "pageSize": 20, "sortCode": "title+"} | 페이지: content, totalElements, totalPages, number |
PUT /batch 목록 | 한 트랜잭션에서 여러 건 수정 - 행마다 update(dto) 를 거침 |
POST /delete 키 목록 | 여러 건 삭제. true/false 목록 |
GET /schema/info | 범용 화면용 필드 설명 |
POST /{key}/<명령> | 생명주기 이동이나 업무 명령 (DDD 번들) |
POST /signin, POST /signout, GET /me | @signin 이 있을 때 |
목록용 GET 은 없습니다. 컨트롤러는 모든 동작을 springdoc 문서로 남깁니다(/swagger-ui.html, /v3/api-docs).
@expose(methods="...") - 제공할 엔드포인트
3.0.261105-rc10부터. 컨트롤러가 제공할 것을 나열합니다. 적지 않은 것은 생성하지 않습니다.
Receipt:
meta: entity @root @expose(20, methods="read,search") # 애플리케이션이 쓰고, HTTP 로는 조회만
| 이름 | 엔드포인트 |
|---|---|
create | POST / |
read | GET /{key} |
search | POST /search |
update | PUT /, PUT /batch |
delete | DELETE /{key}, POST /delete |
GET /schema/info, 명령, 로그인은 남습니다. TypeScript 클라이언트는 적은 것만 부르고, 유스케이스(헥사고날, DDD)는
적은 쓰기만 약속합니다. 서비스는 모든 메서드를 두므로 애플리케이션은 계속 쓸 수 있습니다. methods 가 없으면 위 표 전체입니다.
모르는 이름은 빌드 경고이고 아무것도 제공하지 않습니다 - 오타는 엔드포인트를 줄일 뿐 늘리지 않습니다. 나중에 목록을
줄이면 고치지 않았거나 다른 곳만 고친 컨트롤러에서 엔드포인트가 빠지고, 빠질 엔드포인트를 고친 컨트롤러는 그대로 남으며
빌드가 아직 제공하는 것을 짚어 줍니다.
@restExclude(...) - deprecated
@expose(methods) 로 대체되었습니다. 계속 적용되고, 빌드가 같은 엔드포인트가 되는 methods 목록을 - 그런 목록이
없으면(batchUpdate 없는 update) 가장 가까운 것과 달라지는 점을 - 알려 줍니다.
서비스·컨트롤러·유스케이스·TypeScript 클라이언트에서 메서드를 뺍니다. 이름은 서비스의 save, update,
batchUpdate, delete(목록), deleteById, updateBySpecification 과 컨트롤러의 create, read, search, update,
batchUpdate, deleteBatch(POST /delete), deleteById, schemaInfo 입니다. v3 에서는 뺀 메서드를 부르는 것도 함께
빠져 컴파일이 깨지지 않습니다: update 는 batchUpdate 를, deleteById 는 delete 와 deleteBatch 를, delete 는
deleteBatch 를, save 는 create 를 데려갑니다. Kotlin 서비스는 메서드를 그대로 두고 컨트롤러와 유스케이스만 따릅니다.
2.x 호환 출력은 적힌 대로만 뺍니다.
v3 쓰기 경로가 확인하는 것
jpa-springboot-v3, ddd-springboot, hexagonal-springboot 와 Kotlin 판의 코어 서비스는 쓰기 전에 확인합니다(2.x 호환
jpa-springboot 는 바뀌지 않음):
| 경우 | 동작 | 응답 |
|---|---|---|
| 이미 있는 키로 생성 | 거절, 덮어쓰지 않음 | 409 |
보낸 version 이 행의 것과 다른 수정 | 거절 (낙관적 잠금) | 409 |
수정 값이 제약 위반 (@Size ...) | 거절 - 보낸 필드만 검사 | 400 |
| 없는 행 수정 | EntityNotFoundException | 404 |
| 없는 행을 가리키는 다른 애그리거트의 키 (DDD) | reference.<Entity>.<relation> | 404 Member 99 does not exist |
지금 상태로는 할 수 없는 명령·삭제 (states:) | state.... | 409 |
| 다른 불변식 위반 | InvariantViolationException | 422 |
| 손으로 쓴 규칙이 거절 | 아래 런타임 예외 | 400 / 401 / 403 / 404 / 409 / 502 |
DDD 번들의 매퍼는 @jpa:Version 과 Spring Data 감사 필드를 요청에서 쓰지 않습니다: 응답에는 나가므로 클라이언트가
버전을 되보내지만, 요청으로 감사 기록을 위조할 수는 없습니다.
오류 응답: application/problem+json
모든 v3 번들은 노출된 엔티티가 있는 도메인마다 컨트롤러 패키지에 XxxApiErrors(@RestControllerAdvice, RFC 9457
ProblemDetail)를 하나 생성합니다 - 한 번만 생성이라 응답 형식은 개발자 것입니다. 이 파일은 런타임의
RequestShapeErrors 도 가져와, Spring MVC 가 컨트롤러를 부르기 전에 거절한 요청을 애플리케이션 전체에서 같은 모양으로
답합니다.
HTTP/1.1 409
Content-Type: application/problem+json
{ "title": "Not allowed in this state",
"detail": "PurchaseOrder cannot cancel when status is SHIPPED; it must be [PLACED]",
"code": "state.PurchaseOrder.status.cancel", "status": 409 }
필드 검증의 400 에는 errors("필드: 메시지")와 fieldErrors 가 있습니다:
{ "status": 400, "code": "invalid",
"fieldErrors": [{ "field": "lines", "code": "Size", "message": "the cart is empty" }] }