간단정리
DAO : 실제 Database에 접근하는 역할을 하는 객체.
DTO : 데이터를 전달하기 위한 객체
VO : 값 자체를 표현하는 객체. (변경 불가능하며 오직 읽기만 가능 (Read-Only))
Entity : 실제 DB 테이블과 매핑이 되는 클래스.
DAO (Data Access Object)
DAO는 Data Access Object의 약자로, 실제로 Database(DB)의 데이터에 접근하기 위한 객체를 가리킨다.
DB에 접근하기 위한 로직과 비즈니스 로직을 분리하기 위해 사용한다.
직접 DB에 접근하여 data를 삽입, 삭제, 조회 등 조작할 수 있는 기능을 수행한다.
JPA에서는 DB에 데이터를 CRUD 하는 JpaRepository<>를 상속받는 Repository 객체들이 DAO라고 볼 수 있습니다
- Data Access Object 의 약자로, Database에 접근하는 역할을 하는 객체.
- 프로젝트의 서비스 모델에 해당하는 부분과 데이터베이스를 연결하는 역할 (MVC 패턴의 Model)
- 실제로 데이터의 CRUD 작업을 시행하는 클래스. 즉, 데이터에 대한 CRUD 기능을 전담하는 오브젝트
- 효율적인 커넥션 관리와 보안성.
- DAO는 비즈니스 로직을 분리하여 도메인 로직으로부터 DB와 관련한 메커니즘을 숨기기 위해 사용
public class TestDao {
public void add(TestDto dto) throws ClassNotFoundException, SQLException {
private static final String DRIVER = "com.mysql.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost:3306/dao_Db";
private static final String USER = "root";
private static final String PASSWORD = "1234";
String sql = "SELECT * FROM vouchers";
try {
con = DriverManager.getConnection(URL, USER, PASSWORD);
stmt = con.createStatement();
res = stmt.executeQuery(sql);
while (res.next()) {
System.out.println(res.getString("id") + " ");
System.out.println(res.getString("value") + " ");
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
preparedStatement.setString(1, dto.getName());
preparedStatement.setInt(2, dto.getValue());
preparedStatement.setString(3, dto.getData());
preparedStatement.executeUpdate();
preparedStatement.close();
connection.close();
}
}
@Repository
public class UserDao {
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource){
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public long createUser(PostUserReq postUserReq){
String createUserQuery = "INSERT INTO Users(email, profilePicUrl, name, password)\n" +
"VALUES\n" +
" (?, ?, ?, ?);";
Object[] createUserQueryParams = new Object[]{postUserReq.getEmail(), postUserReq.getProfilePicUrl(),
postUserReq.getName(), postUserReq.getPassword()};
// 데이터 삽입 쿼리
this.jdbcTemplate.update(createUserQuery, createUserQueryParams);
String getNewUserIdQuery = "SELECT LAST_INSERT_ID();";
return this.jdbcTemplate.queryForObject(getNewUserIdQuery, long.class);
}
}
DTO(Data Transfer Object)
DTO는 Data Transfer Object의 약자로, 계층 간(Controlelr, View, Business Layer) 데이터 교환을 위한 객체 클래스(Java Bean) 이다 .
계층이란 Presentation(View, Controller), Business(Service), Persistence(DAO, Repository) 등을 의미한다.
DTO는 로직을 가지지 않는 데이터 객체이고, getter, setter 메소드만 가진 클래스로
순수하게 데이터를 담아 계층 간으로 전달하는 객체이다.
비즈니스 로직을 갖고 있지 않은 순수한 데이터 객체이며 메서드로는 데이터에 대한 getter/setter 만을 갖는다.
여기서 getter/setter 이외에 다른 로직이 필요 없는 이유를 좀 더 자세히 살펴보자면, 만약 DTO가 데이터 전달 만을 위한 객체라고 한다면 완전히 데이터 '전달' 용도로만 사용하기 때문에 getter/setter로직만이 필요하지 다른 로직이 필요하지 않은 것이다.
가변 객체로써 DTO를 만들 때는 setter() 메소드를 이용하여 구현할 수 있고, 불변 객체로써 DTO를 만들 때는 생성자를 이용해서 구현시 getter() 메소드만 구현할 수 있다. 이렇게 구현함으로써 데이터를 전달하는 과정에서 데이터의 불변성을 보장한다.
- Data Transfer Object의 약자로, 데이터를 전달하기 위한 객체
- DTO는 로직을 가지지 않는 순수한 데이터 객체(getter & setter 만 가진 클래스).
- 여러 레이어(Layer)간 데이터를 주고 받을 때 사용할 수 있고 주로 View와 Controller 사이에서 활용.
- DTO는 getter / setter 메소드를 포함한다. 하지만, 이외의 다른 비즈니스 로직은 포함하지 않는다.
- DTO는 어떻게 구현하느냐에 따라 가변 객체로 활용할 수도 있고 불변 객체로 활용할 수도 있다.
- DTO는 데이터 전달 만을 위한 객체라는 게 핵심 정의이다. 그렇기 때문에 완전히 데이터 전달 용도로만 사용하는게 목적이라면, getter/setter만 필요하지, 이외의 비즈니스 로직은 굳이 있을 필요가 없다.
* 프로퍼티(Property) 개념,
객체지향 언어인 자바에서 객체는 고유한 속성(특징)을 가지는데, 그 속성을 칭하는 단어를 프로퍼티(Property)라고 합니다. 프로퍼티는 필드(데이터 멤버)와 메서드의 중간에 있는 클래스 멤버의 특수한 유형입니다.
프로퍼티의 읽기와 쓰기는 일반적으로 getter, setter 메소드 호출로 변환되며, 이 속성의 실제 값을 담는 곳을 필드(field, 멤버 변수)라고 합니다.
아래의 GetUserRes 클래스를 예로 들면 GetUserRes는 userName의 속성을 가지고 있는 객체이고, 자바 빈 정의에 의해 getter(), setter() 메서드로 실제 값(field)에 접근할 수 있습니다.
* 자바 빈(JavaBean)이란,
JavaBean 규약에 따라 작성된 자바 클래스로 클래스 외부에서 필드에 접근할 때는 반드시 메서드를 통해 접근해야 하며, 이때 get, set 으로 시작하는 메소드를 이용합니다.
@Getter
@Setter
public class GetUserRes {
private long userId;
private String userName;
private String email;
private String password;
}
// 불변 객체 DTO
// 생성시 지정했던 값이 변하지 않고 getter() 메소드만 사용 가능
public class DtoEx {
private final String name;
private final int age;
public DtoEx(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
// 가변 객체 DTO
// 기본생성자로 생성 후 값을 유동적으로 변경
public class DtoEx {
private String name;
private int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
VO(Value Object)
VO는 Value Object의 약자로, 값 그 자체를 나태는 객체이다.
"변경 불가능하며 오직 읽기만 가능 (Read-Only)"
DTO와 반대로 로직을 포함할 수 있으며, VO의 경우 특정 값 자체를 표현하기 때문에 불변성의 보장을 위해 생성자를 사용하여야 한다.
VO는 서로 다른 이름을 갖는 VO 인스턴스라도 모든 속성 값이 같다면 두 인스턴스는 같은 객체인 것이 핵심이다.
VO는 객체들의 주소 값이 달라도 데이터 값이 같으면 동일한 것으로 여긴다.
예를 들어 모두 똑같은 자동차가 색깔만 다르다고 하더라도 이를 별개의 객체로 보는 것이 아니라 하나의 객체(자동차)로 보는것과 같다.
Read-Only 속성을 가진 값 오브젝트이다. 자바에서 단순히 값 타입을 표현하기 위하여 불변 클래스(Read-Only)를 만들어 사용한다.
따라서 getter기능만 존재한다.
필요한 경우 값 비교를 위해 equals()와 hashCode() 메서드를 오버라이딩 해줘야한다.
- VO는 Value Object의 약자로 값 자체를 표현하는 객체.
- DTO와 유사하지만 DTO는 setter를 가지고 있어 값이 변할 수 있다.
- VO는 getter() 메소드를 포함해서, 이외의 비즈니스 로직도 포함할 수 있다.
- 사용하는 도중에 변경 불가하여 setter() 메소드는 가지지 않아. 오로지, 읽기 기능만 가능하다.(read-Only)
- VO는 두 객체의 모든 필드 값들이 동일하면 두 객체는 같다! 가 핵심 정의이다. 그렇기 때문에 완전히 값 자체 표현 용도로만 사용하는 게 목적이라면, 두 객체의 모든 필드 값들이 모두 같으면 같은 객체이도록 만드는 것(equals() 와 hashCode()의 오버라이딩)이 중요하지, 메소드는 어떤 메소드가 있든 말든 상관 없다.
// VO예제
// 값을 표현만 하기 때문에 setter()메소드는 사용하지 않는다.
public class VoEx {
static class Money{
private final int value;
Money(int value) {
this.value = value;
}
public int getValue() {
return value;
}
// 사용자 생성 메소드이지만 setter형태의 메소드가 아니므로 무관함
public String printMoney(){
return this.value + "원";
}
// 두 객체의 모든 필드 값들이 모두 같으면 같은 객체이도록 만들기 위한 오버라이딩
@Override
public int hashCode() {
return Objects.hash(value);
}
@Override
public boolean equals(Object obj) {
if(this == obj)
return true;
if(obj == null || getClass() != obj.getClass())
return false;
Money money = (Money) obj;
if(value == money.value)
return true;
else
return false;
}
}
}
@Getter
public enum BaseResponseStatus {
SUCCESS(true, 1000, "요청에 성공하였습니다."),
REQUEST_ERROR(false, 2000, "입력값을 확인해주세요."),
RESPONSE_ERROR(false, 3000, "값을 불러오는데 실패하였습니다."),
DATABASE_ERROR(false, 4000, "데이터베이스 연결에 실패하였습니다.");
private final boolean isSuccess;
private final int code;
private final String message;
private BaseResponseStatus(boolean isSuccess, int code, String message) {
this.isSuccess = isSuccess;
this.code = code;
this.message = message;
}
}
Entity
Entity 클래스는 실제 DB 테이블과 매핑되는 핵심 클래스로, 데이터베이스의 테이블에 존재하는 컬럼들을 필드로 가지는 객체입니다.
(DB의 테이블과 1:1로 매핑되며, 테이블이 가지지 않는 컬럼을 필드로 가져서는 안 됩니다.)
Entity는 데이터베이스 영속성(persistent)의 목적으로 사용되는 객체이며, 때문에 요청(Request)이나 응답(Response) 값을 전달하는 클래스로 사용하는 것은 좋지 않습니다.
또 많은 서비스 클래스와 비즈니스 로직들이 Entity 클래스를 기준으로 동작하기 때문에 Entity 클래스가 변경되면 여러 클래스에 영향을 줄 수 있습니다.
Entity에서는 setter 메서드의 사용을 지양해야 합니다.
이유는 변경되지 않는 인스턴스에 대해서도 setter로 접근이 가능해지기 때문에 객체의 일관성, 안전성을 보장하기 힘들어집니다.
(setter 메서드가 있다는 것은 불변하지 않다는 것이 됩니다.)
따라서 Entity에는 setter 대신 Constructor(생성자) 또는 Builder를 사용하게 되는데요.
setter 메서드가 아닌 생성자(Constructor)를 이용해서 초기화하는 경우 불변 객체로 활용할 수 있고, 불변 객체로 만들면 데이터를 전달하는 과정에서 데이터가 변조되지 않음을 보장할 수 있습니다.
- Entity는 실제 DB 테이블과 매핑이 되는 클래스.
- Entity를 데이터를 전달하는 클래스로 사용하면 안된다.
- Entity를 기준으로 테이블이 형성되고, 칼럼이 변경되곤 한다.
- Entity는 비즈니스 로직을 포함할 수도, setter() 메소드를 포함할 수도 있으나 지양하는게 좋습니다.
public class EntityEx {
private final Long id;
private final String name;
private final int value;
public EntityEx(Long id, String name, int value) {
this.id = id;
this.name = name;
this.value = value;
}
}
DTO vs VO
DTO와 VO의 차이점을 정리해보면 아래와 같다.
DTO | VO | |
목적 | 계층간 데이터 전달 | 값 자체 표현 |
동등성 | 필드값이 같아도 같은 객채x | 필드값이 같으면 같은 객체 |
가변성 | setter 존재시 가변 setter 비 존재시 불가변 |
불변 |
로직 | getter/setter 외의 로직이 필요하지 않음 | getter 외의 로직이 있어도 무방 |
- DTO는 가변의 성격을 가진 클래스이며 데이터 전송을 위해 존재한다. 따라서 getter와 setter 기능을 모두 가지고 있다.
- 그에 반해, VO는 값 그 자체의 의미를 가진 불변 클래스(Read-Only)를 의미한다. 따라서 getter 기능만 존재한다.
→ DTO는 인스턴스 개념이라면, VO는 리터럴 개념으로 이해하면 편하다.
연관된 글 :
[Spring] 스프링 프로젝트 구조 (DTO, Entity and Mapper)
참고:
[Spring] DAO, DTO, VO란? 각각의 개념에 대해 알아보자.
'개발 > Java' 카테고리의 다른 글
[Java] Stream API - map과 flatMap (1) | 2024.06.06 |
---|---|
[JAVA] Lombok 어노테이션 (0) | 2023.04.29 |
[Java] Interface (0) | 2023.04.01 |
[Java] 멀티 스레드(multi thread) (0) | 2023.03.29 |
[Java] 메소드 참조(method reference) (0) | 2023.03.20 |