ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • RestFul 하다?
    카테고리 없음 2024. 5. 4. 15:44

    이 글은

    • RestFul 하다 라는게 뭔지 설명하기 앞서서 Rest api가 뭔지 알아 보자

    REST API

    • Representational State Transfer의 약자
    • 소프트웨어 프로그램 아키텍처의 한 형식이다
    • www 와 같은 분산 하이퍼미디어 시스템을 위한 스포트웨어 개발 아키텍처의 한 형식
    • 기본적으로 웹의 기존 기술과 HTTP 프로토콜을 그대로 활용하기 때문에 웹의 장점을 최대한 활용할 수 있는 아키텍처 스타일 이다
    • HTTP URI를 통해 자원을 명시하고 HTTP Method를 통해 해당 자원에 대한 CRUD OPERATION을 적용하는 것을 의미한다
    • REST는 자원 기반의 구조 설계의 중심에 Resource가 있고 HTTP Method를 통해 Resource를 처리하도록 설계된 아키텍쳐를 의미한다

    HTTP Method

    • POST - 리소스를 생성
    • GET - 리소스를 조회
    • PUT - 리소스를 수정
    • DELETE - 리소스를 삭제

    REST의 구성

    • Resource(자원) - URL
    • Verb(행위) - Http Method
    • Representations(표현)

    RESTful 하다?

    • 일단 예제 코드이다
    • 잘못 된 코드들이다
    • 매핑 어노테이션에서 URI 부분을 수정해야한다
    @RestController  
    @RequestMapping("/task")  
    public class TaskController {  
        private final TaskService taskService;  
    
        public TaskController(TaskService taskService) {  
            this.taskService = taskService;  
        }  
    
        @GetMapping("/list")  
        public ResponseEntity<List<TaskInfoResponseDTO>> taskDtoList(@RequestParam("projectId") Long projectId) {  
            List<TaskInfoResponseDTO> taskListByProject = taskService.findTaskListByProject(projectId);  
    
            return ResponseEntity.ok().body(taskListByProject);  
        }  
    
        @GetMapping("/{taskId}")  
        public ResponseEntity<TaskDto> findTaskDto(@PathVariable("taskId") Long taskId) {  
            Optional<TaskDto> info = taskService.findTask(taskId);  
    
            return info.isPresent()  
                    ? ResponseEntity.ok().body(info.get())  
                    : ResponseEntity.status(HttpStatus.NOT_FOUND).build();  
        }  
    
        @PostMapping("/register")  
        public ResponseEntity<Void> createTask(@Valid @RequestBody TaskRequest taskRequest) {  
            boolean isProcessed = taskService.registerTaskAndTaskTag(taskRequest);  
    
            return isProcessed  
                    ? ResponseEntity.status(HttpStatus.CREATED).build()  
                    : ResponseEntity.status(HttpStatus.CONFLICT).build();  
        }  
    
        @PutMapping("/{taskId}/modify")  
        public ResponseEntity<Void> modifyTask(@PathVariable("taskId") Long taskId,  
                                               @Valid @RequestBody TaskRequest taskRequest) {  
            boolean isProcessed = taskService.modifyTask(taskId, taskRequest);  
    
            return isProcessed  
                    ? ResponseEntity.status(HttpStatus.OK).build()  
                    : ResponseEntity.status(HttpStatus.CONFLICT).build();  
        }  
    
        @DeleteMapping("/{taskId}/delete")  
        @ResponseStatus(HttpStatus.OK)  
        public void deleteTask(@PathVariable("taskId") Long taskId) {  
            taskService.deleteTask(taskId);  
        }  
    }

    1. 각 메소드의 기능은 HTTP 메소드에서 정의 되어 있다

    • 이미 저장하는 기능은 POST, 조회 GET, 수정 PUT, 삭제 DELETE 인데 URI 에 각각의 기능을 중복해서 적어 줄 필요가 없다
    • RESTful 정의에서 각 리소스에 대한 기능을 HTTP 메소드를 이용하여 일관되게 정의할 수 있어야 한다 의 의미가 이런 의미이다

    2. 객체명이 URI에 포함된다면 가급적이면 복수형을 사용해야한다

    • 관례적으로 객체명을 복수로 써 주는것이 좋다
    • 예를들어 위의 코드에서는 task -> tasks 이렇게 해야한다

    수정 코드

    @RestController  
    @RequestMapping("/tasks")  
    public class TaskController {  
        private final TaskService taskService;  
    
        public TaskController(TaskService taskService) {  
            this.taskService = taskService;  
        }  
    
        @GetMapping("/list")  
        public ResponseEntity<List<TaskInfoResponseDTO>> taskDtoList(@RequestParam("projectId") Long projectId) {  
            List<TaskInfoResponseDTO> taskListByProject = taskService.findTaskListByProject(projectId);  
    
            return ResponseEntity.ok().body(taskListByProject);  
        }  
    
        @GetMapping("/{taskId}")  
        public ResponseEntity<TaskDto> findTaskDto(@PathVariable("taskId") Long taskId) {  
            Optional<TaskDto> info = taskService.findTask(taskId);  
    
            return info.isPresent()  
                    ? ResponseEntity.ok().body(info.get())  
                    : ResponseEntity.status(HttpStatus.NOT_FOUND).build();  
        }  
    
        @PostMapping
        public ResponseEntity<Void> createTask(@Valid @RequestBody TaskRequest taskRequest) {  
            boolean isProcessed = taskService.registerTaskAndTaskTag(taskRequest);  
    
            return isProcessed  
                    ? ResponseEntity.status(HttpStatus.CREATED).build()  
                    : ResponseEntity.status(HttpStatus.CONFLICT).build();  
        }  
    
        @PutMapping("/{taskId}")  
        public ResponseEntity<Void> modifyTask(@PathVariable("taskId") Long taskId,  
                                               @Valid @RequestBody TaskRequest taskRequest) {  
            boolean isProcessed = taskService.modifyTask(taskId, taskRequest);  
    
            return isProcessed  
                    ? ResponseEntity.status(HttpStatus.OK).build()  
                    : ResponseEntity.status(HttpStatus.CONFLICT).build();  
        }  
    
        @DeleteMapping("/{taskId}")  
        @ResponseStatus(HttpStatus.OK)  
        public void deleteTask(@PathVariable("taskId") Long taskId) {  
            taskService.deleteTask(taskId);  
        }  
    }

    하지만

    • 기준이 다 다르고 확실하게 정의해 놓은 것 이 없다
    • RESTful 의 가장 중요한 것은 URI의 명확한 정의를 통해 실무에서 API 사용에 있어 원할한 소통이 가능해야 한다는 것이다

    이것 이외의 규칙들

    • "/" 슬래시를 통해 계층 관계를 표시하여 구분
    • "_ " 밑줄 사용 금지
    • URI 경로는 소문자
    • 파일 확장자는 포함 금지
    • 이미 HTTP METHOD 어노테이션을 사용함으로써 URI에 명시하지 않아도 된다 즉, CRUD 기능을 나타내는 것은 URI에 사용 X
    • 프로그래밍 언어의 Method명을 이용 금지
    • 명사 단수형 보단 복수형 ex) task -> tasks
      1. API에 있어서 서브 도메인은 일관성있게 사용해야함

    결론

    • 이러한 규칙들이 있지만 항상 잊지 않고 생각해야 하는건 URI의 명확한 정의를 통해 실무에서 API사용에 있어서 원할한 소통이 가능 해야한다
    • 이러한 규칙들이 있지만 그 팀이나 조직의 규칙에 맞게 따라야 한다
Designed by Tistory.