1장 오브젝트와 의존관계

조창훈·2023년 9월 21일

스프링의 핵심가치

  • 자바는 객체지향 프로그래밍이 가능하다!

스프링을 잘 이해하려면

  • 오브젝트를 잘 이해해야함
    • 생성, 관계, 사용, 소멸까지의 과정을 잘 관찰해야함
    • 어떻게 설계되어야 하는지, 어떤 단위로 만들어저야하는지 고민
  • 오브젝트를 어떻게 설계해야하나?
    • 객체지향 설계의 기초와 원칙에 따라
    • 디자인 패턴
    • 리팩토링
    • 단위 테스트

1.1 초난감 DAO

DAO란

  • DB를 사용해 데이터를 조회하거나, 조작하는 기능을 전담하도록 만든 오브젝트

1.1.1 User

코드의 간결함을 위해 롬복을 이용

  • 자바 빈 규약을 따르는 오브젝트 작성
@Getter
@Setter
public class User{
	private String id;
    private String name;
    private String password;
}

자바 빈

  • 디폴트 생성자와 프로퍼티를 지닌 오브젝트
    • 디폴트 생성자 : 툴이나 프레임워크에서 생성자를 이용해 오브젝트 작성
    • 프로퍼티 : 오브젝트가 가지고 있는 데이터를 수정 또는 조회하기 위한 변수

1.1.2 UserDAO

  • 사용자 정보를 DB에 넣고 관리할 수 있는 클래스
  • 맨 처음에는 설계, 스프링의 가치 이런거 상관없이 되는 코드로 작성
  • mysql 버전에 따라서 코드를 살짝 변경하였다.
package user.dao;


import user.domain.User;

import java.sql.*;

public class UserDAO {
    public void add(User user) throws ClassNotFoundException, SQLException{
        
    
       Class.forName("사용하는 드라이버클래스명");

       Connection c = DriverManager.getConnection(
               "dburl","대충유저이름","대충비밀번호"
       );
       
        
        PreparedStatement ps = c.prepareStatement(
                "insert into users(id,name,password) values(?,?,?)"
        );
        
        ps.setString(1, user.getId());
        ps.setString(2, user.getName());
        ps.setString(3, user.getPassword());
        ps.executeUpdate();
        
        ps.close();
        c.close();
    }
    public User get(String id) throws ClassNotFoundException, SQLException{
       Class.forName("사용하는 드라이버클래스명");

       Connection c = DriverManager.getConnection(
               "dburl","대충유저이름","대충비밀번호"
       );
        
        PreparedStatement ps = c.prepareStatement(
                "select * from users where id = ?"
        );
        
        ps.setString(1, id);

        ResultSet rs = ps.executeQuery();
        rs.next();
        
        User user = new User();
        user.setId(rs.getString("id"));
        user.setName(rs.getString("name"));
        user.setPassword(rs.getString("password"));

        rs.close();
        ps.close();
        c.close();

        return user;
    }

    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        UserDAO dao = new UserDAO();
        User user = new User();

        user.setId("cvcvcx");
        user.setName("조창훈");
        user.setPassword("12345678");

        dao.add(user);

        System.out.println(user.getId() + "등록 성공");

        User user2 = dao.get(user.getId());
        System.out.println(user2.getName());
        System.out.println(user2.getPassword());
        System.out.println(user2.getId() + "조회 성공!");
    }
}
  • 위 코드는 일단 동작을 하긴 한다.
  • 하지만 문제점이 여러개 존재한다.
    • 관심사의 분리가 되어있지 않음
    • 코드의 확장을 고려하지 않음

1.2 DAO의 분리

1.2.1 관심사의 분리

객체 지향의 세계에서는 모든것이 변한다.

  • 오브젝트의 설계와 이를 구현한 코드는 시대의 흐름에 따라서 변할 수 밖에 없다.
  • 그래서 개발자가 객체를 설계할 때 가장 염두에 두어야 할 사항은 미래의 변화를 어떻게 대비할 것인가이다.
  • 그걸 위해서 가장 효율적으로 일하는 방법은 분리와 확장을 고려하여 설계하는 방법이다.

코드변화로 생기는 문제 - 관심사의 분리

  • 변화는 대체로 집중된 한 가지 관심에서만 일어남
    • ex) DB접속용 비밀번호를 변경한다.
  • 하지만 그에 대한 작업은 한 곳에 집중되지 않는다.
    • ex) DB 접속용 비밀번호가 써져있는 모든 메서드를 수정해야한다.
    • 위 코드에서는 get과 add 모두 변경해야함.
  • 위와 같은 일이 벌어지지 않도록 하기 위해서는 관심사의 집중이 필요하다.
    • DB 접속용 정보같은 곳은 한 곳(하나의 메서드?) 에서 관리할 수 있도록 해야한다!
  • 위와 같은 문제를 해결할 수 있는 개념이 관심사의 분리다.

1.2.2 커넥션 만들기의 추출

userDAO의 관심사항

  • DB와의 연결을 위한 커넥션을 어떻게 가져오는가
    • 어떤 DB를 사용하는가?
    • 어떤 로그인 정보를 사용하는가?
    • 생성하는 방법은 어떤 것인가?
  • 사용자 등록을 위한 SQL문장을 담을 Statement를 만들고 실행시키는 것
    • 파라미터로 넘어온 사용자 정보를 Statement에 바인딩
    • DB에서 실행
  • 작업이 끝난 뒤 Statement, Connection을 닫아주는 것

중복 코드의 메서드 추출

getConnection() 메서드 생성
 public Connection getConnection() throws SQLException, ClassNotFoundException {
            Class.forName("사용하는 드라이버클래스명");

            Connection c = DriverManager.getConnection(
                    "dburl","대충유저이름","대충비밀번호"
            );
            return c;
    }
  • 위와 같이 Connection 을 반환하는 메서드를 생성하고, 위 add와 get을 getConnection 메서드를 사용하도록 수정한다.
  • 위와같이, 리팩토링 전과 같은 결과가 잘 나온다.
  • 이런식으로 공통의 기능을 가진 중복된 코드를 뽑아내는 것을
    메소드 추출이라고 한다.

1.2.3 DB 커넥션 만들기의 독립

  • 발전된 DAO 코드를 누가 가져가서 사용하고 싶어하는데,
    getConnection 부분에서 Conntection을 만드는 부분만 자기들이 만들고 싶어한다고 하는 경우.
  • DAO 코드를 공유하지 않고, getConnection 부분만 자기들이 만들게 할 수는 없을까?

상속을 통한 확장

  • UserDAO에서 구현코드를 제거하고, getConnection을 추상 메서드로 만든다.

  • 위와같이 클래스도 추상클래스로 변경해야함

  • 추상 메서드라서 코드는 없지만 메서드는 존재한다.

  • 즉, 메서드getConnection()을 호출하는 코드는 유지가능

    • 추상클래스 UserDAO를 N사와D사에게 판매
    • 각각 UserDAO를 상속한 서브클래스 작성
    • getConnection 메서드를 확장한 후, UserDAO 기능 사용가능
    • 추상 클래스라서 바로 사용못하는 모습
    • UserDAO를 상속받은 클래스를 새로 만들어야한다.
리팩토링 결과

  • 위와같이 잘 처리되는 것을 알 수 있다.

디자인 패턴

템플릿 메서드 패턴과 팩토리 메서드 패턴의 차이점

  • 템플릿 메서드 패턴은 행위패턴
    • 서브클래스에서 오버라이딩해서 알고리즘(로직)을 구현
  • 팩토리 메서드는 생성패턴
    • 서브클래스에서 오버라이딩해서 객체를 생성하는 코드를 구현

둘은 구성이 같지만, 목적이 생성이냐 행위인가에 따라서 다른패턴이다.

1.3 DAO의 확장

위 디자인 패턴을 적용했을 때 발생하는 문제

  • 상속을 사용했음
    • 이미 다른 목적으로 상속하고있을 경우에는?
      자바는 다중상속을 허용하지 않음
    • 또한, 이후 다른 목적으로 상속하고 싶을 때에도, 상속을 적용하기 힘듬
  • 상속을 통한 상하위 클래스의 관계는 생각보다 밀접
    • 커넥션을 가져오는 것 이외의 관심사 - Statement제작, 리소스종료
      등을 아직 공유하고있음
    • 슈퍼클래스 내부에 변경이 있을 때, 서브클래스를 모두 변경해야 할 수도 있음
  • DB커넥션을 연결하는 기능을 타 클래스에서 사용할 수 없음
    • DB커넥션을 연결하는 기능은 UserDAO에서 확장된 클래스에만 사용할 수 있도록 하고있기 때문에, 타 클래스에서는 이 기능을 사용할 수 없음.
    • 만약 하려면 또다른 연결점이 생겨버림
profile
초전도-개발자의 길

0개의 댓글