회고를 lv1부터 lv5까지 구현하고 나서 한번에 쓰는거라 순서가 뒤죽박죽이다.
댓글 조회할 때 그냥 return 했더니 scheduleId가 존재하지 않아도 빈배열로 리턴이 됨 → 에러가 떨어지지 않음 왜?
List<Comment> findByScheduleIdOrderByCreatedAtDesc(Long scheduleId);
이 메서드 호출 했을 때 scheduleId
를 참조하는 댓글이 없다면 빈 리스트 반환이 기본 동작이라고 한다. 이 쿼리는 단순히 조건을 만족하는 comments를 찾는 쿼리기 때문에 댓글이 없는 거와 일정이 없는 것을 구분한다. 그래서 만약 존재하지 않는 글의 댓글을 조회했을 때 에러를 떨구고 싶다면 일정이 있는지 없는지를 체크해 주면 된다.
그래서 나는 댓글 조회에
scheduleService.findScheduleOrThrow(scheduleId);
이걸 추가해 줬다!
repository에서 jpa를 extends 받았기 때문에 기본 제공되는 기능을 사용할 수 있는 거까지는 이해 완료. 간단한 것들은 다 있는 것 같았다. 근데
findByScheduleIdOrderByCreatedAtDesc
이거나
countByScheduleId
이런 식으로 여러 개 넣어서 쓰는 게 작동이 된다는 게 너무 신기함.
→ 직접 구현하지 않아도 사용할 수 있는 이유는 Spring Data JAP는 메서드 이름 기반 쿼리 생성 기능이 있음. 그래서 중요한 게 메서드 이름으로 쿼리르 만드는 것임!!!
Long countByScheduleId(Long scheduleId);
SELECT COUNT(*) FROM comment WHERE schedule_id = ?
이렇게 자동 생성해 주는데… 그래서 궁금했던 게 그러면
countCommentsByScheduleId
는 동작하는가 이름이 이렇게 직관적인데! 삐빕 동작하지 않는다. comments
를 쓰면 comments
라는 필드가 있다고 잘못 해석하다고 한다ㅠ… 내 Comment
안엔 그런 필드가 없거든…!!
그럼 countByScheduleId
이건 왜 동작하냐면 Comment 엔티티
안에 schedule
이 있기 때문이다. 그래서 schedule.id
를 통해 scheduleId
로 파싱 가능한 것이다.
만약 countCommentsByScheduleId
이걸 쓰고 싶다면 직접 쿼리를 작성해야 한다고 해서 Spring JPA 규칙에 맞는 메서드명으로 사용하는 것이 편하고 좋다.
Sort처리하는 것과 findByWriterOrderByUpdatedAtDesc
쓰는것…
if (writer == null || writer.isEmpty()) {
schedules = scheduleRepository.findAll(Sort.by(Sort.Direction.DESC, "updatedAt"));
} else {
schedules = scheduleRepository.findByWriterOrderByUpdatedAtDesc(writer);
}
writer가 주어지면 → 특정 작성자 일정만 조회 (수정일 순)
writer가 없으면 → 전체 일정 조회 (수정일 순)
동적으로 정렬이 필요하다거나 조건이 없을 수도 있는 경우(전체 조회)
→ findAll(Sort.by(...))
방식을 사용하는 것이 좋음
정확한 필터 + 정렬을 동시에 사용해야 한다면
→ findByXXXOrderByYYYDesc()
처럼 자동 쿼리가 직관적이고 빠름
이런 방법도 있고 저런 방법도 있고~
Comment entity
에서 다른 entity를 불러와서 사용하려 하니까 빨간 줄
그래서 인텔리제이가 제안하는 걸 눌렀더니 @ManyToOne
과 @JoinColumn
이라는 게 붙여졌다. 영어로만 본다면 다대일과 컬럼 조인해주는 것 같은데 SQL로 처리하는 걸 이렇게 어노테이션을 사용해서 구현하는건가? 싶었다
@ManyToOne
→ 다대일 관계 (여러 개의 댓글이 하나의 일정에 달릴 수 있다는 뜻)@JoinColumn(name = "schedule_id", nullable = false)
→ 데이터베이스를 어떻게 연결할 것인지 지정하는 부분인데
name = "schedule_id"
컬럼을 생성해서 해당 댓글이 어느 일정에 속해있는지 외래키로 저장nullable = false
→ 댓글은 일정 없이 존재할 수 없다.@ManyToOne
과 @JoinColumn
은 SQL의 JOIN
문과 직접적으로 같지는 않다고 한다. 하지만 DB 테이블 간 관계를 정의하고 JPA가 내부적으로 쿼리를 생성해서 실행하게 되는 것!!… 대충 비슷하다고 보면 될듯??
어쨌든 @JoinColumn
덕분에 각 댓글에 일정 아이디가 들어가서 어느 일정에 속하는지 연결해주는 것이다!!
nullable = false
? 없는 값을 막아주는 건가 null
만 막아주는건가?
이건 1번거랑 이어진다고 보는데 저건 DB 레벨에서의 null
허용 여부를 설정하는거지 존재하지 않는 스케줄id
를 막아주는 것은 절대 아니다.
just 스케줄이 null
이면 DB에 저장이 안 되는 것만 보장. 그래서 서비스에서 일정 있는지 없는지 체크하는 로직을 넣어줘야만 하는 것~
댓글 수정과 삭제 기능..
이건 연습할 겸 추가로 수정과 삭제를 만들어봤는데 수정과 삭제를 할 때는 꼭 scheduleId
가 필요하지 않은 것 같다고 느꼈다… 근데 Restful하게 짜려면 넣어야할 것 같기도 하고… 근데 그러면 필요도 없는 schedulId
를 다 받아놔야 하기 때문에 뭔가 의미없는 일을 하는 것 같기도 해서 일단은! 바로 /comments/{id}
로 해서 만들었다. 뭐가 더 알맞은 건지 모르겠다.
단건 조회에 댓글 추가하기
말만 들으면 엄청 손쉽게 할 거라고 생각했는데 생각보다 난이도가 있었다
일단 전체 조회할때는 나오지 않고 단건 조회할때만 나와야한다…? 여기부터 동공지진 그러면 dto를 수정하는 게 아니라 새로 만들어야 하네
그럼 일단 DetailResponseDto를 따로 생성해줘야 하고 스케줄 엔티티에는 Comment list를 추가해줘야 한다.
@OneToMany(mappedBy = "schedule")
private List<Comment> comments = new ArrayList<>();
처음엔 그냥 밑에거만 추가해서 했는데 빌드했더니 에러. JPA 어노테이션이 빠졌다는 뜻. 내가 dto에서 schedule.getComments
를 했는데 그거때문에 안 되나보다 일단 댓글은 스케줄에 연결되서 저장되니까 이건 단방향 관계로 이어져 있는데 스케줄 기준에서 댓글을 조회하는 기능을 쓰려면 @OneToMany
를 추가해서 스케줄 기준으로 연결된 댓글 목록을 가지고 올 수 있게 된다.
나는 댓글을 그냥 스케줄의 findById로 불러와서 보여줬는데 @OneToMany
가 지연 로딩 방식이라고 한다. 즉시 로딩으로 바꿔주려면 @EntityGraph
를 사용해서 그냥 넣어주기만 하면 되는 것 같다 (이론 모름)
근데 찾아보니 일반적인 일반적으로 댓글은 사용자가 모든 댓글을 보지 않을 가능성이 높기 때문에 지연 로딩이 더 나은 선택일 수도 있다고 한다!
댓글 수가 적거나 페이지 로딩 속도보다 데이터 접근 속도가 중요한 경우엔 즉시 로딩이 더 낫다고 한다. @OneToMany
찾아보다가 여기까지 와버렸다 여튼 지연로딩, 즉시로딩 그런 게 있나보다!
제공된 강의 듣고 다른 반 강의도 다시보기로 도강하느라 과제 구현 시간이 너무 촉박했다… 일단 도전과제 Lv6까지 했고 Lv7은 나중에…
스웨거를 작성해 보려 했는데 Delete에 request Body가 들어가면 안 된다고 한다… 엥! 그래서 일단은 스웨거에서는 param으로 설정해 놓긴 했다. 근데 Param으로 하면 url에 비밀번호가 보여질 것이라 생각해서 body로 넣었다. 근데 스프링에서는 작동이 잘 됐었는데 스웨거만 안 되는건가 싶기도 하다.
→ 원래는 안 됐는데 최신에 되는 것 같음 Delete 매핑 대신 Post를 쓰고 url에 /delete 를 추가해서 구현했어도 됐을 것 같다
→ 어차피 나중엔 header를 쓰니까~