티스토리 뷰

프로그래밍/Web

DAO, DTO, Service

국윤창 2018. 1. 28. 04:27

Spring Framework의 MVC에서 Model은 Service, DAO, DTO로 나눌 수 있다. 한 번 살펴보자.

DAO

Data Access Object의 줄임말이다. DB를 사용해 데이터를 조회하거나 조작하는 기능을 담당하는 것들을 DAO라고 부른다. domain logic (비즈니스 로직이나 DB와 관련없는 코드들)을 persistence mechanism과 분리하기 위해 사용한다. 

 

  • persistence layer: Database에 data를 CRUD(Create, Read, Update, Drop)하는 계층

이렇게 따로 분리해놓는 이유는 HTTP Request를 Web Application이 받게 되면 Thread를 생성하게 되는데 비즈니스 로직이 DB로부터 데이터를 얻어오기 위해 매번 Driver를 로드하고 Connection 객체를 생성하게 되면 엄청 많은 커넥션이 일어나므로 DAO를 하나 만들어 DB 전용 객체로만 쓰는 것이다. 이러면 부담이 줄어들게 된다.

이 개념은 DBCP(Database Connection Pool)로부터 나왔다. WAS(Web Application Server)이 실행되면 일정량의 DB Connection 객체를 Pool에다 저장해 두고, HTTP Request에 따라 필요할 때마다 Pool에서 Connection 객체를 가져다 쓰고 반환하는 것이다. 아래 그림 1과 같다.

DBCP
[그림 1] Database Connection Pool

보통 connection pool은 요청으로 생기는 thread(thread도 pool을 만들수도 있다.)보다 적게 만든다. 왜냐면 모든 Request가 DB에 관련된 것은 아니기 때문이다.

 

그럼 Spring에서 DAO는 어떻게 만들까? 우선 Spring에서 Singleton 패턴을 권장한다는 것을 기억하자. Spring에서 관리되는 Singleton 패턴이라 하면 당연히 Bean이다. Spring에서 DAO는 @Repository annotation으로 정의한다. class 선언 시 바로 @Repository annotation을 사용해도 되지만 참조한 글에 따르면 메소드 헤더만 정의한 interface를 정의하고 이것을 구현한 class에 annotation을 붙여 사용한다고 한다. 이유는 interface로 구성하면 확장성과 유연성이 높아지기 때문이다. 참조한 글 외에도 여러 잘하는 프로그래머 글에서도 이 방법으로 구현하는 것 같으니 한 번 따라해보자.

UserDao.java

public interface UserDao {
    /**
     * user 테이블에서 모든 유저의 정보를 가져온다.
     * 
     * @return 모든 유저의 정보
     */
    public List<User> getUsers();
}

UserDaoImpl.java

@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Override
    public List<User> getUsers()
    {
        // 리스트 생성
        List<User> result = new ArrayList<User>();

        // 데이터베이스에서 유저 목록을 가져온다.
        result.add(...);
        ...

        return result;
    }
}

DTO

Data Transfer Object의 줄임말이다. VO(Value Object)라고도 표현하는데, 계층간 데이터 교환을 위한 자바빈즈(Java Beans)다.

이 객체는 데이터베이스 레코드의 데이터를 매핑하기 위한 데이터 객체를 말한다. DTO는 보통 로직을 가지고 있지 않고 data와 그 data에 접근을 위한 getter, setter만 가지고 있다.

정리하면 DTO는 Database에서 Data를 얻어 Service나 Controller 등으로 보낼 때 사용하는 객체를 말한다. 위 코드에서도 DAO가 Database로부터 Data를 얻은 뒤 List에 담아서 보내주고 있다. DTO는 아래 코드와 같이 쓰일 수 있다.

public class User {
    private String name;
    private int age;

    public String getName() {
        return this.name;
    }

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

    public void getAge() {
        return this.age;
    }

    public void setAge(int age)
    {
        this.age = age;
    }

    @Override
    public String toString() {
        return "name='" + name + "', age=" + age;
    }
}

위와 같이 DTO는 데이터를 주고받을 포맷이라고 할 수 있다.

Service

마지막으로 Service에 대해서 살펴보자. Service는 비지니스 로직이 들어가는 부분이다. Controller가 Request를 받으면 적절한 Service에 전달하고, 전달 받은 Service는 비즈니스 로직을 처리한다. DAO로 데이터베이스를 접근하고, DTO로 데이터를 전달받은 다음, 적절한 처리를 해 반환한다. 아래 코드처럼 쓰일 수 있다.

public interface UserService {
    /**
     * 유저 정보를 텍스트 파일로 저장한다.
     * 
     * @param path 저장할 파일의 경로
     * @return 저장한 유저의 개수
     */
    public int saveUsersAsTextFile(String path);
}
@Service("userService")
public class UserServiceImpl implements UserService {
    private static final Logger LOGGER = Logger.getLogger("UserServiceImpl");

    @Autowired
    private UserDao userDao;

    @Override
    public int saveUsersAsTextFile(String path) {
        List<User> users = userDao.getUsers();

        // 비즈니스 로직
        try (FileOutputStream fileOutputStream = new FileOutputStream(path)) {
            StringBuilder result = new StringBuilder();
            for(User user : users) {
                result.append(user);
                result.append('\n');
            }

            fileOutputStream.write(result.toString().getBytes());
        } catch (IOException exception) {
            LOGGER.log(Level.SEVERE, "파일을 쓸 수 없습니다.");
            throw new IllegalStateException(String.format("Can't write a file. path: %s", path));
        }

        return users.size();
    }
}

위 코드는 DAO로부터 DTO 리스트를 받고, DTO의 리스트를 파일로 저장하는 코드이다. @Autowired annotation으로 userDao bean을 찾아서 연결한 것을 볼 수 있다. (Spring에서는 DI(Dependency Injection, 의존성 주입)이라고 한다.)

 

Controller에서 서비스 호출은 아래 코드처럼 쓰일 수 있다.

@Controller
public class MainController {
    @Autowired
    private UserService userService;

    @RequestMapping(value = "/save/users", method = RequestMethod.GET)
    public ModelAndView saveUsers(ModelAndView mv) {
        // 유저를 얻어와서 텍스트 파일로 저장한다.
        int saveCount = userService.saveUsersAsTextFile("users.txt");

        // 뷰에서 결과를 보여주기 위해 저장한 개수를 뷰에 넘긴다.
        mv.addObject("saveCount", saveCount);
        mv.setViewName("saveUsersResultView");

        return mv;
    }
}

참조

DAO, DTO 관련

http://genesis8.tistory.com/214

 

DAO / VO / DTO란?

원본 출처 : http://lbass.tistory.com/entry/DAO-%EB%9E%80 http://choijaehyuk.com/128 http://everyit.tistory.com/4 DAO란 Data Access Object의 약어로서 실질적으로 DB에 접근하는 객체를 말한다. DAO의 사..

genesis8.tistory.com

DB Connection Pool 관련

https://www.holaxprogramming.com/2013/01/10/devops-how-to-manage-dbcp/

 

DB Connection Pool에 대한 이야기

DB Connection Pool은 왜 필요할까?

www.holaxprogramming.com

 

 

 

'프로그래밍 > Web' 카테고리의 다른 글

Spring과 Cookie & Session  (0) 2018.02.05
Mybatis & MySQL  (0) 2018.01.29
DAO, DTO, Service  (4) 2018.01.28
Annotation과 Bean  (2) 2018.01.27
Spring Bean  (2) 2018.01.27
Spring MVC 맛보기  (0) 2018.01.21
댓글
  • 프로필사진 개발자자자 댓글보시나요~?
    List<TestListResponseDTO> testList = userListDAO.getUserListdao();

    여기서 userListDAO 는 어디서 나오는건가요~?
    2020.01.18 01:01 신고
  • 프로필사진 국윤창 안녕하세요. 꽤 오래전에 쓴 글이라 그런지 제가봐도 이상한점이 좀 많네요. 네이밍도 그렇구요.

    testListDao.getTestListdao(); 가 맞습니다. 오타가 있었던 모양이네요.

    곧 수정하겠습니다 감사합니다
    2020.01.18 01:42 신고
  • 프로필사진 김문규 잘읽었어요! 정말 디테일하게 알려주셔서 많이 도움돼요!! 2020.03.02 12:30
  • 프로필사진 jeon 좋은 글 감사합니다. 평소에 제대로 알지도 못한 채 쓰고 있었다는 걸 알게 되었네요...
    덕분에 정리가 잘되었습니다. 감사합니다.
    2021.10.10 13:45
댓글쓰기 폼