From 28f25345fe84a667668f925c3a9f9d0ba9a960f2 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Wed, 26 Nov 2025 23:41:19 +0900 Subject: [PATCH 01/10] =?UTF-8?q?docs:=201=EB=8B=A8=EA=B3=84=20=EC=9A=94?= =?UTF-8?q?=EA=B5=AC=EC=82=AC=ED=95=AD=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...24\352\265\254\354\202\254\355\225\255.md" | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 "docs/step1\354\232\224\352\265\254\354\202\254\355\225\255.md" diff --git "a/docs/step1\354\232\224\352\265\254\354\202\254\355\225\255.md" "b/docs/step1\354\232\224\352\265\254\354\202\254\355\225\255.md" new file mode 100644 index 0000000000..ed1a675dca --- /dev/null +++ "b/docs/step1\354\232\224\352\265\254\354\202\254\355\225\255.md" @@ -0,0 +1,20 @@ +# 문자열 사칙 연산 계산기 구현 +## 기능 요구사항 + +## 삭제 방식 변경 +- [ ] 질문 데이터를 완전히 삭제하지 않는다(soft delete) +- [ ] deleted 상태(boolean type)를 true로 변경한다 + +## 삭제 권한 검증 +- [ ] 로그인 사용자와 질문 작성자가 같은 경우에만 삭제 가능하다 +- [ ] 답변이 없는 경우 삭제가 가능하다 +- [ ] 질문자와 모든 답변 작성자가 같은 경우 삭제가 가능하다 +- [ ] 질문자와 답변자가 다른 경우 질문을 삭제할 수 없다 + +## 연관 데이터 처리 +- [ ] 질문을 삭제할 때 관련된 모든 답변도 함께 삭제한다 +- [ ] 답변의 삭제도 soft deled로 deleted 상태를 true로 변경한다 + +## 삭제 이력 관리 +- [ ] 질문 삭제 시 DeleteHistory를 생성하여 이력을 남긴다 +- [ ] 답변 삭제 시에도 DeleteHistory를 생성하여 이력을 남긴다 From dd20c191d3ae78a09ef9ea4f9d4165426f8a5319 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Thu, 27 Nov 2025 00:13:59 +0900 Subject: [PATCH 02/10] =?UTF-8?q?feat:=20=EC=A7=88=EB=AC=B8=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=EB=A5=BC=20soft=20delete=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...24\352\265\254\354\202\254\355\225\255.md" | 20 ++++---- src/main/java/nextstep/qna/domain/Answer.java | 5 ++ .../nextstep/qna/domain/DeleteHistory.java | 12 +++++ .../java/nextstep/qna/domain/Question.java | 36 +++++++++++++ .../nextstep/qna/domain/QuestionTest.java | 51 +++++++++++++++++++ 5 files changed, 114 insertions(+), 10 deletions(-) diff --git "a/docs/step1\354\232\224\352\265\254\354\202\254\355\225\255.md" "b/docs/step1\354\232\224\352\265\254\354\202\254\355\225\255.md" index ed1a675dca..7871c6d742 100644 --- "a/docs/step1\354\232\224\352\265\254\354\202\254\355\225\255.md" +++ "b/docs/step1\354\232\224\352\265\254\354\202\254\355\225\255.md" @@ -2,19 +2,19 @@ ## 기능 요구사항 ## 삭제 방식 변경 -- [ ] 질문 데이터를 완전히 삭제하지 않는다(soft delete) -- [ ] deleted 상태(boolean type)를 true로 변경한다 +- [x] 질문 데이터를 완전히 삭제하지 않는다(soft delete) +- [x] deleted 상태(boolean type)를 true로 변경한다 ## 삭제 권한 검증 -- [ ] 로그인 사용자와 질문 작성자가 같은 경우에만 삭제 가능하다 -- [ ] 답변이 없는 경우 삭제가 가능하다 -- [ ] 질문자와 모든 답변 작성자가 같은 경우 삭제가 가능하다 -- [ ] 질문자와 답변자가 다른 경우 질문을 삭제할 수 없다 +- [x] 로그인 사용자와 질문 작성자가 같은 경우에만 삭제 가능하다 +- [x] 답변이 없는 경우 삭제가 가능하다 +- [x] 질문자와 모든 답변 작성자가 같은 경우 삭제가 가능하다 +- [x] 질문자와 답변자가 다른 경우 질문을 삭제할 수 없다 ## 연관 데이터 처리 -- [ ] 질문을 삭제할 때 관련된 모든 답변도 함께 삭제한다 -- [ ] 답변의 삭제도 soft deled로 deleted 상태를 true로 변경한다 +- [x] 질문을 삭제할 때 관련된 모든 답변도 함께 삭제한다 +- [x] 답변의 삭제도 soft deled로 deleted 상태를 true로 변경한다 ## 삭제 이력 관리 -- [ ] 질문 삭제 시 DeleteHistory를 생성하여 이력을 남긴다 -- [ ] 답변 삭제 시에도 DeleteHistory를 생성하여 이력을 남긴다 +- [x] 질문 삭제 시 DeleteHistory를 생성하여 이력을 남긴다 +- [x] 답변 삭제 시에도 DeleteHistory를 생성하여 이력을 남긴다 diff --git a/src/main/java/nextstep/qna/domain/Answer.java b/src/main/java/nextstep/qna/domain/Answer.java index cf681811e7..d380b641d6 100644 --- a/src/main/java/nextstep/qna/domain/Answer.java +++ b/src/main/java/nextstep/qna/domain/Answer.java @@ -72,6 +72,11 @@ public void toQuestion(Question question) { this.question = question; } + public DeleteHistory delete(){ + this.deleted = true; + return new DeleteHistory(ContentType.QUESTION, id, writer, LocalDateTime.now()); + } + @Override public String toString() { return "Answer [id=" + getId() + ", writer=" + writer + ", contents=" + contents + "]"; diff --git a/src/main/java/nextstep/qna/domain/DeleteHistory.java b/src/main/java/nextstep/qna/domain/DeleteHistory.java index 43c37e5e5c..f450df8ac4 100644 --- a/src/main/java/nextstep/qna/domain/DeleteHistory.java +++ b/src/main/java/nextstep/qna/domain/DeleteHistory.java @@ -26,6 +26,18 @@ public DeleteHistory(ContentType contentType, Long contentId, NsUser deletedBy, this.createdDate = createdDate; } + public ContentType getContentType() { + return contentType; + } + + public Long getContentId() { + return contentId; + } + + public NsUser getDeletedBy() { + return deletedBy; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/nextstep/qna/domain/Question.java b/src/main/java/nextstep/qna/domain/Question.java index b623c52c76..320c7d4036 100644 --- a/src/main/java/nextstep/qna/domain/Question.java +++ b/src/main/java/nextstep/qna/domain/Question.java @@ -1,5 +1,6 @@ package nextstep.qna.domain; +import nextstep.qna.CannotDeleteException; import nextstep.users.domain.NsUser; import java.time.LocalDateTime; @@ -85,6 +86,41 @@ public List getAnswers() { return answers; } + public List delete(NsUser loginUser) throws CannotDeleteException { + if (!isOwner(loginUser)) { + throw new CannotDeleteException("질문을 삭제할 권한이 없습니다."); + } + + checkDeletableAnswers(loginUser); + + List deleteHistories = new ArrayList<>(); + deleteHistories.add(deleteQuestion()); + deleteHistories.addAll(deleteAnswers()); + + return deleteHistories; + } + + private void checkDeletableAnswers(NsUser loginUser) throws CannotDeleteException { + for (Answer answer : answers) { + if (!answer.isOwner(loginUser)) { + throw new CannotDeleteException("다른 사람의 답변이 존재하여 삭제할 수 없습니다."); + } + } + } + + private DeleteHistory deleteQuestion() { + this.deleted = true; + return new DeleteHistory(ContentType.QUESTION, id, writer, LocalDateTime.now()); + } + + private List deleteAnswers() { + List histories = new ArrayList<>(); + for (Answer answer : answers) { + histories.add(answer.delete()); + } + return histories; + } + @Override public String toString() { return "Question [id=" + getId() + ", title=" + title + ", contents=" + contents + ", writer=" + writer + "]"; diff --git a/src/test/java/nextstep/qna/domain/QuestionTest.java b/src/test/java/nextstep/qna/domain/QuestionTest.java index 3b87823963..b4e205752f 100644 --- a/src/test/java/nextstep/qna/domain/QuestionTest.java +++ b/src/test/java/nextstep/qna/domain/QuestionTest.java @@ -1,8 +1,59 @@ package nextstep.qna.domain; +import nextstep.qna.CannotDeleteException; import nextstep.users.domain.NsUserTest; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; public class QuestionTest { public static final Question Q1 = new Question(NsUserTest.JAVAJIGI, "title1", "contents1"); public static final Question Q2 = new Question(NsUserTest.SANJIGI, "title2", "contents2"); + + @Test + public void 답변없는질문_삭제() throws CannotDeleteException { + List deleteHistories = Q1.delete(NsUserTest.JAVAJIGI); + + assertThat(Q1.isDeleted()).isTrue(); + assertThat(deleteHistories).hasSize(1); + assertThat(deleteHistories.get(0).getContentType()).isEqualTo(ContentType.QUESTION); + } + + @Test + public void 질문자와_답변자가_같은경우_삭제() throws CannotDeleteException { + Answer answer1 = new Answer(11L, NsUserTest.JAVAJIGI, Q1, "answer1"); + Answer answer2 = new Answer(12L, NsUserTest.JAVAJIGI, Q1, "answer2"); + Q1.addAnswer(answer1); + Q1.addAnswer(answer2); + + List deleteHistories = Q1.delete(NsUserTest.JAVAJIGI); + + assertThat(Q1.isDeleted()).isTrue(); + assertThat(answer1.isDeleted()).isTrue(); + assertThat(answer2.isDeleted()).isTrue(); + assertThat(deleteHistories).hasSize(3); + } + + @Test + public void 다른사람의_답변이_있는경우_삭제() { + Question question = new Question(1L, NsUserTest.JAVAJIGI, "title", "contents"); + Answer answer = new Answer(11L, NsUserTest.SANJIGI, question, "answer"); + question.addAnswer(answer); + + assertThatThrownBy(() -> { + question.delete(NsUserTest.JAVAJIGI); + }).isInstanceOf(CannotDeleteException.class) + .hasMessageContaining("다른 사람의 답변이 존재하여 삭제할 수 없습니다."); + } + + @Test + public void 질문자가_아닌경우_삭제불가() { + assertThatThrownBy(() -> { + Q1.delete(NsUserTest.SANJIGI); + }).isInstanceOf(CannotDeleteException.class) + .hasMessageContaining("질문을 삭제할 권한이 없습니다."); + } } From 358644a26301581b1ed7329150aab38fa3f7cb10 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Thu, 27 Nov 2025 00:20:41 +0900 Subject: [PATCH 03/10] =?UTF-8?q?docs:=20=EB=A6=AC=ED=8C=A9=ED=84=B0?= =?UTF-8?q?=EB=A7=81=20=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...step1\354\232\224\352\265\254\354\202\254\355\225\255.md" | 5 +++++ 1 file changed, 5 insertions(+) diff --git "a/docs/step1\354\232\224\352\265\254\354\202\254\355\225\255.md" "b/docs/step1\354\232\224\352\265\254\354\202\254\355\225\255.md" index 7871c6d742..8e307667da 100644 --- "a/docs/step1\354\232\224\352\265\254\354\202\254\355\225\255.md" +++ "b/docs/step1\354\232\224\352\265\254\354\202\254\355\225\255.md" @@ -18,3 +18,8 @@ ## 삭제 이력 관리 - [x] 질문 삭제 시 DeleteHistory를 생성하여 이력을 남긴다 - [x] 답변 삭제 시에도 DeleteHistory를 생성하여 이력을 남긴다 + + +## 리팩터링 요구사항 +- [ ] deleteQuestion() 메서드의 비즈니스 로직을 도메인으로 이동한다. + From 83711f376aaebc21f0bc8b296bab2ca8fc8a4f54 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Thu, 27 Nov 2025 00:27:33 +0900 Subject: [PATCH 04/10] =?UTF-8?q?refactor:=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=84=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/qna/domain/Answer.java | 2 +- .../java/nextstep/qna/service/QnAService.java | 26 ++++--------------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/src/main/java/nextstep/qna/domain/Answer.java b/src/main/java/nextstep/qna/domain/Answer.java index d380b641d6..306f17f3ec 100644 --- a/src/main/java/nextstep/qna/domain/Answer.java +++ b/src/main/java/nextstep/qna/domain/Answer.java @@ -74,7 +74,7 @@ public void toQuestion(Question question) { public DeleteHistory delete(){ this.deleted = true; - return new DeleteHistory(ContentType.QUESTION, id, writer, LocalDateTime.now()); + return new DeleteHistory(ContentType.ANSWER, id, writer, LocalDateTime.now()); } @Override diff --git a/src/main/java/nextstep/qna/service/QnAService.java b/src/main/java/nextstep/qna/service/QnAService.java index 5741c84d65..666a3d8cf5 100644 --- a/src/main/java/nextstep/qna/service/QnAService.java +++ b/src/main/java/nextstep/qna/service/QnAService.java @@ -2,14 +2,15 @@ import nextstep.qna.CannotDeleteException; import nextstep.qna.NotFoundException; -import nextstep.qna.domain.*; +import nextstep.qna.domain.AnswerRepository; +import nextstep.qna.domain.DeleteHistory; +import nextstep.qna.domain.Question; +import nextstep.qna.domain.QuestionRepository; import nextstep.users.domain.NsUser; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.List; @Service("qnaService") @@ -26,24 +27,7 @@ public class QnAService { @Transactional public void deleteQuestion(NsUser loginUser, long questionId) throws CannotDeleteException { Question question = questionRepository.findById(questionId).orElseThrow(NotFoundException::new); - if (!question.isOwner(loginUser)) { - throw new CannotDeleteException("질문을 삭제할 권한이 없습니다."); - } - - List answers = question.getAnswers(); - for (Answer answer : answers) { - if (!answer.isOwner(loginUser)) { - throw new CannotDeleteException("다른 사람이 쓴 답변이 있어 삭제할 수 없습니다."); - } - } - - List deleteHistories = new ArrayList<>(); - question.setDeleted(true); - deleteHistories.add(new DeleteHistory(ContentType.QUESTION, questionId, question.getWriter(), LocalDateTime.now())); - for (Answer answer : answers) { - answer.setDeleted(true); - deleteHistories.add(new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter(), LocalDateTime.now())); - } + List deleteHistories = question.delete(loginUser); deleteHistoryService.saveAll(deleteHistories); } } From c939d7bd07449cdd3aa7560c52163b3fa31477b6 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Thu, 27 Nov 2025 00:44:11 +0900 Subject: [PATCH 05/10] =?UTF-8?q?refactor:=20stream=EC=9D=84=20=ED=99=9C?= =?UTF-8?q?=EC=9A=A9=ED=95=B4=EC=84=9C=20ident=EC=A4=84=EC=9D=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/qna/domain/Question.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/nextstep/qna/domain/Question.java b/src/main/java/nextstep/qna/domain/Question.java index 320c7d4036..13dcdc31d1 100644 --- a/src/main/java/nextstep/qna/domain/Question.java +++ b/src/main/java/nextstep/qna/domain/Question.java @@ -101,10 +101,8 @@ public List delete(NsUser loginUser) throws CannotDeleteException } private void checkDeletableAnswers(NsUser loginUser) throws CannotDeleteException { - for (Answer answer : answers) { - if (!answer.isOwner(loginUser)) { - throw new CannotDeleteException("다른 사람의 답변이 존재하여 삭제할 수 없습니다."); - } + if(answers.stream().anyMatch(answer -> !answer.isOwner(loginUser))) { + throw new CannotDeleteException("다른 사람의 답변이 존재하여 삭제할 수 없습니다."); } } From 96a98e5b05165678f020b70422740273f2985cc2 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Thu, 27 Nov 2025 01:00:20 +0900 Subject: [PATCH 06/10] =?UTF-8?q?refactor:=20answers=20=EC=9D=BC=EA=B8=89?= =?UTF-8?q?=EC=BB=AC=EB=A0=89=EC=85=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/qna/domain/Answers.java | 41 ++++++++++++++ .../java/nextstep/qna/domain/Question.java | 14 ++--- .../java/nextstep/qna/domain/AnswersTest.java | 53 +++++++++++++++++++ 3 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 src/main/java/nextstep/qna/domain/Answers.java create mode 100644 src/test/java/nextstep/qna/domain/AnswersTest.java diff --git a/src/main/java/nextstep/qna/domain/Answers.java b/src/main/java/nextstep/qna/domain/Answers.java new file mode 100644 index 0000000000..21ac9252d3 --- /dev/null +++ b/src/main/java/nextstep/qna/domain/Answers.java @@ -0,0 +1,41 @@ +package nextstep.qna.domain; + +import nextstep.qna.CannotDeleteException; +import nextstep.users.domain.NsUser; + +import java.util.ArrayList; +import java.util.List; + +public class Answers { + private final List answers; + + public Answers() { + this(new ArrayList<>()); + } + + public Answers(List answers) { + this.answers = new ArrayList<>(answers); + } + + public void add(Answer answer) { + answers.add(answer); + } + + public void checkDeletable(NsUser loginUser) throws CannotDeleteException { + if (answers.stream().anyMatch(answer -> !answer.isOwner(loginUser))) { + throw new CannotDeleteException("다른 사람의 답변이 존재하여 삭제할 수 없습니다."); + } + } + + public List delete() { + List histories = new ArrayList<>(); + for (Answer answer : answers) { + histories.add(answer.delete()); + } + return histories; + } + + public List getAnswers() { + return new ArrayList<>(answers); + } +} diff --git a/src/main/java/nextstep/qna/domain/Question.java b/src/main/java/nextstep/qna/domain/Question.java index 13dcdc31d1..91ce5298a5 100644 --- a/src/main/java/nextstep/qna/domain/Question.java +++ b/src/main/java/nextstep/qna/domain/Question.java @@ -16,7 +16,7 @@ public class Question { private NsUser writer; - private List answers = new ArrayList<>(); + private Answers answers = new Answers(); private boolean deleted = false; @@ -83,7 +83,7 @@ public boolean isDeleted() { } public List getAnswers() { - return answers; + return answers.getAnswers(); } public List delete(NsUser loginUser) throws CannotDeleteException { @@ -101,9 +101,7 @@ public List delete(NsUser loginUser) throws CannotDeleteException } private void checkDeletableAnswers(NsUser loginUser) throws CannotDeleteException { - if(answers.stream().anyMatch(answer -> !answer.isOwner(loginUser))) { - throw new CannotDeleteException("다른 사람의 답변이 존재하여 삭제할 수 없습니다."); - } + answers.checkDeletable(loginUser); } private DeleteHistory deleteQuestion() { @@ -112,11 +110,7 @@ private DeleteHistory deleteQuestion() { } private List deleteAnswers() { - List histories = new ArrayList<>(); - for (Answer answer : answers) { - histories.add(answer.delete()); - } - return histories; + return answers.delete(); } @Override diff --git a/src/test/java/nextstep/qna/domain/AnswersTest.java b/src/test/java/nextstep/qna/domain/AnswersTest.java new file mode 100644 index 0000000000..9000d5b1e8 --- /dev/null +++ b/src/test/java/nextstep/qna/domain/AnswersTest.java @@ -0,0 +1,53 @@ +package nextstep.qna.domain; + +import nextstep.qna.CannotDeleteException; +import nextstep.users.domain.NsUserTest; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class AnswersTest { + + @Test + public void 모든_답변이_같은작성자면_삭제_가능() throws CannotDeleteException { + Question question = new Question(1L, NsUserTest.JAVAJIGI, "title", "contents"); + Answer answer1 = new Answer(11L, NsUserTest.JAVAJIGI, question, "answer1"); + Answer answer2 = new Answer(12L, NsUserTest.JAVAJIGI, question, "answer2"); + Answers answers = new Answers(Arrays.asList(answer1, answer2)); + + answers.checkDeletable(NsUserTest.JAVAJIGI); + } + + @Test + public void 다른_작성자의_답변이_있으면_예외() { + Question question = new Question(1L, NsUserTest.JAVAJIGI, "title", "contents"); + Answer answer1 = new Answer(11L, NsUserTest.JAVAJIGI, question, "answer1"); + Answer answer2 = new Answer(12L, NsUserTest.SANJIGI, question, "answer2"); + Answers answers = new Answers(Arrays.asList(answer1, answer2)); + + assertThatThrownBy(() -> { + answers.checkDeletable(NsUserTest.JAVAJIGI); + }).isInstanceOf(CannotDeleteException.class) + .hasMessageContaining("다른 사람이 쓴 답변이 있어 삭제할 수 없습니다."); + } + + @Test + public void 모든_답변_삭제_및_DeleteHistory_반환() { + Question question = new Question(1L, NsUserTest.JAVAJIGI, "title", "contents"); + Answer answer1 = new Answer(11L, NsUserTest.JAVAJIGI, question, "answer1"); + Answer answer2 = new Answer(12L, NsUserTest.JAVAJIGI, question, "answer2"); + Answers answers = new Answers(Arrays.asList(answer1, answer2)); + + List deleteHistories = answers.delete(); + + assertThat(answer1.isDeleted()).isTrue(); + assertThat(answer2.isDeleted()).isTrue(); + assertThat(deleteHistories).hasSize(2); + assertThat(deleteHistories.get(0).getContentType()).isEqualTo(ContentType.ANSWER); + assertThat(deleteHistories.get(1).getContentType()).isEqualTo(ContentType.ANSWER); + } +} From f128cc2f1f6ead7f5c4061610666cd879405eed3 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Fri, 28 Nov 2025 00:30:17 +0900 Subject: [PATCH 07/10] =?UTF-8?q?refactor:=20answers=EC=9D=98=20delete?= =?UTF-8?q?=EA=B0=80=20=EA=B2=80=EC=A6=9D=EA=B3=BC=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=EC=9D=98=20=EC=B1=85=EC=9E=84=EC=9D=84=20=EB=AA=A8=EB=91=90=20?= =?UTF-8?q?=EA=B0=96=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/qna/domain/Answers.java | 14 +++++++------- src/main/java/nextstep/qna/domain/Question.java | 12 +----------- src/test/java/nextstep/qna/domain/AnswersTest.java | 10 +++++----- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/main/java/nextstep/qna/domain/Answers.java b/src/main/java/nextstep/qna/domain/Answers.java index 21ac9252d3..6c14fd1a62 100644 --- a/src/main/java/nextstep/qna/domain/Answers.java +++ b/src/main/java/nextstep/qna/domain/Answers.java @@ -21,19 +21,19 @@ public void add(Answer answer) { answers.add(answer); } - public void checkDeletable(NsUser loginUser) throws CannotDeleteException { - if (answers.stream().anyMatch(answer -> !answer.isOwner(loginUser))) { - throw new CannotDeleteException("다른 사람의 답변이 존재하여 삭제할 수 없습니다."); - } - } - - public List delete() { + public List delete(NsUser loginUser) throws CannotDeleteException { + checkDeletable(loginUser); List histories = new ArrayList<>(); for (Answer answer : answers) { histories.add(answer.delete()); } return histories; } + private void checkDeletable(NsUser loginUser) throws CannotDeleteException { + if (answers.stream().anyMatch(answer -> !answer.isOwner(loginUser))) { + throw new CannotDeleteException("다른 사람의 답변이 존재하여 삭제할 수 없습니다."); + } + } public List getAnswers() { return new ArrayList<>(answers); diff --git a/src/main/java/nextstep/qna/domain/Question.java b/src/main/java/nextstep/qna/domain/Question.java index 91ce5298a5..d01c08b335 100644 --- a/src/main/java/nextstep/qna/domain/Question.java +++ b/src/main/java/nextstep/qna/domain/Question.java @@ -91,28 +91,18 @@ public List delete(NsUser loginUser) throws CannotDeleteException throw new CannotDeleteException("질문을 삭제할 권한이 없습니다."); } - checkDeletableAnswers(loginUser); - List deleteHistories = new ArrayList<>(); deleteHistories.add(deleteQuestion()); - deleteHistories.addAll(deleteAnswers()); + deleteHistories.addAll(answers.delete(loginUser)); return deleteHistories; } - private void checkDeletableAnswers(NsUser loginUser) throws CannotDeleteException { - answers.checkDeletable(loginUser); - } - private DeleteHistory deleteQuestion() { this.deleted = true; return new DeleteHistory(ContentType.QUESTION, id, writer, LocalDateTime.now()); } - private List deleteAnswers() { - return answers.delete(); - } - @Override public String toString() { return "Question [id=" + getId() + ", title=" + title + ", contents=" + contents + ", writer=" + writer + "]"; diff --git a/src/test/java/nextstep/qna/domain/AnswersTest.java b/src/test/java/nextstep/qna/domain/AnswersTest.java index 9000d5b1e8..6f33063d2e 100644 --- a/src/test/java/nextstep/qna/domain/AnswersTest.java +++ b/src/test/java/nextstep/qna/domain/AnswersTest.java @@ -19,7 +19,7 @@ public class AnswersTest { Answer answer2 = new Answer(12L, NsUserTest.JAVAJIGI, question, "answer2"); Answers answers = new Answers(Arrays.asList(answer1, answer2)); - answers.checkDeletable(NsUserTest.JAVAJIGI); + List delete = answers.delete(NsUserTest.JAVAJIGI); } @Test @@ -30,19 +30,19 @@ public class AnswersTest { Answers answers = new Answers(Arrays.asList(answer1, answer2)); assertThatThrownBy(() -> { - answers.checkDeletable(NsUserTest.JAVAJIGI); + answers.delete(NsUserTest.JAVAJIGI); }).isInstanceOf(CannotDeleteException.class) - .hasMessageContaining("다른 사람이 쓴 답변이 있어 삭제할 수 없습니다."); + .hasMessageContaining("다른 사람의 답변이 존재하여 삭제할 수 없습니다."); } @Test - public void 모든_답변_삭제_및_DeleteHistory_반환() { + public void 모든_답변_삭제_및_DeleteHistory_반환() throws CannotDeleteException { Question question = new Question(1L, NsUserTest.JAVAJIGI, "title", "contents"); Answer answer1 = new Answer(11L, NsUserTest.JAVAJIGI, question, "answer1"); Answer answer2 = new Answer(12L, NsUserTest.JAVAJIGI, question, "answer2"); Answers answers = new Answers(Arrays.asList(answer1, answer2)); - List deleteHistories = answers.delete(); + List deleteHistories = answers.delete(NsUserTest.JAVAJIGI); assertThat(answer1.isDeleted()).isTrue(); assertThat(answer2.isDeleted()).isTrue(); From 5713dc8d9d66fdc259cbdcac1b4202a503ca8706 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Fri, 28 Nov 2025 00:52:32 +0900 Subject: [PATCH 08/10] =?UTF-8?q?refactor:=20=EB=AF=B8=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20getter=EC=99=80=20=EB=AA=A8=EB=93=A0=20setter=EB=A5=BC=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/nextstep/qna/domain/Answer.java | 9 ------- .../java/nextstep/qna/domain/Answers.java | 5 +--- .../java/nextstep/qna/domain/Question.java | 27 ------------------- 3 files changed, 1 insertion(+), 40 deletions(-) diff --git a/src/main/java/nextstep/qna/domain/Answer.java b/src/main/java/nextstep/qna/domain/Answer.java index 306f17f3ec..861b2ccc0b 100644 --- a/src/main/java/nextstep/qna/domain/Answer.java +++ b/src/main/java/nextstep/qna/domain/Answer.java @@ -47,11 +47,6 @@ public Long getId() { return id; } - public Answer setDeleted(boolean deleted) { - this.deleted = deleted; - return this; - } - public boolean isDeleted() { return deleted; } @@ -64,10 +59,6 @@ public NsUser getWriter() { return writer; } - public String getContents() { - return contents; - } - public void toQuestion(Question question) { this.question = question; } diff --git a/src/main/java/nextstep/qna/domain/Answers.java b/src/main/java/nextstep/qna/domain/Answers.java index 6c14fd1a62..6d229036b4 100644 --- a/src/main/java/nextstep/qna/domain/Answers.java +++ b/src/main/java/nextstep/qna/domain/Answers.java @@ -29,13 +29,10 @@ public List delete(NsUser loginUser) throws CannotDeleteException } return histories; } + private void checkDeletable(NsUser loginUser) throws CannotDeleteException { if (answers.stream().anyMatch(answer -> !answer.isOwner(loginUser))) { throw new CannotDeleteException("다른 사람의 답변이 존재하여 삭제할 수 없습니다."); } } - - public List getAnswers() { - return new ArrayList<>(answers); - } } diff --git a/src/main/java/nextstep/qna/domain/Question.java b/src/main/java/nextstep/qna/domain/Question.java index d01c08b335..4fe36096c1 100644 --- a/src/main/java/nextstep/qna/domain/Question.java +++ b/src/main/java/nextstep/qna/domain/Question.java @@ -42,24 +42,6 @@ public Long getId() { return id; } - public String getTitle() { - return title; - } - - public Question setTitle(String title) { - this.title = title; - return this; - } - - public String getContents() { - return contents; - } - - public Question setContents(String contents) { - this.contents = contents; - return this; - } - public NsUser getWriter() { return writer; } @@ -73,19 +55,10 @@ public boolean isOwner(NsUser loginUser) { return writer.equals(loginUser); } - public Question setDeleted(boolean deleted) { - this.deleted = deleted; - return this; - } - public boolean isDeleted() { return deleted; } - public List getAnswers() { - return answers.getAnswers(); - } - public List delete(NsUser loginUser) throws CannotDeleteException { if (!isOwner(loginUser)) { throw new CannotDeleteException("질문을 삭제할 권한이 없습니다."); From 62487bbc3d15062662073765757e8ff8764f62e5 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Fri, 28 Nov 2025 01:14:15 +0900 Subject: [PATCH 09/10] =?UTF-8?q?refactor:=20baseEntity=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/qna/domain/BaseEntity.java | 19 +++++++++++++++++++ .../java/nextstep/qna/domain/Question.java | 17 ++++------------- 2 files changed, 23 insertions(+), 13 deletions(-) create mode 100644 src/main/java/nextstep/qna/domain/BaseEntity.java diff --git a/src/main/java/nextstep/qna/domain/BaseEntity.java b/src/main/java/nextstep/qna/domain/BaseEntity.java new file mode 100644 index 0000000000..4611df2b8b --- /dev/null +++ b/src/main/java/nextstep/qna/domain/BaseEntity.java @@ -0,0 +1,19 @@ +package nextstep.qna.domain; + +import java.time.LocalDateTime; + +public abstract class BaseEntity { + private Long id; + private LocalDateTime createdDate = LocalDateTime.now(); + private LocalDateTime updatedDate; + + public BaseEntity() {} + + public BaseEntity(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } +} diff --git a/src/main/java/nextstep/qna/domain/Question.java b/src/main/java/nextstep/qna/domain/Question.java index 4fe36096c1..e771733baf 100644 --- a/src/main/java/nextstep/qna/domain/Question.java +++ b/src/main/java/nextstep/qna/domain/Question.java @@ -7,9 +7,7 @@ import java.util.ArrayList; import java.util.List; -public class Question { - private Long id; - +public class Question extends BaseEntity { private String title; private String contents; @@ -20,11 +18,8 @@ public class Question { private boolean deleted = false; - private LocalDateTime createdDate = LocalDateTime.now(); - - private LocalDateTime updatedDate; - public Question() { + super(); } public Question(NsUser writer, String title, String contents) { @@ -32,16 +27,12 @@ public Question(NsUser writer, String title, String contents) { } public Question(Long id, NsUser writer, String title, String contents) { - this.id = id; + super(id); this.writer = writer; this.title = title; this.contents = contents; } - public Long getId() { - return id; - } - public NsUser getWriter() { return writer; } @@ -73,7 +64,7 @@ public List delete(NsUser loginUser) throws CannotDeleteException private DeleteHistory deleteQuestion() { this.deleted = true; - return new DeleteHistory(ContentType.QUESTION, id, writer, LocalDateTime.now()); + return new DeleteHistory(ContentType.QUESTION, getId(), writer, LocalDateTime.now()); } @Override From 33aa64c4bcfa21dc410c0e970379083112bf8552 Mon Sep 17 00:00:00 2001 From: yangseung-in Date: Fri, 28 Nov 2025 01:24:22 +0900 Subject: [PATCH 10/10] =?UTF-8?q?refactor:=20QuestionContent=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/nextstep/qna/domain/Question.java | 13 +++++++------ .../nextstep/qna/domain/QuestionContent.java | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 src/main/java/nextstep/qna/domain/QuestionContent.java diff --git a/src/main/java/nextstep/qna/domain/Question.java b/src/main/java/nextstep/qna/domain/Question.java index e771733baf..798de6302a 100644 --- a/src/main/java/nextstep/qna/domain/Question.java +++ b/src/main/java/nextstep/qna/domain/Question.java @@ -8,9 +8,7 @@ import java.util.List; public class Question extends BaseEntity { - private String title; - - private String contents; + private QuestionContent content; private NsUser writer; @@ -27,10 +25,13 @@ public Question(NsUser writer, String title, String contents) { } public Question(Long id, NsUser writer, String title, String contents) { + this(id, writer, new QuestionContent(title, contents)); + } + + public Question(Long id, NsUser writer, QuestionContent content) { super(id); this.writer = writer; - this.title = title; - this.contents = contents; + this.content = content; } public NsUser getWriter() { @@ -69,6 +70,6 @@ private DeleteHistory deleteQuestion() { @Override public String toString() { - return "Question [id=" + getId() + ", title=" + title + ", contents=" + contents + ", writer=" + writer + "]"; + return "Question [id=" + getId() + ", title=" + content.getTitle() + ", contents=" + content.getContents() + ", writer=" + writer + "]"; } } diff --git a/src/main/java/nextstep/qna/domain/QuestionContent.java b/src/main/java/nextstep/qna/domain/QuestionContent.java new file mode 100644 index 0000000000..556ed13a71 --- /dev/null +++ b/src/main/java/nextstep/qna/domain/QuestionContent.java @@ -0,0 +1,18 @@ +package nextstep.qna.domain; + +public class QuestionContent { + private String title; + private String contents; + + public QuestionContent(String title, String contents) { + this.title = title; + this.contents = contents; + } + public String getTitle() { + return title; + } + + public String getContents() { + return contents; + } +}