#7. Spring Framework [기초]
1. 프레임 워크 (Framework)
소프트웨어의 구체적인 부분에 해당하는 설계와 구현을 재사용이 가능하게
일련의 협업화된 형태로 클래스들을 제공하는 것.
즉, 프로그래밍을 하기 위한 어떠한 기본적인 틀이나 구조를 제공하는 것이다.
[장점]
1. 효율적인 코드 작성
Framework에서 어플간의 통신, 데이터 저장소에 저장 등 기본적인 기능을 제공하므로
개발자는 핵심 로직을 개발하는 것에 집중할 수 있다.
2. 작성 이후 효율적인 관리 및 유지 보수
Framework의 규약에 맞게 코드를 작성하므로, 유지보수의 경우 수월하게 관리가 가능.
다른 사람이 작성한 코드의 경우에도 Framework 규약에 맞추어 작성했으므로 수정 및 확장이 가능.
[단점]
1. 진입 장벽
사용하고자 하는 Framework에서 정하는 규약에 대한 학습이 필요.
Spring의 경우, Java언어에 대한 이해도 필요하지만, Spring 이라는 Framework에 대한 학습도 필요하다.
2. 규약에 맞추어 개발해야하는 강제성
Framework를 사용하여 작성하는 만큼 해당 Framework의 규약을 벗어나기가 어렵다.
2. 라이브러리 (Library)
어플리케이션을 개발하는데 사용되는 필요한 기능을 미리 구현해놓은 집합체.
Framework는 교체하기 어렵지만, Library는 교체하기가 쉽다.
Ex) Framework : 자동자 뼈대, 프레임. Library : 자동차 기능을 제공하는 부품. 와이퍼, 라이트, 핸들 etc..
[프레임워크과 라이브러리의 차이점]
프레임워크(Framework)
어플리케이션 흐름의 주도권이 프레임워크(Framework)에 있다.
라이브러리(Library)
어플리케이션 흐름의 주도권이 개발자에 있다.
=> 주도권이 어디에 있느냐에 대해 설명하기 위해서는 우선 Spring Framework의 특징을 살펴보자.
3. Spring Framework의 특징
A. POJO ( Plain Old Java Object )
객체지향적 원리에 충실하며, 환경과 기술에 종속되지 않고, 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트.
( = Java 언어 규약에 의해 강제된 것 이외의 제한에 구속되지 않는 Java 오브젝트 )
따라서 POJO는 다음과 같은것이 해당되면 안된다.
1. 미리 지정된 클래스를 extends하면 안됨.
public class Foo extends javax.servlet.http.HttpServlet { ... }
2. 미리 정의된 인터페이스를 implements하면 안됨.
public class Bar implements javax.ejb.EntityBean { ... }
3. 미리 정의된 애너테이션을 포함하면 안됨.
@javax.persistence.Entity public class Baz { ... }
[객체 지향적 원칙]
1. 높은 응집력
비슷한 일을 하는 기능, 하나의 책임에 포함되는 기능들이 잘 뭉쳐있다.
2. 낮은 결합도
하나의 클래스를 변경하는데 의존하는 다른 클래스들이 모두 수정되지 않도록 설계해야 한다.
[객체 지향 설계 : SOLID]
1. S : 단일 책임 원칙
하나의 클래스는 하나의 책임만 가진다.
2. O : 개방-폐쇄 원칙
소프트웨어 요소는 확장에는 개방되지만, 변경에는 폐쇄되야 한다. (변하는 것과 변하지 않는 것 구분)
3. L : 리스코프 치환 원칙
프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
자식클래스의 객체는 부모클래스의 참조변수에 대입해서 부모클래스의 역할 수행에 문제가 없어야 한다.
4. I : 인터페이스 분리 원칙
인터페이스 단일 책임을 위한 원칙. 하나의 인터페이스를 조금 더 구체적인 인터페이스로 쪼개는 것이 낫다.
Ex) 새의 행동 (일반적인 인터페이스) => 새의 나는 행동(구체적인 인터페이스), 새의 우는 행동(구체적인 인터페이스)
5. D : 의존 관계 역전 원칙
프로그래머는 추상화에 의존해야지 구체화에 의존하면 안된다. 의존성 원칙은 이 원칙에 기반함.
자주 변경되는 구체 클래스에 의존하지 않고, 추상화된 클래스에 의존하는 것을 의미.
구체 클래스가 추상 클래스에 의존하므로 의존 관계가 역전.
[POJO 프로그래밍이 필요한 이유]
1. 재사용이 가능, 확장 가능한 유연한 코드
2. 코드가 간결해짐.
3. 디버깅에 용이함.
4. 테스트가 간편하다.
5. 객체지향적 설계를 제한 없이 적용 가능
B. 제어의 역전 (IoC : Inversion Of COntrol)
개발자가 직접 객체를 생성하고 없는지에 대한 결정을 하는 것이 아니라,
컨테이너에게 그 역할을 맡기고 개발에 있어서 편의성과 재사용성의 극대화를 추구하는 개념.
B. 의존성 주입 (DI : Dependency Injection)
의존성 주입은 IoC의 개념을 조금 구체화 시킨 것.
Ex) AAA 클래스 : MenuController // BBB클래스 : MenuService // CCC클래스 : Cafeclient
AAA클래스가 BBB클래스의 기능을 사용하고자 한다.
<일반적인 경우>
AAA 클래스에서 BBB 클래스의 객체b를 생성하고, b를 통해 BBB 클래스의 기능을 사용한다.
EX)
class AAA {
BBB b = new BBB();
b.BBB클래스의_메소드;
}
<의존성 주입의 경우>
AAA클래스에서 필드변수로 private BBB b 선언, 생성자 선언의 매개인자(파라미터)로 AAA (BBB b) 작성.
CCC클래스에서 BBB클래스 객체b 생성하고, AAA클래스 객체 생성할 때 = new AAA (B클래스_객체명); 키워드를 사용.
class CCC {
BBB b = new BBB();
AAA a = new AAA(b);
a.BBB클래스의_메소드;
}
를 작성하면 CCC클래스라는 외부에서 AAA클래스 객체 선언할 때 생성자를 통해 b 객체를 주입하고 있다.
이 경우를 외부에서 객체를 주입한다 라고 한다.
[의존성 주입이 필요한 경우]
만약 BBB클래스의 내용을 변경해야하거나, BBB클래스를 DDD클래스로 대체해야 하는 경우라면,
BBB클래스와 의존 관계에 있는 CCC클래스와 AAA클래스에서 BBB클래스를 모두 변경해야한다.
이렇게 변동사항 발생의 경우, 해당 클래스와 의존관계에 있는 모든 객체들을 수정해야하는 불편함을 줄이기 위해 필요.
[의존성 주입의 대표적인 방법 : interface ]
Spring IoC / DI 에 관한 학습. // 추가 업뎃 예정
우선 Spring IoC / DI 관련 학습에 있어서 많은 도움을 주신 동기 윤*현님과 김*소님께 감사의 말씀을 드립니다. 1. IoC (Inversion Of Control) : 제어의 역전. 2. DI (Despendency Injection) : 의존성 주입. 3. 실제 예
dvdhan.tistory.com
[의존성 주입의 장점]
1. 결합도가 줄어든다.
객체끼리 의존한다는 것은, 그 의존 대상의 변화에 취약하다는 뜻,
DI를 이용하면 주입받는 대상이 바뀔지 몰라도, 해당 객체의 구현 자체를 수정할 일이 없다.
2. 유연성이 높아진다.
DI를 이용하면 생성자의 인수만 다른 객체로 변경하면 얼마든지 처리가 가능하다.
3. 테스트하기 쉬워진다.
DI를 이용한 객체는 자신이 의존하고 잇는 인터페이스가 어떤 클래스에 구현되어 있는지 몰라도 되므로 테스트가 쉽다.
4. 가독성이 높아진다.
클래스의 생성자로 객체를 전달받는 코드가 있다면, 객체를 외부에서 주입받고 있다, 의존성 주입이 있다 라고 생각하자.
C. 관심 지향 프로그래밍 ( AOP : Aspect Oriented Programming )
- 공통 관심 사항과 핵심 관심 사항 -
공통 관심 사항 : 어플리케이션 전반에 걸쳐 공통적으로 사용되는 기능들의 관심사.
ex) 로깅, 보안, 트랜잭션
핵심 관심 사항 : 어플리케이션의 주목적을 달성하기 위한 핵심 로직에 대한 관심사.
ex) 메뉴 등록, 메뉴 주문, 주문 변경
위의 그림을 보면 공통 관심 사항이 핵심 관심 사항의 항목들을 화살표로 관통하고 있다.
이는 공통 관심 사항이 어플리케이션의 핵심 로직(핵심 관심 사항)에 두루 사용된다는 의미.
또는 그래프 상으로도 공통 관심 사항, 핵심 관심 사항 이 서로 떨어져 있는데 이는 두 가지 사항이 분리되어있단 의미이다.
[핵심 포인트]
AOP는 어플리케이션의 핵심 로직에서 로깅, 보안, 트랜잭션 같은 공통적인 기능을 분리하는 것.
** 트랜잭션 **
데이터를 처리하는 하나의 작업 단위. (트랜잭션 = 작업A + 작업B) // ACID 특성을 가지고 있다.
[AOP가 필요한 이유]
1. 코드의 간결성을 유지.
2. 객체 지향 설계 원칙에 맞는 코드 구현.
3. 코드의 재사용.
어플리케이션 전반에 걸쳐 트랜잭션 관련한 중복된 코드들이 수도 없이 나타나는데,
이 중복된 코드들을 AOP 기능을 통해 공통화하여 재사용이 가능하도록 한다.
@Transactional 애너테이션을 이용하면 Spring AOP 기능을 통해 트랜잭션을 적용한다.
D. PSA (Portable Service Abstraction)
추상화
프로그래밍에서 어떤 클래스의 본질적인 특성만 추출해서 일반화 하는 것을 의미.
Java에서 대표적인 방법 : 인터페이스, 추상 클래스
Ex)
1. 추상화 클래스
2. 추상화 클래스를 상속하는 클래스 A, B, C
3. 추상 클래스를 구체화한 클래스들을 객체화 할 때 본인의 타입이 아닌, 추상 클래스 타입으로 할당하여 생성함.
클라이언가 추상화 된 상위 클래스를 일관되게 바라보며 하위 클래스의 기능을 사용하는 것이 PSA의 기본 개념
[서비스 추상화]
DbClient는 DB서비스를 이용하는데, OrcleJDBC, MariaDBJdbc, SQLiteJdbc 등에 직접적인 연결이 아닌
JdbcConnector 인터페이스 상속을 통해 간접적으로 연결되어 Connector 객체를 얻고 있다.
어느 DB를 이용하든, getConnection() 메소드를 일관되게 사용해야 한다.
이처럼 어플리케이션에서 특정 서비스를 이용할 때, 서비스의 기능에 접근하는 방식 자체를 일관되게 유지하면서
기술 자체를 유연하게 사용할 수 있도록 하는 것을 PSA(일관된 서비스 추상화) 라고 한다.
[PSA가 필요한 이유]
어떤 서비스를 이용하기 위한 접근 방식을 일관된 방식으로 유지함으로써,
애플리케이션에서 사용하는 기술이 변경되더라도, 최소한의 변경만으로 변경된 요구사항을 반영하기 위함.
=> PSA를 통해 애플리케이션의 요구 사항 변경에 유연하게 대처가 가능함
EX) 트랜잭션 서비스, 메일 서비스, Spring Data 서비스
[핵심 포인트]
1. 객체 지향 프로그래밍 세계에서 어떤 클래스의 본질적인 특성만 추출해서 일반화 하는 것을 추상화라고 한다.
2. 클라이언트가 추상화된 상위 클래스를 일관되게 바라보며, 하위 클래스의 기능을 사용하는 것이 PSA.
3. 서비스의 기능에 접근하는 방식을 일관되게 유지하며, 기술 자체를 유연하게 사용할 수 있도록 하는 것.
4. 즉 애플리케이션에서 사용하는 기술이 변경되더라도, 최소한의 변경만으로 요구사항을 반영하기 위함.
=========== Spring 특징 4가지 최종 키워드 정리 ==============
라이브러리
프로그래밍의 기능을 구현하는데 미리 만들어놓은 코드들.
// 개발에 있어 주 흐름이 개발자에게 있다.
프레임워크
프로그래밍을 함에 있어서 최소한의 규약을 만들어 기본적인 개발의 틀을 제공해주는 것
// 개발에 있어 주 흐름이 프레임워크에게 있다.
POJO
Java 외의 어떠한 규약이나 환경에 종속되지 않은 순수 객체 오브젝트.
IoC/DI
IoC : 일반적인 흐름이 뒤바뀐 것.
DI : 특정 객체를 사용할 때 사용할 객체를 본인이 생성하는게 아닌 중간객체를 통해 외부로부터 사용할 객체를 주입받는것
중간객체에서 생성자의 파라미터로 인터페이스 혹은 인터페이스를 구현한 클래스들을 주입하도록 사전에 기입해
중간객체를 객체화할 때 생성자() 속에 인터페이스 혹은 인터페이스를 구현한 클래스의 객체를 할당하여
중간객체에서 사용할 구현 클래스들은 외부로부터 주입받아 사용하는 것.
AOP
어플리케이션의 기능들 중 비즈니스 핵심 로직 기능과 어플리케이션 전체에서 공통적으로 사용하는 기능을 분리한 것.
PSR
클라이언트 (서버측 기능을 이용하는 쪽, 웹 브라우저) 가 상위 객체(추상 클래스, 인터페이스)만을 사용 객체로 이용하며
그 상위 객체를 구현 혹은 구체화하는 하위 클래스들의 기능들을 사용할 수 있게 하는 것.
객체화 할 때 하위 클래스의 생성자로 생성하되 상위 추상 클래스 타입으로 객체화를 하고,
그 객체를 이용해 하위 클래스의 기능을 사용하는 것.