-
Notifications
You must be signed in to change notification settings - Fork 5
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
[BE] issue 329: 링크 모음 패지키 이동 리팩토링 #350
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
베루스 안녕하세요! 코드 반영해 주신 내용 확인했어요!
이전보다 중복이 많이 줄었들었네요 👍. 고생하셨습니다.
그런데 전반적으로 코드의 변화도 많고, 제네릭이 많이 쓰이고 있어서 코드를 이해하는 데 어려운 것 같아요..
우선 리뷰 남겼는데, 답변 남겨주신 내용 바탕으로 조금 더 이해한 후에 다시 리뷰하겠습니다!
추가적으로 부탁드릴 부분이 있는데요.
혹시 이번 리팩터링으로 변경된 구조에 대해 PR 내용에 조금 더 설명을 부탁드려도 될까요? 구조에 대한 그림이나 설명이 있으면 조금 더 이해하는 데 수월할 것 같습니다!
또 개인적으로 Article과 Content가 서로를 참조하고 있는 구조인데, 서로 타입 간의 관계가 있다 보니 조금 복잡하게 느껴지는 것 같아요!
둘을 합칠 수 있으면 복잡도가 조금 내려갈 것 같은데, 어떻게 생각하시는지 궁금하네요!
throw new UneditableArticleException(studyRoom.getId(), accessor, CommunityArticle.class); | ||
} | ||
|
||
return new CommunityArticle(studyRoom, accessor.getMemberId(), this); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Article이 Content를 필드로 가지고 있는데, Content에서 Article 객체를 생성하고 있는 게 조금 어색한 것 같아요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이건 만나서 얘기하시죠...
이거 말고는 방법이 없네요 ㅠㅠ
|
||
@MappedSuperclass | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public abstract class Article<T extends Content<? extends Article<T>>> extends BaseEntity { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Content와 Article의 관계에 대해서 설명해 주실 수 있을까요??
CommunityContent implements Content<CommunityArticle>
CommunityArticle extends Article<CommunityContent>
서로를 양방향으로 구현한 것 같이 보이는 데, 해당 구조에 대해서 설명해 주실 수 있나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
금욜날 얘기해요~
final StudyRoom studyRoom = studyRoomRepository.findByStudyId(studyId) | ||
.orElseThrow(StudyNotFoundException::new); | ||
final C content = articleRequest.createContent(); | ||
final A article = content.createArticle(studyRoom, new Accessor(memberId, studyId)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Content와 Article로 객체의 역할을 나누신 이유도 궁금해요!
둘을 합칠 수는 없을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
둘을 합치기가 전 힘들었습니다...
만나서 얘기하시죠
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Transactional | ||
public abstract class AbstractArticleService<A extends Article<C>, C extends Content<A>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
community, notice, link 글들을 한 번에 처리할 수 있는 서비스를 만드셨군요! 👍
개인적으로 셋을 합침으로써 중복은 정말 많이 줄었들었지만, 제네릭이 많이 쓰이다 보니 코드가 조금 복잡하게 느껴지는 것 같아요..
고민이 되는 부분이네요... ㅠ
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 고민입니다.... ㅠㅠㅠ
만나서 얘기하시죠
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 베루스! 많은양 리팩터링하시느라 고생하셨습니다!!
Generic을 활용하여 중복되는 CUD 로직을 공통화한 부분과 권한 체크하는 부분을 인터페이스로 풀어내는 부분도 인상깊었습니다!!
몇가지 코멘트 남겼습니다~_~ 확인 부탁드려요!!
...n/java/com/woowacourse/moamoa/referenceroom/controller/SearchingReferenceRoomController.java
Show resolved
Hide resolved
.../main/java/com/woowacourse/moamoa/study/domain/exception/NotParticipatedMemberException.java
Show resolved
Hide resolved
...nd/src/main/java/com/woowacourse/moamoa/studyroom/controller/CommunityArticleController.java
Outdated
Show resolved
Hide resolved
backend/src/test/java/com/woowacourse/moamoa/studyroom/service/CommunityArticleServiceTest.java
Outdated
Show resolved
Hide resolved
backend/src/test/java/com/woowacourse/moamoa/studyroom/service/CommunityArticleServiceTest.java
Outdated
Show resolved
Hide resolved
backend/src/test/java/com/woowacourse/moamoa/studyroom/service/CommunityArticleServiceTest.java
Outdated
Show resolved
Hide resolved
backend/src/test/java/com/woowacourse/moamoa/studyroom/service/LinkArticleServiceTest.java
Show resolved
Hide resolved
backend/src/test/java/com/woowacourse/moamoa/studyroom/service/NoticeArticleServiceTest.java
Outdated
Show resolved
Hide resolved
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
리뷰 반영해주신 부분 확인하였습니다!!
다만, 피드백 반영 부분 확인하면서 코드를 다시 보니 생성부분에서 특히 article이 content에 의존하고 있다는 느낌을 받았는데요, 당연히 현재 content 내용에 의해서 article의 종류(?)가 달라지기 때문에 의존하는게 맞을 수 있다는 생각도 들면서 정말 여기에 의존하는게 맞을까? 하는 생각도 들었어요...ㅠ.ㅠ
한 번 다시 고민해볼 포인트라고 생각되어 코멘트로 남겨봅니다!
고생하셨습니다!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
루스루스베! 빠르게 회의 내용 반영해서 구현해주셨군요..!!
고생하셨습니다!!👍👍
수정해야할 부분은 보이지 않았고, 궁금한 점이나 의견 몇가지 제시해보았습니다! Approve 할게요!
그런데 제가 잘 기억나지 않아서 그러는데..
당시 회의에서 나왔던 내용이 크게 Generic 을 제거하고 type 을 통해서 공지사항과 게시글을 구분한다.
이외에도 추가적으로 나왔던 내용이 있었을까요?? (테스트에서 private 메소드 이런 부분 제외하고 구현 상에서..기록 해놓지 않으니...기억이 가물가물..ㅠ.ㅠ)
final Article article = articleService.createArticle(id, studyId, request, type); | ||
final URI location = URI.create("/api/studies/" + studyId + "/" + type.lowerName() + "/articles/" + article.getId()); | ||
final ArticleType type = ArticleType.valueOf(typeName.toUpperCase()); | ||
final Article article = articleService.createArticle(id, studyId, request.createContent(), type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reqeust.createContent()
를 통해서 Content 도메인을 만들어 넘기고 있군요..!!
service로 reqeust만 넘기고 Response를 받는 것은 어떨까요??
풀어쓰면 다음과 같은 것 같아서..
final Content content = request.createContent();
final Article article = articleService.createArticle(id, studyId, content, type);
추가적으로 Article을 반환하는 것이 아닌 DTO 를 반환하는것은 어떤가요??
} | ||
|
||
@Transactional | ||
public LinkArticle createArticle(final Long memberId, final Long studyId, final LinkContent content) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
어 혹시 create 시에는 도메인을 반환하도록 하는 특별한 이유가 있을까요?? 궁금합니다..!!
@Entity | ||
@Table(name = "link") | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@Where(clause = "deleted = false") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JPA 로 조회시에 항상 where 절에 delete = false 가 추가된다고 이해하면 될까요??
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@ManyToOne(fetch = FetchType.EAGER) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StudyRoom에 대해서 EAGER 로 해주신 이유를 공유해주시면 좋을 것 같습니다!
articleRepository.findById()
시에 article, study, study_member테이블에 대해서 각각 쿼리를 날리는 것이 아닌 한 번에 가져오기 위함이라고 이해하면 될까요??
final String sql = "SELECT {}.id as article_id, {}.title as article_title, {}.content as article_content, " | ||
+ "{}.created_date as article_created_date, {}.last_modified_date as article_last_modified_date, " | ||
public Optional<ArticleData> getById(final Long articleId, final ArticleType type) { | ||
final String sql = "SELECT article.id as article_id, article.title as article_title, article.content as article_content, " | ||
+ "article.created_date as article_created_date, article.last_modified_date as article_last_modified_date, " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
쿼리를 변경하면서 {} 를 제거해주셨군요..!! 쿼리 가독성이 훨씬 좋아진 것 같아요!👍
@ParameterizedTest | ||
@EnumSource(ArticleType.class) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오..이렇게 Enum 상수를 parameterized 로 테스트할 수 있군요! 하나 배워갑니다~_~
deleted boolean NOT NULL, | ||
id BIGINT PRIMARY KEY AUTO_INCREMENT, | ||
study_id BIGINT NOT NULL, | ||
author_id BIGINT NOT NULL, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
type VARCHAR(255) NOT NULL, | ||
deleted boolean NOT NULL, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍👍
@RequestMapping("api/studies/{study-id}/{article-type}/articles") | ||
@RequestMapping("api/studies/{study-id}/{type}/articles") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
API가 길어지면서 articles 는 제거해도 괜찮지 않을까 의견 제시해봅니다!!
api/studies/1/community/4
, api/studies/1/notice/5
@RequestMapping("/api/studies/{study-id}/reference-room/links") | ||
@RequiredArgsConstructor | ||
public class ReferenceRoomController { | ||
public class LinkArticleController { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review의 경우에도 /api/studies/4/links/5
와 같이 reference-room 을 생략해도 자원을 명시하는데 문제가 없어 보여 어떤지 의견 제시해봅니다!
@@ -120,6 +107,8 @@ CREATE TABLE notice | |||
study_id BIGINT, | |||
created_date DATETIME not null, | |||
last_modified_date DATETIME not null, | |||
type VARCHAR(255) NOT NULL, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
article 테이블로 통합 및 type 추가 👍
private Content content; | ||
|
||
@Enumerated(EnumType.STRING) | ||
private ArticleType type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
} | ||
|
||
public void update(final Accessor accessor, final Content content) { | ||
if (type.isUneditableAccessor(studyRoom, authorId, accessor)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 방법으로 푸니 로직이 훨씬 간결해졌네요 👍
|
||
@Override | ||
boolean isUneditableAccessor(final StudyRoom studyRoom, final Long authorId, final Accessor accessor) { | ||
return !(studyRoom.isPermittedAccessor(accessor) && authorId.equals(accessor.getMemberId())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Accessor 객체의 역할이 궁금해요 ~
- authorId.equals(accessor.getMemberId()를 Accessor에 메시지를 보내도 좋을 것 같아용
final StudyRoom studyRoom = createStudyRoom(owner); | ||
final LinkArticle sut = createLinkArticle(owner, studyRoom); | ||
|
||
sut.delete(new Accessor(1L, 1L)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new Accessor(OWNER_ID, STUDY_ID);
new Accessor(owner.getId(), studyRoom.getId());
new Accessor(1L, 1L);
통일하면 좋을 것 같아요.
여러 곳에서 같은 값이 쓰이면 필드로 빼는 게 관리하기 더 좋을 것 같아요.
요약
reference room 패키지 study room으로 이동
세부사항
close #329