Framework란?
Spring Framework를 알아보기 앞서 Framework의 의미에 대해 알아보겠습니다.
사전적 의미로 frame은 "틀" + work는 "일하다" 라는 의미로 "틀을 사용하여 일한다." 정도의 의미를 가지고 있습니다.
즉, 프레임워크(Framework)는 일정한 틀과 뼈대를 제공받아 일한다는 의미로 제공받은 일정한 요소와 기능, 규칙 등을 기반으로 무언가 만드는 일이며, 이것을 IT 업계에서는 Framework라고 부릅니다.
프레임워크는 개발시 필요한 여러 요소들을 제공해주고, 개발의 편리함과 생산성을 높여줍니다.
또 프레임워크는 사용목적에 따라 다양한 종류의 프레임워크가 존재합니다.
Spring Framework란?
Spring Framework는 JAVA를 위한 오픈 소스 프레임워크로서 간단히 Spring이라고도 부릅니다.
스프링 프레임워크는 엔터프라이즈급 애플리케이션을 개발하기 위한 모든 기능을 종합적으로 제공하는 경량화된 솔루션이며, 동적인 웹 사이트를 개발하기 위한 여러가지 서비스를 제공합니다.
엔터프라이즈급 개발이란 뜻대로만 풀이하면 기업을 대상으로 하는 개발이라는 뜻입니다.
즉, 대규모의 데이터 처리와 트랜잭션이 동시에 여러 사용자로부터 행해지는 큰 규모의 환경을 엔터프라이즈 환경이라 합니다.
스프링 프레임워크는 경량 컨테이너로 자바 객체를 담고 직접 관리합니다.
객체의 생성 및 소멸 그리고 라이프 사이클을 관리하며 언제든 스프링 컨테이너로부터 필요한 객체를 가져와 사용할 수 있습니다.
이는 스프링이 IoC기반의 프레임워크임을 의미합니다.
Spring Framework의 특징
위의 이미지는 스프링 프레임워크의 특징을 나타냅니다.
스프링의 중요한 3가지 기술이면서 특징이라 할 수 있는 것에는 IoC/DI, AOP, PSA 가 있습니다.
이미지를 보면 스프링의 특징들이 POJO를 둘러싼 형태를 볼 수 있는데, 이는 POJO라는 개념은 IoC/DI, AOP, PSA를 통해 달성할 수 있음을 의미합니다.
POJO(Plain Old Java Object)
POJO란 "Plain Old Java Object"의 약자로, 이를 직역하면 순수한 오래된 자바 객체라는 뜻 즉, 특정 자바 모델이나 기능, 환경, 규약, 프레임워크 등을 따르지 않은 순수한 자바 오브젝트를 말합니다.
이전 EJB(Enterprise Java Beans)는 확장, 재사용이 가능한 로직을 개발하기 위해 사용 되었는데, EJB는 한가지 기능을 위해 불필요하고 복잡한 로직이 과도하게 들어가는 단점이 있었습니다. 그래서 다시 조명을 받은게 POJO 입니다. POJO는 getter/setter를 가진 단순 자바 오브젝트로 정의하고 있습니다. getter/setter 같은 단순 오브젝트는 의존성이 없고 추후 테스트 및 유지보수가 편리한 장점을 가집니다. 이러한 장점들로 인해 객체지향적인 다양한 설계와 구현이 가능해지고 POJO의 기반의 Framework가 조명을 받고 있습니다.
- getter/setter만 가지고 있는 코드는 특정 기술에 종속되어 있지 않아 POJO라고 부를 수 있습니다.
public class Board {
private String title;
private String content;
private String writer;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
}
[ POJO 프로그래밍이 필요한 이유 ]
- 특정 환경이나 기술에 종속적이지 않으며 재사용이 가능하고, 확장 가능한 유연한 코드를 작성할 수 있다.
- 저수준 레벨의 기술과 환경에 종속적인 코드를 애플리케이션 코드에서 제거 함으로써 코드가 깔끔해진다.
- 코드가 깔끔해지기 때문에 디버깅하기도 상대적으로 쉽다.
- 특정 기술이나 환경에 종속적이지 않기 때문에 테스트 역시 단순해진다.
- 객체지향적인 설계를 제한없이 적용할 수 있다.
IoC(Inversion of Control)
스프링 프레임워크는 IoC기반의 프레임 워크이고 Inversion of Control는 사전적 의미로 제어의 역전입니다.
그렇다면 제어의 역전은 무엇일까요?
일반적으로 지금까지 프로그램은 객체 결정 및 생성 -> 의존성 객체 생성 -> 객체 내의 메소드를 호출하는 작업을 반복하였습니다.
이는 객체들의 프로그램의 흐름을 결정하고 각 객체를 구성하는 작업에 직접적으로 관여한 것입니다.
즉, 모든 작업을 사용자가 제어하는 구조인 것입니다.
하지만 IoC에서는 이 흐름의 구조가 역전됩니다.
IoC에서의 객체는 자기가 사용할 객체를 선택하거나 생성하지 않습니다. 또한 자신이 어디서 만들어지고 어떻게 사용되는지 또한 모릅니다. 자신의 모든 권한을 다른 대상에게 위임함으로써 제어권한을 위임받은 특별한 객체에 의해 결정되고 만들어집니다.
즉, 제어의 흐름을 사용자가 컨트롤하지 않고 위임한 특별한 객체에 모든 것을 맡기는 것입니다.
IoC란 기존 사용자가 모든 작업을 통해 제어하던 것을 특별한 객체에 모든 것을 위임하여 객체의 생성부터 생명주기 등 모든 객체에 대한 제어권이 넘어 간 것을 IoC(제어의 역전)이라고 합니다.
DI(Dependency Injection)
스프링은 3가지의 핵심 프로그래밍 모델을 지원하고 있는데, 그 중 하나가 의존성 주입(DI : Dependency Injection)입니다.
DI란 외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴으로, 인터페이스를 사이에 둬서 클래스 레벨에서는 의존관계가 고정되지 않도록 하고 런타임 시에 관계를 동적으로 주입하여 유연성을 확보하고 결합도를 낮출 수 있게 해줍니다.
의존성이란 한 객체가 다른 객체를 사용할 때 의존성이 있다고 합니다. 예를 들어 다음과 같이 Store 객체가 Pencil 객체를 사용하고 있는 경우에 우리는 Store객체가 Pencil 객체에 의존성이 있다고 표현합니다.
public class Store {
private Pencil pencil;
}
그리고 두 객체 간의 관계(의존성)를 맺어주는 것을 의존성 주입이라고 하며 생성자 주입, 필드 주입, 수정자 주입 등 다양한 주입 방법이 있습니다. Spring 4부터는 생성자 주입을 강력히 권장하고 있습니다.
의존성 주입(DI : Dependency Injection)이 필요한 이유
예를 들어 연필이라는 상품과 1개의 연필을 판매하는 Store 클래스가 있다고 해봅시다.
public class Store {
private Pencil pencil;
public Store() {
this.pencil = new Pencil();
}
}
위와 같은 예시 클래스는 크게 다음과 같은 문제점을 갖고 있습니다.
- 두 클래스가 강하게 결합되어 있다.
- 객체들 간의 관계가 아니라 클래스 간의 관계가 맺어져있다.
1. 두 클래스가 강하게 결합되어 있다.
위와 같은 Store 클래스는 현재 Pencil 클래스와 강하게 결합되어 있다는 문제점을 가지고 있습니다. 두 클래스가 강하게 결합되어 있어서 만약 Store에서 Pencil이 아닌 Food와 같은 다른 상품을 판매하고자 한다면 Store 클래스의 생성자에 변경이 필요합니다. 즉, 유연성이 떨어집니다. 각각의 다른 상품들을 판매하기 위해 생성자만 다르고 나머지는 중복되는 Store 클래스들이 파생되는 것은 좋지 못합니다. 이에 대한 해결책으로 상속을 떠올릴 수 있지만, 상속은 제약이 많고 확장성이 떨어지므로 피하는 것이 좋습니다.
2. 객체들 간의 관계가 아니라 클래스 간의 관계가 맺어져있다.
위의 Store 클래스와 Pecil 클래스는 객체들 간의 관계가 아니라 클래스들 간의 관계가 맺어져 있다는 문제가 있습니다. 올바른 객체지향적 설계라면 객체들 간에 관계가 맺어져야 합니다. 객체들 간에 관계가 맺어졌다면 다른 객체의 구체 클래스(Pencil인지 Food인지 등)를 전혀 알지 못하더라도, (해당 클래스가 인터페이스를 구현했다면) 인터페이스의 타입(Product)으로 사용할 수 있습니다.
결국 위와 같은 문제점이 발생하는 근본적인 이유는 Store에서 불필요하게 어떤 제품을 판매할 지에 대한 관심이 분리되지 않았기 때문입니다. Spring에서는 의존성 주입(DI : Dependency Injection)을 적용하여 이러한 문제를 해결하고자 합니다.
[ 의존성 주입(DI : Dependency Injection)을 통한 문제 해결 ]
위와 같은 문제를 해결하기 위해서는 우선 다형성이 필요합니다. Pencil, Food 등의 여러 가지 제품을 하나로 표현하기 위해서는 Product라는 Interface가 필요합니다. 그리고 Pencil에서 Product 인터페이스를 우선 구현해주도록 합시다.
public interface Product {
}
public class Pencil implements Product {
}
이제 저희는 Store와 Pencil이 강하게 결합되어 있는 부분을 제거해주어야 합니다. 이를 제거하기 위해서는 다음과 같이 외부에서 상품을 주입(Injection) 받아야 합니다. 그래야 Store에서 구체 클래스에 의존하지 않게 됩니다.
public class Store {
private Product product;
public Store(Product product) {
this.product = product;
}
}
이러한 이유로 우리는 Spring이라는 DI 컨테이너를 필요로 하는 것 입니다. Store에서 Product 객체를 주입하기 위해서는 애플리케이션 실행 시점에 필요한 객체(빈)를 생성해야 하며, 의존성이 있는 두 객체를 연결하기 위해 한 객체를 다른 객체로 주입시켜야 합니다. 예를 들어 다음과 같이 Pencil이라는 객체를 만들고, 그 객체를 Store로 주입시켜주는 역할을 위해 DI 컨테이너가 필요한것 입니다.
public class BeanFactory {
public void store() {
// Create Bean
Product pencil = new Pencil();
// Dependency Injection
Store store = new Store(pencil);
}
}
이러한 부분은 스프링 프레임워크가 완벽하게 지원 해줍니다. 스프링은 특정 위치부터 클래스를 탐색하고, 객체를 만들며 객체들의 관계까지 설정해줍니다. 이러한 이유로 스프링은 DI 컨테이너라고도 불립니다. 그리고 이러한 개념은 제어의 역전(IoC : Inversion of Control)이라고 불리기도 합니다. 어떠한 객체를 사용할지에 대한 책임은 프레임워크에게 넘어갔고, 자신은 수동적으로 주입받는 객체를 사용하기 때문입니다.
[ 의존성 주입(Dependency Injection) 정리 ]
한 객체가 어떤 객체(구체 클래스)에 의존할 것인지는 별도의 관심사입니다. Spring은 의존성 주입을 도와주는 DI 컨테이너로써, 강하게 결합된 클래스들을 분리하고, 애플리케이션 실행 시점에 객체 간의 관계를 결정해 줌으로써 결합도를 낮추고 유연성을 확보해줍니다. 이러한 방법은 상속보다 훨씬 유연합니다. 단, 한 객체가 다른 객체를 주입받으려면 반드시 DI 컨테이너에 의해 관리되어야 한다는 것 입니다.
- 두 객체 간의 관계라는 관심사의 분리
- 두 객체 간의 결합도를 낮춤
- 객체의 유연성을 높임
- 테스트 작성을 용이하게 함
AOP(Aspect Oriented Programming)
AOP(Aspect Oriented Programming)은 관점 지향 프로그래밍이라는 뜻으로, 어떤 로직을 기준으로 핵심적인 관점, 공통적인 관점(부가적인 관점)으로 나누어서 보고 그 관점을 기준으로 모듈화 하겠다는 의미입니다.
OOP(Object Oriented Programming)는 객체지향 원칙에 따라 관심사가 같은 데이터를 한곳에 모아 분리하고 낮은 결합도를 갖도록하여 독립적이고, 유연한 모듈로 캡슐화를 하는 것을 얘기합니다. 하지만 이러한 과정에서 중복된 코드들이 많아지고 가독성, 확장성, 유지보수성을 떨어뜨립니다. 이러한 문제를 보완하기 위해 나온 것이 AOP 입니다.
AOP에서는 핵심기능과 공통기능을 분리시켜 핵심 로직에 영향을 끼치지 않게 공통기능을 끼워 넣는 개발 형태이며, 이렇게 개발함에 따라 무분별하게 중복되는 코드를 한 곳에 모아 중복을 제거 할 수 있어지고, 공통기능을 한 곳에 보관함으로써 공통 기능 하나의 수정으로 모든 핵심기능들의 공통 기능을 수정 할 수 있어 효율적인 유지보수가 가능하며 재사용성이 극대화 됩니다.
애플리케이션 전반에 걸쳐 공통적으로 사용되는 기능들에 대한 관심사를 바로 공통 관심 사항(Cross-cutting concern)이라 부르며, 애플리케이션의 주목적을 달성하기 위한 핵심 로직에 대한 관심사를 핵심 관심 사항(Core concern)이라고 부릅니다.
위 사진을 예로 들자면, 커피 주문 애플리케이션에서 커피 메뉴 등록, 커피 주문, 커피 주문 변경 등등 기능들이 핵심 관심 사항이고, 공통적으로 처리해야 할 부분인 로깅, 보안, 트랜잭션 같은 경우를 공통 관심 사항이라고 할 수 있습니다.
AOP를 간단하게 말하자면, 공통된 기능을 재사용하는 기법이라 말할 수 있습니다.
[ AOP(Aspect Oriented Programming)의 이점 ]
- 코드의 간결성 유지
- 객체 지향 설계 원칙에 맞는 코드 구현
- 코드의 재사용
PSA(Portable Service Abstraction)
PSA(Portable Service Abstraction)은 하나의 추상화로 여러 서비스를 묶어둔 것, 환경의 변화와 관계없이 일관된 방식으로 기술에 접근할 수 있는 환경을 제공하려는 추상화 구조를 말합니다.
추상화 계층을 사용해서 어떤 기술을 내부에 숨기고 개발자에게 편의성을 제공해주는 것을 서비스 추상화(Service Abstrcation)이라 합니다. 이러한 서비스 추상화로 제공되는 기술을 다른 기술 스택으로 간편하게 바꿀수 있는 확장성을 갖고 있는 것이 PSA 입니다.
ex ) Spring Web MVC, Spring Transaction, Spring Cache 등등
try {
dbConnection.setAutoCommit(false);
doSomething()...
dbConnection.commit();
System.out.println("Done!");
} catch(SQLException e) {
dbConnection.rollback();
} finally {
...
dbConnection.close();
}
위 코드로 JDBC 기반의 DB에 접근할 수 있고, JPA를 활용하여 ORM 접근을 할 수 있습니다. 하나의 추상화로 여러 서비스를 묶어둔 PSA 덕분입니다.
PlatformTransactionManager라는 최상위 Manager를 사용하고, 각 사용자의 선언에 따라서
JPATransactionManager, DatasourceTransactionManager, HibernateTransactionManager 등을 상황에 맞게 의존성 주입을 받아 사용하게 됩니다. 따라서 개발자는 내부적으로 어떠한 Database Mapping 전략을 사용하던지 관계없이 @Transactional이라는 어노테이션을 활용하면 트랜잭션 처리를 할 수 있게 됩니다.
그렇기 때문에 PSA를 통해서 애플리케이션의 요구 사항 변경에 유연하게 대처할 수 있습니다.
reference
[Spring] Spring Framework란?(1)
[Spring] 스프링(Spring Framework)의 정의와 특징
[Spring] Spring Framework란? 기본 개념 핵심 정리
'Back-End > Spring' 카테고리의 다른 글
[Spring] JPA Entity Mapping (0) | 2024.07.16 |
---|---|
[Spring] JPA 프로젝트 생성 및 예제 실습 -1- (0) | 2024.07.11 |
[Spring] JPA(Java Persistence API)란? (0) | 2024.07.09 |
[Spring] DispatcherServlet이란? (0) | 2024.06.24 |
[Spring] MVC Pattern (0) | 2024.06.24 |