-
Notifications
You must be signed in to change notification settings - Fork 18
[H팀 임재영] 백엔드 API 과제 제출 #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| package com.example.techeer_partners_api_session.Service; | ||
|
|
||
| import com.example.techeer_partners_api_session.dto.TaskRequestDto; | ||
| import com.example.techeer_partners_api_session.entity.Task; | ||
| import com.example.techeer_partners_api_session.repository.TaskRepository; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @Service | ||
| public class TaskService { | ||
|
|
||
| private final TaskRepository taskRepository; | ||
|
|
||
| public TaskService(TaskRepository taskRepository){ | ||
| this.taskRepository = taskRepository; | ||
| } | ||
|
|
||
| public void createTask(TaskRequestDto dto){ | ||
| Task task = new Task(dto.getTitle()); // 할 일 객체 생성 | ||
| taskRepository.save(task); // 객체 저장 | ||
| } | ||
|
|
||
| public List<TaskRequestDto> getAllTasks() { // 전체 할 일 조회 | ||
| List<Task> tasks = taskRepository.findAll(); | ||
| // Task 객체를 TaskRequestDto로 변환하여 반환 | ||
| return tasks.stream() | ||
| .map(Task::toRequestDto) | ||
| .toList(); | ||
| } | ||
|
|
||
| public List<TaskRequestDto> getTasksByIsDoneStatus(boolean isDone) { // 할 일 상태 조회 | ||
| List<Task> tasks = taskRepository.findByIsDone(isDone); | ||
| // Task 객체를 TaskRequestDto로 변환하여 반환 | ||
| return tasks.stream() | ||
| .map(Task::toRequestDto) | ||
| .toList(); | ||
| } | ||
|
|
||
| public TaskRequestDto updateTask(Long id, TaskRequestDto dto) { | ||
| // 1. Task 찾기 | ||
| Task task = taskRepository.findById(id) | ||
| .orElseThrow(() -> new IllegalArgumentException("해당 ID의 할 일이 존재하지 않습니다.")); | ||
|
|
||
| // 2. Task 수정 | ||
| if (dto.getTitle() != null) { | ||
| task.setTitle(dto.getTitle()); | ||
| } | ||
| task.setIsDone(dto.getIsDone()); // true/false 업데이트 | ||
|
|
||
| // 3. 저장 후 반환 | ||
| taskRepository.save(task); | ||
| return task.toRequestDto(); | ||
| } | ||
|
|
||
| public void deleteTask(Long id) { | ||
| Task task = taskRepository.findById(id) | ||
| .orElseThrow(() -> new IllegalArgumentException("해당 ID의 할 일이 존재하지 않습니다.")); | ||
| taskRepository.delete(task); | ||
| } | ||
| } |
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GlobalExceptionHandler를 사용하여 상위 레벨에서 예외를 처리하는 방식은 좋은 설계 방식이라고 생각합니다. 현재는 IllegalArgumentException만을 처리하고있지만 커스텀 예외 클래스를 더 추가해준다면 해당 클래스의 이점이 더 살아날 것 같습니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package com.example.techeer_partners_api_session.controller; | ||
|
|
||
| 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 java.util.HashMap; | ||
| import java.util.Map; | ||
|
|
||
| @ControllerAdvice | ||
| public class GlobalExceptionHandler { | ||
|
|
||
| // IllegalArgumentException 처리 | ||
| @ExceptionHandler | ||
| public ResponseEntity<Map<String, String>> handleIllegalArgumentException(IllegalArgumentException ex){ | ||
| Map<String, String> response = new HashMap<>(); | ||
| response.put("error", ex.getMessage()); // 에러 메시지 | ||
| return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); // 404 상태 코드 | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| package com.example.techeer_partners_api_session.controller; | ||
|
|
||
| import com.example.techeer_partners_api_session.Service.TaskService; | ||
| import com.example.techeer_partners_api_session.dto.TaskRequestDto; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.*; | ||
|
|
||
| import java.util.HashMap; | ||
| import java.util.LinkedHashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/tasks") // 상위 경로 | ||
| public class TaskController { | ||
| private final TaskService taskService; | ||
|
|
||
| public TaskController(TaskService taskService){ | ||
| this.taskService = taskService; | ||
| } | ||
|
|
||
| @PostMapping | ||
| public ResponseEntity<Map<String, String>> createTask (@RequestBody TaskRequestDto dto) { | ||
| taskService.createTask(dto); | ||
| Map<String, String> response = new HashMap<>(); | ||
| response.put("message", "할 일이 성공적으로 생성되었습니다."); | ||
| return ResponseEntity.status(201).body(response); | ||
| } | ||
|
|
||
| // 1. 전체 할 일 조회 | ||
| @GetMapping | ||
| public ResponseEntity<Map<String, Object>> getAllTasks() { | ||
| List<TaskRequestDto> tasks = taskService.getAllTasks(); | ||
|
|
||
| // 응답 데이터 구성 | ||
| Map<String, Object> response = new LinkedHashMap<>(); | ||
| response.put("status", "success"); | ||
| response.put("message", "모든 일이 조회되었습니다."); | ||
| response.put("data", tasks); | ||
|
|
||
| // 201 상태 코드와 함께 DTO 반환 | ||
| return ResponseEntity.status(201).body(response); | ||
| } | ||
|
|
||
| // 2. 완료된 할 일 조회 | ||
| @GetMapping("/completed") // 하위 경로 | ||
| public ResponseEntity<Map<String, Object>> getIsDoneTasks() { | ||
| List<TaskRequestDto> isDoneTasks = taskService.getTasksByIsDoneStatus(true); | ||
|
|
||
| // 응답 데이터 구성 | ||
| Map<String, Object> response = new LinkedHashMap<>(); | ||
| response.put("status", "success"); | ||
| response.put("message", "완료 된 일이 조회되었습니다."); | ||
| response.put("data", isDoneTasks); | ||
|
|
||
| // 201 상태 코드와 함께 DTO 반환 | ||
| return ResponseEntity.ok(response); | ||
| } | ||
|
|
||
| // 3. 미완료된 할 일 조회 | ||
| @GetMapping("/uncompleted") // 하위 경로 | ||
| public ResponseEntity<Map<String, Object>> getNotIsDoneTasks() { | ||
| List<TaskRequestDto> notIsDoneTasks = taskService.getTasksByIsDoneStatus(false); | ||
|
|
||
| // 응답 데이터 구성 | ||
| Map<String, Object> response = new LinkedHashMap<>(); | ||
| response.put("status", "success"); | ||
| response.put("message", "미완료 된 일이 조회되었습니다."); | ||
| response.put("data", notIsDoneTasks); | ||
|
|
||
| // 201 상태 코드와 함께 DTO 반환 | ||
| return ResponseEntity.ok(response); | ||
| } | ||
|
|
||
| @PatchMapping("/{id}") | ||
| public ResponseEntity<Map<String, Object>> updateTask(@PathVariable Long id, @RequestBody TaskRequestDto dto) { | ||
| TaskRequestDto updatedTask = taskService.updateTask(id, dto); | ||
|
|
||
| Map<String, Object> response = new LinkedHashMap<>(); | ||
| response.put("status", "success"); | ||
| response.put("message", "할 일이 성공적으로 수정되었습니다."); | ||
| response.put("data", updatedTask); | ||
|
|
||
| return ResponseEntity.ok(response); | ||
| } | ||
|
|
||
| @DeleteMapping("/{id}") | ||
| public ResponseEntity<Map<String, String>> deleteTask(@PathVariable Long id) { | ||
| taskService.deleteTask(id); | ||
| Map<String, String> response = new HashMap<>(); | ||
| response.put("message", "할 일이 성공적으로 삭제되었습니다."); | ||
| return ResponseEntity.ok(response); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package com.example.techeer_partners_api_session.dto; | ||
|
|
||
|
|
||
| public class TaskRequestDto { | ||
|
|
||
| private final String title; | ||
|
|
||
| private final boolean isDone; | ||
|
|
||
| public TaskRequestDto(String title, boolean isDone){ | ||
| this.title = title; | ||
| this.isDone = isDone; | ||
| } | ||
|
|
||
| public String getTitle() { | ||
| return title; | ||
| } | ||
|
|
||
| public boolean getIsDone() { | ||
| return isDone; | ||
| } | ||
| } |
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 현재 Task 클래스에서 setTitle과 setIsDone을 통해 외부에서 필드값을 직접 수정하도록 하고있습니다. 단순한 프로젝트에서 해당 방식은 문제가 되지 않지만 객체 지향적인 설계를 지향한다고 했을 때 개선이 필요한 부분이기도합니다. set 메서드는 상태를 무분별하게 변경할 수 있는 위험을 내포하고 있으므로 지양하는 것이 바람직합니다. 간단한 구조더라도 update와 같은 메서드의 도입을 통해 상태 변경을 별도로 관리하려는 습관을 들여보면 좋을 것 같습니다. 👍 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| package com.example.techeer_partners_api_session.entity; | ||
|
|
||
| import com.example.techeer_partners_api_session.dto.TaskRequestDto; | ||
| import jakarta.persistence.*; | ||
|
|
||
| @Entity | ||
| public class Task { | ||
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| private Long id; | ||
|
|
||
| @Column(nullable = false) | ||
| private String title; | ||
|
|
||
| private boolean isDone = false; | ||
|
|
||
| public Task() {} // JPA의 엔티티 클래스 접근을 위한 기본 생성자 | ||
|
|
||
| public Task(String title){ // 할 일 제목 생성 | ||
| this.title = title; | ||
| } | ||
|
|
||
| // Task 객체를 TaskRequestDto로 변환 | ||
| public TaskRequestDto toRequestDto() { | ||
| return new TaskRequestDto(this.title, this.isDone); | ||
| } | ||
|
|
||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 내부의 dto 변환 메서드를 통해 getter를 우회하신 것은 좋습니다 👍 |
||
| public void setTitle(String title) { | ||
| this.title = title; | ||
| } | ||
|
|
||
| public void setIsDone(boolean done) { | ||
| this.isDone = done; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.example.techeer_partners_api_session.repository; | ||
|
|
||
| import com.example.techeer_partners_api_session.entity.Task; | ||
| import org.springframework.data.jpa.repository.JpaRepository; | ||
| import org.springframework.stereotype.Repository; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @Repository | ||
| public interface TaskRepository extends JpaRepository<Task, Long> { | ||
| List<Task> findByIsDone(boolean isDone); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RequestDto는 보통 Request Body의 형식에 맞춰 구성하게 됩니다. 그러나 현재 반환에도 RequestDto가 사용되고 있습니다.
Response Body 형식에 맞는 별도의 dto를 구현하고 그것을 반환에 이용하는 것이 맞는 방식이 될 것 같습니다.
ex) 현재 TaskRequestDto에는 id 값을 포함하고있지 않아 추후 클라이언트측에서 path parameter에 필요한 id 값을 넣어 요청을 보낼 수 없을 것 같습니다. id가 포함된 TaskResponseDto를 별도로 구현해야합니다