게시판 서비스 - DTO 설계, 유효성 검증

Sarah·2026년 4월 27일
  • Servie 계층까지 구현하고 나서, 엔티티가 가지고 있는 필드실제 화면에 넘여주고 싶은 데이터 필드 혹은 넘겨 받아야 하는 데이터 필드가 다름을 깨달았다.

  • 엔티티는 DB 설계를 위한 도메인 모델이고, DTO는 클라이언트와 통신을 위한 데이터 모델이다. 엔티티와 DTO를 분리함으로써 엔티티 내부 구조가 외부에 그대로 노출되는 것을 방지하고 API 스펙이 변경되어도 엔티티를 보호할 수 있는 관심사 분리를 실천했다.

  • 클라이언트로부터 요청을 받을 때 필요한 필드만 담은 요청DTO (RequestDto)와 클라이언트에 넘겨줄 필드만 담은 응답DTO (ReponseDto)를 생성하여 리팩토링 하였다.


User

UserJoinRequestDto

  • 회원 가입 할 때 필요한 필드만 담은 DTO
  • name (이름), email (이메일), password (비밀번호)
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class UserJoinRequestDto {
    @NotBlank (message = "이름은 필수 입력값입니다.")
    private String name;
    @NotBlank (message = "이메일은 필수 입력값입니다.")
    @Email(message = "이메일 형식이 올바르지 않습니다.")
    private String email;

    @NotBlank(message = "비밀번호는 필수 입력값입니다.")
    @Size(min = 8, message = "비밀번호는 8자 이상이어야 합니다.")
    private String password;
}

유효성 검증 (Bean Validation)

  • @NotBlank @Email @Size 등 유효성 검증 어노테이션을 사용하여 데이터가 Service 계층에 도달하기 전 유효한 데이터가 들어왔는지 DTO 레벨에서 검증하도록 설계했다.

UserReponseDto

  • 회원 정보 조회할 때 보여줄 필드만 담은 DTO
  • userId (아이디), name (이름), email (이메일), joinedDate (가입한 날짜)
  • password는 개인정보이므로 조회할 때 보이면 안되기 때문에 제외하였다.
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class UserResponseDto {
    private Long userId;
    private String name;
    private String email;
    private String joinedDate;

    public UserResponseDto(User user) {
        this.userId = user.getId();
        this.name = user.getName();
        this.email = user.getEmail();
        this.joinedDate = user.getJoinedDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }
}

엔티티의 필드에 있는 값을 DTO 필드에 그대로 넣어서 DTO 를 생성 할 수 있도록 User 엔티티를 매개변수로 받는 생성자를 만들었다.

getJoinedDate().format() 메서드를 사용하여 날짜를 원하는 포맷으로 변경하여 DTO에 넣었다.


Board

BoardSaveRequestDto

  • 게시글 작성 할 때 필요한 필드만 담은 DTO
  • title (제목), content (내용)

BoardUpdateRequestDto

  • 게시글 수정 할 때 필요한 필드만 담은 DTO
  • title (제목), content (내용)

BoardListResponseDto

  • 게시글 목록에서 보여줄 필드만 담은 DTO
  • boardId (게시글 번호), title (제목), writerName (작성자), createdDate (작성 일자)
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class BoardListResponseDto {
    private Long boardId;
    private String title;
    private String writerName;
    private String createdDate;

    public BoardListResponseDto(Board board) {
        this.boardId = board.getId();
        this.title = board.getTitle();
        this.writerName = board.getUser().getName();
        this.createdDate = board.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }
}

BoardDetailResponseDto

  • 게시글 상세 조회했을 때 보여줄 필드만 담은 DTO
  • boardId (게시글 번호), title(제목), content (내용), createdDateTime(작성 일자), writerName(작성자)
  • writerEmail (작성자 이메일) 은 추후 로그인 기능 도입 후 인가 기능 구현할 때 작성자가 본인이 맞는지 확인할 때 사용해야 해서 넣었음.
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class BoardDetailResponseDto {
    private Long boardId;
    private String title;
    private String content;
    private String createdDateTime;
    private Long userId;
    private String writerName;
    private String writerEmail;

    public BoardDetailResponseDto(Board board) {
        this.boardId = board.getId();
        this.title = board.getTitle();
        this.content = board.getContent();
        this.writerName = board.getUser().getName();
        this.createdDateTime = board.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
        this.userId = board.getUser().getId();
        this.writerEmail = board.getUser().getEmail();
    }
}

BoardDetailResponseDto - 상세 화면에서는 작성일자를 2026-04-27 12:00 형식으로,
BoardListResponseDto - 목록 화면에서 작성일자는 2026-04-27 으로 표현하였다.


Comment

CommentSaveRequestDto

  • 댓글 작성 할 때 필요한 필드만 담은 DTO
  • content (내용)

CommentUpdateRequestDto

  • 댓글 수정 할 때 필요한 필드만 담은 DTO
  • content (내용)

CommentResponseDto

  • 댓글 조회 할 때 보여줄 필드만 담은 DTO
  • content (내용), writerName (작성자), createdDateTime (작성일자)
  • writerEmail (작성자 이메일) 은 추후 로그인 기능 도입 후 인가 기능 구현할 때 작성자가 본인이 맞는지 확인할 때 사용해야 해서 넣었음.
  • boardId : '내가 쓴 댓글 목록' 에서 해당 게시글로 이동할 때 필요한 boardId
  • boardTitle : '내가 쓴 댓글 목록' 에서 어떤 게시글(제목)에 썼는지 같이 보여주기 위해서 넣음
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class CommentResponseDto {
    private Long commentId;
    private String content;
    private String boardTitle;
    private String writerName;
    private String createdDateTime;
    private Long boardId;
    private String writerEmail;

    public CommentResponseDto(Comment comment) {
        this.commentId = comment.getId();
        this.content = comment.getContent();
        this.boardTitle = comment.getBoard().getTitle();
        this.writerName = comment.getUser().getName();
        this.createdDateTime = comment.getCreateAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
        this.boardId = comment.getBoard().getId();
        this.writerEmail = comment.getUser().getEmail();
    }
}
profile
헤맨 만큼 내 땅

0개의 댓글