SPARTA 과제/SPRING

댓글 기능 구현, 정렬

kjw81024 2026. 2. 4. 15:25

어제 댓글때문에 머리가 깨지는 줄 알았어요... 퇴실하고 컴퓨터 끄고 나서도 계속 댓글구현 어떻게 할지만 생각나서 자기전에 급하게 메모까지 했습니다... 고양이랑 자기로 약속한 시간 넘어서까지 하느라 미안해서 간식줬더니 제 마음을 아는지 모르는지 한껏 올라간 꼬리... 

우측하단 고양이가 타코에요

 

하루가 지났으니 댓글 출력 등록하고, 어제 발생했던 exception 정리하겠습니다


2/3 생겼던 문제 정리

 

1. Entity 에 FK 연결을 어떻게 해줘야할지 모르겠다

Schedule Entity 를 Comment Entity 에 직접 선언하고 @ManyToOne 으로 연결, 수많은.. 오류 발생

-> 이런 연결방식은 양방향이라 아직 안 배웠고 숙련 Spring 단계 때 배우니까 사용하지말고 기다리라고 하심 

-> 그냥 ScheduleId 라는 private field 를 만들고 Id를 받아올 때 사용하기

 

2. Comment list 를 Schedule 단건조회에 추가해주기 

이부분이 제일 어려웠던 거 같다 

DTO 는 박스같은 느낌이라 보내려고 하는 정보들을 모두 담아서 보내야한다고 생각

-> 단건조회일때는 comment list 를 박스에 넣어줘야하고, 다건조회일때는 comment list 를 보내면 안 됨 

DTO 를 단건조회와 다건조회로 분리해줘야하나? 라는 생각

 

Schedule 에 List<Comment> 를 직접 만들어주기 싫어서 더 헷갈렸던 거 같다

 

근데 일단 다건조회때는 무조건 Comment 가 들어가있는 List 를 만들어줘야 출력이 될 거 같은데 Comment Entity 를 직접보내는건 또 걱정돼서 DTO 로 보내야하나? 싶기도 하고 ..

일단 (1)Comment 를 직접 보내는 형식으로 다시 구현해보기

 

새로 만들었던 모든 코드들은 정리...


(1) 단건조회에만 Comment List 를 추가했다 

import java.time.LocalDateTime;
import java.util.List;

@Getter
public class GetScheduleResponse {
    private final Long id;
    private final String name;
    private final String contents;
    private final String writer;
    private final List<Comment> commentList;
    private final LocalDateTime createdAt;
    private final LocalDateTime modifiedAt;

    public GetScheduleResponse(Long id, String name, String contents, String writer, 
    	List<Comment> commentList, LocalDateTime createdAt, LocalDateTime modifiedAt) {
        this.id = id;
        this.name = name;
        this.contents = contents;
        this.writer = writer;
        this.commentList = commentList; #<<< here!
        this.createdAt = createdAt;
        this.modifiedAt = modifiedAt;
    }
}

 

이렇게 되면 위 코드에 체크해둔 getScheduleResponse 의 생성자에 List<Comment> commentList 를 전달해줘야하기 때문에 CommentRepository 에 함수를 만들어줬다

처음알았는데 findBy뒤에 기준으로 할 필드를 작성하면 신기하게도 ... 함수를 그냥 사용할 수 있다 JPA 가 만들어줌

public interface CommentRepository extends JpaRepository<Comment, Long> {
    List<Comment> findByScheduleId(Long scheduleId);
}

 

ScheduleService 에서 CommentRepository 에 직접 접근하는건 뭔가 이상해서 CommentService 에 함수내용을 구현했다

(ScheduleService 는 ScheduleRepository 에만 접근하고, CommentService 는 CommentRepository 에만 접근하도록)

public List<Comment> findCommentsByScheduleId(Long scheduleId){
	return commentRepository.findByScheduleId(scheduleId);
}

 

정상적으로 메시지가 출력되는것을 확인할 수 있다. 

하지만 ... 큰 문제... pw 가 그대로 출력되고 있다

당연히 Comment 를 직접 넘겨주기때문에 Comment 의 field 전체가 출력되어버리는것이다. 

(2) DTO 를 새로 만들어서 pw 를 제외한 값만 넘겨줄 수 있도록 해야하나? 싶어서 새로 만들려고 했는데 생각해보니 DTO 를 원래 Controller - Service 를 연결해줄때 사용했는데 같은 계층 내부에서도 사용해도 되는지 헷갈렸다.....

검색해보니 특정 계층 내부에서도 필요한 데이터 구성을 위해 사용한다고 한다.


(2) DTO 를 새로 만들어서 값 넘겨주기

# GetScheduleResponse class
private final List<GetCommentResponse> commentList;

위와 동일하지만 commentList 의 자료형을 GetCommentResponse 로 바꿔줬다. 아래와 같이 pw 가 제외된 Comment 요소들을 반환하는 DTO 도 만들어줬다

import lombok.Getter;

@Getter
public class GetCommentResponse {
    private final Long id;
    private final Long scheduleId;
    private final String contents;
    private final String writer;

    public GetCommentResponse(Long id, Long scheduleId, String contents, String writer) {
        this.id = id;
        this.scheduleId = scheduleId;
        this.contents = contents;
        this.writer = writer;
    }
}

 

CommentService 에서는새로운 Dto List 를 만들기 위해 반복문을 통해 확인했다. 기본적인 다건조회 형식과 동일하다

함수는 findCommentsByScheduleId 이고, commentRepository 의 findByScheduleId 와 헷갈리지 않도록 주의해야한다...

함수명 위치 역할
findByScheduleId  commentRepository  특정 ScheduleId 를 매개변수로 하여 List<Comment> 를 반환한다.
findCommentsByScheduleId  commentService findByScheduleId 를 호출하고, 반복문을 돌며 필요한 요소만 필터링 하여 List<GetCommentResponse> 를 반환한다.
public List<GetCommentResponse> findCommentsByScheduleId(Long scheduleId){
    List<Comment> commentList = commentRepository.findByScheduleId(scheduleId);
    List<GetCommentResponse> dtos = new ArrayList<>();

    for (Comment comment : commentList) {
        GetCommentResponse dto = new GetCommentResponse(
                comment.getId(),
                comment.getScheduleId(),
                comment.getContents(),
                comment.getWriter()
        );
        dtos.add(dto);
    }

    return dtos;
}

이제 PW 가 제외된 제대로 된 commentList 값이 출력된다. 

 

사실 과제에서는 댓글 생성만 하면 되는거였지만... 댓글을 삭제하고싶어질수도 있으니까 이왕 pw 까지 만들어둔거 pw를 받아 삭제까지만 만들어두었다


예외처리

예외처리를 추가로 만들어주었다/ 원래는 IllegalStateException 이었음

추가로 어제 발생한 Exception들을 정리하면 

BeanCreationException Spring 이 Bean 을 생성하는 과정에서 의존성, 설정, 어노테이션 오류가 있을 때 발생
JPA 매핑 오류, 순환 참조, 생성자 주입 실패

어제 Schedule Entity 를 Comment Entity 에 직접 선언하면서 오류가 터진듯하다.
@OneToMany(mappedBy = "scheduleId") 에서 Schedule entity 의 필드명이 그냥 id 였고, Fk 를 관리하는 주체가 Comment 였는데 반대로 주체로 생각했다 
createPluginException 빌드 도구가 플러그인을 생성하거나 로드하는 과정에서 발생
의존성 오류때문에 발생
StringIndexOutOfBoundsException 어제 Comment list 를 만들고 출력하면서 index 를 잘못 설정해준듯하다

이부분은 숙련주차에서 배운다고 하니 exception들은 간단히 보고 넘어갔다


컬럼 출력 정렬

데이터베이스의 특정 컬럼을 기준으로 오름차순, 내림차순으로 가져오는 방법으로 Repository 에서 OrderBy 를 사용한다고 작성되어있다. 

https://sshinmj.tistory.com/17

 

[JPA] OrderBy, DateIs - 오름차순/내림차순, 특정 날짜 기준으로 데이터 나누기

데이터 베이스의 특정 컬럼를 기준으로 오름차순/내림차순으로 가져오기 Repository에서 ‘OrderBy' 사용! - 사용 예시 List findByUserOrderByCreatedDateDesc(User user); >> 위 코드는 Orders 테이블의 ‘CreatedDate’

sshinmj.tistory.com

public interface ScheduleRepository extends JpaRepository<Schedule, Long> {
    List<Schedule> findAllOrderByModifiedAtDesc();
}

 

나도 수정일 기준 내림차순 정렬 함수로 작성하고 Service 에서 호출해주었다

...

정리해보면

Bean 을 생성하는 과정 중 쿼리 생성에서 문제가 발생했다. 

중요한 부분은 

QueryCreationException, PropertyReferenceException 이었고, 메서드 네이밍 규칙을 따르지 않아서 스프링이 쿼리로 변환하지 못했다고 작성되어있다. Schedule Repository 의 findAllOrderByModifiedAtDesc() 의 명이 틀렸다고 해서 다시보니까 형식이 findAllByOrderByModifiedAtDesc() 요거였다 .... 수정!

 

실행이 되어서 확인을 해봤다

POST 입력 -> GET -> id 2 스케줄 수정 -> GET

처음엔 생성일 기준 내림차순이었을테니까, 수정이후 2번이 가장 위에 있으면 된다

수정 전

modifiedAt 이 최신인것 기준으로 정렬되어 출력된다. 생성시 modifiedAt 은 createAt 과 동일하므로 순서가 맞다

2번을 업데이트 해주고 modifiedAt 이 정상적으로 업데이트된것을 확인할 수 있다

231 기준으로 출력된 모습도 확인 완료

'SPARTA 과제 > SPRING' 카테고리의 다른 글

숙련) 일정관리앱 트러블슈팅 (1)  (0) 2026.02.11
숙련) 일정관리앱 업그레이드 초기설정  (1) 2026.02.10
Schedule README  (0) 2026.02.05
유저 입력 검증  (0) 2026.02.04
기본 DTO 작성  (0) 2026.02.03