public class RequestCollaboRequestDto {
public String title;
public String contents;
public CollaboRequestDto tocollaboRequestDto(){
return CollaboRequestDto.builder()
.title(title)
.contents(contents)
.build();
}
}
콜라보 요청하는 사람으로부터 제목과 내용을 받아옵니다.
사실 음악 파일과 음악 종류(? 악기 등)을 받아와야하는데
Music Entity를 따로 설계를 해버려서 방법을 찾는 중입니다.(양방향 설계를 하면 되려나?)
자바의정석으로는 알수없는 스프링의 세계에요. 이제는 그냥 되는대로 그때그때 에러와 에러를 에러와 함께 스프링과 자바에 대한 부족함을 극복하고 있습니다.
BeanCreationException
BeanCreationException
위의 사진처럼 에러가 떴습니다. 저는 kakao 로그인을 구현 하려고 했습니다. 지난 주차에서 다른 조원분이 하신 코드를 그대로 가져왔는데 에러가 떴습니다. 어차피 사용자 정보를 User가 아니고 Member로 저장한 거 밖에 다른 게 없다고 생각했기 때문입니다. 그런데!!!! 왜 자꾸 저렇게 에러가 뜨는지 모르겠었어요.
구글 해보니 Bean을 제대로 설정해주지 않아서, 제대로 주입해주지 않아서 생긴 문제라고 했습니다.
@Component, @Service, @Controller같은 Bean 등록이 안되있어서 그렇다고 하더라고요. 그런데 확인 결과 해당 어노테이션들은 잘 붙어있었습니다.
해결
저희팀에는 숙련된 조교분이 계십니다.
저희반자체에 컴공 전공하신 분들이 절반이 넘는다고 합니다.
사전스터디가 첫 코딩이었던 (그게 벌써 3개월도 전입니다....) 저와 같은 비전공자 노베이스 분들께는 그저 빛입니다.
제가 하루종일 고민하고 찾아봐도 모르겠었는데 설마 이거겠어 했던게 이거였습니다.
팀원분이 바로 보고 application.properites에 등록했어요? 라고 문제를 찾아내셨습니다.
마케팅 포인트로 이 부트캠프는 프로젝트를 4개 한다였는데 사실은 1개라고 합니다. 1주짜리는 포트폴리오가 될 수 없고 마지막에 하는 6주짜리 프로젝트 하나에 영혼을 갈아넣어 취업용 프로젝트를 만들어야 한다고 합니다. 잘 모르는 비전공자한테는 다소 당황스럽습니다. 근데 뭐 이것도 말하기 나름이겠지요.
이번주차랑 지난주차는 잠시 쉬어가면서 실전주차 프로젝트를 위한 연습입니다.
이제 끝까지 고수하던 긴 손톱을 제거하고 개발자 면접용 손톱으로 바꾸고 취업을 준비해야겠네요.
영속성 전이를 사용하거나 for문을 돌려서 순수 자바만을 사용했을 때는 아래처럼 일일히 데이터마다 쿼리를 날려서 지우게 했습니다. 지금은 그 수가 적어서 상관이 없지만 나중에 대용량 데이터를 다루게 될 때에 과부하가 걸릴 수도 있다는 판단이 들었습니다.
cascade.remove 사용했을 때 query중첩 for문으로 삭제했을 때 query
Where in query 사용
where in 을 사용해서 query를 날리면 댓글을 하나하나 삭제를 하는 것이아니라 한번에 날려줍니다.
public interface CommentRepository extends JpaRepository<Comment, Long>{
@Modifying //기존에 있는 메서드를 변경하기 때문에
@Query("delete from Comment c where c.id in :ids")
void deleteAllByIdIn(@Param("ids") List<Long> ids);
}
위의 코드는 where in을 사용해서 comment와 Likecomment, likepost, post 를 한번씩만 쿼리를 날려서 관련 목록을 다 삭제하였습니다.
주말에 오랜만에 놀았더니 오전에 집중하는 게 힘들었던 월요일입니다. 점심시간에 파워냅 1시간 하고 돌아와서 다시 정신 차리고 합니다. 오늘은 영하 15도까지 내려갔다고 하네요. 집에 보일러가 고장나서 슬픈 캥거루족입니다. 얼른 개발자로 취직해서 판교에서 자취하고 싶어요. 어제 오랜만에 대학 친구들을 만났는데 한 친구가 미국 온라인 대학원을 다니고 있다는 소식을 들었습니다. 오프라인과 같은 학위를 받지만 전체 온라인 수업이라 학비도 상대적으로 저렴했습니다. 저도 나중에 취업하고 다녀볼까봐요. BBA & 컴공 master?!
추워요...
좋아요를 하고 취소하는 API를 만들어 놓고 정작 좋아요 갯수를 세는 기능이 없어서 오늘은 좋아요 갯수 세는 기능을 추가했습니다.
생각보다 간단해서 낮잠 자고 멀쩡한 정신으로 해결했습니다. 다음과 같이 @Service에 코드를 추가했습니다.
좋아요 취소 시 좋아요 수 -1
Long likeCount = likePostRepository.countByPostId(postId);
post.setLikecount(post.getLikeCount()-1);
좋아요 성공 시 좋아요 수 +1
Long likeCount = likePostRepository.countByPostId(postId);
post.setLikecount(post.getLikeCount()+1);
숫자를 저장할 Long 타입 변수를 만들고 해당 게시물 Id를 이용해서 좋아요 수를 저장했습니다.
post Entity에 있는 Likecount 변수에 넣어주었습니다.
Comment를 좋아요 한 변수는 처음에 0으로 설정해서 Nullpointerexception error를 방지합니다.
CASCADE 영속성 전이
조장님이 던져준 미션인 cascade.remove를 사용하지 않고 게시물 삭제와 동시에 연관된 좋아요, 댓글, 댓글좋아요를 삭제하는 방법에 대해 연구했습니다. Cascade를 사용하면 연관된 테이블이 자동으로 함께 지워진다는 편리함이 있지만 주의해야할 점이 있습니다.
이에 대해 인프런 JPA 강의 Q&A에서 김영한님의 친절한 답변이 다음과 같이 달렸습니다.
cascade 옵션은 단순히 생각하면, 그냥 persist() 호출을 줄여줄 수 있기 때문에, 유용해 보이지만, 반대로 생각하면, Order 엔티티를 저장할 때, 연관된 어떤 엔티티들이 함께 저장될까? 를 계속 코드를 보며 추적해야 합니다. 따라서 질문하신 것 처럼 어디까지는 cascade로 함께 저장하고, 어디까지는 함께 저장하면 안될까? 하는 명확한 기준이 필요합니다. 그래서 이런 기준을 잡기 애매한 경우에는 사실 사용하지 않는 것이 좋습니다. 통상적으로 권장하는 cascade 범위는, 완전히 개인 소유하는 엔티티일 때, 예를 들어서 게시판과 첨부파일이 있을 때 첨부파일은 게시판 엔티티만 참조하므로, 개인 소유 입니다. 이런 경우에는 사용해도 됩니다. 그럼 반대로 개인 소유하지 않는 엔티티는 무엇일까요? 예를 들어서, 회원, 상품 등등이 있습니다. 이 예제에서 Order -> OrderItem을 개인소유 하기 때문에 cascade를 사용했습니다. 그런데 Order 입장에서 Delivery는 좀 애매합니다. 여기서는 프로젝트 규모가 작기 때문에 매우 단순하게 표현했지만, 실무에서 프로젝트 규모가 커지면, Delivery로 여러곳에서 참조될 수 있습니다. 그러면 사용하면 안됩니다. 추가로 도메인 주도 설계(DDD)의 Aggregate Root 개념을 이해하고, 프로젝트 적용하면 여기에 맞추어 cascade 옵션을 더 잘 활용할 수 있습니다. 정리하면 1. 완전 개인 소유인 경우에 사용할 수 있다. 2. DDD의 Aggregate Root와 어울린다. 3. 애매하면 사용하지 않는다.
더 자세한 내용은 JPA 기본편 섹션 8에 있는 영속성 전이(CASCADE)와 고아 객체를 참고해주세요. 감사합니다.
어제는 오랜만에 일찍 체크아웃을 했습니다. 주특기인 스프링을 배우는 3주동안 거의 계속 새벽 2시, 3시 심하면 5시에 잠들었었거든요. 자바를 접한지 한달, 스프링을 접한지 3주동안 쉼없이 계속 달렸습니다. 일찍이라고 해봤자 사실 11시 반쯤 맥북프로를 닫았던 것 같습니다. 내일은 제 생일이라 오늘은 9시 칼퇴 예정입니다.
저는 이번에 진행하는 첫 미니프로젝트에서 좋아요 기능을 담당하고 있습니다. 고수분들(왜 취직 안하고 부트캠프에 온건지 심히 의문이 드는)이 많아서 조금 걱정이 되긴 합니다만 그래도 좋아요 기능 오늘 완성 했습니다.
그랬더니 조장님이 다른 미션을 던져 주셨네요.
게시판과 댓글을 연관관계 설정 하고 나서 게시글을 지우면 댓글도 다 같이 지워지게 CASCADE.REMOVE 를 사용했었는데 이를 사용하면 위험(?) 하다고 하니 다른 방법으로 DELETE 요청을 구현하라는 미션입니다. 일단 왜 CASCADE.REMOVE가 위험한지를 알아야겠네요. 다음주의 미션이고 일단은 어제 밤에 진행됐던 프론트앤드, 백앤드 간의 서버 통신 및 배포에 관한 세션을 복습 할 예정입니다.
아래는 오늘 구현 완료한 좋아요 기능 입니다. @Service 부분만 공유할게요.
LikeService
package com.hanghae99.catsanddogs.service;
import com.hanghae99.catsanddogs.dto.LikePostResponseDto;
import com.hanghae99.catsanddogs.dto.ResponseMessage;
import com.hanghae99.catsanddogs.entity.*;
import com.hanghae99.catsanddogs.exception.CustomException;
import com.hanghae99.catsanddogs.exception.ErrorCode;
import com.hanghae99.catsanddogs.repository.CommentRepository;
import com.hanghae99.catsanddogs.repository.LikeCommentRepository;
import com.hanghae99.catsanddogs.repository.LikePostRepository;
import com.hanghae99.catsanddogs.repository.PostRepository;
import com.hanghae99.catsanddogs.security.UserDetails.UserDetailsImpl;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
@Getter
@RequiredArgsConstructor
@Service
public class LikeService {
private final PostRepository postRepository;
private final CommentRepository commentRepository;
private final LikePostRepository likePostRepository;
private final LikeCommentRepository likeCommentRepository;
@Transactional
public boolean likePost(Long postId, User user) {
Post post = postRepository.findById(postId).orElseThrow(
() -> new CustomException(ErrorCode.CONTENT_NOT_FOUND)
);
Long userId = user.getId();
//좋아요 했는지 확인
Optional <LikePost> likePost = likePostRepository.findByPostIdAndUserId(postId, userId);
if (likePost.isPresent()) {
LikePost like = likePost.get();
likePostRepository.delete(like);
return false;
} else{
LikePost like = new LikePost(postId, userId);
likePostRepository.save(like);
return true;
}
}
public boolean likeComment(Long commentId, User user) {
Comment comment = commentRepository.findById(commentId).orElseThrow(
() -> new CustomException(ErrorCode.COMMENT_NOT_FOUND)
);
Long userId = user.getId();
Optional<LikeComment> likeComment = likeCommentRepository.findByCommentIdAndUserId(commentId, userId);
if(likeComment.isPresent()){
LikeComment like = likeComment.get();
likeCommentRepository.delete(like);
return false;
} else{
LikeComment like = new LikeComment(commentId, userId);
likeCommentRepository.save(like);
return true;
}
}
}
어제 댓글 구현을 stream을 통해 구현을 했는데 @JsonIgnore를 사용하면 순환참조를 방지할 수 있다는 이야기를 들었습니다. 지금은 아직 배우는 단계이기 때문에 순수 자바를 이용해서 for문을 사용해 댓글 목록을 조회하는 것을 추천받기는 했습니다.
오늘은 첫 포트폴리오 프로젝트를 시작했습니다. 제가 기획한 프로젝트가 선택되었는데 프로젝트 명은 '멍냥의 전당'으로 반려동물의 사진과 함께 게시글을 올리고 공유하는 웹사이트입니다. 요즘 '개발바닥'이라는 유튜브를 정주행하고 있는데 그 채널에 나오는 호돌맨님이 다니는 회사가 반려생활이라고 해서 찾아봤었는데 아마 거기서 아이디어가 떠오르지 않았나 싶습니다. 조원들에게 피칭할 때는 저희 온라인 부트캠프 한다고 캠을 켜놓고 있다보면 조원들의 강아지나 고양이가 왔다갔다하는 것을 볼 수 있는데 모두에게 자신의 반려동물을 자랑하고 보여줄 수 있는 웹사이트를 만들면 어떻겠냐고 했습니다. 혹시나 나중에 반려생활같은 반려동물 관련 서비스에 지원한다면 첫 포트폴리오가 도움이 될 수도 있다고 생각했습니다. 물론 기술 스택이 중요하겠지만요.
저는 댓글 좋아요, 게시글 좋아요 기능을 담당하기로 했습니다. 이번 주 내내 댓글을 연구했어서 좋아요 기능까지 못했거든요. 프론트 앤드 분들과의 첫 협업인데 조원들 중에 실력자가 많아서 든든합니다. 다들 왜 부트캠프를 하는지 이해가 안될 정도 입니다. 저는 이제 한달이 조금 지난 왕초보 개발자 지망생인데 말이죠. 처음에 비전공자를 위한 부트캠프라고 홍보해서 들어왔는데 전공자분들이 더 많은 거 같아서 당황스럽기는 합니다. 마케팅 타겟은 물론 비전공자 쪽이 훨씨 많아서 그럴수 있다고 생각합니다. 사업이 더 커질려면 인구가 많은 쪽을 공략해서 파이를 키우는게 맞지만 너무 이렇게 바를 올려버린다는 느낌이 들면 아예 처음 시작하는 사람한테는 이 부트캠프를 추천할 수는 없을 것 같습니다. 어느정도 컴퓨터 프로그래밍에 대한 지식이 있는, 최소로 본인이 선택할 주특기의 언어 정도는 이미 좀 익숙한 상태에서 지원한다면 괜찮을 수도 있다고 생각합니다. 하지만 저는 시작하기 직전에 주특기 설명 세션을 통해 결정을 했기 때문에 미리 공부할 시간 따위는 없었다고 합니다. 그래도 새벽 2~3시까지 매일 몰입하면서 경험치가 오르고 있다는 게 느껴지기는 하지만 아직 레벨 0, 튜토리얼에 머물러 있기는 합니다. 앞으로 미니 프로젝트를 진행하고, 클론 프로젝트, 실전프로젝트까지 하고 2월에 수료를 하게 되면 드디어 개발자 레벨 1 정도라고 할 수 있지 않을까 기대해봅니다.