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

[한현승] Good-Night-3rd-Hackathon-Backend 제출 #21

Open
wants to merge 34 commits into
base: main
Choose a base branch
from

Conversation

82everywin
Copy link
Member

SpringBoot로 구현하였습니다.

주말동안 가족행사로 부득이하게 어제 저녁부터 시작하였습니다.
감사합니다.😊

@Operation(summary = "댓글 등록")
@PostMapping("/{wishId}")
public ResponseEntity<CommentInfoResponseDto> createComment(@PathVariable Long wishId, @Valid @RequestBody CommentCreateRequestDto commentCreateRequestDto){
CommentInfoResponseDto responseDto = commentService.create(wishId, commentCreateRequestDto);

Choose a reason for hiding this comment

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

CommentInfoResponseDto는 Presentation Layer인데 비즈니스 레이어인 commentService에서 CommentInfoResponseDto를 반환하는거는 레이어 역행입니다.

Copy link
Member Author

Choose a reason for hiding this comment

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

이해했습니다! 감사합니다. 말씀해주신 토대로 Presentation Layer 와 Business Layer 에 맞춰서, 서비스, 컨트롤러 로직 수정 했습니다! 감사합니다!

Comment on lines 27 to 29
@Operation(summary = "댓글 조회")
@GetMapping("/{wishId}")
public ResponseEntity<Page<CommentInfoResponseDto>> getComments(@PathVariable Long wishId,

Choose a reason for hiding this comment

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

여기도 wishId들어가는게 이상합니다.

Copy link
Member Author

Choose a reason for hiding this comment

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

해당 소원에 댓글이 달려서 경로변수로 wishId를 넣고, 댓글 조회는 해당 소원의 모든 댓글을 확인할 수 있도록 하기 위해 wishId라고 적었는데 혹시, 변경한다면 id로 변경하는게 나은 방법일까요? 아니면 다른 방식이 더 나은가요??

Choose a reason for hiding this comment

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

wish는 별도의 리소스이기 때문에 WishController가 되어야될거 같아요.

Comment on lines 31 to 32
@RequestParam(defaultValue = "5") int size){

Choose a reason for hiding this comment

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

페이징 사이즈 5는 너무 작은데, 크게 잡으면 좋아요

Copy link
Member Author

Choose a reason for hiding this comment

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

처음 생각할 때, 소원이 많이 없을 것이라 생각하여, 페이징을 작게 잡은 것 같습니다! 10으로 잡겠습니다!

Choose a reason for hiding this comment

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

100으로 잡으면 괜찮습니다.

Comment on lines 33 to 34
Page<CommentInfoResponseDto> comments = commentService.getComments(wishId, page, size);
return ResponseEntity.status(HttpStatus.OK).body(comments);

Choose a reason for hiding this comment

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

여기도 위와 유사하게 레이어 역행인데, 서비스에서 List<CommentInfo> 같은걸 반환해서 여기서 ResponseDto로 매핑을 해줘야합니다.

Copy link
Member Author

Choose a reason for hiding this comment

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

감사합니다!
서비스 계층에서 Page<Comment> 값으로 반환하도록 하였고, 컨트롤러에서 Page<CommentInfoResponseDto>로 반환하도록 하여, dto를 컨트롤러에서 매핑을 했는데 이렇게 하는게 맞을까요?

Choose a reason for hiding this comment

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

네네 맞아요

Comment on lines +36 to +39
public ResponseEntity<Void> deleteWish(@PathVariable Long id){
wishService.softDelete(id);
return ResponseEntity.noContent().build();
}

Choose a reason for hiding this comment

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

성공하면 HttpOk를 응답해줘도 될거같네요

Copy link
Member Author

Choose a reason for hiding this comment

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

상태 코드 204는 OK처리를 하지만, response body에 아무것도 담지 않을 때 사용한다고 배웠습니다. 그래서 삭제처리를 하면 204를 쓰는게 좋다고 배웠는데, soft-delete 처리를 할 때는 200처리가 더 나을까요?

Choose a reason for hiding this comment

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

그런 의견이면 204도 괜찮겠네요. 저는 그냥 200 OK를 사용해서 의견을 드려봤습니다.

Comment on lines 44 to 53
List<Wish> pendingWishes = wishService.getPendingWishes();
List<WishInfoResponseDto> responseDtos= new ArrayList<>();
for(Wish wish : pendingWishes){
WishInfoResponseDto responseDto = WishInfoResponseDto.builder()
.id(wish.getId())
.title(wish.getTitle())
.content(wish.getContent())
.category(wish.getCategory())
.build();
responseDtos.add(responseDto);

Choose a reason for hiding this comment

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

map을 활용하여 아래와 같이 작성하면 불필요한 변수나 리스트 사용을 안할거 같아요

List<WishInfoResponseDto> responseDtos = pendingWishes.stream().map(wish -> WishInfoResponseDto.ofCreate(
                wish.getId(), wish.getTitle(), wish.getContent(), wish.getCategory()
        )).toList();


 public static class WishInfoResponseDto {
        private Long id;
        private String title;
        private String content;
        private Category category;

        private WishInfoResponseDto(Long id, String title, String content, Category category) {
            this.id = id;
            this.title = title;
            this.content = content;
            this.category = category;
        }

        public static WishInfoResponseDto ofCreate(Long id, String title, String content, Category category) {
            return new WishInfoResponseDto(id, title, content, category);
        }
    }

Copy link
Member Author

Choose a reason for hiding this comment

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

감사합니다!!

다만, 말씀 해주신 부분에서 WishInfoResponseDto에서 @Builder로 인해서 명시적인 생성자를 작성하지 않아도 오류없이 작동하기는 하는데, 이렇게 작성해도 될까요?

@Builder 
@Getter
public static class WishInfoResponseDto {
        private Long id;
        private String title;
        private String content;
        private Category category;

        public static WishInfoResponseDto ofCreate(Long id, String title, String content, Category category) {
            return new WishInfoResponseDto(id, title, content, category);
        }
    }

Choose a reason for hiding this comment

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

�개인의 취향이라 상관없을거 같아요,

Comment on lines +16 to +18
public static class CommentCreateRequestDto {

@NotBlank(message = "댓글은 필수 입력 항목입니다.")

Choose a reason for hiding this comment

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

취향의 차이이긴한데 저희는 static class를 사용하진 않고 다 클래스 분리합니다.

Copy link
Member Author

Choose a reason for hiding this comment

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

분리해서 사용한다면, 제가 작성한 static class가 모두 각 한 개씩 class로 분리하여 사용하신다는 말씀인거죠..?

Comment on lines 32 to 39
Wish wish = wishRepository.findById(id)
.filter(w -> {
if (w.getIsConfirm() != Confirm.CONFIRM) {
throw new BaseException(ErrorCode.NOT_CONFIRMED);
}
return true;
})
.orElseThrow(() -> new BaseException(ErrorCode.NOT_EXIST_WISH));

Choose a reason for hiding this comment

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

한번에 1가지 역할만하는게 좋을거 같은데, 우선 Wish를 찾고 나서 CONFRIRM 체크하는 비즈니스 로직을 if로 처리하면 가독성이 더 좋을거 같습니다.

Copy link
Member Author

@82everywin 82everywin Sep 11, 2024

Choose a reason for hiding this comment

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

말씀해주신 것처럼 filter로 계속 처리하기 보다는 if로 처리하는 게 훨씬 깔끔 하게 수정된 것 같습니다!

        WishEntity wish = wishRepository.findById(id).orElseThrow(() -> new BaseException(ErrorCode.NOT_EXIST_WISH));

        if(wish.getIsConfirm() != Confirm.CONFIRM)
            throw new BaseException(ErrorCode.NOT_EXIST_WISH);

이렇게 코드 수정을 해봤는데 괜찮을까요?

Choose a reason for hiding this comment

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

네네 상관없을거 같네요. wish.getIsConfirm() != Confirm.CONFIRM는 객체에서 isNotConfirmed()로 만들수 있겠네요

import java.util.List;
import java.util.Optional;

public interface WishRepository extends JpaRepository<Wish, Long> {

Choose a reason for hiding this comment

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

jpa엔티티뒤에는 postfix로 Entity를 붙이면 persistence layer라는걸 알 수 있어서 좋습니다. Wish가 순간 비즈니스 레이어인줄 알았어요

Copy link
Member Author

Choose a reason for hiding this comment

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

아 넵! 생각하지 못한 부분이라 미흡했던 것 같습니다! 수정하도록 하겠습니다!

Comment on lines 53 to 54
@Transactional(readOnly = true)
public Page<CommentInfoResponseDto> getComments(Long wishId, int page, int size){

Choose a reason for hiding this comment

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

데이터 변경하는게 없기 때문에 굳이 @Transactional(readOnly=true)를 붙일필요는 없을거 같아요.
JPA 내부 read 메서드들에 @Transactional(readOnly=true 가 붙어있습니다.

Copy link
Member Author

@82everywin 82everywin Sep 11, 2024

Choose a reason for hiding this comment

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

감사합니다. @Transactional(readOnly=true)이 읽기 전용으로 쿼리를 진행하도록 하여 데이터의 무결성을 보장하기 위해 사용하였습니다. 그러나 이 어노테이션을 붙일 필요가 없다고 말씀 주신 이유가 데이터를 변경하는 것이 없기 때문에 굳이 필요하지 않다고 말씀 주신건지 궁금합니다.
또한 혹시, JPA 내부 메서드들에 @Transactional(readOnly=true)가 어노테이션 되어있기 때문에, 중복으로 할 필요가 없다는 의미로 말씀 주신 건지 궁금합니다.

Choose a reason for hiding this comment

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

둘 다 맞는데요. JPA 내부적으로 어노테이션이 붙어있기도 하고, 데이터 변경이 없기 때문에 @Transactional을 안붙여도 될거같다고 의견을 드렸습니다.

Comment on lines 58 to 71
Wish wish = wishRepository.findById(wishId)
.filter(w -> {
if(w.getIsConfirm() != Confirm.CONFIRM) {
throw new BaseException(ErrorCode.NOT_CONFIRMED);
}
return true;
})
.filter(w -> {
if(w.isDeleted()){
throw new BaseException(ErrorCode.NOT_EXIST_WISH);
}
return true;
})
.orElseThrow(()-> new BaseException(ErrorCode.NOT_EXIST_WISH));

Choose a reason for hiding this comment

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

wish먼저 조회하고 filter치면 가독성이 더 좋을거 같아요

Copy link
Member Author

Choose a reason for hiding this comment

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

넵! 감사합니다! wish가 나온 후에 filter 로직을 적용하도록 하겠습니다!


@Service
@RequiredArgsConstructor
public class WishService {

Choose a reason for hiding this comment

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

비즈니스 레이어에서 resonseDto들을 반환하는건 적절하지 않습니다. 비즈니스 객체를 반환하고 Controller에서 객체 조립하는게 맞는거 같아요

Copy link
Member Author

Choose a reason for hiding this comment

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

그렇군요..! Service 계층에서는 비지니스로직을 처리하고, Wish 객체 자체를 반환하고, Controller에서 이 객체를 Dto에 맞게 조립했는데 제가 이해한 결과로 한 코드 수정이 맞을까요?

Choose a reason for hiding this comment

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

네네 맞아요

})
.orElseThrow(()-> new BaseException(ErrorCode.NOT_EXIST_WISH));

return WishCreateMapper.INSTANCE.toDto(wish);

Choose a reason for hiding this comment

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

Mapper를 이렇게 사용하는 이유가 있나요??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants