💡Spring Framework: 자바 엔터프라이즈 애플리케이션 개발에 사용되는 오픈소스 경량급 애플리케이션 프레임워크이다. 흔히 스프링이라고 부른다.
스프링이란?
애플리케이션의 기본 틀! 스프링 컨테이너
스프링은 스프링 컨테이너 또는 애플레이케이션 컨텍스트라고 불리는 스프링 런타임 엔진을 제공한다. 설정정보를 참고해서 애플리케이션을 구성하는 오브젝트를 생성하고 관리한다. 보통 웹 모듈에서 동작하는 서비스나 서블릿으로 등록해서 사용한다.
공통 프로그래밍 모델 - Ioc/DI, PSA, AOP
프레임워크는 애플리케이션을 구성하는 오브젝트가 생성되고 동작하는 방식에 대한 틀을 제공해줄 뿐만 아니라, 어떻게 작성돼야 하는지에 대한 기준도 제시해준다. 이런 틀을 보통 프로그래밍 모델이라고 한다. 스프링은 IoC/DI, PSA, AOP 이렇게 세가지 핵심 프로그래밍 모델을 지원한다.
기술 API
스프링은 엔터프라이즈 애플리케이션을 개발의 다양한 영역에 바로 활용할 수 있는 방대한 양의 기술 API를 제공한다. UI작성은 물론이고, 웹 프레젠테이션 계층, 비즈니스 서비스 계층, 기반 서비스 계층, 도메인 계층, 데이터 엑세스 계층 등에서 필요한 주요 기술을 일관된 방식으로 사용할 수 있도록 지원해주는 기능과 전략 클래스 등을 제공한다.
스프링의 모든 기술은 자바 엔터프라이즈 플랫폼에 기반을 두고 있다.
💡 스프링을 사용한다는 것은 바로 이 세가지 요소를 적극적으로 활용해서 애플리케이션을 개발한다는 뜻이다. 클래스는 스프링 컨테이너 위에서 오브젝트로 만들어져 동작하게 만들고, 코드는 스프링의 프로그래밍 모델을 따라서 작성하고, 엔터프라이즈 기술을 사용할 때는 스프링이 제공하는 기술 API와 서비스를 활용해주면 된다.
스프링의 중요한 가치
1. 단순함
스프링은 EJB라는 표준 기술을 비판하면서 등장했다. EJB는 불필요하게 복잡한 기술이었기 때문에 스프링이 지향하는 것은 목적을 이룰 수 있는 가장 단순하고 명쾌한 접근 방법이다. 스프링은 객체지향 언어의 장점을 살릴 수 있는 도구이다. 그래서 스프링이 강력히 주장하는 것은 가장 단순한 객체지향적인 개발 모델인 POJO 프로그래밍이다.
2. 유연성
스프링은 유연성과 확장성이 매우 뛰어나다. 스프링의 유연성으로 인해 스프링은 다른 많은 프레임워크와 편리하게 접목돼서 사용될 수 있다. 그래서 여러 프레임워크를 함께 사용하게 해주는 접착 프레임워크라고도 불린다. ex)JUnit, Mockito
스프링 기능의 대부분은 핵심 기능을 확장해서 발전시킨 결과물이다. 스프링은 개발자들에게 스프링을 확장해서 사용하도록 권장하고 있다.
스프링의 특징(트라이앵글)
스프링의 중요한 3가지 기술이면서 특징이라 할 수 있는 것에는 IoC/DI, AOP, PSA 가 있다. 그림을 보면 POJO를 둘러싼 형태를 볼 수 있는데, 이는 POJO 프로그래밍을 IoC/DI, AOP, PSA를 통해서 달성할 수 있음을 의미한다.
POJO(Plain Old Java Object)
💡POJO(Plain Old Java Object): 오래된 방식의 간단한 자바 오브젝트. 쉽게 말해 특정 자바 모델이나 기능,환경, 규약, 프레임워크 등을 따르지 않은 순수한 자바 오브젝트를 지칭한다.
진정한 POJO란 객체지향적인 원리에 충실하면서, 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트이다. 그런 POJO에 애플리케이션의 핵심 로직과 기능을 담아 설계하고 개발하는 방법을 POJO 프로그래밍이라고 할 수 있다.
객체지향적인 설계원칙에 충실하도록 만들게 하며, 테스트에 용이하니 POJO프로그래밍을 지향하도록 하자.
특정 기술과 함께 사용되는 경우?
하이버네이트를 사용하게 되는 경우 특정 기술에 종속적이게 되니, POJO가 아니잖아요!
스프링에선 POJO를 유지하면서 하이버네이트를 사용할 수 있다고 말할 수 있다. 스프링에서 정한 표준 인터페이스가 있기 때문이다. 스프링 개발자들은 ORM이라는 기술을 사용하기 위해서 'JPA'라는 표준 인터페이스를 정해두었다. 그리고 여러 ORM 프레임워크들은 이 JPA라는 표준 인터페이스 아래, 구현되어 실행된다. 이것이 스프링이 새로운 엔터프라이즈 기술을 도입하면서도 POJO를 유지하는 방법이다. 이런 방법을 스프링의 PSA라고 얘기한다.
POJO 프로그래밍을 위한 규칙
- Java나 Java의 스펙(사양)에 정의된 것 이외에는 다른 기술이나 규약에 얽매이지 않아야 한다.
getter, setter만 가지고 있는 코드는 특정 기술에 종속되어 있지 않아 POJO라고 부를 수 있다.
public class User {
private String userName;
private String id;
private String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
(1) 에서는 ActionForm을 상속하고 있으며, (2)에서도 Action클래스를 상속받고 있다.
이렇게 특정 기술을 상속해서 코드를 작성하게 되면 명시적으로 사용했던 부분을 일일이 제거하거나 수정해야되는 일이 발생할 수 있다. 이는 특정 기술에 종속적이기 때문에 POJO라고 부를 수 없다.
public class MessageForm extends ActionForm{ // (1)
String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
public class MessageAction extends Action{ // (2)
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
MessageForm messageForm = (MessageForm) form;
messageForm .setMessage("Hello World");
return mapping.findForward("success");
}
}
- 특정 환경에 종속적이지 않아야한다.
순수 JAVA로 작성한 애플리케이션 코드내에서 Tomcat이 지원하는 API를 직접 가져다가 사용하고 있는데, Tomcat을 Zetty로 바꿔달라는 요건이 들어왔다 가정하자.
이럴 경우 Tomcat API 코드들을 모두 걷어내고 Zetty로 수정하여야 한다.
POJO 프로그래밍이 필요한 이유
- 특정 환경이나 기술에 종속적이지 않으면 재사용 가능하고, 확장 가능한 유연한 코드를 작성할 수 있다.
- 저수준 레벨의 기술과 환경에 종속적인 코드를 애플리케이션 코드에서 제거 함으로써 코드가 깔끔해진다.
- 코드가 깔끔해지기 때문에 디버깅하기도 상대적으로 쉽다.
- 특정 기술이나 환경에 종속적이지 않기 때문에 테스트 역시 단순해진다.
- 객체지향적인 설계를 제한없이 적용할 수 있다.(가장 중요한 이유)
IoC(Inversion of Control) / DI(Dependency Injection)
💡IoC(Inversion of Control): 제어의 역전이라는 의미로, 애플리케이션 흐름의 주도권이 뒤바뀐 것을 말한다. 쉽게 말해 메소드나 객체의 호출작업을 개발자가 결정하는 것이 아니라, 외부에서 결정되는 것을 의미한다.
💡DI(Dependency Injection): 의존성 주입이라는 의미로, 객체를 직접 생성하는 것이 아니라 외부에서 생성한 후 주입 시켜주는 방법을 말한다.
보통 개발할 때 개발자가 프로그램의 흐름을 주도하기 마련이다. 하지만 스프링에서는 프로그램의 흐름을 프레임워크가 주도한다. 객체의 생성부터 생명주기를 스프링 컨테이너가 관리한다. 즉, 제어권이 넘어갔다하여 제어의 역전, IoC라고 한다. 그래서 스프링 컨테이너를 IoC컨테이너라고도 부른다.
제어권이 스프링 컨테이너로 넘어옴으로써 DI가 가능해진 것이다.
스프링에서의 IoC
- 객체생성
- 의존성 객체 주입(스스로 만드는 것이 아닌 제어권을 스프링에게 위임하여 스프링이 만들어 놓은 객체를 외부에서 주입)
- 의존성 객체 메소드 호출
이렇게 생성된 객체는 스프링에선 Bean이라 부른다. 스프링이 실행될 때 생성하며, 필요한 곳에 주입시켜준다. 이 Bean들은 컨테이너에서 싱글톤으로 관리된다.
IoC를 통해 객체끼리 느슨하게 결합될 수 있고, 유연한 코드를 작성할 수 있게 되었다.
DI를 하는 세가지 방법
DI방식을 사용하게 되면 개방 폐쇄 원칙(OCP)의 이점과 같은 이점을 얻을 수 있다. 확장에는 열려있고, 변경에는 닫혀있어 재사용이 가능한 것이다.
DI 방식에는 3가지가 있다. 생성자 주입, Setter주입, 필드 주입 이렇게 3가지이다.
1. Setter Injection(setter메서드 주입)
// setter 메서드를 통한 의존 주입
public class MemberService {
private MemberRepository memberRepository;
@Autowired // 없어도 됨
public void setMemberRepository(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
}
2. Field Injection(필드 주입)
// 필드에 애노테이션을 붙여 의존 주입
public class MemberService {
@Autowired
private MemberRepository memberRepository;
}
3. Contructor Injection(생성자 주입)🌟
// 생성자를 통한 의존 주입
@RequiredArgsConstructor //final이 붙은 필드에 생성자를 만들어줌, 이 애노테이션만 붙여도 의존 주입이 가능함
public class MemberService {
private final MemberRepository memberRepository;
// RequiredArgsConstructor 애노테이션을 붙이면 주석처럼 생성자를 만들지 않아도 됨
//@Autowired 생성자가 하나 있을 때 스프링이 알아서 등록을 해줌
/*public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}*/
}
Spring을 포함한 DI 프레임워크에서는 생정자 주입을 권장하고 있다. 객체의 불변성을 확보할 수 있으며, 테스트가 편리하고, final키워드와 Lombok을 활용해서 간결하게 작성이 가능하고, 순환 참조 에러를 방지할 수 있다.
자세하게 내용을 다루지는 않겠다. 아무튼 생성자 주입 방식을 사용하자!
AOP(Aspect Oriented Programming)
💡AOP(Aspect Oriented Programming): 관점 지향 프로그래밍이라는 의미로, 어떤 로직을 기준으로 핵심적인 관점, 공통적인 관점(부가적인 관점)으로 나누어서 보고 그 관점을 기준으로 모듈화하겠다는 의미이다.
애플리케이션 전반에 걸쳐 공통적으로 사용되는 기능들에 대한 관심사를 바로 공통 관심 사항(Cross-cutting concern)이라 부르며, 애플리케이션의 주목적을 달성하기 위한 핵심 로직에 대한 관심사를 핵심 관심 사항(Core concern)이라 부른다.
위 사진을 예로 들자면, 커피 주문 애플리케이션에서 커피 메뉴 등록, 커피 주문, 커피 주문 변경 등등 기능들이 핵심 관심 사항이고, 공통적으로 처리해야 할 부분인 로깅, 보안, 트랜잭션 같은 경우를 공통 관심 사항이라 할 수 있다.
AOP를 간단하게 말하자면, 공통된 기능을 재사용하는 기법이라 말할 수 있다.
AOP를 함으로써 우리는 이러한 이점을 얻을 수 있다.
- 코드의 간결성 유지
- 객체 지향 설계 원칙에 맞는 코드 구현
- 코드의 재사용
스프링에서의 AOP은 자세하게 다루지 않겠다.. Aspect니 포인트 컷이니.. 너무 길어질 것 같으니까..
PSA(Portable Service Abstraction)
💡PSA(Portable Service Abstraction): 하나의 추상화로 여러 서비스를 묶어둔 것, 환경의 변화와 관계없이 일관된 방식으로 기술에 접근할 수 있는 환경을 제공하려는 추상화 구조를 말한다.
추상화 계층을 사용해서 어떤 기술을 내부에 숨기고 개발자에게 편의성을 제공해주는 것을 서비스 추상화(Service Abstraction)이라 한다. 이러한 서비스 추상화로 제공되는 기술을 다른 기술 스택으로 간편하게 바꿀 수 있는 확장성을 갖고 있는 것이 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를 사용하고, 각각 사용자의 선언에 따라서 JPATransactionManger, DatasourceTransactionManager, HibernateTransactionManger 등을 상황에 맞게 의존성 주입을 받아 사용하게 된다.
따라서 개발자는 내부적으로 어떠한 Database Mapping전략을 사용하던지 관계없이 @Transactional이라는 어노테이션을 활용하면 Transaction 처리를 할 수 있게 된다.
그렇기 때문에 PSA를 통해서 애플리케이션의 요구 사항 변경에 유연하게 대처할 수 있다.
Reference
https://chanho0912.tistory.com/17
토비의 스프링
Codestates 학습 자료
'Spring > 개념' 카테고리의 다른 글
[Spring] 스프링 AOP (0) | 2023.05.11 |
---|---|
[Spring Security] 스프링 시큐리티의 동작 구조 (0) | 2023.05.11 |
[Spring Security] 스프링 시큐리티와 FilterChain (1) | 2023.05.11 |
[Spring] Spring 기초 및 모듈 구성, Sprig VS SpringBoot (0) | 2023.05.02 |
[Spring] Filter, Interceptor, AOP의 차이점에 대해 (0) | 2023.04.10 |