우아한 테크코스에서 처음 스프링을 사용하는 미션을 진행하면서 처음 접하는 개념들이 많았지만 가장 많은 고민을 했던 ‘데이터베이스의 관리는 어떻게 이루어지는가?’에 대해 정리하고자 한다. 우선 본격적인 내용을 소개하기 앞서 이해하면 좋은 개념 3가지를 간단히 정리하고자 한다. DAO, DTO, Entity를 스프링을 이용한 웹 프로그래밍 관점에서 주관적으로 정리했으므로 일반적인 개념들과 다를수도 있다.
DAO (Data Access Object)
프로그래밍 분야에서 DAO는 데이터베이스나 여러 persistence mechanism (영속구조) 에 대한 추상 인터페이스를 제공하는 객체이다. DB에 접근하려는 여러 호출들을 persistence layer에 매핑시킴으로써 DAO는 데이터베이스의 상세한 사항을 노출시키지 않고 특정 데이터의 일부 동작을 제공한다. 풀어 말하자면 세상에는 여러 종류의 DB가 존재하고 각 DB마다 CRUD 방법이 다르다. 이 때 DB에 데이터를 저장하기 위해 saveData라는 메서드를 DAO 외부에서 호출했을 때 DAO는 각 DB에 맞는 문법을 호출해준다. 코드로 DAO를 추상 인터페이스로 활용하는 간단한 예시를 들어보았다.
public interface DBDao() {
public void saveData(Data data);
...
}
public class MySqlDao implements DBDao() {
public void saveData(Data data) {
//MySql에 data를 저장하는 문법 호출
...
}
...
}
public class MongoDBDao implements DBDao() {
public void saveData(Data data) {
//MongoDB에 data를 저장하는 문법 호출
...
}
...
}
DTO (Data Transfer Object)
프로그래밍 분야에서 DTO는 계층 간에 데이터를 전달하는 객체이다. DTO의 형식은 거의 정형화되어 있는데 DTO는 순수하게 데이터를 저장하고 비지니스 로직을 포함할 수 없으며 getter와 setter만을 가진다.
Entity
스프링에서 Entity는 간단히 말해 데이터베이스 내의 테이블과 1대 1 매핑되는 자료구조로 이해할 수 있다. Entity의 필드는 테이블의 Column을 의미하고 Entity 객체는 테이블의 한 행을 의미한다. JPA를 사용하게 되면 @Entity 어노테이션을 사용하여 데이터베이스의 테이블과 직접 매핑하는 기능을 제공하기도 한다.
DAO는 어떤 객체를 전달해주어야 하는가?
웹 자동차 경주 미션을 진행하는 도중 경주 결과를 데이터베이스에 저장하는 기능과 해당 테이블에서 경기 결과들을 읽어오는 기능을 구현해야 했다. DB에 접근하기 위해 다음과 같은 DAO와 메서드들을 선언해주었다.
CREATE TABLE PLAY_RESULT
(
id INT NOT NULL AUTO_INCREMENT,
winners VARCHAR(50) NOT NULL,
play_count INT NOT NULL,
created_at DATETIME NOT NULL default current_timestamp,
PRIMARY KEY (id)
);
@Repository
public class PlayResultDao {
...
// PLAY_RESULT에 데이터를 저장한다
public int insertResult(final PlayResultDto playResultDto) {
return insertPlayResult.executeAndReturnKey(
PlayResultToMap.convert(dto -> {
Map<String, Object> params = new HashMap<>();
params.put("winners", dto.getWinners());
params.put("play_count", dto.getPlayCount());
return params;
}
,playResultDto)).intValue();
}
// PLAY_RESULT에서 데이터를 읽어온다
public List<PlayResultEntity> getResult() {
String sql = "select * from play_result";
return jdbcTemplate.query(sql, playResultRowMapper);
}
}
이 때 getResult 메서드는 Entity를 반환하지만 insertResult 메서드는 PLAY_RESULT에 저장할 때 자동으로 값이 지정되지 않는 winners와 play_count만 필요하기에 해당 필드를 가지는 DTO를 전달하도록 설계해주었다. 그렇게 구현을 마치고 리뷰를 받았는데 DAO의 메서드의 파라미터로 DTO보다 Entity를 사용하는 것이 나을 것이라는 코멘트가 들어왔다.
해당 코멘트에 대해 스스로 고민해보고 많은 크루들과 얘기를 나눈 결과 DAO에 파라미터를 전달할 때나 DAO의 반환값은 Entity를 반환하는 것이 좋다는 결론을 내렸다. 결론을 내리기까지의 일련의 과정을 한 번 더 공부하고 공유할 겸 리뷰 내용을 남긴다.
정리
스스로 생각을 정리할 때 DAO 메서드의 인자로 Entity를 전달하는 것이 낫다고 생각한 이유들은 다음과 같다.
- 타인이 보기에 코드가 이해하기 쉽다. Entity는 일반적으로 테이블과 1대 1 매핑되어 있기 때문에 DAO에 어떤 자료구조가 전달해주어야 하고, 받을 수 있는지 예상하기 쉽다.
- Entity 내부 필드에 null이 저장된 채로 전달되어도 상관이 없다. DB의 모든 테이블의 column이 반드시 NOT NULL인 것도 아니고, 위의 예시와 같이 자동으로 입력되는 값이 있다면 null이 저장되어 있을 수 있다.
- DAO의 외부 객체들에 대한 의존도를 떨어트린다. DAO에 값을 저장하기 위해 여러 종류의 DTO가 들어올 수 있는데 이에 대응하려면 DAO 내에 무수히 많은 메서드가 생길 수 있다. 처음에는 이에 대응하기 위해 DAO에 전달될 수 있는 DTO는 일정한 형식을 가지도록 인터페이스화 시켜주었는데 사실상 해당 인터페이스는 Entity와 같은 역할을 하는 것으로 볼 수 있다. 따라서 다름 사람이 코드를 읽었을 때 더 직관적인 Entity를 사용하는 것이 낫다.
'Spring' 카테고리의 다른 글
@DirtiesContext를 사용하지 않고 테스트 데이터베이스 초기화하기 (0) | 2023.07.16 |
---|---|
[Spring 공식문서] Spring Core 1.5 (0) | 2023.05.07 |
[Spring 공식문서] Spring Core 1.4 (0) | 2023.04.30 |
[Spring 공식문서] Spring Core 1.1 ~ 1.3 (0) | 2023.04.16 |