자바객체( POJO, DTO , DAO , VO )

이정수·2025년 10월 15일

Spring

목록 보기
11/18
post-thumbnail

자바 객체 관련
。대표적으로 POJO , Java Bean , Spring Bean이 존재
Spring Bean

POJO( Plain Old Java Object )
。 특정 기술 (ex : Framework )에 종속되어있지않은 상태인 순수한 자바객체
DTO , VO 등의 경우도 POJO
Spring Bean에 등록되지 않은 자바객체POJO라고 간주할 수 있다.

EJB에 종속된 무거운 자바 객체를 생성하는것에 반발하여 도출된 용어

Spring Framework에서 Spring Context에 의해 관리되는 경우 Spring Bean이고 관리되지않는 경우 POJO

  • Java Bean
    EJB에 의한 제약조건이 부여된 자바객체
    Java Bean \in POJO

  • Java Bean 특징
class JavaBean implements Serializable {
    private int age;
    public JavaBean(){}
    public int getAge(){
        return this.age;
    }
    public void setAge(int a){
        this.age = a;
    }
}

。 모든 필드private로 구성되며 오직 메소드로서 getter & setter로 접근 가능
캡슐화

인자가 없는 기본생성자만 가진다.

Serializable interface를 상속하여 전송용도로 활용할 수 있음

  • EJB( Jarkata Enterprise Beans )
    서버측 어플리케이션생산성 향상과 이동성을 실현하기 위해 제작한 규격.
    ▶ 기업환경의 시스템을 구현하기 위한 Server측Component 모델

  • Serializable 인터페이스 : java.io.Serializable
    객체직렬화(Selialization)하는 용도의 인터페이스
    ▶ 해당 인터페이스를 상속하여 네트워크를 통해 객체를 전송하거나 ( Web API ) , 객체파일이나 DB에 저장 시 활용

    。해당 인터페이스에는 객체이진데이터( ByteStream )으로 직렬화하거나 역직렬화하는 기능을 정의
    Byte Stream : 데이터Byte 단위로 input , output 할 수 있는 Stream 객체
    • 직렬화 (Serialization) :
      객체Byte Stream으로 변환하여 파일에 저장하거나 네트워크를 통해 전송할 수 있도록 하는 과정

    • 역직렬화 ( Deserialization ) :
      Byte Stream객체로 변환하는 과정.

Persistence LayerJava Bean 객체
DAO , DTO , VO

DAO ( Data Access Object ) :
DB의 데이터로 접근하는 Transaction Object로서, DB의 데이터를 조회하거나 조작하는 기능을 수행하는 객체.

SpringJdbcTemplate, JPA와 같은 DB와 CRUD하는 기능을 제공하는 Repository Class를 선언 및 Spring Bean으로 생성하여 DB와의 CRUD를 수행하는 DAO로서 작용하도록 설정.

VO ( Value Object )
DTO와 달리 Getter만 제공하는 간단한 형태의 POJO
도메인 객체 ( = Business Model ) 또는 JPADB Entity를 선언 시 활용

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Entity
// 도메인 객체와 DB Entity 역할을 동시에 수행
public class Member {
	@Id
	private Long id;
	// 유저로 부터 받는 부분
	private String loginId;
	private String password;
	private String name;
	private String email;
	private String mobile;
	private Gender gender;
	private LocalDate birthday;
	private LocalDateTime createdAt;
	private LocalDateTime updatedAt;
}

도메인DB Entity 역할을 동시에 수행하는 클래스

DTO ( Data Transfer Object )
계층( 레이어 )간 데이터 교환에서 데이터를 전송하는 객체를 의미하는 POJO

。 순전히 데이터를 저장 및 회수하는 기능을 제외하고 아무 기능을 가지고 있지 않음.
Getter , Setter Method만 포함하고있는 순수한 Class.

1계층 간 전달용도로 사용되야하며 2계층 이상으로 동일한 DTO가 전달되는것은 지양해야하나, 팀원간의 협의에 따라 전달되는 경우도 존재.
ex ) @Controller에서 @RequestBody를 통해 매핑되어 수신한 DTO@Service Class메서드까지 전달하여 사용

RecordDTO를 선언하여 불변객체를 통해 데이터전송을 수행하여 불변성을 보장가능
▶ 해당 DTO의 값 재할당을 금지하고 읽기만 가능

。주로 API 요청/응답에서 Message Body를 바인딩하여 데이터를 전달하는 용도로 사용
DTO 객체@ResponseBodyJacksonMessageConverter를 통해 JSON으로 변환되어 Http Body에 적재하기 적합

DTOSpring Bean을 사용하지 못하는 이유
Spring Bean은 기본적으로 싱글톤패턴이므로 데이터 전달용도로는 부적합

DTO를 선언하는 3가지 방법

  • 1. 기능 별로 요청응답DTO를 각각 생성
    불변을 위해 주로 Record로 선언
 // Bean에 대해 Validation 을 수행
public record MemberCreateRequest(
	@NotBlank
 	String loginId,
	// 최소 8자 이상 대문자 소문자 숫자 특수문자 포함
	@NotBlank
	@Pattern(regexp="^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^])[A-Za-z\\d!@#$%^]{8,}$")
	String password,
	@NotBlank
	String name,
	@NotBlank
	@Pattern(regexp="^[0-9A-Za-z._%+-]+@[0-9A-Za-z.-]+\\.[A-Za-z]{2,6}$")
	String email,
	@NotBlank
	@Pattern(regexp="^(0\\d{1,2})-(\\d{3,4})-(\\d{4})$")
	String mobile,
	@NotNull
	Gender gender,
	@NotNull
	LocalDate birthday
) { }

Member를 생성하는 기능요청에 사용되는 DTO

  • 2. 요청 / 응답을 각각 클래스로 분할 및 기능 별 DTORecord 또는 Static Nested Class로 구현
    Nested Class의 경우에도 기본적으로 default로 설정되어있으므로 다른 패키지클래스에서 DTO로서 사용 할 수 있으므로 public으로 명시적으로 선언

    Swagger를 통한 API 문서화를 수행하는 경우 API 문서에서 동일한 명칭의 DTO 간 충돌이 발생할 수 있으므로 @Schema(name = "API 문서내 이름")으로 별칭을 부여해야한다.
public class ProductRequest {
	@Schema(name = "ProductReqpuest.Create")
	public record Create(
		@NotBlank String name,
		@NotNull Long price,
		@NotNull Long quantity
	){}
	@Getter
	@AllArgsConstructor
    @Schema(name = "ProductReqpuest.Update")
	public static class Update{
		@NotBlank
		private String name;
		@NotNull
		private Long price;
		@NotNull
		private Long quantity;
	}
}

요청DTO를 포함하는 역할의 클래스 내 각 기능별 DTO를 구현

	@PostMapping
	@ResponseStatus(HttpStatus.CREATED)
	public void create(@RequestBody @Valid ProductRequest.Create request){
		productService.create(
			request.getName(),
			request.getPrice(),
			request.getQuantity()
		);
	}

▶ 사용하는쪽은 다음처럼 클래스.중첩클래스명로서 Type을 선언하여 사용

  • 3. 요청 / 응답을 각각 인터페이스로 분할 및 기능 별 DTORecord 또는 Class로 내부에 구현
    인터페이스 내부에 DTO로 활용할 Record 또는 클래스를 정의
public interface MemberResponse {
    @Schema(name = "ProductReqpuest.Details")
	record Details(
		String loginId,
		String name,
		String email
	){
		// 정적 팩토리 메서드
		public static Details of(MemberEntity memberEntity){
			return new Details(
				memberEntity.getLoginId(),
				memberEntity.getName(),
				memberEntity.getEmail()
			);
		}
	}
}

클라이언트에게 DB Entity 객체 반환 시 연관관계 객체 등 모든 field를 포함해서 반환하면 안되므로 정적 팩토리 메서드를 통해 DTO를 생성하여 반환

。사용하는 쪽은 인터페이스명.내부클래스Data Type으로 설정하여 사용

profile
공부기록 블로그

0개의 댓글