티스토리 뷰

Spring

02)DAO

K_sanghoon 2018. 3. 13. 18:28

@markdown

## Summary:


> Spring에서 DAO를 어떻게 관리하고 작성하는에 대한 정리


: 모든 예제는 '토비의 스프링 3.1'에서 발췌


---


## Comum Elements


- [DAO](#DAO)


---


## **DAO**

> Data Access Object의 약자로 DB를 이용하여 데이터를 조회하거나 그 조작 기능을 전답하도록 만든 오브젝트


- **User클래스 **


    : User 정보를 갖는 클래스

``` java

public class User{

    String id;

    String name;

    String password;


    public String getId(){

        return id;

    }

    public void setId(String id){

        this.id = id;

    }

    public String getName(){

        return name;

    }

    public void setName(String name){

        this.name = name;

    }

    public String getPassword(){

        return password;

    }

    public void setPassword(String id){

        this.password = password;

    }

}

```

실제 DB에도 위와같은 프로퍼티를 갖는 테이블이 만들어질 것이며 DAO는 위 클래스를 프로퍼티를 통해 디비내용을 조작할 것이다.


- **UserDAO**


     : User정보를 조작하는 DAO다. 먼저 사용자 정보를 등록하고, 수정하는 내용이다.


```Java

public class UserDao{

    public void add(User user) throws ClassNotFoundException, SQLException{

        Class.forNam("com.mysql.jdbc.Drver");

        Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook", "spring", "book");

        //디비를 연결하기 위한 Connection을 설정하는 부분이다.


        PreparedStatement ps = c.prepareStatement("insert into user(id, name, password) values (?,?,?)");

        //디비에서 실행할 쿼리문을 작성

        

        ps.setString(1, user.getId());

        ps.setString(2, user.getName());

        ps.setString(3, user.getPassword());

        //각 ?에 읽어온 user 정보를 넣어주는 부분


        ps.excuteUpdate();

        //실제 디비내용을 업데이트. 즉, 쿼리문 실행


        ps.close();

        c.close();

        //볼 일 다 봤으니... 닫아주기(연결 종료)

    }


     public User get(String id) throws ClassNotFoundException, SQLException{

        Class.forNam("com.mysql.jdbc.Drver");

        Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook", "spring", "book");


        PreparedStatement ps = c.prepareStatement("select * from where id = ?");

        

        ps.setString(1, id);

    

        ResultSet rs = ps.excuteUpdate();

        rs.next();

        User user = new User();


        user.setId(rs.getString("id"));

        user.setName(rs.getString("name"));

        user.setPassword(rs.getString("password"));

        //user 객체에 대입


        ps.close();

        c.close();

        

        return user;

    }

}

```

단순하게 디비내용을 조작하거나 내용을 저장하기 위해 몇가지 단계를 거치는 것을 볼수 있다.

1. 디비 연결

2. 쿼리문을 담을 Satement 생성

3. 값을 가져오는 경우는 그 결과물을 저장할 ResultSet 생성

4. 생성된 ResultSet을 저장하고자하는 Object에 저장

5. 디비 연결관계 종료


더 자세한 내용이 있을 수도 있지만, 내가 이해한 바로는 이정도 단계를 거치는 것 같다. 상황에 따라서는 더 다양한 구조가 존재할 수는 있지만 가장 기본이 될거 같다.


이 정도 상황에서 코드를 살펴보았을때 내가 생각 할 수 있는 문제점은 몇가지가 있었다.

1. 모든 조작에 관련하여 그 해당 메소드는 전부 디비를 연결하는 쿼리문을 갖어야 한다는 것이다. 즉, 코드의 변경이나 확장에 있어서 문제가 발생할 수 있다.


더 많이 찾을 수 있을 줄 알았지만....내가 찾은 문제점은 이정도이다.

앞으로 본 책에서는 이 코드를 어떻게 개선하는지 알아 보겠다.


---


### **DAO의 분리**

> **관심사의 분리** : 추후 코드의 분리와 확장(변경??)을 고려하여 같은 내용의 코드를 분리하자


> **커넥션 만들기의 추출** : 위의 DAO에서의 관심사를 구분하고 따로 분리한다.


> **DB커넥션 만들기의 독립** : 서로 다른 DB에 저장하기 위해 DB커넥션의 독립


먼저 관심사의 분리는 단순하게 표현(설명?)하면 각 메소드에 중복된 내용을 따로 분리한다는 내용이다. 즉 공통의 관심사항을 하나의 메소드로 만들어 낸다는 것이다.


그럼, 위의 UserDao의 관심사항을 살펴보면, 각 정보를 조작할 수 있어야한다. 즉, 내용의 추가(삽입), 제거, 변경, 조회가 가능해야하며(물론 위에서는 추가와 조회만 다루었다) 저장하고자 하는 DB에 연결과 조작이 완료한 후 그 연결을 종료해 주어야한다.


그럼 각 메소드들이 공통적으로 갖는 내용을 확인해 보면.... 각 메소드 들은 다른 조작을 할 것임을 알 수 있다. 즉, DB연결과 연결종료가 공통된 관심사항임을 알 수 있으며, 따로 관리를 해줄수 있다는 것을 확인 할 수 있을 것이다.


- 나는 저정도가 끝인줄 알았는데 책을 확인하니 디비에 보낼 쿼리문을 담는 Statement를 생성하고 실행하는 것 또한 **공통의 관심사임**을 확인 할 수 있었다.


그럼 이중 디비를 연결하는 부분을 다른 메소드로 분리해 보겠다.


```Java

public class UserDao{

    public void add(User user) throws ClassNotFoundException, SQLException{

        Connection c = getConnection();


        PreparedStatement ps = c.prepareStatement("insert into user(id, name, password) values (?,?,?)");

        

        ps.setString(1, user.getId());

        ps.setString(2, user.getName());

        ps.setString(3, user.getPassword());


        ps.excuteUpdate();


        ps.close();

        c.close();

    }


     public User get(String id) throws ClassNotFoundException, SQLException{

        Connection c = getConnection();

        PreparedStatement ps = c.prepareStatement("select * from where id = ?");

        

        ps.setString(1, id);

    

        ResultSet rs = ps.excuteUpdate();

        rs.next();

        User user = new User();


        user.setId(rs.getString("id"));

        user.setName(rs.getString("name"));

        user.setPassword(rs.getString("password"));

        //user 객체에 대입


        ps.close();

        c.close();

        

        return user;

    }


    public Connection getConnection() throws ClassNotFoundException, SQLException{

        Class.forNam("com.mysql.jdbc.Drver");

        

        Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook", "spring", "book");


        return c;

    }

    // 중복된 코드를 독립적 메소드로 분리하여 중복을 제거

}

```

위 코드에서 보이는 바와 같이...디비에 연결을 하던 부분을 독립적으로 분리 했음을 확인 할 수 있다.


앞으로 더 다양한 메소드들이 생성될 경우에도 해당 메소드를 통해 관리 될 수 있을 것이다.


이렇게 메소드를 분리(추출)하는 기법을 리팩토링에서는 **extract method**라고 부른다고 한다.


그러면, 만약 서로 다른 디비를 사용하고 가각 독자적 방법을 적용하여 DB커넥션을 적용하고 싶을 경우에는 어떻게 되는지도 확인해보겠다.


이 경우는 기존의 getConnection() 추상메소드로 만들어 상속을 통해 확장시키는 방법이 있다


```Java

public class UserDao{

    public void add(User user) throws ClassNotFoundException, SQLException{

        Connection c = getConnection();

        ...

    }


     public User get(String id) throws ClassNotFoundException, SQLException{

        Connection c = getConnection();

        ...

    }


    public abstract Connection getConnection() throws ClassNotFoundException, SQLException{

    }

    //구현코드는 각 Connection을 적용하는 방법을 다루는 곳에 맡기고 추상 메소드로 메소드 자체만 존재시킨다.

}


public class NUserDao extends UserDao{

    public Conncetion getConnection() throws ClassNotFoundException, SQLException{

        NUserDao의 Connection 생성코드

    }

}


public class DUserDao extends UserDao{

    public Conncetion getConnection() throws ClassNotFoundException, SQLException{

        DUserDao의 Connection 생성코드

    }

}

```


위에서 처럼 추상메소드를 이용하거나 혹은 오버라이딩 가능한 protected 메소드 등으로 서브클래스에서 해당 메소드를 필요에 맞게 구현하여 사용하는 방법을 디자인 패턴에서 **템플릿 메소드 패턴**이라고 한다고 한다. 여기서 슈퍼클래스에서 서브클래스에서 어떤 클래으의 오브젝트를 만들어 리턴하는지에 대해서 관심을 갖지 않는 것은 **랙토리 메소드 패턴**이라고 한다고 한다.

해당 패턴의 자세한 내용은 나중에 확인하도록 하고 위의 기능을 살펴보면, 각각 해당 커넥션을 이용하는 입장에서 자신이 적용하고자하는 방법을 통해 connection을 만들수 있고 관리할 수 있게 된것이다. 즉, UserDao가 더욱 확장에 용이해 졌다는 점이다. 하지만... 상속을 이용할 시에는 문제점이 있다.


자바는 클래스의 다중상속을 허용하지 않는 다는 점이다... 즉, 다른 목적으로 상속을 시킬 수 없다는 점이다.

그리고, 생각보다 관계는 밀접하여... 슈퍼클래스의 내용이 바뀌면 서브클래스의 내용을 바꿔야 하는 경우도 있다는 점과 UserDao외의 다른 DAO가 생성된다면 해당 DAO를 위한 connection메소드 또한 매번 만들어주는 문제도 있을 것이다.


그럼...아예  connection 메소드를 다른 클래스로 분리하는 방법이 있는지 생각해 볼 수 있을 것 같다. 물론 아직, 확신은 못하겠다. 다른 방법이 있을 수도 있고...


계속해서 책을 읽어보고 확인해보도록 하겠다.


---


### **DAO의 확장**

> 모든  Object는 변한다. 이점을 고려해야한다. 즉, 각 오브젝트를 최대한 독립적으로 구성하여 변화에 유연하게 대처하고 확장해 나갈 수 있도록 해줘야한다.


지금까지 계속 보던 코드를 이어서 확인해보면 현재 상속을 통해 독립성을 구축했다. 하지만, 문제가 존재한다는 것을 인지하고 있었고 아예 클래스로 분리하는 것이 방법이 될 수 있지 않을까? 하고 생각했고, 실제 그런 방법을 이용 하게 되었다.


connection메소드를 아예 다른 클래스로 분리하고 해당 클래스를 UserDao가 이용하는 것이다.


```Java

public class UserDao{

    private SimpleConnectionMaker simpleConnectionMaker;


    public UserDao(){

        simpleConnectionMaker = new SimpleConnectionMaker();

        //한번만 만들어 사용하는 방법을 이용한다.

        //물론 해당 클래스의 특징과 사용에 따라 달라질수 있다.

    }


    public void add(User user) throws ClassNotFoundException, SQLException{

        Connection c = simpleConnectionMaker.makeNewConnection();

        ...

    }


     public User get(String id) throws ClassNotFoundException, SQLException{

        Connection c = simpleConnectionMaker.makeNewConnection();

        ...

    }

}


public class SimpleConnectionMaker{

    public Connection makeNewConnection() throws ClassNotFoundException,SQLException{

        Class.forName("com.mysql.jdbc.Driver");

        Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook", "spring", "book");


        return c;

    }

}

```



---

'Spring' 카테고리의 다른 글

01) 스프링은 무엇일까?  (0) 2018.03.12
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함