Skip to content

[수강신청] 1단계 - 레거시 코드 리팩터링 #666

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

Merged
merged 7 commits into from
Apr 8, 2025
14 changes: 9 additions & 5 deletions src/main/java/nextstep/qna/domain/Answer.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ public Answer(NsUser writer, Question question, String contents) {

public Answer(Long id, NsUser writer, Question question, String contents) {
this.id = id;
if(writer == null) {
if (writer == null) {
throw new UnAuthorizedException();
}

if(question == null) {
if (question == null) {
throw new NotFoundException();
}

Expand All @@ -47,9 +47,9 @@ public Long getId() {
return id;
}

public Answer setDeleted(boolean deleted) {
this.deleted = deleted;
return this;
public DeleteHistory delete() {
this.deleted = true;
return new DeleteHistory(ContentType.ANSWER, id, writer, LocalDateTime.now());
}

public boolean isDeleted() {
Expand All @@ -60,6 +60,10 @@ public boolean isOwner(NsUser writer) {
return this.writer.equals(writer);
}

public boolean isNotOwner(NsUser writer) {
return !isOwner(writer);
}

public NsUser getWriter() {
return writer;
}
Expand Down
44 changes: 40 additions & 4 deletions src/main/java/nextstep/qna/domain/Question.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
package nextstep.qna.domain;

import nextstep.qna.CannotDeleteException;
import nextstep.qna.domain.qustion.QuestionDeletable;
import nextstep.qna.domain.qustion.QuestionDeletableAnswer;
import nextstep.qna.domain.qustion.QuestionDeletableWriter;
import nextstep.users.domain.NsUser;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

public class Question {

private static final List<QuestionDeletable> deletableConditions = List.of(
new QuestionDeletableAnswer(), new QuestionDeletableWriter());

private Long id;

private String title;
Expand Down Expand Up @@ -68,13 +76,40 @@ public void addAnswer(Answer answer) {
answers.add(answer);
}

public List<DeleteHistory> delete(NsUser loginUser) throws CannotDeleteException {
checkDeletable(loginUser);

Choose a reason for hiding this comment

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

👍


List<DeleteHistory> deleteHistories = new ArrayList<>();
deleted = true;
deleteHistories.add(
new DeleteHistory(ContentType.QUESTION, id, writer, LocalDateTime.now()));
for (Answer answer : answers) {
deleteHistories.add(answer.delete());

Choose a reason for hiding this comment

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

👍

}
return deleteHistories;
}

private void checkDeletable(NsUser loginUser) throws CannotDeleteException {
for (QuestionDeletable condition : deletableConditions) {
condition.checkDeletable(this, loginUser);
}
}

public boolean isOwner(NsUser loginUser) {
return writer.equals(loginUser);
}

public Question setDeleted(boolean deleted) {
this.deleted = deleted;
return this;
public boolean answerEmpty() {
return answers.isEmpty();
}

public boolean allAnswerFromWriter() {
for (Answer answer : answers) {
if (answer.isNotOwner(writer)) {
return false;
}
}
return true;
}

public boolean isDeleted() {
Expand All @@ -87,6 +122,7 @@ public List<Answer> getAnswers() {

@Override
public String toString() {
return "Question [id=" + getId() + ", title=" + title + ", contents=" + contents + ", writer=" + writer + "]";
return "Question [id=" + getId() + ", title=" + title + ", contents=" + contents
+ ", writer=" + writer + "]";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package nextstep.qna.domain.qustion;

import nextstep.qna.CannotDeleteException;
import nextstep.qna.domain.Question;
import nextstep.users.domain.NsUser;

public interface QuestionDeletable {
void checkDeletable(Question question, NsUser loginUser) throws CannotDeleteException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package nextstep.qna.domain.qustion;

import nextstep.qna.CannotDeleteException;
import nextstep.qna.domain.Question;
import nextstep.users.domain.NsUser;

public class QuestionDeletableAnswer implements QuestionDeletable {

static final String REASON = "다른 사람이 쓴 답변이 있어 삭제할 수 없습니다.";

@Override
public void checkDeletable(Question question, NsUser loginUser) throws CannotDeleteException {
if (question.answerEmpty() || question.allAnswerFromWriter()) {
return;
}
throw new CannotDeleteException(REASON);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package nextstep.qna.domain.qustion;

import nextstep.qna.CannotDeleteException;
import nextstep.qna.domain.Question;
import nextstep.users.domain.NsUser;

public class QuestionDeletableWriter implements QuestionDeletable {

static final String REASON = "질문을 삭제할 권한이 없습니다.";

@Override
public void checkDeletable(Question question, NsUser loginUser) throws CannotDeleteException {
if (question.isOwner(loginUser)) {
return;
}
throw new CannotDeleteException(REASON);
}
}
23 changes: 1 addition & 22 deletions src/main/java/nextstep/qna/service/QnAService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Service("qnaService")
public class QnAService {
Expand All @@ -26,24 +23,6 @@ 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<Answer> answers = question.getAnswers();
for (Answer answer : answers) {
if (!answer.isOwner(loginUser)) {
throw new CannotDeleteException("다른 사람이 쓴 답변이 있어 삭제할 수 없습니다.");
}
}

List<DeleteHistory> 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()));
}
deleteHistoryService.saveAll(deleteHistories);
deleteHistoryService.saveAll(question.delete(loginUser));
}
}
15 changes: 15 additions & 0 deletions src/test/java/nextstep/qna/domain/AnswerTest.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
package nextstep.qna.domain;

import java.time.LocalDateTime;
import nextstep.users.domain.NsUserTest;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class AnswerTest {
public static final Answer A1 = new Answer(NsUserTest.JAVAJIGI, QuestionTest.Q1, "Answers Contents1");
public static final Answer A2 = new Answer(NsUserTest.SANJIGI, QuestionTest.Q1, "Answers Contents2");

@Test
void 삭제() {
Answer answer = new Answer(11L, NsUserTest.JAVAJIGI, QuestionTest.Q1, "Answers Contents1");
assertThat(answer.isDeleted()).isFalse();

DeleteHistory history = answer.delete();
assertThat(answer.isDeleted()).isTrue();
assertThat(history).isEqualTo(
new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter(), LocalDateTime.now()));
}
}
55 changes: 55 additions & 0 deletions src/test/java/nextstep/qna/domain/QuestionTest.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,63 @@
package nextstep.qna.domain;

import nextstep.qna.CannotDeleteException;
import nextstep.users.domain.NsUserTest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.time.LocalDateTime;
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 QuestionTest {
public static final Question Q1 = new Question(NsUserTest.JAVAJIGI, "title1", "contents1");
public static final Question Q2 = new Question(NsUserTest.SANJIGI, "title2", "contents2");


private Question question;
private Answer answer;

@BeforeEach
public void setUp() {
question = new Question(1L, NsUserTest.JAVAJIGI, "title1", "contents1");
answer = new Answer(11L, NsUserTest.JAVAJIGI, QuestionTest.Q1, "Answers Contents1");
question.addAnswer(answer);
}

@Test
public void delete_성공() throws Exception {
assertThat(question.isDeleted()).isFalse();
List<DeleteHistory> deleteHistories = question.delete(NsUserTest.JAVAJIGI);

assertThat(question.isDeleted()).isTrue();
assertThat(deleteHistories).isEqualTo(Arrays.asList(
new DeleteHistory(ContentType.QUESTION, question.getId(), question.getWriter(), LocalDateTime.now()),
new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter(), LocalDateTime.now())));
}

@Test
public void delete_다른_사람이_쓴_글() {
assertThatThrownBy(() -> question.delete(NsUserTest.SANJIGI)).isInstanceOf(CannotDeleteException.class);
}

@Test
public void delete_성공_질문자_답변자_같음() throws Exception {
List<DeleteHistory> deleteHistories = question.delete(NsUserTest.JAVAJIGI);

assertThat(question.isDeleted()).isTrue();
assertThat(answer.isDeleted()).isTrue();

assertThat(deleteHistories).isEqualTo(Arrays.asList(
new DeleteHistory(ContentType.QUESTION, question.getId(), question.getWriter(), LocalDateTime.now()),
new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter(), LocalDateTime.now())));
}

@Test
public void delete_답변_중_다른_사람이_쓴_글() {
assertThatThrownBy(() -> question.delete(NsUserTest.SANJIGI)).isInstanceOf(CannotDeleteException.class);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package nextstep.qna.domain.question;

import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import nextstep.qna.CannotDeleteException;
import nextstep.qna.domain.AnswerTest;
import nextstep.qna.domain.Question;
import nextstep.qna.domain.qustion.QuestionDeletable;
import nextstep.qna.domain.qustion.QuestionDeletableAnswer;
import nextstep.users.domain.NsUserTest;
import org.junit.jupiter.api.Test;

class QuestionDeletableAnswerTest {

@Test
void 답변없음_삭제가능() {
Question question = new Question(NsUserTest.JAVAJIGI, "title1", "contents1");
QuestionDeletable questionDeletable = new QuestionDeletableAnswer();

assertThatNoException().isThrownBy(() -> questionDeletable.checkDeletable(question, question.getWriter()));
}

@Test
void 작성자와_같은답변_삭제가능() {
Question question = new Question(NsUserTest.JAVAJIGI, "title1", "contents1");
QuestionDeletable questionDeletable = new QuestionDeletableAnswer();
question.addAnswer(AnswerTest.A1);

assertThatNoException().isThrownBy(() -> questionDeletable.checkDeletable(question, question.getWriter()));
}

@Test
void 작성자와_다른답변_삭제불가() {
Question question = new Question(NsUserTest.JAVAJIGI, "title1", "contents1");
QuestionDeletable questionDeletable = new QuestionDeletableAnswer();
question.addAnswer(AnswerTest.A2);

assertThatThrownBy(() -> questionDeletable.checkDeletable(question, question.getWriter())).isInstanceOf(
CannotDeleteException.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package nextstep.qna.domain.question;

import nextstep.qna.CannotDeleteException;
import nextstep.qna.domain.Question;
import nextstep.qna.domain.qustion.QuestionDeletable;
import nextstep.qna.domain.qustion.QuestionDeletableWriter;
import nextstep.users.domain.NsUserTest;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class QuestionDeletableWriterTest {

@Test
void 작성자라_삭제가능() {
Question question = new Question(NsUserTest.JAVAJIGI, "title1", "contents1");
QuestionDeletable questionDeletable = new QuestionDeletableWriter();

assertThatNoException().isThrownBy(() -> questionDeletable.checkDeletable(question, NsUserTest.JAVAJIGI));
}

@Test
void 작성자가_아니라_삭제불가() {
Question question = new Question(NsUserTest.JAVAJIGI, "title1", "contents1");
QuestionDeletable questionDeletable = new QuestionDeletableWriter();

assertThatThrownBy(() -> questionDeletable.checkDeletable(question, NsUserTest.SANJIGI)).isInstanceOf(
CannotDeleteException.class);
}
}