+ 방금 알게 된 사실 : 서비스는 서비스를 참조할 수 있지만 단방향으로만 참조해야 한다. 양쪽에서 동일하게 참조할 시 순환참조가 되기 때문에 하면 안 된다.
또한 두 가지 관점이 있다. 이는 개인의 선택에 따라 결정된다. 아래의 코드는 2번에 해당하는 코드이다.
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class LikeService {
private final PostRepository postRepository;
private final CommentRepository commentRepository;
private final LikeRepository likeRepository;
@Transactional
public LikeResponseDto pressLike(User user, Long postId, Long commentId) {
Post post = checkPost(postId);
Comment comment = checkComment(commentId);
Likes likes = likeRepository.findByUserAndPostAndComment(user, post, comment)
.orElseGet(() -> saveCommentLike(user, post, comment));
Boolean updated = likes.updateLike();
comment.updateLikeCnt(updated);
return LikeResponseDto.of(likes.getIsLiked());
}
@Transactional
public Likes saveCommentLike(User user, Post post, Comment comment) {
Likes likes = Likes.builder()
.user(user)
.post(post)
.comment(comment)
.isLiked(DEFAULT_LIKE)
.build();
return likeRepository.save(likes);
}
private Comment checkComment(Long commentId) {
return commentRepository.findById(commentId).orElseThrow(
() -> new CommentExistsException(CommentErrorCode.NOT_EXISTS_COMMENT));
}
private Post checkPost(Long postId) {
return postRepository.findById(postId).orElseThrow(
() -> new CommentExistsException(CommentErrorCode.NOT_EXISTS_POST));
}
}
작성 완료한 이 코드를 이용하여 알고 있는 개념들을 정리해 보겠다.
간단하게 이해한 내용을 기술하겠다.
@Service
public class UserService {
private UserRepository userRepository;
private MemberService memberService;
@Autowired
public UserService(UserRepository userRepository, MemberService memberService) {
this.userRepository = userRepository;
this.memberService = memberService;
}
}
출처: https://mangkyu.tistory.com/125 [MangKyu's Diary:티스토리]
하지만 위의 코드에서는 생성자가 보이지 않을 것이다. 그 이유는 @RequiredArgsConstructor 때문이다.
Lombok은 Java 라이브러리로 반복 메서드 작성 코드를 줄여주는 코드 다이어트 라이브러리다.(ex. Getter, Setter 등)
위의 코드를 보면 초기값 세팅이 필요한 final 변수가 있다. 그렇기에 @RequiredArgsConstructor 를 사용했다.
다음으로 @Service 이것은 무엇일까?
역시 컴포넌트로 DI의 한 부분이다. Spring MVC에서 Model과 관련되어 있다. 즉 @Controller가 클라이언트로 받아온 요청에 따른 비즈니스 로직을 수행하는 Component이다. @Controller >> @Service >> @Repository 로 이어지는 Spring MVC 흐름에서 Service를 담당하고 있다는 것을 프로그램에게 알려주기 위한 것이라고 이해하고 있다.
그럼 마지막으로 남은 @Transactional 은 무엇일까?
힘들게 데이터베이스에 저장한 데이터를 망치면 얼마나 억울한가. Transactional 은 데이터의 정합성을 지키기 위한 어노테이션이다. 제대로 된 설명을 하기 위해서는 꽤나 깊게 들어가야 하고 필자는 그 정도의 실력은 안되기 때문에 현재는 대략적으로 이런 느낌이다 정도로 이해하고 있는 수준이다.
Transacrtonal 은
정도로 이해하고 있다. 그리고 Transactional 을 readonly 로 한 것은 어떤 이유가 있는지 모르겠지만 팀원이 이 방식이 좀 더 데이터 안정성(?)을 높일 수 있다고 했다. 막연하게 생가하기에는 Transactional 을 통해 읽기 전용으로 만들고 필요한 부분에서 Transaction을 사용하므로 데이터가 변질될 위험이 적다고 생각되는데 적지 않은 것과 어떤 차이가 있는지 아직은 모르겠다.
마지막으로 빌더패턴이다. 최상단의 코드에서는 빌더를 사용해 Likes likes 를 만드는 부분만 있고 빌더 생성자의 모습은 보여주지 않고 있다.
빌더패턴을 사용하는 이유는 단순하다.
을 위해서 사용한다.
@Builder 는 클래스 레벨과 생성자 레벨에서 사용할 수 있다. 자세히는 모르지만 클래스 레벨에서 사용하는 것은 좀 까다롭고 다양한 제약사항이 존재한다고 알고 있다.
가장 큰 차이는
그렇기에 매개 변수를 설정할 수 있고 직접 사용해 본 생성자 레벨의 @Builder 를 정리하겠다.
@Builder
private Likes(Long id, User user, Post post, Comment comment, Boolean isLiked) {
this.id = id;
this.user = user;
this.post = post;
this.comment = comment;
this.isLiked = isLiked;
}
빌더 생성자이다. Likes 라는 객체를 만들 때 필요한 매개 변수들을 일목요연하게 볼 수 있다. 그리고 사용할 때는 최상단의 코드 처럼 .builder( ) 로 빌더를 연 다음 매개 변수들을 .~~.@@.##.$$ 이런 식으로 연달아서 작성한 후 .build( )로 빌더를 닫아주면 된다.
최상단의 코드 중에서 Repository 에서 필요한 Likes를 가져오는 쿼리문도 코드 속에 있지만 아직 정리할 만큼 소화하지 못한 것 같아서 나중에 정리하겠다는 다짐을 하며 이만 글을 줄이겠다.
[TIL#25-1] 가을에 시작한 Spring <PLUS 복습> (0) | 2023.12.15 |
---|---|
[TIL#24] 가을에 시작한 Spring <Transactional / FetchType.LAZY or EAGER / cascade> (0) | 2023.12.12 |
[TIL#21] 가을에 시작한 Spring <ERD 작성> (0) | 2023.11.16 |
[TIL#15-1] 가을에 시작한 Spring part.2 <테스트 코드, Spring MVC, Controller, 정적페이지&동적페이지> (1) | 2023.11.03 |
[TIL#14-2] 가을에 시작한 Spring part.1 <그레이들, 서버, HTTP> (0) | 2023.11.02 |