기술 지식 쌓아가기 📚/Backend 🍔

[DB] 데이터베이스와의 상호작용을 쉽게 만드는 DAO 패턴 (+ DTO까지!)

코양이🤍 2024. 10. 7. 18:08

🚩 DAO (Data Access Object) 패턴 소개

소프트웨어 개발에서 데이터베이스와의 상호작용은 매우 중요합니다.

그러나 데이터베이스 접근 로직이 코드에 직접 삽입되면, 유지보수와 테스트가 어려워질 수 있습니다.

데이터베이스에 저장된 정보를 쉽게 관리하고 접근하기 위해, DAO (Data Access Object) 패턴이 사용됩니다.

이 글에서는 DAO 패턴의 개념을 쉽게 설명하고, 실제 예시를 통해 이해를 돕겠습니다

 

⭐ DAO 패턴의 개념

DAO 패턴은 데이터를 저장하고 가져오는 과정에서 발생하는 복잡성을 줄여주는 설계 패턴입니다.

이를 위해 다음과 같은 역할을 합니다.

 

  1. 추상화: 데이터베이스와의 상호작용을 단순화합니다. 비즈니스 로직(애플리케이션의 주요 기능)은 데이터가 어떻게 저장되고 가져오는지에 대한 세부 정보를 몰라도 작동할 수 있습니다.
  2. 구분: 데이터 접근 로직과 비즈니스 로직을 분리하여 관리합니다. 이렇게 하면 데이터베이스 구조나 기술을 변경할 때, 비즈니스 로직에 영향을 주지 않고 쉽게 수정할 수 있습니다.

예를 들어, 사용자의 정보를 저장하는 애플리케이션이 있다고 가정해 봅시다. DAO 패턴을 사용하면 사용자의 정보를 저장하고 가져오는 기능을 UserDAO라는 클래스로 만들 수 있습니다. 이 클래스는 데이터베이스와 직접 상호작용하며, 비즈니스 로직에서는 이 클래스를 통해 사용자 정보를 쉽게 다룰 수 있습니다.

 

⭐ 데이터베이스 변경의 용이성

DAO 패턴의 큰 장점 중 하나는 데이터베이스를 변경할 때 유연하다는 점입니다.

예를 들어, 처음에는 MySQL 데이터베이스를 사용하다가 MongoDB로 변경하고 싶다면, UserDAO 클래스의 구현만 수정하면 됩니다.

비즈니스 로직에서는 여전히 같은 인터페이스를 사용하므로, 코드의 다른 부분은 손대지 않아도 됩니다.

 

⭐ DAO 구현 방법

DAO를 구현할 때는 일반적으로 인터페이스와 그 구현 클래스를 정의합니다.

인터페이스에는 데이터베이스와의 상호작용을 위한 메서드가 포함되며, 구현 클래스에서는 이러한 메서드를 구체적으로 정의합니다.

이 과정에서 JDBC, JPA, Hibernate 등 다양한 ORM(Object-Relational Mapping) 도구를 사용할 수 있습니다.

 

⭐ 간단한 예시

아래는 Spring에서 DAO 패턴을 사용하는 간단한 예시입니다. 이 예시는 사용자의 정보를 관리하는 User 객체와 이를 위한 UserDAO 인터페이스 및 그 구현 클래스를 보여줍니다.

✅ User 클래스

public class User {
    private Long id;
    private String name;
    private String email;

    // 생성자, 게터, 세터
    public User(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    // Getters and Setters
}

 

✅ UserDAO 인터페이스

import java.util.List;

public interface UserDAO {
    void addUser(User user);
    User getUser(Long id);
    List<User> getAllUsers();
    void updateUser(User user);
    void deleteUser(Long id);
}

사용자 정보를 처리하기 위한 메서드를 정의합니다.

✅ UserDAOImpl 클래스

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;

@Repository
public class UserDAOImpl implements UserDAO {
    private final JdbcTemplate jdbcTemplate;

    public UserDAOImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public void addUser(User user) {
        String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
        jdbcTemplate.update(sql, user.getName(), user.getEmail());
    }

    @Override
    public User getUser(Long id) {
        String sql = "SELECT * FROM users WHERE id = ?";
        return jdbcTemplate.queryForObject(sql, new Object[]{id}, 
            (rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"), rs.getString("email")));
    }

    @Override
    public List<User> getAllUsers() {
        String sql = "SELECT * FROM users";
        return jdbcTemplate.query(sql, 
            (rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"), rs.getString("email")));
    }

    @Override
    public void updateUser(User user) {
        String sql = "UPDATE users SET name = ?, email = ? WHERE id = ?";
        jdbcTemplate.update(sql, user.getName(), user.getEmail(), user.getId());
    }

    @Override
    public void deleteUser(Long id) {
        String sql = "DELETE FROM users WHERE id = ?";
        jdbcTemplate.update(sql, id);
    }
}

실제 데이터베이스와 상호작용하는 구현체입니다.

 UserService 클래스

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    private final UserDAO userDAO;

    @Autowired
    public UserService(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    public void createUser(String name, String email) {
        User user = new User(null, name, email);
        userDAO.addUser(user);
    }

    public User findUser(Long id) {
        return userDAO.getUser(id);
    }

    public List<User> listUsers() {
        return userDAO.getAllUsers();
    }

    public void modifyUser(User user) {
        userDAO.updateUser(user);
    }

    public void removeUser(Long id) {
        userDAO.deleteUser(id);
    }
}

비즈니스 로직을 처리하며 DAO를 사용하여 사용자 정보를 관리합니다.

 

⭐ 테스트와 유지보수의 장점

DAO 패턴은 단순히 데이터베이스와의 상호작용을 효율적으로 관리할 뿐만 아니라, 테스트와 유지보수에도 큰 장점을 제공합니다.

예를 들어, 단위 테스트를 진행할 때, DAO를 목(mock) 객체로 쉽게 교체할 수 있어 데이터베이스와의 의존성을 줄일 수 있습니다.

 

🚩 결론

결론적으로, DAO 패턴은 데이터 접근을 보다 구조적이고 효율적으로 만들어주는 강력한 도구입니다.

개발 과정에서 DAO를 적극 활용하면 코드의 품질을 높이고, 변화에 유연하게 대응할 수 있습니다.

 


 

⭐ DTO와 DAO

✅ DTO (Data Transfer Object)

DTO는 데이터를 전송하기 위한 객체로, 속성을 담고 있으며 데이터베이스와의 직접적인 상호작용을 포함하지 않습니다.

// UserDTO.java
public class UserDTO {
    private Long id;
    private String name;
    private String email;

    // 생성자
    public UserDTO(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }

    // Getters and Setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

 

  • 목적: UserDTO는 사용자 정보를 전송하기 위해 정의된 객체입니다. 예를 들어, 클라이언트에서 서버로 사용자 정보를 전송하거나 서버에서 클라이언트로 응답할 때 사용됩니다.
  • 구조: DTO는 데이터 속성만 포함하며, 비즈니스 로직이나 데이터베이스 접근에 대한 내용은 포함하지 않습니다.

 

✅ DAO (Data Access Object)

DAO는 데이터베이스와의 상호작용을 관리하는 객체입니다.

import java.util.List;

// UserDAO.java
public interface UserDAO {
    void addUser(UserDTO user);
    UserDTO getUser(Long id);
    List<UserDTO> getAllUsers();
    void updateUser(UserDTO user);
    void deleteUser(Long id);
}

// UserDAOImpl.java
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;

@Repository
public class UserDAOImpl implements UserDAO {
    private final JdbcTemplate jdbcTemplate;

    public UserDAOImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public void addUser(UserDTO user) {
        String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
        jdbcTemplate.update(sql, user.getName(), user.getEmail());
    }

    @Override
    public UserDTO getUser(Long id) {
        String sql = "SELECT * FROM users WHERE id = ?";
        return jdbcTemplate.queryForObject(sql, new Object[]{id},
            (rs, rowNum) -> new UserDTO(rs.getLong("id"), rs.getString("name"), rs.getString("email")));
    }

    @Override
    public List<UserDTO> getAllUsers() {
        String sql = "SELECT * FROM users";
        return jdbcTemplate.query(sql,
            (rs, rowNum) -> new UserDTO(rs.getLong("id"), rs.getString("name"), rs.getString("email")));
    }

    @Override
    public void updateUser(UserDTO user) {
        String sql = "UPDATE users SET name = ?, email = ? WHERE id = ?";
        jdbcTemplate.update(sql, user.getName(), user.getEmail(), user.getId());
    }

    @Override
    public void deleteUser(Long id) {
        String sql = "DELETE FROM users WHERE id = ?";
        jdbcTemplate.update(sql, id);
    }
}

 

  • 목적: UserDAO는 사용자 정보를 데이터베이스에 추가, 조회, 수정 및 삭제하는 메서드를 정의합니다. UserDAOImpl은 이 메서드들을 구현하여 실제 데이터베이스와 상호작용합니다.
  • 구조: DAO는 데이터베이스 쿼리 및 그 결과를 처리하는 로직을 포함하고 있습니다. 이때 DTO를 사용하여 데이터를 전달합니다.

 

⭐ DTO와 DAO의 차이 요약

 

  • 목적:
    • DTO: 데이터를 전송하는 데 중점을 둡니다. UserDTO는 사용자 정보를 담기 위한 객체입니다.
    • DAO: 데이터베이스와의 상호작용을 처리합니다. UserDAO와 UserDAOImpl은 데이터베이스에서 사용자 정보를 관리하는 데 필요한 메서드를 정의하고 구현합니다.
  • 구조:
    • DTO: 속성만 포함하며 비즈니스 로직이나 데이터베이스 접근을 포함하지 않습니다.
    • DAO: 데이터베이스 쿼리 및 관련 로직을 포함하여, 비즈니스 로직과의 상호작용을 관리합니다.
  • 사용 위치:
    • DTO: 데이터 전송이 필요한 경우, 예를 들어 API 응답이나 요청 시 사용됩니다.
    • DAO: 비즈니스 로직에서 호출되어 데이터베이스 작업을 수행합니다.

 

⭐ DAO와 DTO를 함께 사용하는 이유

대부분의 프로젝트에서는 DAODTO를 함께 사용하는 것이 일반적입니다. 이 두 개념은 서로 보완적이며, 각각의 역할이 다르기 때문에 함께 사용하면 더 효율적이고 구조적인 애플리케이션을 만들 수 있습니다.

 

  • 역할 분리:
    • DAO는 데이터베이스와의 상호작용을 처리하고, CRUD 작업을 수행하는 데 특화되어 있습니다.
    • DTO는 데이터 전송을 위한 객체로, 네트워크나 API를 통해 데이터를 주고받는 역할을 합니다.
  • 유지보수성:
    • 데이터베이스 접근 로직과 데이터 전송 로직을 분리하면, 한쪽의 변경이 다른 쪽에 미치는 영향을 최소화할 수 있습니다. 예를 들어, 데이터베이스 구조를 변경하더라도 DTO는 영향을 받지 않습니다.
  • 테스트 용이성:
    • DAO는 데이터베이스와의 의존성이 있기 때문에, 이를 목(mock) 객체로 대체하여 테스트할 수 있습니다. DTO는 단순한 데이터 구조이므로, 비즈니스 로직에서 쉽게 사용할 수 있습니다.