[Java] Java에서 Query문 리팩토링(3)

배지원·2022년 10월 20일
0

실습

목록 보기
4/24
post-custom-banner

이전에 했던 프로젝트를 인터페이스 클래스를 사용하여 나중에 Spring에서 사용할때 다중 상속을 받을 수 있도록 리팩토링 하였다.(DB별 설정 각자 받음)
이전 프로젝트 : https://velog.io/@qowl880/Java-Java%EC%97%90%EC%84%9C-Query%EB%AC%B8-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%812

1. Interface 리팩토링

  • 공통되는 메서드를 Interface로 따로 구분하여 사용함

1. Interface

public interface ConnectionMaker {      // makeConnection을 각 DB에 맞게 사용할 수 있도록 불러오기 위한 interface
   Connection makeConnection() throws SQLException, ClassNotFoundException;
}
  • 공통 메서드를 interface로 연관관계를 만든다.

2. Aws(interface 상속받음)

    @Override
    public Connection makeConnection() throws SQLException, ClassNotFoundException {
        Map<String, String> env = System.getenv(); // 환경변수를 사용하여
        String dbHost = env.get("DB_HOST");
        String dbUser = env.get("DB_USER");
        String dbPassword = env.get("DB_PASSWORD");

        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection conn = DriverManager.getConnection(dbHost, dbUser, dbPassword);
        return conn;
    }
  • 인터페이스를 상속받아 인터페이스의 메서드를 자신의 설정으로 오버라이딩 한다.

3. Local(interface 상속받음)

public class LocalConnectionMaker implements ConnectionMaker {
    @Override
    public Connection makeConnection() throws SQLException {
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/likelion-db","root","password");
        // 원래는 dbHost, dbUser,dbPassword를 가져와 넣어야 하지만 root는 주소를 오픈해도 해커가 침투하기가 힘들다

        return conn;
    }
}
  • 인터페이스를 상속받아 인터페이스의 메서드를 자신의 설정으로 오버라이딩 한다.

4. TDD

  • 정상작동 하는 코드인지 확인하기 위해 Junit를 통해 테스트를 해본다.
class UserDaoInterfaceTest {
    @Test
    void addAndSelect() throws ClassNotFoundException, SQLException {
        UserDaoInterface userDao = new UserDaoInterface();
        String id = "12";
        User user = new User(id,"test","1234");    // user 값을 DTO에 저장함
        userDao.add(user);						   // DB 값 Insert

        User selectedUser = userDao.select(id);        // DTO에 저장되어 있는 데이터를 가져와 비교함
        Assertions.assertEquals("test",selectedUser.getName());
    }
}
  • 테스트 코드를 통해 id,user,password를 입력하여 데이터 베이스에 Insert를 한다.
  • select 메서드를 호출하여 해당 id에 해당하는 값들을 가져와 DTO에 저장하고 그 중 getName( )으로 이름만 가져오는데 "test"와 같다면 정상 테스트, 다르다면 오류 출력한다.

5. CODE

1. Interface

public interface ConnectionMaker {      // makeConnection을 각 DB에 맞게 사용할 수 있도록 불러오기 위한 interface
    Connection makeConnection() throws SQLException, ClassNotFoundException;
}

2. Aws 주소

public class AWSConnectionMaker implements ConnectionMaker {
    @Override
    public Connection makeConnection() throws SQLException, ClassNotFoundException {
        Map<String, String> env = System.getenv(); // 환경변수를 사용하여
        String dbHost = env.get("DB_HOST");
        String dbUser = env.get("DB_USER");
        String dbPassword = env.get("DB_PASSWORD");

        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection conn = DriverManager.getConnection(dbHost, dbUser, dbPassword);
        return conn;
    }
}

3. Local 주소

public class LocalConnectionMaker implements ConnectionMaker {
    @Override
    public Connection makeConnection() throws SQLException {
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/likelion-db","root","password");

        return conn;
    }
}


2. Spring 연동

  • 스프링과 연동하기 위한 리팩토링 과정은 끝났다 이제 스프링과 연동해보도록 하겠다.

1. build.gradle 설정 추가

  • 이전에 mysql를 추가해줬던 것 처럼 build.gradle파일에서 dependencies부분 안에서 alt+insert를 눌러 add dependency를 통해 스프링을 사용하기 위한 설정인 spring-boot-starter-jdbc와 테스트 실행을 위한 spring-boot-startter-test를 추가해 준다.


2. Factory Bean 파일 추가

  • spring에서는 직접 new를 사용하여 생성한 객체가 아니라 spring에 의하여 관리당하는 자바 객체를 사용한다. 이렇게 spring에 의하여 생성되고 관리되는 자바 객체를 Bean이라고 한다. 따라서 Bean을 통해 사용할 객체를 지정을 해줘야 spring에서 동작이 가능하다.

※ Bean 관련 설명 : https://velog.io/@qowl880/Bean%EC%9D%B4%EB%9E%80

Bean 파일 추가하는 법

@Configuration
public class HelloConfiguration {
   @Bean
   public HelloController sampleController() {
       return new SampleController;
   }
}
  • @Configuration과 @Bean Annotation 을 이용하여 Bean을 등록할 수 있다. @Configuration를 사용하여 Spring Project에서의 Configuration 역할을 하는 Class를 지정할 수 있다. 해당 File 하위에 Bean 으로 등록하고자 하는 Class에 @Bean Annotation을 사용해주면 간단하게 Bean을 등록할 수 있다.

    Factory 클래스

  • UserDao, UserDaoTest등에서 ConnectionMakerInterface의 구현제인 AWSConnectionMaker나 LocalConnectionMaker를 주입받아야함 따라서 주입받는 부분을 팩토리에 모아놓음으로써 객체들을 한곳에서 관리할 수 있음

    @Configuration
    public class UserDaoFactory {
    
       @Bean
       public UserDaoInterface awsUserDao(){
           AWSConnectionMaker awsConnectionMaker = new AWSConnectionMaker();
           UserDaoInterface userDao = new UserDaoInterface(awsConnectionMaker);
           return userDao;
       }
    
       @Bean
       public UserDaoInterface localUserDao(){
           UserDaoInterface userDao = new UserDaoInterface(new LocalConnectionMaker());
           return userDao;
       }
    }

3. TDD

  • spring을 테스트 하기 위해서는 추가해줘야 하는 설정이 있다. 이에 대한 내용들은 정리해둔 블로그에서 확인이 가능하다
    정리 블로그 : https://velog.io/@qowl880/test

    @ExtendWith(SpringExtension.class)  // spring에서 테스트 하기 위한 확장자
    @ContextConfiguration(classes = UserDaoFactory.class)
    class UserDaoInterfaceTest {
    
       @Autowired
       ApplicationContext context; // Spring ApplicationContext를 사용하기 위해서는
       // @ExtendWith 과 @ContextConfiguration를 추가해줘야 한다.
    
       @Test
       void addAndSelect() throws ClassNotFoundException, SQLException {
           UserDaoInterface userDao = new UserDaoFactory().awsUserDao();
           // awsUserDao()가  UserDaoInterface를 반환하기 때문에 다형성 가능
           String id = "3";
           userDao.add(new User(id,"Spring","123"));
    
           User user = userDao.select(id);
           Assertions.assertEquals("Spring",user.getName());
       }
    }

4. CODE


인터페이스

public interface ConnectionMaker {      // makeConnection을 각 DB에 맞게 사용할 수 있도록 불러오기 위한 interface
    Connection makeConnection() throws SQLException, ClassNotFoundException;
}

AWS 설정

public class AWSConnectionMaker implements ConnectionMaker{
    @Override
    public Connection makeConnection() throws SQLException, ClassNotFoundException {
        Map<String, String> env = System.getenv(); // 환경변수를 사용하여
        String dbHost = env.get("DB_HOST");
        String dbUser = env.get("DB_USER");
        String dbPassword = env.get("DB_PASSWORD");

        Class.forName("com.mysql.cj.jdbc.Driver");
        Connection conn = DriverManager.getConnection(dbHost, dbUser, dbPassword);
        return conn;
    }
}

Local 설정

public class LocalConnectionMaker implements  ConnectionMaker{
    @Override
    public Connection makeConnection() throws SQLException {
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/likelion-db","root","password");

        return conn;
    }
}

DAO

public class UserDaoInterface {

    private ConnectionMaker connectionMaker;    // interface의 makeConnection()를 가져옴
    public UserDaoInterface(){                  // 생성자를 통해 AWS DB의 makeConnection()을 오버라이딩하여 사용
        this.connectionMaker = new AWSConnectionMaker();
    }
    public UserDaoInterface(ConnectionMaker connectionMaker){
        this.connectionMaker = connectionMaker;
    }

    public void add(User user) throws ClassNotFoundException {

        try{
   // db 연결(호스트,이름,비밀번호)
            Connection conn = connectionMaker.makeConnection();     // 설정들을 모아둔 메서드 호출
            
            PreparedStatement ps =  conn.prepareStatement("INSERT INTO users(id,name,password) VALUES(?,?,?)");
            ps.setString(1,user.getId());        // mysql 테이블로 값 insert
            ps.setString(2,user.getName());
            ps.setString(3,user.getPassword());

            ps.executeUpdate();     // ctrl + enter 즉, mysql에서 번개모양을 눌러 최신화 한다는 느낌
            ps.close();
            conn.close();
            System.out.println("데이터가 insert 됬습니다.");

        }catch (SQLException e){
            throw new RuntimeException(e);
        }

    }

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

        try {
            Connection conn = connectionMaker.makeConnection();
            PreparedStatement ps = conn.prepareStatement("SELECT id,name,password FROM users WHERE id = ?");
            ps.setString(1, id);     // id는 get(String id)로 받은 id
            ResultSet rs = ps.executeQuery();   // 쿼리문을 저장함 insert문과 달리 excuteQuery() 사용
            // rs에는 쿼리 실행 결과가 담겨져 있다. (select * from users where id = 1;)
            rs.next();

            // User 생성자를 통해 쿼리문에 id값을 넣어 찾은 id, name,password 값을 저장한다.
            User user = new User(rs.getString("id"), rs.getString("name"), rs.getString("password"));
            rs.close();
            ps.close();
            conn.close();
            return user;
        }catch (SQLException e){
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        UserDaoInterface userDao = new UserDaoInterface();
        userDao.add(new User("7","Ruru","1234qwer"));   // user로 값을 받아 DTO에 저장한 후 mysql로 데이터 보냄
        System.out.println(userDao.select("1"));
    }
}

Factory(bean 폴더)

@Configuration
public class UserDaoFactory {

    @Bean
    public UserDaoInterface awsUserDao(){
        AWSConnectionMaker awsConnectionMaker = new AWSConnectionMaker();
        UserDaoInterface userDao = new UserDaoInterface(awsConnectionMaker);
        return userDao;
    }

    @Bean
    public UserDaoInterface localUserDao(){
        UserDaoInterface userDao = new UserDaoInterface(new LocalConnectionMaker());
        return userDao;
    }
}
profile
Web Developer
post-custom-banner

0개의 댓글