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

TopOfTheHead·2025년 10월 15일

Spring

목록 보기
9/16
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에 접근하여 상호작용하는 객체
DB의 데이터로 접근하는 Transaction Object로서, DB의 데이터를 조회하거나 조작하는 기능을 수행

DB Connection을 통해 DB상호작용하는 객체
SOLID단일책임 원칙에 의해 DB 연결 기능이 없고 오직 상호작용 기능만 수행

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

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

。주로 Java Record를 통해 생성

VOBusiness Logic이 구현된 메서드를 구현하면 안된다.
getter 또는 에 대한 유효성 검증 메서드만 구현.

DB Entity와는 다른 개념
Entity는 오직 Class를 활용하여 구현

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Member {
	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;
}

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

Repository에서 DB Entity 자체가 아닌, 필요한 일부의 데이터를 포함하여 클라이언트에게 반환하는 운반용 객체 용도로서도 사용

。 순전히 데이터를 저장 및 회수하는 기능을 제외하고 아무 기능을 가지고 있지 않음.
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개의 댓글