👩🏻💻 Programming/SpringBoot
Entity, DTO, 그리고 DAO
한국의 메타몽
2021. 9. 5. 21:49
핵심 요약
[ Entity ]
- DB 테이블과 매핑되는 클래스
-> @Entity, @Column, @Id 등을 이용 - DB 테이블 내에 존재하는 컬럼만을 속성(필드)으로 가져야 한다.
- 즉, 테이블의 컬럼 == Entity의 필드이다.
- Entity는 상속을 받거나 구현체이면 안된다.
[ Entity 코드 예시 ]
import javax.persistence.*;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
privae Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false)
private String email;
@Builder
public User(String name, String email) {
this.name = name;
this.email = email;
}
public User update(String name, String email) {
this.name = name;
this.email = email;
return this;
}
}
[ Entity 클래스와 DTO 클래스를 분리하는 이유 ]
- View Layer와 DB Layer의 역할을 철저하게 분리하기 위해서
- 테이블에 매핑되는 Entity 클래스가 변경되면 여러 클래스에 영향을 끼치는 반면, View와 통신하는 DTO 클래스(Request / Response 클래스)는 자주 변경되므로 분리해야 한다.
- Domain Model을 아무리 잘 설계했다 하더라도 각 View 내에서 Domain Model의 getter만을 이용해서 원하는 정보를 표시하기가 어려운 경우가 종종있다. 이런 경우 Domain Model 내에 Presentation을 위한 필드나 로직을 추가하게 되는데, 이러한 방식이 모델링의 순수성을 깨고 Domain Model 객체를 망가뜨리게 된다.
[ DTO ]
- 계층간 데이터 교환을 위한 객체
- DB에서 얻은 데이터를 Controller나 Service 로직에서 사용하기 위한 객체
- 로직을 갖고 있지 않는 순수한 데이터 객체이며, getter / setter 메서드만을 갖는다.
- Request와 Response용 DTO는 View를 위한 클래스이다.
-> 자주 변경이 필요한 클래스
-> toEntity() 메소드를 통해서 DTO에 필요한 부분을 이용하여 Entity로 만든다. - VO(Value Object) vs DTO
-> VO와 DTO는 동일한 개념이지만 VO는 ReadOnly의 속성을 갖는다.
[ DTO 코드 예시 ]
class RGBColorDto {
private int red;
private int green;
private int blue;
public RGBColor(int red, int green, int blue) {
this.red = red;
this.green = green;
this.blue = blue;
}
public int getRed() {
return red;
}
public int setRed(int red) {
this.red = red;
}
...
}
[ DAO ]
- DAO(Data Access Object)는 DB를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 오브젝트
- 서비스와 DB사이에서 데이터를 옮기는 역할을 한다. 즉, 쿼리를 날려 데이터를 가져오고 저장한다.
- DB에 로그인, 입력, 받아오기, 수정, 삭제 등의 작업들을 정의한 클래스이다.
- JPA Repository 객체들이 일반적으로 DAO의 역할을 한다.
[User.java]
public class User {
String id;
String name;
String password;
public String getId(){return id;}
public String getName(){return name;}
public String getPassword(){return password;}
public void setId(String id){this.id = id;}
public void setName(String name){this.name = name;}
public void setPassword(String password){this.password = password;}
}
예컨데 위와 같이 id, name, password 3개의 프로퍼티를 가진 User 클래스가 있다고 가정하자.
User 클래스의 프로퍼티와 동일한 DB 테이블을 하나 만들고, 이제 사용자 정보를 DB에 넣고 관리할 수 있는 DAO 클래스를 만들어보자.
사용자 정보를 관리하는 DAO이므로 UserDao라는 이름의 클래스를 하나 만들어보자.
[UserDao.java]
public class UserDao {
public void add(User user) throws ClassNotFoundException, SQLException{ // 6
Class.forName("com.mysql.jdbc.Driver");
Connection c = DriverManager.getConnection( // 1
"jdbc:mysql://localhost/springbook", "spring", "book");
PreparedStatement ps = c.prepareStatement("insert into user(id, name, password) // 2 values(?,?,?)");
ps.setString(1,user.getId());
ps.setString(2,user.getName());
ps.setString(3,user.getPassword());
ps.executeUpdate(); // 3
ps.close(); // 5
c.close();
}
public User get(String id) throws ClassNotFoundException, SQLException{
// ... 이하 생략
}
}
JDBC에 접근하는 순서는 다음과 같다. (아래 순서는 위 코드의 주석에 달아둠)
- DB연결을 위한 Connection을 가져옴
- SQL을 담은 Statement(또는 PreparedStatement)를 만듦
- 만들어진 Statement를 실행
- 조회의 경우 SQL 쿼리의 실행 결과를 ResultSet으로 받아서 정보를 저장할 오브젝트(여기서는 User)에 옮겨주기
- 작업 중에 생성된 Connection, Statement, ResultSet같은 리소스는 작업을 마친 후 반드시 닫아주기
- JDBC API가 만들어내는 예외를 잡아서 직접 처리하거나, 메소드에 Throws를 선언해서 예외가 발생하면 메소드 밖으로 던지기
위와 같은 코드를 작성한 뒤, main()에서 아래와 같이 테스트해볼 수 있다.
public static void main(String[] args) {
UserDao dao = new UserDao();
User user = new User();
user.setId("Metamong");
user.setName("메타몽");
user.setPassword("change");
dao.add(user);
System.out.println(user.getId() + "님의 정보 등록 성공!");
}
물론 위의 코드들은 실제로 현업에서는 사용해서는 안된다. 단순히 개념 이해 차원에서만 활용하자.
자세히보면 add, get과 같은 DB와 연결하는 메소드를 실행할때마다 Connection을 새로 만드는 코드가 보여진다.
프로그래밍의 기초 개념인 "관심사의 분리", 즉, 관심이 같은 것 끼리, 공통된 것 끼리 묶어나가는 과정이 진행되어야 비로소 온전한 DAO를 활용할 수 있다.
전체 구조
참고 자료
장생놈님 - DAO, DTO, VO, Entity
heejeong Kwon님 - [DAO] DAO, DTO, Entity Class의 차이