//////
Search
🗒️

스프링 복습

날짜
2022/10/25
작성자
황민우
카테고리
회고

스프링 복습

리팩토링 전

UserDao 전체 코드
UserDao의 각 메소드는 크게 DB연결, 쿼리 요청, 결과 처리, 리소스 반환 (close()) 등의 역할을 하고 있다.
예시 코드
하나의 클래스, 메소드에서 너무 많은 역할을 맞고 있고 예를 들어 DB연결방법이 바뀌면 각 메소드를 찾아가 코드를 수정해야하는 번거로움이 있다.

메소드 분리

public UserDao { private Connection getConnection() throws ClassNotFoundException, SQLException { Map<String, String> env = System.getenv(); Class.forName("com.mysql.cj.jdbc.Driver"); return DriverManager.getConnection(env.get("DB_HOST"), env.get("DB_USER"), env.get("DB_PASSWORD")); } public void add(final User user) throws ClassNotFoundException, SQLException { Connection c = getConnection(); // ... } // ... }
Java
복사
DB연결이 모든 메소드에서 반복되고 있으므로 먼저 메소드로 분리해보았다.
getConnection() 메소드에서 DB에 연결하고 그 객체(Connection)을 반환해준다.
그리고 DB연결이 필요한 메소드에서 getConnection() 메소드를 사용해 Connection 을 받아와 사용한다.

인터페이스 사용

위 코드에서 getConnection() 메소드는 UserDao 에서만 사용가능하다.
UserDao 외에 여러 Dao 클래스들을 만들경우 Dao 클래스 안에 각각 getConnection() 을 만드는 것은 매우 비효율적이다. 또한 메소드로 분리한 이유가 DB 연결 방법이 바뀔시 수정하는 코드를 줄이기 위함이었는데, Dao 클래스 마다 getConnection() 메소드를 작성한다면 또 다시 일일이 getConnection() 메소드를 찾아 수정해야할 것이다.
DB연결 과정을 인터페이스와 구현체로 아예 Dao 클래스 밖으로 분리해보자.
ConnectionMaker
public interface ConnectionMaker { Connection getConnection() throws ClassNotFoundException, SQLException; }
Java
복사
ConnectionMaker 인터페이스에서 DB연결 객체인 Connection 을 반환해주는 getConnection() 메소드를 정의한다.
AwsConnectionMaker
public class AwsConnectionMaker implements ConnectionMaker { @Override public Connection getConnection() throws ClassNotFoundException, SQLException { Map<String, String> env = System.getenv(); Class.forName("com.mysql.cj.jdbc.Driver"); return DriverManager.getConnection(env.get("DB_HOST"), env.get("DB_USER"), env.get("DB_PASSWORD")); } }
Java
복사
AwsConnectionMaker : ConnectionMaker 인터페이스의 구현체
실질적으로 DB연결하는 방법을 구현하고 반환해주는 객체이다.
만약, Aws 서버가 아닌 로컬환경의 DB를 사용하고 싶다면?
AwsConnectionMaker 와 마찬가지로 ConnectionMaker 인터페이스를 구현한 LocalConnectionMaker 를 작성해주면 된다.
UserDao
public class UserDao { private ConnectionMaker connectionMaker; public UserDao(ConnectionMaker connectionMaker) { this.connectionMaker = connectionMaker; } public void add(final User user) throws ClassNotFoundException, SQLException { Connection c = connectionMaker.getConnection(); // ... } }
Java
복사
UserDao 안에서 위에서 분리한 AwsConnectionMakerLocalConnectionMaker 를 사용하기 위해 직접 객체를 생성한다면 분리한 의미가 없다.
AwsConnectionMaker 를 사용하기 위해 생성자를 만들고
public UserDao(AwsConnectionMaker awsConnectionMaker) { this.awsConnectionMaker = awsConnectionMaker; }
Java
복사
LocalConnectionMaker 를 사용하기 위해 생성자를 또 만들어야하기 때문이다.
public UserDao(LocalConnectionMaker localConnectionMaker) { this.localConnectionMaker = localConnectionMaker; }
Java
복사
따라서, 인터페이스로 분리하고 다형성을 활용하여 “주입” 받는 것이다.
public UserDao(ConnectionMaker connectionMaker) { this.connectionMaker = connectionMaker; }
Java
복사
UserDaoTest
public class UserDaoTest { private UserDao dao = new UserDao(new AwsConnectionMaker()); @Test // ... }
Java
복사
(위 아래는 같은 코드이다.)
public class UserDaoTest { ConnectionMaker awsConnectionMaker = new AwsConnectionMaker(); private UserDao dao = new UserDao(awsConnectionMaker); @Test // ... }
Java
복사
내가 Aws DB말고 로컬 DB에 연결하고 싶다면 위 코드들에서 그저 AwsConnectionMaker 객체를 LocalConnectionMaker 객체로 변경해주기만하면 끝이다. 다른 파일은 수정할 필요가 없다.
public class UserDaoTest { private UserDao dao = new UserDao(new LocalConnectionMaker()); @Test // ... }
Java
복사

Factory 적용

UserDaoTest 클래스에서 처음 작성한 UserDao 클래스처럼 여러 역할을 가지고 있다.
Test를 실행하는 가장 중요한 역할과 AwsConnectionMaker 객체를 생성해서 UserDao 객체에 주입하는 역할을 맡고 있다.
두 번째 역할인 주입을 대신 맡아줄 UserFactory 클래스를 만들어보자.
UserDaoFactory
public class UserDaoFactory { public UserDao userDao() { return new UserDao(connectionMaker()); } public ConnectionMaker connectionMaker() { return new AwsConnectionMaker(); } }
Java
복사
UserDaoTest
public class UserDaoTest { UserDaoFactory userDaoFactory = new UserDaoFactory(); private UserDao dao = userDaoFactory.userDao(); @Test // ... }
Java
복사
객체 생성을 맡아주는 UserDaoFactory 덕분에 UserDaoTest 는 Test에 집중할 수 있게 되었다.