Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Copy link
Copy Markdown
Owner

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를 별도로 구현해야합니다

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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ public static void main(String[] args) {
SpringApplication.run(TecheerPartnersApiSessionApplication.class, args);
}

}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GlobalExceptionHandler를 사용하여 상위 레벨에서 예외를 처리하는 방식은 좋은 설계 방식이라고 생각합니다. 현재는 IllegalArgumentException만을 처리하고있지만 커스텀 예외 클래스를 더 추가해준다면 해당 클래스의 이점이 더 살아날 것 같습니다.
다만 패키지의 위치는 조금 애매해 보입니다. 전역적인 예외처리를 담당하는 만큼 별도의 exception이나 error 패키지를 두는 것이 좋을 것 같습니다 👍

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;
}
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The 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);
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The 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);
}