
작년에 프로젝트를 진행하면서 배웠던 내용인데 다시한번 기억을 상기시키고 좀 더 심화해서 공부하기 위해 한번 정리해놓는게 좋겠다고 생각해서 정리하려고 한다. 여기서는 REST의 기본 특징, 설계 원칙, 그리고 실제 Spring Boot를 이용한 구현 예제를 알아보도록 한다.
보기 쉽게 표로 정리하자면

다음과 같다.
URI는 자원을 나타내는 명사를 사용합니다.
올바른 예: /users, /orders, /products
잘못된 예: /getUser, /createOrder (동작을 나타내는 동사 사용 금지)
자원 간의 관계를 계층적으로 표현합니다.
예: /users/{userId}/orders → 특정 사용자의 주문 목록
소문자 사용, 하이픈(-)이나 밑줄(_)을 통한 단어 구분, 불필요한 복잡성 배제
이부분은 상대적으로 다른 내용에 비해 쉬운 부분이다. get(자원조회), post(자원 생성), put(자원 전체 수정), patch(자원 부분 수정), delete(자원 삭제)가 있다.

200 OK: 요청 성공 (GET, PUT, PATCH)
201 Created: 새로운 자원 생성 성공 (POST)
204 No Content: 삭제나 업데이트 후 별도 응답 내용이 없을 때 (DELETE)
400 Bad Request: 잘못된 요청
401 Unauthorized: 인증 실패
403 Forbidden: 권한 없음
404 Not Found: 자원 없음
500 Internal Server Error: 서버 내부 오류
이 API가 잘 작동하는지 확인하고 싶을땐 POSTMAN이나 SWAGGER를 사용하면 확인할 수 있다.
JWT, OAuth2 등을 활용한 안전한 인증 방식을 도입한다.
모든 API 통신은 HTTPS를 사용하여 암호화 처리해야 한다.
클라이언트와 서버 도메인이 다를 경우 CORS 설정은 필수이다.
package com.example.demo.controller;
import com.example.demo.exception.ResourceNotFoundException;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
// GET /api/users
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.findAllUsers();
return ResponseEntity.ok(users);
}
// GET /api/users/{id}
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
User user = userService.findUserById(id)
.orElseThrow(() -> new ResourceNotFoundException("User with id " + id + " not found"));
return ResponseEntity.ok(user);
}
// POST /api/users
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User createdUser = userService.createUser(user);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
// PUT /api/users/{id}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User userDetails) {
User updatedUser = userService.updateUser(id, userDetails);
return ResponseEntity.ok(updatedUser);
}
// DELETE /api/users/{id}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
}
에러가 발생햇을 때 일관된 응답을 제공하기 위해 @ControllerAdvice를 활용한 전역 예외 처리 클래스를 작성한다.
package com.example.demo.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex,
HttpServletRequest request) {
ErrorResponse errorResponse = new ErrorResponse(
LocalDateTime.now(),
HttpStatus.NOT_FOUND.value(),
"Not Found",
ex.getMessage(),
request.getRequestURI()
);
return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
}
// 기타 예외에 대한 핸들러 추가 가능 (400, 500 등)
}
에러 응답 클래스의 예시는 다음과 같다.
package com.example.demo.exception;
import java.time.LocalDateTime;
public class ErrorResponse {
private LocalDateTime timestamp;
private int status;
private String error;
private String message;
private String path;
public ErrorResponse(LocalDateTime timestamp, int status, String error, String message, String path) {
this.timestamp = timestamp;
this.status = status;
this.error = error;
this.message = message;
this.path = path;
}
// Getter, Setter 생략
}
위 코드는 스프링 부트를 활용한 RESTful API예제로 이해하기 쉽게 설계되었다.