본문 바로가기
Spring/10. rest-api

REST API

by 989898 2025. 3. 14.

1. 자원(Resource) 설계

자원은 REST API에서 다루는 데이터나 엔터티를 의미합니다. 자원을 명확히 식별하기 위해 URI를 사용합니다.

핵심 원칙

  • 자원 이름은 복수형 명사 사용
    • 예시: /users, /posts, /comments
  • 자원 계층 구조 명확히 표현
    • 예시: /blogs/{blogId}/posts/{postId}
  • 직관적이고 이해하기 쉬운 URI 사용
    • 예시:
      • 모든 사용자 조회: GET /users
      • 특정 사용자 조회: GET /users/{id}

URI 설계 시 주의사항

  • 슬래시(/)는 계층 구조를 나타냅니다.
    • 올바른 예: /blogs/1/posts/100
    • 잘못된 예: /blogs/1/posts/100/
  • 하이픈(-)을 사용하여 가독성을 높입니다.
    • 올바른 예: /blog-members
    • 밑줄(_)은 사용하지 않습니다.
  • 소문자만 사용합니다. (대소문자 구분됨)
    • 올바른 예: /members, 잘못된 예: /Members
  • 파일 확장자는 포함하지 않습니다.
    • 잘못된 예: /photo.jpg
    • 올바른 예: Accept 헤더 사용 (Accept: image/jpeg)

코드 예시 (Spring Boot)

@RestController
@RequestMapping("/api/v1/users")
public class UserController {

    // 모든 사용자 조회 (Collection)
    @GetMapping
    public List<User> getUsers() {
        return userService.getAllUsers();
    }

    // 특정 사용자 조회
    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        if(user == null){
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(user);
    }
}
 

HTTP 메서드 활용

HTTP 메서드는 자원에 대한 행위를 정의합니다.

| 메서드 | 역할 | 의미 |
|--------|-------------------|
| GET | 자원 조회 |
| POST | 자원 생성 |
| PUT | 자원 전체 수정 |
| PATCH | 자원의 일부 수정 |
| DELETE | 자원 삭제 |

각 메서드의 실습 코드 예시 (Spring Boot)

@RestController
@RequestMapping("/api/v1/users")
public class UserController {

    // GET: 모든 사용자 조회
    @GetMapping
    public List<User> getAllUsers() {
        return userService.findAll();
    }

    // GET: 특정 사용자 조회
    @GetMapping("/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        if(user == null){
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(user);
    }

    // POST: 새 사용자 생성
    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User newUser = userService.save(user);
        return ResponseEntity.status(HttpStatus.CREATED).body(newUser);
    }

    // PUT: 사용자 전체 정보 수정(덮어쓰기)
    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
        User existingUser = userService.findById(id);
        if(existingUser == null){
            return ResponseEntity.notFound().build();
        }
        user.setId(id);
        User updatedUser = userService.save(user);
        return ResponseEntity.ok(updatedUser);
    }

    // PATCH: 사용자 정보 일부 수정
    @PatchMapping("/{id}")
    public ResponseEntity<User> partialUpdate(@PathVariable Long id, @RequestBody Map<String, Object> updates) {
        User updatedUser = userService.partialUpdate(id, updates);
        if(updatedUser == null){
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(updatedUser);
    }

    // DELETE: 사용자 삭제
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        boolean deleted = userService.delete(id);
        if(!deleted){
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.noContent().build();
    }
}
 

상태 코드(Status Codes)

HTTP 상태 코드는 API 응답 결과를 명확히 전달하는 중요한 요소입니다.

주요 상태 코드 정리

| 상태 코드 | 의미 | 상황 |
|-----------|------|
| 200 OK | 요청 성공적으로 처리됨 (주로 GET 요청 성공 시) |
| 201 Created | 새 자원이 성공적으로 생성됨 |
| 204 No Content | 삭제 성공 또는 응답 본문 없음 |
| 400 Bad Request | 클라이언트의 요청이 잘못됨 |
| 401 Unauthorized | 인증되지 않은 요청 |
| 403 Forbidden | 권한 부족으로 접근 불가 |
| 404 Not Found | 리소스를 찾을 수 없음 |
| 405 Method Not Allowed | 허용되지 않은 HTTP 메서드 사용 |
| 409 Conflict | 요청이 서버 상태와 충돌할 때 |
| 500 Internal Server Error | 서버 내부 오류 |

상태 코드 활용 코드 예시

@RestController
@RequestMapping("/api/v1/users")
public class UserController {

    @GetMapping("/{id}")
    public ResponseEntity<?> getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        
        if (user == null) {
            Map<String, Object> error = new HashMap<>();
            error.put("status", "error");
            error.put("message", "User not found");
            error.put("error_code", 404);
            return new ResponseEntity<>(error, HttpStatus.NOT_FOUND); //404 Not Found
        }

        return ResponseEntity.ok(user); //200 OK
    }

    @PostMapping
    public ResponseEntity<?> createUser(@RequestBody User user) {
        if(user.getEmail() == null) {
            Map<String, Object> error = new HashMap<>();
            error.put("status", "error");
            error.put("message", "Email is required");
            error.put("error_code", 400);
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
        }

        User created = userService.save(user);
        
        return ResponseEntity.status(HttpStatus.CREATED).body(createdUser); //201 Created
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
       boolean deleted = userService.delete(id);

       if(!deleted){
           return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
       }
       
       return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); //204 No Content
   }
}
 

위의 코드에서:

  • 성공적인 조회는 200 OK
  • 생성 성공은 201 Created
  • 삭제 성공은 204 No Content
  • 클라이언트 오류는 400 Bad Request, 404 Not Found 등 적절한 상태코드를 반환합니다.

위의 세 가지 챕터(자원 설계, HTTP 메서드 활용, 상태 코드 활용)를 충분히 이해하고 위의 코드들을 실습해보면 REST API의 기본적인 CRUD 작업 구현 및 API 설계를 충분히 이해하고 스스로 구현할 수 있게 됩니다.