✅ 커넥션 만들기의 추출
관심사를 분리하는 것은 중복된 코드를 제거하는 것이라고 할 수 있습니다. 하나의 로직을 수행하는데 무수히 많은 중복된 코드가 있다면, 만약 조금만 변동사항이 생겨도 변동사항이 생긴 코드를 하나씩 찾아가며 전부 바꿔야하기 때문에 유지보수가 어렵습니다.
➡️ UserDao의 구현된 add() 메서도 하나를 살펴보며 세가지 관심사항이 도출된다는 것을 알 수 있습니다.
- DB와 연결을 위한 커넥션을 어떻게 가져올까?
어떤 DB를 사용하고, 어떤 드라이버를 사용할 것이고, 어떤 로그인 정보를 쓸 것인가? DB 연결에 관련된 관심사이다.
- SQL 문장을 담을 Statement 객체 생성
파라미터로 넘어온 사용자 정보를 Statement에 바인딩시키고 SQL을 DB를 통해 실행시키는 것에 대한 관심사이다.
- 작업이 끝난 resouce(자원) 반환하기
Statement, Connection 객체를 닫아서 소중한 자원을 돌려주는 것이다.
- 중복 코드의 메소드 추출 : 리팩토링(Refactoring), 메소드 추출(extract method) 기법
package com.jhcode.spring.dao; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import com.jhcode.spring.domain.User; public class UserDao { public void add(User user) throws ClassNotFoundException, SQLException{ Connection con = getConnection(); //...생략...// } public User get(String id) throws ClassNotFoundException, SQLException { Connection con = getConnection(); //...생략...// } public Connection getConnection() throws ClassNotFoundException, SQLException{ String className = "org.mariadb.jdbc.Driver"; String url = "jdbc:mariadb://localhost:3306/toby_study?characterEncoding=UTF-8"; String userId = "root"; String password = "1234"; Class.forName(className); Connection con = DriverManager.getConnection(url, userId, password); return con; } public static void main(String[] args) throws ClassNotFoundException, SQLException { //...생략...// } }
지금은 UserDao 클래스의 메소드가 두 개이지만, 만약 2000개정도라고 상상해보았을 때, DB가 변경된다던가, URL, 로그인 정보가 변경되었을 때 2000개의 메서드를 모두 찾아가 해당 정보를 변경해야 합니다. 하지만 공통된 코드를
getConnection()
메서드에 추출함으로써Connection
을 연결하는 부분이 변경된다면getConnection()
메서드의 코드만 변경하면 되기 때문에 유지보수 측면에서 효과적입니다.수정 후에도 문제가 없다는 것을 확인하기 위해선 기존에 test로 활용했던
main()
를 동작시켜보면 됩니다. 하지만 전에 실행되었던 정보가 DB에 저장되어있으므로 해당 정보를 삭제하고 진행해야지 오류가 나지 않습니다.코드가 변경되어도 문제 없이 동작합니다. 방금 한 작업은 UserDao의 기능에는 아무런 변화를 주지 않았습니다. 앞에서 한 작업은 단지 공통된 코드를 추출하여 별도의 메소드로 분리해낸 것뿐입니다. 기능에는 영향을 주지 않으면서 코드의 구조만 변경시켜 미래의 변화에 좀 더 손쉽게 대응하는 코드를 만드는 것을 리팩토링(refactoring)이라고 하며, 메소드로 중복된 코드를 뽑아내는 것을 메소드 추출(extract method) 기법이라고 합니다.
✅ DB 커넥션 만들기의 독립
Q.UserDao의 코드를 수정하지 않고, 각각의 DB정보, URL, 로그인 정보에 맞는 Connection을 생성하려면 어떻게 해야할까?
우리가 N사와 D사에 해당 코드를 판매한다고 생각하고, N사와 D사가 자신의 회사에 맞는 Connection 코드를 작성하고자 할 때는 어떻게 해야할까요? UserDao 클래스를 상속해서 각각 NUserDao와 DUserDao라는 서브클래스를 만들면 됩니다. UserDao 클래스의 getConnection() 메서드를 추상메서드로 만들고 서브클래스는 UserDao 클래스를 상속받아 추상메서드를 구현합니다.
![](https://blog.kakaocdn.net/dn/b668TE/btsmlmne3vq/uKpddLzjOrT8euHsSI1Dd0/img.png)
- 상속을 통한 확장
- UserDao
package com.jhcode.spring.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import com.jhcode.spring.domain.User; public abstract class UserDao { public abstract Connection getConnection() throws ClassNotFoundException, SQLException; //...생략
- NUserDao
package com.jhcode.spring.dao; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class NUserDao extends UserDao { @Override public Connection getConnection() throws ClassNotFoundException, SQLException { //== N사 DB Connection 생성 코드 ==// String className = "org.mariadb.jdbc.Driver"; String url = "jdbc:mariadb://localhost:3306/toby_study?characterEncoding=UTF-8"; String userId = "root"; String password = "1234"; Class.forName(className); Connection con = DriverManager.getConnection(url, userId, password); return con; } }
- DUserDao
package com.jhcode.spring.dao; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DUserDao extends UserDao { @Override public Connection getConnection() throws ClassNotFoundException, SQLException { //== D사 DB Connection 생성 코드 ==// String className = "org.mariadb.jdbc.Driver"; String url = "jdbc:mariadb://localhost:3306/toby_study?characterEncoding=UTF-8"; String userId = "root"; String password = "1234"; Class.forName(className); Connection con = DriverManager.getConnection(url, userId, password); return con; } }
DAO의 핵심기능인 어떻게 데이터를 등록하고 가져올 것인가, DB의 연결 방법(Connection)은 어떻게 할 것인가, 두 관심사가 클래스 레벨에서 구분되어졌습니다. 이 코드는 아래와 같은 디자인 패턴이 사용되었습니다
🔎-디자인패턴이란? 소프트웨어 설계 시 특정 상황에서 자주 만나는 문제를 해결하기 위해 사용할 수 있는 재사용 가능한 솔루션을 말합니다. 패턴에서 가장 중요한 것은 각 패턴의 핵심이 담긴 목적 또는 의도입니다. 패턴을 적용할 상황, 해결해야 할 문제, 솔루션의 구조와 각 요소의 역할과 함께 핵심 의도가 무엇인지를 파악해야 합니다.
- UserDao
- 템플릿 메소드 패턴(template method pattern)
- 슈퍼클래스(부모클래스)의 일부 기능을 추상 메소드나 오버라이딩이 가능한 protected 메소드 등으로 만든 뒤 서브 클래스에서 필요에 맞게 구현해서 사용하도록 하는 디자인 패턴
- 상속을 통해 슈퍼클래스의 기능을 확장할 때 사용하는 가장 대표적인 방법이다.
- 변하지 않는 기능은 슈퍼클래스에 만들어두고 자주 변경되며 확장할 기능은 서브클래스에서 만든다.
- 팩토리 메소드 패턴(factory method pattern)
- 서브클래스에서 다양한 방법으로 구체적인 오브젝트 생성 방법을 결정하게 하는 디자인 패턴
- 서브클래스에서 오브젝트 생성 방법과 클래스를 결정할 수 있도록 미리 정의해둔 메소드를 팩토리 메소드라고 하고, 이 방식을 통해 오브젝트 생성 방법을 구현하여, 슈퍼클래스의 기존 코드에서 독립시키는 방법을 팩토리 메소드 패턴이라고 한다.
![](https://blog.kakaocdn.net/dn/0EElG/btsmiGAiDNY/Yz4jSrhxJhAIbOqgkzshp1/img.png)
👉템플릿 메소드 패턴과 팩토리 메소드 패턴으로 관심 사항이 다른 코드를 분리해내고, 서로 독립적으로 변경 또는 확장할 수 있도록 만드는 것은 간단하지만 여러 가지 상속의 단점을 가지고 있습니다.
- 계층 구조의 제한성 : 복잡한 계층 구조로 인해 유지보수가 어려워질 수 있음.
- 강력한 결합 : 상위 클래스와 하위 클래스 간에 강한 결합이 생기며, 변경에 취약해질 수 있음.
- 다중 상속의 불가능 : 다중 상속을 지원하지 않아 여러 부모 클래스의 기능을 동시에 상속받을 수 없음.
프로젝트 환경설정
- IDE : STS3 - 3.9.18.RELEASE
- SpringFramework : 5.3.20
- Java : 11
- Maven
📖토비 스프링 3.1 -p.60~70
🚩jhcode33의 toby-spring-study.git으로 이동하기
Uploaded by N2T