Spring 이란?
너무나 당연하게 쓰고 있던 Spring에 대해서 궁금증이 생겼기에 이 글을 쓴다.
Spring framework란?
자바 플랫폼을 위한 오픈소스 애플리케이션 프레임워크로 엔터프라이즈급 애플리케이션 개발을 위한 모든 기능을
종합적으로 제공하는 경량화된 솔루션
경량 컨테이너로 자바 객체를 담고 직접 관리
엔터프라이즈급 개발 환경
대규모 데이터 처리와 트랜잭션이 동시에 여러 사용자로부터 행해지는 매우 큰 규모의 환경
객체의 생성 및 소멸, 라이프사이클을 관리하며 언제든 spring 컨테이너로부터 필요한 객체를 가져와 사용하기 때문에
spring은 IOC(Inversion Of Control) 기반의 Framework임을 알 수 있다
IOC
Spring에서는 일반적인 Java 객체를 new로 생성하여 개발자가 관리하는 것이 아니라
Spring Container에 모두 맡긴다.
개발자 -> 프레임 워크로 제어의 객체 관리 권한이 넘어갔기 때문에 이를, "제어의 역전"이라고 한다.
Spring 이전에는 비즈니스 로직을 구현하기 위해 기술 자체에 대한 공부를 추가적으로 해야만 했었다.
Spring 이후부터는 개발 초기에 기본적인 설정과 적용시킬 기술들만 잘 선택을 해준다면,
기술보다는 애플리케이션의 로직 자체에 더 집중하여 비즈니스 로직을 구현할 수 있게 되었다.
Spring 특징
POJO 프로그래밍을 지향한다는 특징을 가지고 있다.
POJO(Plain Old Java Object)
순수 Java만을 통해 생성한 객체를 의미
어떤 객체가 외부의 라이브러리나 외부의 모듈을 가져와 사용 중이라면, 그 객체는 POJO라고 할 수 없고, 다른 기술을 사용하지 않는 순수한 Java만을 사용하여 만든 객체
POJO가 중요한 이유
외부 라이브러리를 import 하여 라이브러리의 메서드를 사용하고 있는 객체가 있다고 가정하면, 순수 Java 외의 기술을 사용하기 때문에 POJO가 아닌 상황이다. 이때 이 객체가 사용하는 기술이 변화가 생겨 기존 기술과 관련된 코드를 고쳐야 하는 상황이 발생하면 해당 기술을 사용하고 있는 모든 객체들의 코드를 전부 바꿔줘야 한다.
해당 객체가 외부 모듈에 직접적으로 의존하기 때문에 발생하는 문제이다. 반면 POJO의 경우에는 특정 기술이나 환경에 종속되지 않고, 외부 기술이나 규약 변화에 얽매이지 않아 보다 유연한 변화와 확장을 대처할 수 있다.
이러한 POJO를 사용해 비즈니스 로직을 구현하면 객체지향 설계를 제한 없이 적용할 수 있고, 코드가 단순해져 테스트와 디버깅 또한 쉬워진다. 이처럼 비즈니스 로직을 구현하는데 POJO 방식을 적극적으로 활용하는 프로그래밍 패러다임을
POJO 프로그래밍이라고 한다.
이러한 POJO 프로그래밍을 위해 스프링이 지원하는 기술에는 IoC/DI, AOP, PSA가 있다.
1. IoC(Inversion of Control)
의존성에 대한 컨트롤이 뒤바뀌었다?
> 기존 사용자가 모든 작업을 제어하던 것을 특별한 객체에 모든 것을 위임하여 객체의 생성부터
생명주기 등 모든 객체에 대한 제어권이 넘어간 것이다.
- 기존에는 의존성에 대한 컨트롤은 자기 자신이 들고 있었다.
class OwnerController{ private OwnerRepository repository = new OwnerRepository(); }
- Spring은 나 외의 누군가가 밖에서 의존성을 넣어 준다.
class OwnerController{ private OwnerRepository repo; public OwnerController(OwnerRepository repo){ this.repo = repo; } /* ... repo를 사용하는 코드 작성 ... */ } # 이것이 바로 의존성 주입이고 이런 형태가 IOC 이다. class OwnerContorllerTest{ @Test public void create(){ OwnerRepository repo = new OwnerRepository(); OwnerController controller = new OwnerController(repo); } }
1. 일반적으로 지금까지의 프로그램은 객체 결정 및 생성
2. 의존성 객체 생성
3. 객체 내의 메서드 호출 작업을 반복한다.
즉, 모든 작업을 사용자가 제어하는 구조이다.
하지만 IOC에서의 객체는 자기가 사용할 객체를 선택하거나 생성하지 않는다.
자신이 어디서 만들어지고 어떻게 사용되는지를 모름. 자신의 모든 권한을 다른 대상에 위임하여 제어 권한을 위임받은
특별한 객체에 의해 결정되고 만들어짐.
즉, 제어의 흐름을 사용자가 컨트롤하지 않고 위임한 특별한 객체에 모든 것을 맡기게 된다.
2. IoC 컨테이너
spring은 IoC용 컨테이너를 제공해 준다. Bean을 만들고 엮어주며 제공해 주는 역할.
- ApplicationContext(Beanfactory) : 컨테이너의 가장 핵심적인 인터페이스
이런 코드들을 동작하게 만들어 주는 역할로 실제 코드에 ApplicationContext라는 단어는 찾아볼 수 없다.class OwnerController{ private OwnerRepository repo; public OwnerController(OwnerRepository repo){ this.repo = repo; } // repo를 사용하는 코드 작성 }
Bean 들의 의존성을 관리해 준다. - IoC는 DI와 DL에 의해 구현된다
3. Bean
스프링 IoC 컨테이너가 관리하는 객체
- 어노테이션 : 자바의 어노테이션은 소스코드에 추가해서 사용할 수 있는 메타 데이터의 일종으로
주석처럼 기능이 없어 아무 동작을 하지 않는다.
[어노테이션을 마커로 사용해서 어노테이션을 처리하는 것이 있는 것]
등록하는 방법으로 크게 두 가지가 있다.
1. Component Scanning
- @Component
- @Repository
- @Service
- @Controller
Spring Boot에서는 Spring Boot Application이라는 어노테이션을 가진 것이 분명히 존재한다.
이 어노테이션을 따라가 보면 ComponentScan을 찾아볼 수 있는데, 역할은 Component Scan 어노테이션을 처리하는 핸들러의 역할. 물론 구체적 설정에 따라 달라지겠지만, 대부분 모든 패키지에 Component라는 어노테이션이 붙어있는 class를 찾아 bean으로 등록해 준다.
2. 직접 등록
@Bean
public String test(){
return "test";
}
@RestController
public class SampleController{
@Autowired
String test;
@GetMapping("/context")
public String context(){
return "hello" + test;
}
}
Bean을 사용하고 싶으면, 해당 class도 bean이 되어야 한다.
오로지 bean만 bean을 의존할 수 있다.
Bean Scope란?
bean이 존재할 수 있는 범위를 말하는데 스프링 컨테이너에서 함께 시작되어 종료될 때까지 스프링이 관리해 주는데
이유는 스프링 빈들을 singleton scope로 관리되기 때문이다.
- singleton
- 스프링 IoC 컨테이너 안에 단 한 번 생성되어 캐시에 저장된다.
- 모든 후속 request와 bean 이름에 대한 참조는 캐시 된 객체를 반환
- 기본적으로 모든 bean은 scope가 명시되어 있지 않으면 singleton이다. - prototype
- 모든 요청에서 새로운 객체를 생성하여 주입
- stateful 한 bean에는 prototype scope를 사용해야 하며, stateless 한 bean에는 singleton scope를 사용한다.
- 만일 prototype bean에 종속된 singleton bean을 사용하면, 인스턴스화 시 종속송이 해결된다.
singleton bean을 주입할 때, 먼저 prototype bean이 인스턴스화되고 나서 singleton bean에 주입된다.
그 prototype bean은 singleton bean에게 제공되는 유일한 인스턴스가 된다.
4. 의존성 주입(DI : Dependency Injection)
필요한 의존성을 어떻게 받아올 것인지
- 객체가 서로 의존하는 관계가 되게 의존성을 주입한다.
- 객체지향에서의 의존성은 하나의 객체가 어떠한 다른 객체를 사용하고 있음을 의미한다.
- IoC에서의 의존성 주입은 각 클래스 사이에 필요로 하는 의존 관계를 빈 설정 정보를 바탕으로
컨테이너가 자동으로 연결해 준다. - @ Autowired | @Inject를 어디에 붙일지?
- 생성자 : 어떠한 class에 반드시 필요한 의존성이라면 생성자를 추천
- 필드 : setter가 없다면 필드에 붙이기
- Setter : setter를 가지고 있다면, setter에 붙이기(없는데 굳이 만들어서 붙일 필요는 없음)
5. 의존성 검색(DL : Dependency Lookup)
컨테이너에서는 객체들을 관리하기 위해 별도의 저장소에 빈을 저장하는데, 개발자들이 컨테이너에서 제공하는 API를
이용해 저장소에 저장되어 있는 사용하고자 하는 빈을 검색하는 방법이다.
다른 특징
1. AOP(Abstract Oriented Programming)
흩어진 코드를 한 곳으로 모으는 코딩 기법으로 관점지향 프로그래밍이라고도 부른다.
대부분의 소프트웨어 개발 프로세스는 OOP(객체지향 프로그래밍)를 사용한다.
OOP는 객체지향 원칙에 따라 관심사가 같은 데이터를 한 곳에 모아 분리하고 낮은 결합도를 갖게 하여
독립적이고 유연한 모듈로 캡슐화를 하는 것이다.
하지만 중복된 코드들이 많아져 가독성, 확장성, 유지보수성을 떨어뜨린다.
이러한 문제를 보완하기 위해 핵심 기능과 공통 기능을 분리시켜 핵심 로직에 영향을 끼치지 않게
공통 기능을 끼워 넣는 개발 형태로, 무분별하게 중복되는 코드를 한 곳에 모아 중복되는 코드를 제거할 수 있다.
공통 기능을 한 곳에 보관하여 공통 기능 하나의 수정으로 모든 핵심 기능들의 공통 기능을 수정할 수 있어
효율적인 유지 보수가 가능하며 재활용성이 극대화된다. 물론 OOP로도 AOP를 구현 가능하나,
Spring에서는 좀 더 편리하게 AOP를 사용할 수 있도록 지원중이다.
ex) 1. 바이트 코드 조작
2. 프록시 패턴 사용(Spring AOP는 보통 프록시 패턴을 사용)
2. PSA(Portable Service Abstraction)
잘 만든 인터페이스, 일관된 서비스 추상화
MySQL을 사용하다 Maria DB로 데이터베이스를 바꿔야 하는 상황에서 데이터베이스마다 사용 방법이 다르면?
아마 기존에 작성한 코드를 전부 지우고 새로 작성하거나, 다른 코드를 모두 찾아 일일이 수정해 주어야 한다.
그러나 스프링을 사용하면, 동일한 사용법을 유지한 채로 데이터베이스를 바꿀 수 있다.
스프링이 데이터베이스 서비스를 추상화한 인터페이스를 제공해 주기 때문에 가능한 것이다.
즉, 스프링은 Java를 사용하여 데이터베이스에 접근하는 방법을 규정한 인터페이스인 JDBC(Java Database Connectivity)를 제공하고 있다. 이러한 JDBC를 사용하면 이후 데이터베이스를 바꿔도 기존 작성한 데이터베이스 접근 로직을 그대로
사용할 수 있다.
이처럼 특정 기술과 관련된 서비스를 추상화하여 일관된 방식으로 사용될 수 있도록 한 것을 PSA라고 한다.
3. 스프링 캐시
@EnableCaching으로 사용 가능하고, @Cacheable, @CacheEvict 등등을 사용할 수 있다.
스프링은 일부 데이터를 미리 메모리 저장소에 저장하고 저장된 데이터를 다시 읽어 사용하는 캐시 기능을 제공한다.
트랜잭션과 마찬가지로 AOP를 사용하여 캐시 기능을 구현하였고, 캐시 어노테이션을 사용하면 쉽게 구현할 수 있다.
Spring에서 캐시 데이터를 관리하는 기능은 별도의 캐시 프레임워크에 위임한다.
스프링 캐시에 대해서는 다음에 좀 더 자세히 알아보겠다.
4. 스프링 웹 MVC
MVC란 Model View Controller 구조로, 사용자 인터페이스와 비즈니스 로직을 분리하여 개발하는 것을 말한다.
MVC에서는 Model1과 Model2로 나누어져 있고, 일반적인 MVC는 Model2를 지칭한다.
Model
데이터 처리를 담당하는 부분으로 View와 Controller의 어떠한 정보도 가지고 있어서는 안 된다.
Service영역
필요한 HTTP 통신을 하지 않아야 하며, request나 response와 같은 객체를 매개변수로 받으면 안된다.
또한 View에 종속적인 코드가 없어야 하며, View 가 변경되더라도 Service 부분은 그대로 재사용 가능해야 한다.
DAO영역
DB의 data에 접근하기 위한 객체를 실제로 DB에 접근하는 객체이다. 프로젝트의 서비스 모델과 실제 DB를
연결하는 역할을 하며, JPA에서는 DB에 데이터를 CRUD 하는 Repository 객체와 DAO는 비슷하다고 볼 수 있다.
View
사용자 interface를 담당하며, 사용자에게 보이는 부분, View는 Controller를 통해 모델 데이터에 대한
시각화를 담당하며, View는 자신이 보낸 Controller의 정보만 알고 있어야 하는 것이 핵심이다.
Model이 가지고 있는 정보를 저장해서는 안되며, Model과 Controller에 구성요소를 알아서는 안된다.
Controller
View에 받은 요청을 가공하여 Model(Service영역)에 이를 전달. 또한 Model로부터 받은 결과를 View로 넘겨주는 역할이다. 모든 요청 에러와 모델 에러를 처리하며 View와 Controller에 정보를 알고 있어야 한다.
Model과 View의 정보에 대해 알고 있어야 한다.
이렇게 Model, View, Controller를 나누는 이유는 소스를 분리함으로써 각 소스의 목적이 명확해지고,
유지보수 하는 데 있어서 용이해지기 때문이다. Model의 Service영역은 자신을 어떠한 Controller가 호출하든 상관없이
정해진 매개변수만 받으면 자신의 비즈니스 로직을 처리할 수 있어야 한다.
(모듈화를 통해 어디서든 재사용이 가능해야 함)
즉, View의 정보가 달라지더라도 Controller에서 Service에 넘겨줄 매개변수 데이터 가공만 처리하면 되기 때문에
유지보수 비용을 절감할 수 있는 효과가 있다.
Spring framework의 구조
Spring AOP
> Spring framework에서 관점지향 프로그래밍을 할 수 있고,
AOP를 적용할 수 있게 도와주는 Module이다.
Spring ORM
> Object Relational Mapping의 약자로, 간단히 객체와의 관계를 설정하는 것이다.
> Spring에서는 Ibatis, Hibernate, JDO 등 인기 있는 객체 관계형 도구를 사용할 수 있도록 지원한다.
Spring DAO
> Data Access Object의 약자로 Database Data에 접근하는 객체이다.
> Spring JDBC DAO는 추상 레이어를 지원하며, 코딩이나 예외처리하는 부분을 간편화시켜
일관된 방법으로 코드를 짤 수 있게 도와준다.
Spring Web
> Spring에서 Web context module은 Application module에 내장되어 있으며, Web 기반의 응용 프로그램에 대한
context를 제공하여 일반적인 Web Application 개발에 필요한 기본적인 기능을 지원
Spring Context
> Spring framework의 context 정보들을 제공하는 설정 파일
> Spring context에는 JNDI, EJB, Validation, Scheduling, Internalization 등
엔터프라이즈 서비스들을 포함한다.
Spring MVC
> Model2 구조로 Application을 만들 수 있도록 지원.
> MVC(Model-View-Controller) 프레임워크는 웹 응용 프로그램을 작성하기 위한
완전한 기능을 갖춘 MVC를 구현한다.
Spring core
> spring container를 의미
> container는 spring framework의 핵심이며, 그중 핵심은 Bean Factory Container이다.
Bean Factory
IOC 패턴을 적용하여 객체 구성부터 의존성 처리까지 모든 일을 처리하는 역할을 한다.