Skip to content
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

[REFACTOR] 게시글 도메인 리팩터링 #549

Merged
merged 49 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
8364575
refactor: (#512) 게시글 댓글 도메인 리팩토링
woo-chang Sep 5, 2023
9532248
refactor: (#512) 테스트 엔티티 저장 유틸 클래스 구현
woo-chang Sep 7, 2023
0d6999e
refactor: (#512) 게시글 도메인 리팩토링
woo-chang Sep 7, 2023
9d1ca69
refactor: (#512) 게시글 DTO 중복 제거 및 리팩토링
woo-chang Sep 7, 2023
81e6844
refactor: (#512) 게시글 게스트 조회 기능에 대한 레포지토리 리팩토링
woo-chang Sep 7, 2023
e7d0086
refactor: (#512) 게스트 게시글 기능 리팩토링
woo-chang Sep 7, 2023
faef4bd
refactor: (#512) 게시글 조회 관련 레포지토리 리팩토링
woo-chang Sep 8, 2023
7ff5833
refactor: (#512) 게시글 조회 관련 애플리케이션 로직 리팩토링
woo-chang Sep 8, 2023
e299018
refactor: (#512) 게시글 조회 관련 API 리팩토링
woo-chang Sep 9, 2023
72b3da2
refactor: (#512) 사용하지 않는 클래스 제거
woo-chang Sep 10, 2023
b6991b4
refactor: (#512) 게시글 쿼리 기능을 위한 부가 클래스 구현
woo-chang Sep 10, 2023
65af6c4
refactor: (#512) 게시글 도메인 리팩토링
woo-chang Sep 10, 2023
2402ea2
refactor: (#512) 게시글 쿼리 관련 레포지토리 기능 구현
woo-chang Sep 10, 2023
3d5a1de
refactor: (#512) 블라인드 게시글 조회 예외 처리
woo-chang Sep 10, 2023
73bda7c
refactor: (#512) 이미지 파일명 생성 기능 구현
woo-chang Sep 10, 2023
6fe7a98
refactor: (#512) 이미지 저장 및 삭제 기능 구현
woo-chang Sep 10, 2023
0e6704b
refactor: (#512) 게시글 댓글 블라인드 예외 처리
woo-chang Sep 10, 2023
6d19f77
refactor: (#512) 게시글 작성, 수정, 삭제 로직 리팩토링
woo-chang Sep 10, 2023
d1664a4
refactor: (#512) 게시글 작성, 수정, 삭제 API 리팩토링
woo-chang Sep 10, 2023
c434481
chore: (#512) 코드 컨벤션 정리
woo-chang Sep 10, 2023
d9268f0
refactor: (#512) 게시글 요청, 응답 형식 수정
woo-chang Sep 13, 2023
e3d94b0
refactor: (#512) 게시글 도메인 수정
woo-chang Sep 13, 2023
0b39825
refactor: (#512) 요청, 응답 수정에 따른 변화 수정
woo-chang Sep 13, 2023
295f94d
refactor: (#512) 게시글 수정 로직 개선
woo-chang Sep 13, 2023
1ecd08d
refactor: (#512) 요청 파일 크기 제한 설정
woo-chang Sep 13, 2023
6d1d394
refactor: (#512) sequence가 필요한 메서드에서 sequence에 의존하도록 수정
woo-chang Sep 16, 2023
f92607e
refactor: (#512) 코드 컨벤션에 맞게 name 속성 제거
woo-chang Sep 16, 2023
b8f52bb
refactor: (#512) 게시글 옵션 예외 분리
woo-chang Sep 16, 2023
5ce0e1e
refactor: (#512) 임의의 url로 수정
woo-chang Sep 17, 2023
8fb3e1b
refactor: (#512) 람다식 컨벤션 수정
woo-chang Sep 17, 2023
4a38b2a
refactor: (#512) enum 컨벤션 수정
woo-chang Sep 17, 2023
2e42394
refactor: (#512) 변수명 수정
woo-chang Sep 17, 2023
6521e92
refactor: (#512) 쿼리, 커맨드 트랜잭션 방식 명시
woo-chang Sep 17, 2023
b94ee8e
refactor: (#512) early return 방식으로 수정
woo-chang Sep 17, 2023
82d3bcc
refactor: (#512) 메서드 체이닝 방식으로 수정
woo-chang Sep 17, 2023
dd83cdc
refactor: (#512) early return 방식으로 수정
woo-chang Sep 17, 2023
776f051
refactor: (#512) 게시글에 대한 신고 삭제 기능 구현
woo-chang Sep 17, 2023
46db22d
refactor: (#512) 기존 게시글 옵션이 아닌 경우 예외 던지도록 수정
woo-chang Sep 17, 2023
0ea2280
refactor: (#512) 예외 타입 네이밍 수정
woo-chang Sep 19, 2023
fc7c107
refactor: (#512) 투표 수 int -> long 전환
woo-chang Sep 19, 2023
16bbcb9
refactor: (#512) 게시글 옵션 ID 검증 로직 분리
woo-chang Sep 19, 2023
69b9c4d
Merge branch 'dev' into #512
woo-chang Sep 19, 2023
e58ac1f
refactor: (#512) 충돌 해결하면서 터지는 테스트 수정
woo-chang Sep 19, 2023
e7e05ab
refactor: (#512) 코드 컨벤션 수정
woo-chang Sep 19, 2023
f971cbe
Merge branch 'dev' into #512
woo-chang Sep 19, 2023
5c0e7eb
refactor: (#512) hikari, JpaTransactionManager 로그 제거
woo-chang Sep 19, 2023
6f0589a
feat: (#512) tomcat 설정 환경변수 추가
woo-chang Sep 19, 2023
bd78629
feat: (#512) 게시글 리팩토링
woo-chang Sep 19, 2023
443b151
Merge branch 'dev' into #512
woo-chang Sep 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,13 @@
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.apache.commons.lang3.RandomStringUtils;

@Table(indexes = {@Index(columnList = "socialId, socialType")})
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(of = {"id"})
@ToString
@Getter
@Entity
@EqualsAndHashCode(of = {"id"}, callSuper = false)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(indexes = {@Index(columnList = "socialId, socialType")})
public class Member extends BaseEntity {

private static final String INITIAL_NICKNAME_PREFIX = "익명의손님";
Expand All @@ -44,7 +42,6 @@ public class Member extends BaseEntity {
@Column(length = 20)
private Gender gender;

@Column
private Integer birthYear;

Comment on lines 47 to 49
Copy link
Collaborator

Choose a reason for hiding this comment

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

Q
@Column이 사라졌는데 없어도 되나요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@Entity 필드는 자동으로 테이블과 매핑이 되기에 이름이나 별다른 속성이 없으면 제거해도 괜찮을 것 같아서 삭제했습니다 ㅎㅎ

@Enumerated(value = EnumType.STRING)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,28 @@
@Getter
public enum AgeRange {

UNDER_TEENS("10대 미만", 1, 9),
TEENS("10대", 10, 19),
TWENTIES("20대", 20, 29),
THIRTIES("30대", 30, 39),
FORTIES("40대", 40, 49),
FIFTIES("50대", 50, 59),
OVER_SIXTIES("60대 이상", 60, 999),
UNDER_TEENS("10대 미만", 0),
TEENS("10대", 1),
TWENTIES("20대", 2),
THIRTIES("30대", 3),
FORTIES("40대", 4),
FIFTIES("50대", 5),
OVER_SIXTIES("60대 이상", 6),
;

private final String name;
private final int startAge;
private final int endAge;
private final int ageGroup;

AgeRange(
final String name,
final int startAge,
final int endAge
) {
AgeRange(final String name, final int ageGroup) {
this.name = name;
this.startAge = startAge;
this.endAge = endAge;
this.ageGroup = ageGroup;
}

public static AgeRange from(final int age) {
public static AgeRange from(final int ageGroup) {
return Arrays.stream(AgeRange.values())
.filter(ageRange -> ageRange.isBelong(age))
.filter(ageRange -> ageRange.ageGroup == ageGroup)
.findAny()
.orElseThrow(() -> new BadRequestException(MemberExceptionType.INVALID_AGE));
}

private boolean isBelong(final int age) {
return this.startAge <= age && this.endAge >= age;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,28 @@
import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Embeddable
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Nickname {

private static final int MINIMUM_NICKNAME_LENGTH = 2;
private static final int MAXIMUM_NICKNAME_LENGTH = 15;
private static final Pattern PATTERN = Pattern.compile("^[가-힣a-zA-Z0-9]+$");
Copy link
Collaborator

Choose a reason for hiding this comment

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

요런것도 있군요 신기하네요 👍


@Column(name = "nickname", length = 20, unique = true, nullable = false)
private String value;

public Nickname(final String nickname) {
validateNickname(nickname);
this.value = nickname;
public Nickname(final String value) {
validateNickname(value);
this.value = value;
}

private void validateNickname(final String nickname) {
if (nickname.length() < MINIMUM_NICKNAME_LENGTH || nickname.length() > MAXIMUM_NICKNAME_LENGTH) {
private void validateNickname(final String value) {
if (value.length() < MINIMUM_NICKNAME_LENGTH || value.length() > MAXIMUM_NICKNAME_LENGTH) {
throw new BadRequestException(MemberExceptionType.INVALID_NICKNAME_LENGTH);
}
if (!Pattern.matches("^[가-힣a-zA-Z0-9]+$", nickname)) {
if (!PATTERN.matcher(value).matches()) {
throw new BadRequestException(MemberExceptionType.INVALID_NICKNAME_LETTER);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ private List<Post> deletePosts(final Member member) {
}

private List<Comment> deleteComments(final Member member) {
final List<Comment> comments = commentRepository.findAllByMember(member);
final List<Comment> comments = commentRepository.findAllByWriter(member);
final List<Long> commentIds = comments.stream()
.map(Comment::getId)
.toList();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.votogether.domain.post.controller;

import com.votogether.domain.member.entity.Member;
import com.votogether.domain.post.dto.request.post.PostCreateRequest;
import com.votogether.domain.post.dto.request.post.PostUpdateRequest;
import com.votogether.domain.post.service.PostCommandService;
import com.votogether.global.jwt.Auth;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Positive;
import java.net.URI;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Validated
Copy link
Collaborator

Choose a reason for hiding this comment

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

Q
이 어노테이션은 spring에서 제공하는 것 같은데 @Valid와는 어떤 차이가 있나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@Validated는 스프링에서 제공하는 기능으로 바인딩 과정에서 검증이 이루어지는 것이 아닌 AOP 기술을 통해 검증이 진행되게 됩니다.

@Valid는 기본적으로 컨트롤러에서만 동작하며 Java Bean 객체만 검증하기 때문에 Dto가 아닌 @PathVariable, @RequestParm의 검증은 수행할 수 없기에 해당 값들을 검증하기 위해 추가적인 어노테이션을 사용하였습니다!

Copy link
Collaborator

Choose a reason for hiding this comment

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

오 처음 알게된 사실이네요..!
설명 감사합니다 새로운 걸 배워가네요

@RequiredArgsConstructor
@RequestMapping("/posts")
@RestController
public class PostCommandController {

private final PostCommandService postCommandService;

@PostMapping(consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<Void> createPost(
@ModelAttribute @Valid final PostCreateRequest postCreateRequest,
@Auth final Member loginMember
) {
final Long postId = postCommandService.createPost(postCreateRequest, loginMember);
return ResponseEntity.created(URI.create("/posts/" + postId)).build();
}

@PutMapping(value = "/{postId}", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<Void> updatePost(
@PathVariable @Positive(message = "게시글 ID는 양의 정수만 가능합니다.") final Long postId,
@ModelAttribute @Valid final PostUpdateRequest postUpdateRequest,
@Auth final Member loginMember
) {
postCommandService.updatePost(postId, postUpdateRequest, loginMember);
return ResponseEntity.ok().build();
}

@PatchMapping("/{postId}/close")
public ResponseEntity<Void> closePostEarly(
@PathVariable @Positive(message = "게시글 ID는 양의 정수만 가능합니다.") final Long postId,
@Auth final Member loginMember
) {
postCommandService.closePostEarly(postId, loginMember);
return ResponseEntity.ok().build();
}

@DeleteMapping("/{postId}")
public ResponseEntity<Void> deletePost(
@PathVariable @Positive(message = "게시글 ID는 양의 정수만 가능합니다.") final Long postId,
@Auth final Member loginMember
) {
postCommandService.deletePost(postId, loginMember);
return ResponseEntity.noContent().build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package com.votogether.domain.post.controller;

import com.votogether.domain.member.entity.Member;
import com.votogether.domain.post.dto.request.post.PostCreateRequest;
import com.votogether.domain.post.dto.request.post.PostUpdateRequest;
import com.votogether.global.exception.ExceptionResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Positive;
import org.springframework.http.ResponseEntity;

@Tag(name = "게시글 커맨드", description = "게시글 커맨드 API")
public interface PostCommandControllerDocs {

@Operation(summary = "게시글 작성", description = "게시글을 작성한다.")
@ApiResponses({
@ApiResponse(
responseCode = "201",
description = "게시글 작성 성공"
),
@ApiResponse(
responseCode = "400",
description = "정상적이지 않은 요청",
content = @Content(schema = @Schema(implementation = ExceptionResponse.class))
)
})
ResponseEntity<Void> createPost(
@Valid final PostCreateRequest postCreateRequest,
final Member loginMember
);

@Operation(summary = "게시글 수정", description = "게시글을 수정한다.")
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "게시글 수정 성공"
),
@ApiResponse(
responseCode = "400",
description = """
1.게시글 ID가 양의 정수가 아닌 경우

2.정상적이지 않은 요청

3.게시글이 블라인드 처리된 경우

4.게시글 작성자가 아닌 경우
""",
content = @Content(schema = @Schema(implementation = ExceptionResponse.class))
),
@ApiResponse(
responseCode = "404",
description = "존재하지 않는 게시글",
content = @Content(schema = @Schema(implementation = ExceptionResponse.class))
)
})
ResponseEntity<Void> updatePost(
@Parameter(description = "게시글 ID", example = "1")
@Positive(message = "게시글 ID는 양의 정수만 가능합니다.") final Long postId,
@Valid final PostUpdateRequest postUpdateRequest,
final Member loginMember
);

@Operation(summary = "게시글 조기 마감", description = "게시글을 조기 마감한다.")
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "게시글 조기 마감 성공"
),
@ApiResponse(
responseCode = "400",
description = """
1.게시글 ID가 양의 정수가 아닌 경우

2.게시글이 블라인드 처리된 경우

3.게시글 작성자가 아닌 경우
""",
content = @Content(schema = @Schema(implementation = ExceptionResponse.class))
),
@ApiResponse(
responseCode = "404",
description = "존재하지 않는 게시글",
content = @Content(schema = @Schema(implementation = ExceptionResponse.class))
)
})
ResponseEntity<Void> closePostEarly(
@Parameter(description = "게시글 ID", example = "1")
@Positive(message = "게시글 ID는 양의 정수만 가능합니다.") final Long postId,
final Member loginMember
);

@Operation(summary = "게시글 삭제", description = "게시글을 삭제한다.")
@ApiResponses({
@ApiResponse(
responseCode = "204",
description = "게시글 삭제 성공"
),
@ApiResponse(
responseCode = "400",
description = """
1.게시글 ID가 양의 정수가 아닌 경우

2.게시글이 블라인드 처리된 경우

3.게시글 작성자가 아닌 경우

4.삭제할 수 없는 투표 수가 존재하는 게시글인 경우
""",
content = @Content(schema = @Schema(implementation = ExceptionResponse.class))
),
@ApiResponse(
responseCode = "404",
description = "존재하지 않는 게시글",
content = @Content(schema = @Schema(implementation = ExceptionResponse.class))
)
})
ResponseEntity<Void> deletePost(
@Parameter(description = "게시글 ID", example = "1")
@Positive(message = "게시글 ID는 양의 정수만 가능합니다.") final Long postId,
final Member loginMember
);

}
Loading