DTO와 DAO는 왜 나눌까?

wony·2024년 6월 16일

Spring

목록 보기
31/33

DTO(Data Transfer Object)와 DAO(Data Access Object)를 나누는 이유는 소프트웨어 설계 원칙 중 하나인 SRP (Single Responsibility Principle)를 따르기 위함입니다. 각 클래스가 하나의 책임만 가지도록 분리하여 코드의 유지보수성과 확장성을 높이는 것입니다. 다음은 DTO와 DAO를 나누는 구체적인 이유입니다:

1. 역할과 책임 분리

  • DTO (Data Transfer Object):
    • 역할: 데이터를 전달하는 객체입니다. 주로 데이터베이스에서 가져온 데이터를 서블릿이나 JSP와 같은 다른 계층으로 전달하는 데 사용됩니다.
    • 책임: 데이터의 저장과 전달. 비즈니스 로직이나 데이터베이스 액세스 로직을 포함하지 않습니다.
  • DAO (Data Access Object):
    • 역할: 데이터베이스에 접근하여 데이터를 조회하거나 수정하는 기능을 제공합니다.
    • 책임: 데이터베이스 연결 및 쿼리 실행. SQL문을 사용하여 데이터베이스와의 상호작용을 처리합니다. 비즈니스 로직을 포함하지 않습니다.

2. 코드 재사용성 향상

  • DTO는 데이터를 단순히 전달하기 위한 객체이므로, 여러 곳에서 재사용할 수 있습니다.
  • DAO는 데이터베이스와의 상호작용을 처리하므로, 다른 비즈니스 로직이 필요로 하는 데이터베이스 작업을 캡슐화하여 재사용할 수 있습니다.

3. 코드 유지보수성 증가

  • DTODAO를 분리하면, 데이터 구조나 데이터베이스 관련 로직이 변경되더라도 각각의 클래스만 수정하면 됩니다. 예를 들어, 데이터베이스 스키마가 변경되면 DTO는 영향을 받지 않고, DAO만 수정하면 됩니다.
  • DAO는 데이터베이스 작업에 집중하기 때문에, 데이터베이스 연결, 쿼리 실행, 예외 처리 등의 로직을 한 곳에서 관리할 수 있습니다.

4. 테스트 용이성

  • DTO는 단순한 데이터 객체이므로, 테스트하기가 쉽습니다.
  • DAO는 데이터베이스와의 상호작용을 처리하므로, 데이터베이스 관련 테스트를 별도로 수행할 수 있습니다. 또한, 데이터베이스 모킹(mocking)을 통해 비즈니스 로직의 테스트를 더 쉽게 할 수 있습니다.

5. 계층 구조 설계

  • DTO는 프레젠테이션 계층(예: 서블릿, JSP)과 데이터베이스 계층(DAO) 사이에서 데이터를 전달하는 역할을 합니다. 이렇게 계층 간의 의존성을 줄여줍니다.
  • DAO는 데이터베이스와 직접 상호작용하는 데이터베이스 계층의 일부로, 비즈니스 로직과 데이터베이스 액세스를 분리합니다.

예시

이전 예시에서 DTO와 DAO의 역할을 다시 한 번 정리해보면:

  • MemberDTO:

    package dto;
    
    import java.util.Date;
    
    public class MemberDTO {
        private int id;
        private String email;
        private String password;
        private String nickName;
        private String phoneNumber;
        private String userImagePath;
        private Date dateCreated;
        private Date lastUpdated;
    
        // 생성자, Getter, Setter 생략
    }
  • MemberDAO:

    package dao;
    
    import java.sql.*;
    import java.util.*;
    import dto.MemberDTO;
    
    public class MemberDAO {
        private Connection con;
        private PreparedStatement pstmt;
        private ResultSet rs;
    
        // 데이터베이스 연결 정보
        private static final String driver = "oracle.jdbc.OracleDriver";
        private static final String url = "jdbc:oracle:thin:@localhost:1521:xe";
        private static final String id = "connection";
        private static final String pwd = "1234";
    
        public MemberDAO() {
            try {
                Class.forName(driver);
                con = DriverManager.getConnection(url, id, pwd);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public int insertMember(MemberDTO dto) {
            String sql = "INSERT INTO \"USER\" (id, email, password, nick_name, phone_number, user_image_path, date_created, last_updated) " +
                         "VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
            try {
                pstmt = con.prepareStatement(sql);
                pstmt.setInt(1, dto.getId());
                pstmt.setString(2, dto.getEmail());
                pstmt.setString(3, dto.getPassword());
                pstmt.setString(4, dto.getNickName());
                pstmt.setString(5, dto.getPhoneNumber());
                pstmt.setString(6, dto.getUserImagePath());
                pstmt.setDate(7, new java.sql.Date(dto.getDateCreated().getTime()));
                pstmt.setDate(8, new java.sql.Date(dto.getLastUpdated().getTime()));
    
                return pstmt.executeUpdate();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return -1; // 오류 발생 시 -1 반환
        }
    
        // 추가적인 CRUD 메서드 생략
    
        public void close() {
            try {
                if (rs != null) rs.close();
                if (pstmt != null) pstmt.close();
                if (con != null) con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

이와 같이 DTO와 DAO를 나눔으로써 각각의 클래스가 명확한 역할과 책임을 가지게 되며, 결과적으로 코드의 가독성, 유지보수성, 확장성, 재사용성 등이 향상됩니다.

profile
안녕하세요. wony입니다.

0개의 댓글