Skip to content

Commit

Permalink
[BE] refactor: 회원 추가로 인한 스키마 변경 (#1075)
Browse files Browse the repository at this point in the history
* refactor: 리뷰 그룹에 memberId 추가 및 회원 종류에 따른 생성자 분리

* refactor: 회원 세션 유무에 따른 리뷰 그룹 생성 로직 분기처리

* refactor: 리뷰 그룹의 그룹액세스코드 null 처리

* refactor: 확정되지 않은 파라미터 주석처리

* test: 테스트 코드 수정

* refactor: 리뷰 그룹 서비스의 메서드 분리를 다시 병합

* refactor: 리뷰에 작성자id(memberId)추가

* test: 테스트 수정사항 반영

* refactor: 리뷰의 필드명 변경 (reviewerId -> memberId)

* flyway: DB 스키마 변경

* refactor: 접근 제어자 수정

* flyway: 컬럼 데이터 타입 명시

* test: 테스트명 변경

* refactor: 테스트에서만 사용되는 생성자 제거

* test: 회원과 비회원의 리뷰 생성에 따른 리뷰 Fixture 생성 및 적용

* refactor: 컨트롤러의 파라미터로 임시 id가 아닌 null을 전달하도록 변경

* refactor: Review 생성자에 @nullable 표시

* test: api테스트에서 모킹 서비스 인자 전달 nullable로 변경

* chore: ci gradle 캐시 삭제 (임시)

* chore: ci gradle 캐시 삭제 제거

* chore: junit-vintage-engine 제외

* chore: junit-vintage-engine 제외 -> 삭제

* chore: 런타임 열거형 오류 방지 의존성 추가

* test: assertAll 임시 분리

* chore: sql 문법 수정

* chore: mockito extension 제거

* chore: 런타임 열거형 오류 방지 의존성 추가 -> 삭제

* refactor: nullable 추가

* test: ReviewGroupServiceTest 삭제 (임시)

* test: ReviewGroupServiceTest 삭제 (임시) -> 추가

* chore: 런타임 열거형 오류 방지 의존성 추가

---------

Co-authored-by: nayonsoso <[email protected]>
  • Loading branch information
Kimprodp and nayonsoso authored Feb 11, 2025
1 parent ad45ead commit 4248c38
Show file tree
Hide file tree
Showing 36 changed files with 316 additions and 132 deletions.
3 changes: 3 additions & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ dependencies {
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
testImplementation 'io.rest-assured:spring-mock-mvc'
testImplementation 'io.rest-assured:rest-assured'

// 런타임 열거형 오류 방지
implementation 'com.google.code.findbugs:jsr305:3.0.2'
}

ext {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
import reviewme.review.service.dto.request.ReviewRegisterRequest;
import reviewme.review.service.dto.response.detail.ReviewDetailResponse;
import reviewme.review.service.dto.response.gathered.ReviewsGatheredBySectionResponse;
import reviewme.review.service.dto.response.list.AuthoredReviewsResponse;
import reviewme.review.service.dto.response.list.ReceivedReviewPageResponse;
import reviewme.review.service.dto.response.list.ReceivedReviewsSummaryResponse;
import reviewme.review.service.dto.response.list.AuthoredReviewsResponse;
import reviewme.reviewgroup.controller.ReviewGroupSession;
import reviewme.reviewgroup.domain.ReviewGroup;

Expand All @@ -35,9 +35,18 @@ public class ReviewController {
private final ReviewGatheredLookupService reviewGatheredLookupService;

@PostMapping("/v2/reviews")
public ResponseEntity<Void> createReview(@Valid @RequestBody ReviewRegisterRequest request) {
// 회원 세션 추후 추가해야 함
long savedReviewId = reviewRegisterService.registerReview(request);
public ResponseEntity<Void> createReview(
@Valid @RequestBody ReviewRegisterRequest request
/*
TODO: 회원 세션 임시 사용 방식, 이후 리졸버를 통해 객체로 받아와야 함
@Nullable @LoginMember Member member
*/
) {
/*
TODO: 회원 세션 유무에 따른 분기처리 로직
Long memberId = Optional.ofNullable(member).map(Member::getId).orElse(null);
*/
long savedReviewId = reviewRegisterService.registerReview(request, null);
return ResponseEntity.created(URI.create("/reviews/" + savedReviewId)).build();
}

Expand Down
8 changes: 7 additions & 1 deletion backend/src/main/java/reviewme/review/domain/Review.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.lang.Nullable;

@Entity
@Table(name = "new_review")
Expand All @@ -31,6 +32,10 @@ public class Review {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

// 리뷰 작성자 id
@Column(name = "member_id", nullable = true)
private Long memberId;

@Column(name = "template_id", nullable = false)
private long templateId;

Expand All @@ -44,7 +49,8 @@ public class Review {
@Column(name = "created_at", nullable = false)
private LocalDateTime createdAt;

public Review(long templateId, long reviewGroupId, List<Answer> answers) {
public Review(@Nullable Long memberId, long templateId, long reviewGroupId, List<Answer> answers) {
this.memberId = memberId;
this.templateId = templateId;
this.reviewGroupId = reviewGroupId;
this.answers = answers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ public class ReviewRegisterService {
private final ReviewRepository reviewRepository;

@Transactional
public long registerReview(ReviewRegisterRequest request) {
Review review = reviewMapper.mapToReview(request);
public long registerReview(ReviewRegisterRequest request, Long memberId) {
Review review = reviewMapper.mapToReview(request, memberId);
reviewValidator.validate(review);
Review registeredReview = reviewRepository.save(review);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class ReviewMapper {
private final QuestionRepository questionRepository;
private final TemplateRepository templateRepository;

public Review mapToReview(ReviewRegisterRequest request) {
public Review mapToReview(ReviewRegisterRequest request, Long memberId) {
ReviewGroup reviewGroup = reviewGroupRepository.findByReviewRequestCode(request.reviewRequestCode())
.orElseThrow(() -> new ReviewGroupNotFoundByReviewRequestCodeException(request.reviewRequestCode()));
Template template = templateRepository.findById(reviewGroup.getTemplateId())
Expand All @@ -40,7 +40,7 @@ public Review mapToReview(ReviewRegisterRequest request) {
));

List<Answer> answers = getAnswersByQuestionType(request);
return new Review(template.getId(), reviewGroup.getId(), answers);
return new Review(memberId, template.getId(), reviewGroup.getId(), answers);
}

private List<Answer> getAnswersByQuestionType(ReviewRegisterRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,18 @@ public ResponseEntity<ReviewGroupResponse> getReviewGroupSummary(@RequestParam S
@PostMapping("/v2/groups")
public ResponseEntity<ReviewGroupCreationResponse> createReviewGroup(
@Valid @RequestBody ReviewGroupCreationRequest request
/*
TODO: 회원 세션 임시 사용 방식, 이후 리졸버를 통해 객체로 받아와야 함
@Nullable @LoginMember Member member
*/
) {
// 회원 세션 추후 추가해야 함
ReviewGroupCreationResponse response = reviewGroupService.createReviewGroup(request);
/*
TODO: 회원 세션 유무에 따른 분기처리 로직
Long memberId = Optional.ofNullable(member).map(Member::getId).orElse(null);
ReviewGroupCreationResponse response = reviewGroupService.createReviewGroup(request, memberId);
return ResponseEntity.ok(response);
*/
ReviewGroupCreationResponse response = reviewGroupService.createReviewGroup(request, null);
return ResponseEntity.ok(response);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class GroupAccessCode {

private static final Pattern PATTERN = Pattern.compile("^[a-zA-Z0-9]{4,20}$");

@Column(name = "group_access_code", nullable = false)
@Column(name = "group_access_code", nullable = true)
private String code;

public GroupAccessCode(String code) {
Expand Down
35 changes: 27 additions & 8 deletions backend/src/main/java/reviewme/reviewgroup/domain/ReviewGroup.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package reviewme.reviewgroup.domain;

import jakarta.annotation.Nullable;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
Expand Down Expand Up @@ -30,6 +31,12 @@ public class ReviewGroup {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(name = "member_id", nullable = true)
private Long memberId;

@Column(name = "template_id", nullable = false)
private long templateId;

@Column(name = "reviewee", nullable = false)
private String reviewee;

Expand All @@ -42,18 +49,30 @@ public class ReviewGroup {
@Embedded
private GroupAccessCode groupAccessCode;

@Column(name = "template_id", nullable = false)
private long templateId;

public ReviewGroup(String reviewee, String projectName, String reviewRequestCode, String groupAccessCode,
long templateId) {
private ReviewGroup(@Nullable Long memberId, long templateId, String reviewee, String projectName,
String reviewRequestCode, String groupAccessCode) {
validateRevieweeLength(reviewee);
validateProjectNameLength(projectName);
this.memberId = memberId;
this.templateId = templateId;
this.reviewee = reviewee;
this.projectName = projectName;
this.reviewRequestCode = reviewRequestCode;
this.groupAccessCode = new GroupAccessCode(groupAccessCode);
this.templateId = templateId;
if (groupAccessCode != null) {
this.groupAccessCode = new GroupAccessCode(groupAccessCode);
} else {
this.groupAccessCode = null;
}
}

public ReviewGroup(Long memberId, long templateId, String reviewee, String projectName, String reviewRequestCode) {
this(memberId, templateId, reviewee, projectName, reviewRequestCode, null);
}

public ReviewGroup(long templateId, String reviewee, String projectName, String reviewRequestCode,
String groupAccessCode) {
this(null, templateId, reviewee, projectName, reviewRequestCode, groupAccessCode);
}

private void validateRevieweeLength(String reviewee) {
Expand All @@ -71,10 +90,10 @@ private void validateProjectNameLength(String projectName) {
}

public boolean matchesGroupAccessCode(String code) {
return groupAccessCode.matches(code);
return groupAccessCode != null && groupAccessCode.matches(code);
}

public String getGroupAccessCode() {
return groupAccessCode.getCode();
return groupAccessCode != null ? groupAccessCode.getCode() : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import reviewme.reviewgroup.repository.ReviewGroupRepository;
import reviewme.reviewgroup.service.dto.ReviewGroupCreationRequest;
import reviewme.reviewgroup.service.dto.ReviewGroupCreationResponse;
import reviewme.reviewgroup.service.exception.GroupAccessCodeNullException;
import reviewme.reviewgroup.service.exception.ReviewGroupNotFoundByReviewRequestCodeException;
import reviewme.template.domain.Template;
import reviewme.template.repository.TemplateRepository;
Expand All @@ -24,25 +25,37 @@ public class ReviewGroupService {
private final TemplateRepository templateRepository;

@Transactional
public ReviewGroupCreationResponse createReviewGroup(ReviewGroupCreationRequest request) {
// 회원, 비회원 분기 처리 필요
String reviewRequestCode;
do {
reviewRequestCode = randomCodeGenerator.generate(REVIEW_REQUEST_CODE_LENGTH);
} while (reviewGroupRepository.existsByReviewRequestCode(reviewRequestCode));
public ReviewGroupCreationResponse createReviewGroup(ReviewGroupCreationRequest request, Long memberId) {
String reviewRequestCode = generateReviewRequestCode();

Template template = templateRepository.findById(DEFAULT_TEMPLATE_ID)
.orElseThrow(() -> new TemplateNotFoundException(DEFAULT_TEMPLATE_ID));

ReviewGroup reviewGroup = reviewGroupRepository.save(
new ReviewGroup(
request.revieweeName(), request.projectName(), reviewRequestCode, request.groupAccessCode(),
template.getId()
)
);
ReviewGroup reviewGroup;
if (memberId != null) {
reviewGroup = new ReviewGroup(memberId, template.getId(), request.revieweeName(), request.projectName(),
reviewRequestCode);
} else {
if (request.groupAccessCode() == null) {
throw new GroupAccessCodeNullException();
}
reviewGroup = new ReviewGroup(template.getId(), request.revieweeName(), request.projectName(),
reviewRequestCode, request.groupAccessCode());
}

reviewGroupRepository.save(reviewGroup);
return new ReviewGroupCreationResponse(reviewGroup.getReviewRequestCode());
}

private String generateReviewRequestCode() {
String reviewRequestCode;
do {
reviewRequestCode = randomCodeGenerator.generate(REVIEW_REQUEST_CODE_LENGTH);
} while (reviewGroupRepository.existsByReviewRequestCode(reviewRequestCode));

return reviewRequestCode;
}

@Transactional(readOnly = true)
public ReviewGroup getReviewGroupByReviewRequestCode(String reviewRequestCode) {
return reviewGroupRepository.findByReviewRequestCode(reviewRequestCode)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package reviewme.reviewgroup.service.exception;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class GroupAccessCodeNullException extends NullPointerException{
public GroupAccessCodeNullException() {
super("비회원 리뷰 그룹 생성은 그룹 액세스 코드를 필수로 입력해야 해요.");
log.info("Non-member review group creation failed: Group access code is required");
}
}
8 changes: 8 additions & 0 deletions backend/src/main/resources/db/migration/V6__member_id_add.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- 리뷰 그룹에 memberId를 추가합니다.
-- 리뷰 그룹의 group_access_code에 null을 허용합니다.

ALTER TABLE review_group ADD COLUMN member_id BIGINT NULL;
ALTER TABLE review_group MODIFY COLUMN group_access_code VARCHAR(255) NULL;

-- 리뷰에 memberId를 추가합니다.
ALTER TABLE review ADD COLUMN member_id BIGINT NULL;
7 changes: 4 additions & 3 deletions backend/src/test/java/reviewme/api/ReviewApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.nullable;
import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName;
import static org.springframework.restdocs.cookies.CookieDocumentation.requestCookies;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
Expand Down Expand Up @@ -58,7 +59,7 @@ class ReviewApiTest extends ApiTest {

@Test
void 비회원이_리뷰를_등록한다() {
BDDMockito.given(reviewRegisterService.registerReview(any(ReviewRegisterRequest.class)))
BDDMockito.given(reviewRegisterService.registerReview(any(ReviewRegisterRequest.class), anyLong()))
.willReturn(1L);

FieldDescriptor[] requestFieldDescriptors = {
Expand All @@ -85,7 +86,7 @@ class ReviewApiTest extends ApiTest {

@Test
void 회원이_리뷰를_등록한다() {
BDDMockito.given(reviewRegisterService.registerReview(any(ReviewRegisterRequest.class)))
BDDMockito.given(reviewRegisterService.registerReview(any(ReviewRegisterRequest.class), anyLong()))
.willReturn(1L);

CookieDescriptor[] cookieDescriptors = {
Expand Down Expand Up @@ -118,7 +119,7 @@ class ReviewApiTest extends ApiTest {

@Test
void 리뷰_그룹_코드가_올바르지_않은_경우_예외가_발생한다() {
BDDMockito.given(reviewRegisterService.registerReview(any(ReviewRegisterRequest.class)))
BDDMockito.given(reviewRegisterService.registerReview(any(ReviewRegisterRequest.class), nullable(Long.class)))
.willThrow(new ReviewGroupNotFoundByReviewRequestCodeException("ABCD1234"));

FieldDescriptor[] requestFieldDescriptors = {
Expand Down
7 changes: 4 additions & 3 deletions backend/src/test/java/reviewme/api/ReviewGroupApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.nullable;
import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName;
import static org.springframework.restdocs.cookies.CookieDocumentation.requestCookies;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
Expand Down Expand Up @@ -30,7 +31,7 @@ class ReviewGroupApiTest extends ApiTest {

@Test
void 비회원용_리뷰_그룹을_생성한다() {
BDDMockito.given(reviewGroupService.createReviewGroup(any(ReviewGroupCreationRequest.class)))
BDDMockito.given(reviewGroupService.createReviewGroup(any(ReviewGroupCreationRequest.class), nullable(Long.class)))
.willReturn(new ReviewGroupCreationResponse("ABCD1234"));

String request = """
Expand Down Expand Up @@ -67,7 +68,7 @@ class ReviewGroupApiTest extends ApiTest {

@Test
void 회원용_리뷰_그룹을_생성한다() {
BDDMockito.given(reviewGroupService.createReviewGroup(any(ReviewGroupCreationRequest.class)))
BDDMockito.given(reviewGroupService.createReviewGroup(any(ReviewGroupCreationRequest.class), nullable(Long.class)))
.willReturn(new ReviewGroupCreationResponse("ABCD1234"));

CookieDescriptor[] cookieDescriptors = {
Expand Down Expand Up @@ -109,7 +110,7 @@ class ReviewGroupApiTest extends ApiTest {
@Test
void 리뷰_요청_코드로_회원이_만든_리뷰_그룹_정보를_반환한다() {
BDDMockito.given(reviewGroupLookupService.getReviewGroupSummary(anyString()))
.willReturn(new ReviewGroupResponse(1L,"아루", "리뷰미"));
.willReturn(new ReviewGroupResponse(1L, "아루", "리뷰미"));

ParameterDescriptor[] parameterDescriptors = {
parameterWithName("reviewRequestCode").description("리뷰 요청 코드")
Expand Down
16 changes: 16 additions & 0 deletions backend/src/test/java/reviewme/fixture/ReviewFixture.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package reviewme.fixture;

import java.util.List;
import reviewme.review.domain.Answer;
import reviewme.review.domain.Review;

public class ReviewFixture {

public static Review 비회원_작성_리뷰(long templateId, long reviewGroupId, List<Answer> answers) {
return new Review(null, templateId, reviewGroupId, answers);
}

public static Review 회원_작성_리뷰(Long memberId, long templateId, long reviewGroupId, List<Answer> answers) {
return new Review(memberId, templateId, reviewGroupId, answers);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
public class ReviewGroupFixture {

public static ReviewGroup 리뷰_그룹(String reviewRequestCode, String groupAccessCode) {
return new ReviewGroup("revieweeName", "projectName", reviewRequestCode, groupAccessCode, 1L);
return new ReviewGroup(1L, "revieweeName", "projectName", reviewRequestCode, groupAccessCode);
}

public static ReviewGroup 템플릿_지정_리뷰_그룹(long templateId) {
return new ReviewGroup("reviewee", "project", "requestCode", "accessCode", templateId);
return new ReviewGroup(templateId, "reviewee", "project", "requestCode", "accessCode");
}

public static ReviewGroup 리뷰_그룹() {
Expand Down
Loading

0 comments on commit 4248c38

Please sign in to comment.