스프링이란?
개발에 사용되는 애플리케이션 프레임워크. 개발을 빠르고 효율적으로 할 수 있도록 틀과 프로그래밍 모델, 기술 API 등을 제공
1) 애플리케이션 기본틀 - 스프링 컨테이너
독립적으로 동작 할 수 있으나 보통 웹 모듈에서 동작하는 서비스나 서블릿으로 등록하여 사용.
스프링을 사용하기 위해선 스프링 컨테이너를 다루는 방법과 스프링 컨테이너가 Object를 이용 할 수 있도록 설정정보를 작성하는 방법을 익히는 것이 좋다.
2) 공통 프로그래밍 모델 - IoC / DI, 서비스 추상화, AOP
스프링은 3가지 핵심 프로그래밍 모델을 지원
- IoC / DI(생명주기와 의존관계)
유연하고 확장성이 뛰어난 코드를 만들 수 있게 도와주는 객체지향 설계원칙과 디자인 패턴의 핵심 원리를 담고 있는 IoC/DI를 프레임워크의 근간으로 생각
- 서비스 추상화
특정 기술에 종속되지 않고 이식성이 뛰어남.
- AOP
애플리케이션 코드에 산재해서 나타나는 부가적인 기능을 독립적으로 모듈화하는 프로그래밍 모델. 스프링은 이것을 통해 다양한 엔터프라이즈 서비스를 적용하고도 깔끔한 코드를 유지 할 수 있게 해줌.
3) 기술 API
개발의 다양한 영역에 바로 활용 할 수 있는 방대한 양의 기술 API 제공
스프링통해 얻게 되는 가치?
단순함(simplicity)과 유연성(flexibility)
DAO(Data Access Object)
DAO는 DB를 사용해 데이터를 조회하거나 조작하는 기능을 전담하도록 만든 오브젝트.
리팩토링이란?
기존의 코드를 외부의 동작방식에는 변화 없이 내부 구조를 변경해서 재구성하는 기술
import java.sql.Connection;
import java.sql.SQLException;
public abstract 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;
}
public class NUserDao extends UserDao {
public Connection getConnection() throws ClassNotFoundException, SQLException {
//N사 DB Connection 생성코드
}
}
public class DUserDao extends UserDao {
public Connection getConnection() throws ClassNotFoundException, SQLException {
// D가 DB Connection 생성코드
}
}
위에 코드는 상속을 통해 UserDao를 확장 시킨 코드다.
클래스 계층구조를 통해 두 개가 독립적으로 분리가되면서 변경작업이 수월해졌다.
이 경우 UserDao를 수정 할 필요 없이 DB에 연결가능한 코드를 새롭게 만들 수 있다.
이렇게 SuperClass에서 기본적인 로직의 흐름(커넥션 가져오기, SQL 생성, 실행, 반환)을 만들고, 그 기능의 일부를 추상 메소드나 오버라이딩이 가능한 protected 메소드 등으로 만든 뒤 서브클래스에서 이런 메소드를 필요에 맞게 구현해서 사용하는 방법을 디자인 패턴에서는 템플릿 메소드 패턴(Template Method Pattern)이라고 부른다. 이 디자인은 스프링에서 애용되는 디자인 중에 하나이다.
UserDao의 getConnection() 메소드는 Connection 타입 오브젝트를 생성한다는 기능을 정의해 놓은 추상 메소드.
그리고 UserDao의 서브 클래스의 getConnection() 메소드는 어떤 Connection 클래스의 오브젝트를 어떻게 생성할 것인지를 결정하는 방법으로도 볼 수 있다.
이렇게 서브클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는 것을 팩토리 메소드 패턴(Factory Method Pattern)이라고 부른다.
[템플릿 메소드 패턴 약식]
public abstract class Super{
public void templateMethod() {
//기본 알고리즘 코드
hookMethod();
abhstractMethod();
...
}
protected void hookMethod() {} // --> 선택적으로 오버라이드 가능한 훅 메소드
public abstact void abstractMethod(); // --> 서브클래스에서 반드시 구현해야 하는 추상 메소드
}
public class Sub1 extends Supper { // --> 슈퍼클래스의 메소드를 오버라이드 하거나 구현해서
protected void hookMethod() { // 기능을 확장한다. 다양한 확장 클래스 만들 수 있음.
...
}
public void abstactMethod() {
...
}
}
이 방법의 단점은 상속(extends)를 많이 사용했다는것.
상속 자체는 간단해 보이고 사용하기 쉬워보이지만 한계점이 많다.
만약 이미 UserDao가 다른 목적을 위해 상속을 사용하고 있다는 어떻게 할 것인가?
※ 자바는 클래스의 다중 상속을 허용하지 않음!
단지, 커넥션 객체를 가져오는 방법을 분리하기 위해 상속 구조로 만들어버리면, 후에 다른 목적으로 UserDao에 상속을 적용하기 힘들다.
확장된 기능인 DB 커넥션을 생성하는 코드를 다른 DAO 클래스에 적용 할 수 없다는것도 단점. 만약 UserDao외의 DAO 클래스들이 계속 만들어진다면 그때는 상속을 통해 만들어진 getConnection()의 구현 코드가 매 DAO 클래스마다 중복돼서 나타나는 심각한 문제가 발생할 것.
이러한 단점을 보완하고자 한다면,
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();
...
}
DB 커넥션 생성 기능을 독립시킨 SimpleConnectionMaker는
public Class SimpleConnectionMaker { //더 이상 상속을 이용한 확장 방식을 사용 할 필요가 없으니
public Connection makeNewConnection() thorows ClassNotFoundException,//추상클래스로 만들 필요 없음
SQLException{
Class.forName("com.mysql.jdbc.Driver");
Connection c = DriverManager.getConnection(
"jdbc:mysql://localhost/springpractice", "spring", "1234");
return c;
}
}
로 생성!
기능에 변화는 준것이 없으며, 단지 내부 설계를 통해 단점을 개선했다.
하지만 이 방법에도 단점이 있다.
1) SimpleConnectionMaker의 메소드 문제. 우리는 makeNewConnection()을 사용해 DB 커넥션을 가져오게 했는데, 만약D사에서 만든 DB 커넥션 제공 클래스는 openConnection()이라는 메소드 이름을 사용했다면 UserDao내에 있는 add(), get()메소드의 커넥션을 가져오는 코드를 다음처럼 일일이 변경해야한다.
Connection c =simpleConnectionMaker.openConnection();
양이 적을땐 상관없으나 수십, 수백개가되면 그양이 너무 방대해짐!
2) DB 커넥션을 제공하는 클래스가 어떤 것인지를 UserDao가 구체적으로 알고 있어야한다. N사에서 다른 클래스를 구현하면 어쩔수없이 UserDao 자체를 다시 수정해야함.
그래서 이것을 위해 해결하기 위한 방법이 바로 인터페이스의 도입이다.
2개의 클래스가 서로 긴밀하게 연결되어 있지 않게 중간에 추상적인 연결고리를 만들어주는것. 자바가 추상화를 위해 제공하는 가장 유용한 도구가 바로 인터페이스다.
인터페이스는 자신을 구현한 클래스에 대한 구체적인 정보는 모두 감춰버리며, 결국 Object를 만들려면 구체적인 클래스 하나를 선택해야겠지만 인터페이스로 추상화해놓은 최소한의 통로를 통해 접근하는 쪽에서는 Object를 만들 때 사용 할 클래스가 무엇인지 몰라도 상관없다.
해당과정을 인터페이스를 통해 다시 해본다면
public interfacte ConnectionMaker{
public Connection makeConnection() throws ClassNotFoundException, SQLException; }
UserDao 클래스와 함께 ConnectionMaker 인터페이스를 받았을 경우 개발자는
public class DConnectionMaker implements ConnectionMaker {
...
public Connection makeConnection() throws ClassNotException, SQLException{
//D사의 독자적인 방법으로 Connection을 생성하는 코드
}
}
로 변경을 할 수가 있고, 다시 ConnectionMaker 인터페이스를 사용하도록 개선한 UserDao를 구성하면
public class UserDao{
private ConnectionMaker connectionMaker; // 인터페이스를 통해 오브젝트에 접근하므로 구체적인
//클래스 정보를 알 필요가 없음.
public UserDao(){
connectionMaker = new DConnectionMaker();
}
public void add(User user) throws ClassNotFoundException, SQLException {
Connection c = connectionMaker.makeConnection(); //인터페이스에 정의된 메소드를 사용하므로
... //클래스가 바뀐다고해도 메소드 이름이 변경될
//걱정은 할 필요 없음
}
public User get(String id) throws ClassNotFoundException, SQLException {
Connectionc c = connectionMaker.makeConnection();
...
}
그런데 UserDao를 자세히 살펴보면 DConnection이라는 클래스가 보인다. DConnection 클래스의 생성자를 호출해서 오브젝트를 생성하는 코드가 다음과 같이 여전히 UserDao에 남아있다.
connectionMaker = new DConnectionMaker();
제거를 할 수가 없군...? 클래스 이름을 넣어서 Object를 만들지 않으면 어떻게 사용해야하나?
결국 다시 원점으로 왔지만,
이때 필요한것이 관계설정 책임의 분리! 요건 다음에 한 번봐봐야겠다.
- 출처: 토비의 스프링 3.1(vol1. 스프링의 이해와 원리)
'JAVA' 카테고리의 다른 글
[Spring] 1장 오브젝트와 의존관계(3) (0) | 2021.06.09 |
---|---|
[Spring] 1장 오브젝트와 의존관계(2) (0) | 2021.06.08 |
[Java] JDBC를 이용하는 순서 (0) | 2021.02.16 |
[Java] enum을 쓰는 이유. 이놈을 쓰는이유!!! (0) | 2020.09.16 |
[Java] Maven 환경변수 에러 (1) | 2020.09.08 |