Skip to content

Conversation

dongkibravo
Copy link

  • Question 객체 내에 answers를 일급 객체로 변경
  • question 삭제 가능 여부 체크 로직을 question 내부에 넣음
  • deleteHistory 리스트 생성을 delete 발생 이후 question 객체 내부에서 생성해서 qnaService로 반환하도록함

의문:
qnaService에서 get을 통해 answers를 가져와 qnaService.deleteQuestion 상에서 추가해도 좋을거같은데 question.collectDeleteHistory -> answers.collectDeleteHistory -> 반환 -> 반환 을 통하려하니 좀 복잡해지는게 아닌가 생각했습니다.

@neojjc2 neojjc2 self-requested a review April 8, 2025 23:51
@neojjc2 neojjc2 self-assigned this Apr 8, 2025
@neojjc2
Copy link

neojjc2 commented Apr 8, 2025

#tag @neojjc2

import java.util.ArrayList;
import java.util.List;

public class Answers {
Copy link

Choose a reason for hiding this comment

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

일급 컬렉션 사용 좋습니다 👍

다만 Answers가 하는일이 가지고 있는 Answer들을 일괄적으로 삭제 처리 하고
Answer들의 조회 권한만 가지게 하면 될 것 같습니다 🙇

for (Answer answer : answers) {
answer.setDeleted(true);
}
}
Copy link

@neojjc2 neojjc2 Apr 9, 2025

Choose a reason for hiding this comment

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

Answer에게 delete상태로 만들어 달라 라고 하시는건 어떨까요?? 😄
answerset을 해서 값을 변경하는 경우라면 나중에 answerdelete로직이 변경되거나 할 경우
Answersdelete로직이 계속 수정되어야 합니다 🤔
그리고 delete로직 변경에 대해서 Answers, Answer 모두 영향을 받게 되니 한쪽만 영향을 받을 수 있도록
관심사를 좀 더 분리하면 좋을 것 같습니다.

저는 Question에서 해주신 것 처럼,

  • Answersanswer들에 대한 일괄 조작이나, 조회 역할, 작성자가 작성하지 않은 답변 있는지 검증 정도
  • Answer에게는 User를 줄테니 삭제를 하는 역할

이렇게 좀 나눠보면 될 것 같은데 어떻게 보시나요 😄

}
}

public List<DeleteHistory> collectDeleteHistory() {
Copy link

Choose a reason for hiding this comment

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

delete(), collect()를 나눠놓으신 이유가 있으실까요??
이렇게 되면 delete()를 호출하고 collect()를 호출하도록 항상 가이드 문서가 나가거나,
주의를 기울여야 하는 구조가 됩니다.

delete()를 호출하고 난 뒤 collect를 호출하는 타이밍이 느려지거나 혹은 빨라지거나 했을 경우엔
타이밍 이슈가 발생할 수 있고, 구조적으로도 delete를 한 반환 값이 history가 되는게
좀 더 자연스러울 것 같습니다 🙇

public List<DeleteHistory> collectDeleteHistory() {
List<DeleteHistory> deleteHistories = new ArrayList<>();
for (Answer answer : answers) {
deleteHistories.add(new DeleteHistory(ContentType.ANSWER, answer.getId(), answer.getWriter(), LocalDateTime.now()));
Copy link

Choose a reason for hiding this comment

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

DeleteHistory의 경우 변경되는 값이 id, user를 제외하고는 고정 값이 될 것 같은데
정적 팩토리 method 같은 게 제공된다면 사용하는 도메인객체나, 서비스에서는 한결 사용성이 개선 될 것 같습니다 😄

// Question일 경우
deleteHistories.add(DeleteHistory.ofQuestion(id, answer.getWriter());

// Answer일 경우 
deleteHistories.add(DeleteHistory.ofAnswer(answer.getId(), answer.getWriter();

if (!isOwner(loginUser)) {
throw new CannotDeleteException("질문 작성자 아닌 사람은 삭제 권한 없음");
}
if (answers.hasAnswerFromOtherUser(writer)) {
Copy link

@neojjc2 neojjc2 Apr 9, 2025

Choose a reason for hiding this comment

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

answers에게 다른 답변이 있는지 확인 정도는 위임해도 괜찮을 것 같습니다만,
삭제하는 역할과 책임의 경우 answer가 할 수 있도록 해야 검증이 누락되지 않을 것 같습니다 😄

서비스에서 아래와 같이 작성할 경우 검증 없이
바로 answer는 삭제가 될 것 같습니다 🙏

List<Answer> answersOfQuestion = answerRepository.findByQuestion(questionId);
answersOfQuestion.forEach(answer -> answer.setDeleted(true));

@neojjc2
Copy link

neojjc2 commented Apr 9, 2025

  • Question 객체 내에 answers를 일급 객체로 변경
  • question 삭제 가능 여부 체크 로직을 question 내부에 넣음
  • deleteHistory 리스트 생성을 delete 발생 이후 question 객체 내부에서 생성해서 qnaService로 반환하도록함

의문: qnaService에서 get을 통해 answers를 가져와 qnaService.deleteQuestion 상에서 추가해도 좋을거같은데 question.collectDeleteHistory -> answers.collectDeleteHistory -> 반환 -> 반환 을 통하려하니 좀 복잡해지는게 아닌가 생각했습니다.

qnaService에서 get을 통해 answers를 가져와 qnaService.deleteQuestion 상에서 추가하면 기존 구조랑 동일한 구조가 됩니다 😄
이번 미션의 주요 목표는 최대한 Question, Answers, Answer에게 get이나 set을 하지 않고
해당 비즈니스 요건을 만족시킬 수 있도록 도메인 객체들에게 위임하는 연습을 해보시는거라 생각해주시면 될 것 같습니다.

사실 Service에서 객체들을 모아놓고 값들을 가져와서 하는 구현방식이 legacy코드 대부분이 그럴 것 같고
실제 요구사항을 구현하다보면 그게 빠르고 더 직관적일 때가 많은데요, 계속 그런방식으로 되다보니 나중엔
Service가 너무 방대해지고, Service에 있는 로직들이 여기저기 다른 Service에 중복되고
도메인객체는 단순히 Entity객체역할 밖에 하지 않고 Service내에 도메인들간에 의존도가 복잡해져서
뭐 하나 수정하려고 해도 전체를 분석해야 하는 상황을 매일 만나고 있습니다 😄

적절하게 도메인 객체들에게 역할과 책임을 위임함으로써 이런 문제들을 예방할 수 있습니다.
도메인 객체가 단순한 데이터 홀더가 아니라 자신의 비즈니스 로직을 캡슐화하면,
Service는 오케스트레이션에만 집중할 수 있고 중복 코드도 줄어듭니다.

이런 개선이 당장은 시간이 좀 더 들더라도, 장기적으로는 유지보수성과 확장성 측면에서 큰 이득을 가져올 것입니다.
특히 팀원 모두가 코드를 더 쉽게 이해하고 수정할 수 있게 될 것입니다.
그럼 장애상황에서 불려가는 일도 적어질 것 같구요 😆

위 부분은 다소 개인적인 의견일 수 있으니 참고 정도만 해주시고
이번 과정은 그 연습을 해보시는 거라 생각해주시면 될 것 같습니다 🙇

Copy link

@neojjc2 neojjc2 left a comment

Choose a reason for hiding this comment

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

안녕하세요 동휘님 🙇

어느덧 마지막 미션으로 오셨네요 😄
이번 단계 리뷰를 맡게된 정준철 이라고 합니다.
만나서 반갑습니다 🙇

1단계 리팩토링 잘 진행해주셨는데요,
조금 보완되면 좋을 부분들이 있어서
의견 드렸습니다.

문의 주신 부분에 대해서는
별도 코멘트로 남겼습니다. 참고만 해주시면 될 것 같아요 😄

그럼 재 리뷰 요청 기다리고 있겠습니다.
미션 끝날 때 까지 잘 부탁 드리겠습니다 🙇

@dongkibravo
Copy link
Author

안녕하세요 @neojjc2! 마지막 미션을 함께 할 수 있게되서 너무 영광입니다 (_ _)
디테일한 리뷰 너무 감사합니다!! pr 첫메세지에 스스로가 헷갈리는 부분이 커서 무슨말을 하는지도 모르면서 적었는데
리뷰해주신 부분들 읽어보고 적용하면서 문제점들을 많이 찾을 수 있었던 것 같습니다.
코멘트들에 관련해서 적용하여 커밋하였으니 한번 확인 부탁드립니다. 감사합니다!

Copy link

@neojjc2 neojjc2 left a comment

Choose a reason for hiding this comment

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

안녕하세요 동휘님 🙇

깔끔하게 개선해주셨네요 💯
수고 많으셨습니다 👍

그럼 본격 미션으로 들어가보실까요
이만 merge 하겠습니다 🙇

return this;
}
throw new CannotDeleteException("다른 사람이 쓴 답변이 있어 삭제할 수 없습니다.");
}
Copy link

Choose a reason for hiding this comment

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

💯 💯 💯 💯 💯

deleteHistories.add(DeleteHistory.ofQuestion(id, loginUser));
List<DeleteHistory> deletedAnswerHistory = answers.deleteAllAnswers(loginUser);
deleteHistories.addAll(deletedAnswerHistory);
return deleteHistories;
Copy link

Choose a reason for hiding this comment

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

소소한 의견입니다만, DeleteHistory도 일급컬렉션으로 포장해주신 리뷰이 분이 계셨어서
개선까진 아니지만, 참고 해보시면 좋을 것 같아 소개 해드립니다 😄

return DeleteHistories.emptyHistories()
                .record(DeleteHistory.of(ContentType.QUESTION, getId(), writer))
                .recordAll(deletedAnswerHistory);

@neojjc2 neojjc2 merged commit 0aa2a77 into next-step:dongkibravo Apr 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants