diff --git a/back/src/main/java/com/back/domain/roadmap/task/controller/TaskController.java b/back/src/main/java/com/back/domain/roadmap/task/controller/TaskController.java index a51cf520..27e0c108 100644 --- a/back/src/main/java/com/back/domain/roadmap/task/controller/TaskController.java +++ b/back/src/main/java/com/back/domain/roadmap/task/controller/TaskController.java @@ -9,6 +9,7 @@ import com.back.global.rq.Rq; import com.back.global.rsData.RsData; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -23,6 +24,7 @@ @RestController @RequestMapping("/tasks") @RequiredArgsConstructor +@Tag(name = "TaskController", description = "Task 컨트롤러") public class TaskController { private final TaskService taskService; private final Rq rq; diff --git a/back/src/main/java/com/back/domain/roadmap/task/entity/Task.java b/back/src/main/java/com/back/domain/roadmap/task/entity/Task.java index c31fd4a3..7417f306 100644 --- a/back/src/main/java/com/back/domain/roadmap/task/entity/Task.java +++ b/back/src/main/java/com/back/domain/roadmap/task/entity/Task.java @@ -4,27 +4,31 @@ import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; +import java.util.ArrayList; import java.util.List; @Entity @Table(name = "task") -@Getter @Setter +@Getter @NoArgsConstructor public class Task extends BaseEntity { @Column(name = "name", nullable = false, unique = true) private String name; @OneToMany(mappedBy = "task", cascade = CascadeType.ALL) - private List aliases; + private List aliases = new ArrayList<>(); public Task(String name) { this.name = name; + this.aliases = new ArrayList<>(); } public void addAlias(TaskAlias alias) { + if (aliases == null) { + aliases = new ArrayList<>(); + } aliases.add(alias); - alias.setTask(this); + alias.linkToTask(this); } } diff --git a/back/src/main/java/com/back/domain/roadmap/task/entity/TaskAlias.java b/back/src/main/java/com/back/domain/roadmap/task/entity/TaskAlias.java index f0ae86d5..14074abd 100644 --- a/back/src/main/java/com/back/domain/roadmap/task/entity/TaskAlias.java +++ b/back/src/main/java/com/back/domain/roadmap/task/entity/TaskAlias.java @@ -4,11 +4,10 @@ import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; @Entity @Table(name = "task_alias") -@Getter @Setter +@Getter @NoArgsConstructor public class TaskAlias extends BaseEntity { @Column(name = "name", nullable = false, unique = true) @@ -22,4 +21,12 @@ public TaskAlias(String name) { this.name = name; this.task = null; // 기본적으로 연결된 Task가 없음 (pending 상태) } + + public void linkToTask(Task task) { + this.task = task; + } + + public boolean isPending() { + return this.task == null; + } } diff --git a/back/src/main/java/com/back/domain/roadmap/task/service/TaskService.java b/back/src/main/java/com/back/domain/roadmap/task/service/TaskService.java index e5755c0d..e3323c5f 100644 --- a/back/src/main/java/com/back/domain/roadmap/task/service/TaskService.java +++ b/back/src/main/java/com/back/domain/roadmap/task/service/TaskService.java @@ -34,7 +34,7 @@ public Task create(String name) { @Transactional public TaskAlias createAlias(Task task, String aliasName) { TaskAlias alias = new TaskAlias(aliasName); - alias.setTask(task); + alias.linkToTask(task); return taskAliasRepository.save(alias); } @@ -46,24 +46,10 @@ public List searchByKeyword(String keyword){ @Transactional public TaskAlias createPendingAlias(String taskName){ - // 1. 먼저 Alias가 존재하는지 확인 (Pending 포함) - Optional existingAliasOpt = taskAliasRepository.findByNameIgnoreCase(taskName); - if (existingAliasOpt.isPresent()) { - TaskAlias existingAlias = existingAliasOpt.get(); - if (existingAlias.getTask() != null) { - throw new ServiceException("400", "이미 등록된 task의 별칭입니다."); - } else { - throw new ServiceException("400", "이미 제안된 task입니다."); - } - } + // Task나 TaskAlias에 이미 존재하는 이름인지 검증 + validateNewPendingAliasName(taskName); - // 2. Alias가 없다면, Task 테이블에 직접 존재하는지 확인 - Optional existingTaskOpt = taskRepository.findByNameIgnoreCase(taskName); - if(existingTaskOpt.isPresent()){ - throw new ServiceException("400", "이미 등록된 task입니다."); - } - - // 3. 모두 해당 없으면 새로운 pending alias 생성 + // 모든 검증 통과 시 새로운 pending alias 생성 TaskAlias pendingAlias = new TaskAlias(taskName); return taskAliasRepository.save(pendingAlias); } @@ -80,41 +66,26 @@ public Page getPendingTaskAliases(Pageable pageable) { // Pending alias를 기존 Task와 연결 @Transactional public TaskAlias linkPendingAlias(Long aliasId, Long taskId) { - TaskAlias pendingAlias = taskAliasRepository.findById(aliasId) - .orElseThrow(() -> new ServiceException("404", "해당 별칭이 존재하지 않습니다.")); + TaskAlias pendingAlias = findPendingAliasById(aliasId); + Task task = findTaskById(taskId); - if (pendingAlias.getTask() != null) { - throw new ServiceException("400", "이미 연결된 별칭입니다."); - } - - Task task = taskRepository.findById(taskId) - .orElseThrow(() -> new ServiceException("404", "해당 Task가 존재하지 않습니다.")); - - pendingAlias.setTask(task); + pendingAlias.linkToTask(task); return taskAliasRepository.save(pendingAlias); } // Pending alias를 새로운 Task로 생성 @Transactional public Task createTaskFromPending(Long aliasId) { - TaskAlias pendingAlias = taskAliasRepository.findById(aliasId) - .orElseThrow(() -> new ServiceException("404", "해당 별칭이 존재하지 않습니다.")); - - if (pendingAlias.getTask() != null) { - throw new ServiceException("400", "이미 연결된 별칭입니다."); - } + TaskAlias pendingAlias = findPendingAliasById(aliasId); // 동일한 이름의 Task가 이미 존재하는지 확인 - Optional existingTask = taskRepository.findByNameIgnoreCase(pendingAlias.getName()); - if (existingTask.isPresent()) { - throw new ServiceException("400", "이미 존재하는 Task 이름입니다."); - } + validateTaskNameForCreation(pendingAlias.getName()); // 새 Task 생성 Task newTask = create(pendingAlias.getName()); // pending alias를 새 Task와 연결 - pendingAlias.setTask(newTask); + pendingAlias.linkToTask(newTask); taskAliasRepository.save(pendingAlias); return newTask; @@ -123,13 +94,52 @@ public Task createTaskFromPending(Long aliasId) { // Pending alias 삭제 @Transactional public void deletePendingAlias(Long aliasId) { - TaskAlias pendingAlias = taskAliasRepository.findById(aliasId) + TaskAlias pendingAlias = findPendingAliasById(aliasId); + taskAliasRepository.delete(pendingAlias); + } + + // === 검증 로직 메서드들 === + + // TaskAlias를 ID로 조회하고 Pending 상태인지 검증 + private TaskAlias findPendingAliasById(Long aliasId) { + TaskAlias alias = taskAliasRepository.findById(aliasId) .orElseThrow(() -> new ServiceException("404", "해당 별칭이 존재하지 않습니다.")); - if (pendingAlias.getTask() != null) { - throw new ServiceException("400", "연결된 별칭은 삭제할 수 없습니다."); + if (!alias.isPending()) { + throw new ServiceException("400", "이미 연결된 별칭입니다."); } - taskAliasRepository.delete(pendingAlias); + return alias; + } + + // Task를 ID로 조회 + private Task findTaskById(Long taskId) { + return taskRepository.findById(taskId) + .orElseThrow(() -> new ServiceException("404", "해당 Task가 존재하지 않습니다.")); + } + + // Task와 TaskAlias 모두 중복 검증 (새로운 pending alias 생성 시 사용) + private void validateNewPendingAliasName(String taskName) { + // 1. TaskAlias 테이블에서 중복 확인 (Pending 포함) + Optional existingAliasOpt = taskAliasRepository.findByNameIgnoreCase(taskName); + if (existingAliasOpt.isPresent()) { + TaskAlias existingAlias = existingAliasOpt.get(); + if (!existingAlias.isPending()) { + throw new ServiceException("400", "이미 등록된 Task의 별칭입니다."); + } else { + throw new ServiceException("400", "이미 제안된 Task명입니다."); + } + } + + // 2. Task 테이블에서 중복 검증 + validateTaskNameForCreation(taskName); + } + + // 동일한 이름의 Task가 이미 존재하는지 확인 (pending alias를 새 Task로 등록할 때 사용) + private void validateTaskNameForCreation(String taskName) { + Optional existingTaskOpt = taskRepository.findByNameIgnoreCase(taskName); + if (existingTaskOpt.isPresent()) { + throw new ServiceException("400", "이미 등록된 Task명입니다."); + } } } diff --git a/back/src/test/java/com/back/domain/roadmap/task/controller/TaskControllerTest.java b/back/src/test/java/com/back/domain/roadmap/task/controller/TaskControllerTest.java index 89599a6d..3d415173 100644 --- a/back/src/test/java/com/back/domain/roadmap/task/controller/TaskControllerTest.java +++ b/back/src/test/java/com/back/domain/roadmap/task/controller/TaskControllerTest.java @@ -303,7 +303,7 @@ void t12() throws Exception { .andExpect(handler().methodName("createPendingAlias")) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.resultCode").value("400")) - .andExpect(jsonPath("$.msg").value("이미 등록된 task입니다.")); + .andExpect(jsonPath("$.msg").value("이미 등록된 Task명입니다.")); } @Test @@ -328,7 +328,7 @@ void t13() throws Exception { .andExpect(handler().methodName("createPendingAlias")) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.resultCode").value("400")) - .andExpect(jsonPath("$.msg").value("이미 등록된 task의 별칭입니다.")); + .andExpect(jsonPath("$.msg").value("이미 등록된 Task의 별칭입니다.")); } @Test @@ -356,7 +356,7 @@ void t14() throws Exception { .andExpect(handler().methodName("createPendingAlias")) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.resultCode").value("400")) - .andExpect(jsonPath("$.msg").value("이미 제안된 task입니다.")); + .andExpect(jsonPath("$.msg").value("이미 제안된 Task명입니다.")); } @Test diff --git a/back/src/test/java/com/back/domain/roadmap/task/service/TaskServiceTest.java b/back/src/test/java/com/back/domain/roadmap/task/service/TaskServiceTest.java index 748d3c7b..250a2c06 100644 --- a/back/src/test/java/com/back/domain/roadmap/task/service/TaskServiceTest.java +++ b/back/src/test/java/com/back/domain/roadmap/task/service/TaskServiceTest.java @@ -124,7 +124,7 @@ void t7() { // When & Then assertThatThrownBy(() -> taskService.createPendingAlias(existingTaskName)) .isInstanceOf(ServiceException.class) - .hasMessage("400 : 이미 등록된 task입니다."); + .hasMessage("400 : 이미 등록된 Task명입니다."); } @Test @@ -136,7 +136,7 @@ void t8() { // When & Then assertThatThrownBy(() -> taskService.createPendingAlias(existingAliasName)) .isInstanceOf(ServiceException.class) - .hasMessage("400 : 이미 등록된 task의 별칭입니다."); + .hasMessage("400 : 이미 등록된 Task의 별칭입니다."); } @Test @@ -149,7 +149,7 @@ void t9() { // When & Then assertThatThrownBy(() -> taskService.createPendingAlias(aliasName)) .isInstanceOf(ServiceException.class) - .hasMessage("400 : 이미 제안된 task입니다."); + .hasMessage("400 : 이미 제안된 Task명입니다."); } @Test @@ -274,20 +274,6 @@ void t17() { .hasMessage("400 : 이미 연결된 별칭입니다."); } - @Test - @DisplayName("Pending Alias를 새로운 Task로 생성 실패 - 동일한 이름의 Task 이미 존재") - void t18() { - // Given - TaskAlias pendingAlias = taskService.createPendingAlias("중복 task"); // 먼저 pending alias 생성 - taskService.create("중복 task"); // 그 다음에 동일한 이름의 Task 생성 - - - // When & Then - assertThatThrownBy(() -> taskService.createTaskFromPending(pendingAlias.getId())) - .isInstanceOf(ServiceException.class) - .hasMessage("400 : 이미 존재하는 Task 이름입니다."); - } - @Test @DisplayName("Pending Alias 삭제 - 성공") void t19() { @@ -322,7 +308,7 @@ void t21() { // When & Then assertThatThrownBy(() -> taskService.deletePendingAlias(pendingAlias.getId())) .isInstanceOf(ServiceException.class) - .hasMessage("400 : 연결된 별칭은 삭제할 수 없습니다."); + .hasMessage("400 : 이미 연결된 별칭입니다."); } @Test