-
RESTful APIBackend 2025. 11. 21. 07:47
RESTful API 핵심 5줄 요약
- URL은 리소스 (명사), 동사 금지
- 행위는 HTTP 메서드
- 생성은 POST / 조회는 GET / 수정은 PATCH / 삭제는 DELETE
- 응답 코드는 200·201·204·400·404·409 잘 쓰기
- 응답은 DTO로 통일하고 에러는 공통 구조
리소스 네이밍 규칙
REST API에서는 기능 기준이 아니라 리소스 기준으로 URL을 설계한다.
URL에는 동사를 사용하지 않고 명사 복수형을 사용한다.주문 기능 → orders
상점 기능 → stores
메뉴 기능 → menus
쿠폰 기능 → coupons
라이더/배달기사 → riders예시
- 주문 생성
- POST /orders
- Body: 주문 항목, 상점 ID, 주소 등
- 응답: 생성된 주문 정보 + 201 Created
- 주문 상세 조회
GET /orders/{orderId}
- 주문 목록 조회 (필터 + 페이징)
GET /orders?status=DELIVERED&from=2025-11-01&to=2025-11-30&page=0&size=20- 주문 상태 변경 (부분 업데이트)
PATCH /orders/{orderId}
Body: { "status": "CANCELLED" }
- 상점 목록 조회 (카테고리/정렬)
GET /stores?category=CHICKEN&sort=rating,desc&page=0&size=20- 상점의 메뉴 목록 조회
GET /stores/{storeId}/menus- 쿠폰 적용 시 예상 결제 금액 조회
POST /orders/price-preview
Body: 주문 항목 + 쿠폰 정보
응답: { "originalPrice": 23000, "discountedPrice": 18000 }- 특정 상점의 리뷰 목록 조회
GET /stores/{storeId}/reviews?page=0&size=10
- 리뷰 작성
- 주문 기준으로 리뷰 작성
POST /orders/{orderId}/reviews
- 관리자용 주문 검색
GET /admin/orders?storeId=1&status=PAID&from=...&to=...&page=0&size=50→ 전부 복수형 명사 + 쿼리 파라미터로 필터 + 동사 없는 URL = RESTful한 패턴.
예외 처리 기준
| IllegalArgumentException 사용하는 경우
도메인 객체 내부에서 순수 자바 로직 검증 시 사용한다.
즉, API와 무관한 순수 Java 예외 IllegalArgumentException을 사용한다.
예시
- Money 객체 생성 시 음수 금지
- Quantity 객체 생성 시 0 이하 금지
- Value Object 내부 유효성 검증
public class Quantity { public Quantity(int value) { if (value <= 0) { throw new IllegalArgumentException("Quantity must be > 0"); } this.value = value; } }| ApiException 사용하는 경우
API 요청 검증이나 비즈니스 룰 위반 시 사용한다.
예시
- 존재하지 않는 주문 ID
- 잘못된 주문 상태 변경
- 잘못된 enum 값
- 비즈니스 정책 위반
throw new ApiException(ErrorCode.ORDER_NOT_FOUND);REST API에서는 에러 코드, 메시지, HTTP 상태를 함께 전달해야 한다.
{ "code": "ORDER_NOT_FOUND", "message": "존재하지 않는 주문입니다.", "status": 404 }| 계층별 예외
도메인 계층
→ IllegalArgumentException 사용
→ HTTP와 무관한 순수 Java 검증서비스 계층
→ ApiException 사용
→ 비즈니스 로직 검증컨트롤러 계층
→ ApiException 사용
→ 클라이언트 응답 제어Validation
DTO 검증 → @Valid 사용
DTO 내부 필드 검증에 사용한다.
리스트 내부 객체까지 검증하려면 List에도 붙인다.
public ResponseEntity<?> createOrder( @Valid @RequestBody CreateOrderRequest request )@Getter @NoArgsConstructor @AllArgsConstructor public class CreateOrderRequest { @NotNull(message = "storeId는 필수 값입니다.") private Long storeId; @Size(min = 1, message = "주문 항목은 최소 1개 이상이어야 합니다.") @Valid private List<OrderItemRequest> orderItems; }메서드 파라미터 검증 → @Validated 사용
@PathVariable, @RequestParam 검증, 서비스 메서드에서도 사용한다.
@Validated는 반드시 클래스 레벨에 붙여야 동작한다.
@RestController @Validated public class CouponController { @GetMapping("/coupons/{id}") public ResponseEntity<?> getCoupon( @PathVariable @Min(1) Long id ) { return null; } }| @Valid vs @Validated
DTO 필드 검증 → @Valid 사용
DTO 내부 리스트 검증 → @Valid 사용
@PathVariable 검증 → @Validated 사용
@RequestParam 검증 → @Validated 사용
검증 그룹 사용 → @Validated 사용
서비스 메서드 → @Validated 사용
검증 그룹? 상황에 따라 검증 조건을 다르게 적용하는 기능이다.
예시
회원 생성 - 이메일 필수, 비밀번호 필수, ID 없음
회원 수정 - 이메일 선택, 비밀번호 선택, ID 필수
//검증그룹 만들기 public interface CreateGroup {} public interface UpdateGroup {} public class UserRequest { @NotNull(groups = UpdateGroup.class) private Long id; @NotBlank(groups = CreateGroup.class) private String email; @NotBlank(groups = CreateGroup.class) private String password; } //컨트롤러에서 회원 등록,수정 @PostMapping public void createUser( @Validated(CreateGroup.class) @RequestBody UserRequest request ) { } @PatchMapping public void updateUser( @Validated(UpdateGroup.class) @RequestBody UserRequest request ) { }서비스 메서드 → @Validated 사용
@Service @Validated // 여기 public class OrderService { public void createOrder(@Valid OrderCreateCommand command) { } } public class OrderCreateCommand { @NotNull private Long storeId; @Min(1) private int quantity; }| @PathVariable
@PathVariable은 URL 경로에 포함된 값을 변수로 추출한다.
예시
GET /orders/10@GetMapping("/orders/{orderId}") public OrderResponse getOrder(@PathVariable Long orderId) { }| @PathVariable vs @RequestParam
@PathVariable
→ URL 경로 값
→ 리소스 식별에 사용/users/3@GetMapping("/users/{id}") public User getUser(@PathVariable Long id)@RequestParam
→ Query String 값
→ 검색 조건이나 필터에 사용/users?page=2&size=20@GetMapping("/users") public User list(@RequestParam int page, @RequestParam int size)글로벌 예외 처리
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ApiException.class) public ResponseEntity<ErrorResponse> handleApiException(ApiException e) { ErrorCode error = e.getErrorCode(); return ResponseEntity .status(error.getStatus()) .body(new ErrorResponse(error.getCode(), error.getMessage())); } @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ErrorResponse> handleValidException(MethodArgumentNotValidException ex) { String message = ex.getBindingResult() .getFieldErrors() .get(0) .getDefaultMessage(); ErrorResponse body = new ErrorResponse("INVALID_INPUT", message); return ResponseEntity.badRequest().body(body); } @ExceptionHandler(ConstraintViolationException.class) public ResponseEntity<ErrorResponse> handleConstraintViolation(ConstraintViolationException ex) { String message = ex.getConstraintViolations() .iterator() .next() .getMessage(); ErrorResponse body = new ErrorResponse("INVALID_INPUT", message); return ResponseEntity.badRequest().body(body); } @ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleException(Exception ex) { ErrorResponse body = new ErrorResponse("INTERNAL_SERVER_ERROR", "알 수 없는 오류가 발생했습니다."); return ResponseEntity.internalServerError().body(body); } }HTTP 에러 코드 정리
코드 상황 예시 400 Bad Request 요청 값 자체가 잘못된 경우 사용한다. 필수 값 누락
유효성 실패
잘못된 enum 값
JSON 파싱 실패404 Not Found 요청한 리소스가 존재하지 않을 때 사용한다. 존재하지 않는 주문 조회
삭제된 쿠폰 조회
존재하지 않는 사용자 조회409 Conflict 리소스 상태 충돌 시 사용한다. 이미 취소된 주문 재취소
이미 사용된 쿠폰 재사용
중복 회원 가입
상태 변경 충돌500 Internal Server Error 서버 내부 오류 발생 시 사용한다. 코드 버그
NullPointerException
DB 장애
예상하지 못한 시스템 오류'Backend' 카테고리의 다른 글
github(비공개) gcm (0) 2026.04.28 [git] 깃 자주쓰는 명령어 (feat.gitignore, with Claude Code) (0) 2026.03.08 [git] 설치 및 협업 전략 설정 (0) 2026.03.04 기타메모 - json/conditionfiltering (0) 2025.11.22