| 개요
- 스프링은 SOLID 원칙을 담은 아키텍쳐(설계도)에 따라 만든 프레임워크이다.
- 웹 개발 기술의 발전 : HTML -> CGI -> Sevlet/JSP(EJB) -> Spring MVC -> node, ktor 등 경량 웹 프레임워크
- 스프링에 대한 주요 기술은 아래와 같다.
코어 | DI, IoC, 컨테이너 Resource, AOP, Validation, SpEL |
MVC | Web MVC, HTTP 요청/응답처리, 필터와 인터셉터, 예외처리 |
[ 라이브러리와 프레임워크의 차이점 ]
- 프레임워크가 개발을 하기 위한 전체적인 뼈대/틀이라면,
- 라이브러리는 특수한 기능에 대한 도구, 기능의 집합을 말한다.
- 프레임워크와 라이브러리의 가장 큰 차이는, 흐름을 제어하는 것이 누구이냐인데,
프레임워크에서는 IoC(Inversion of Control) 곧, 제어의 역전이라 하여 프레임워크 자체가 흐름을 통제한다.
반면, 라이브러리의 경우, 흐름을 작성하는 사용자(개발자)에 따라 그 방향성이 달라질 수 있다.
[ SOLID 원칙 ]
- SRP : 단일 책임 원칙
- OCP : 개방 폐쇄 원칙 (인터페이스를 통한 사용 기능의 교체 가능성)
- LSP : 리스코프 치환 원칙 (상위 -> 하위 상속된 메소드는 동일 기능이여야한다.)
- ISP : 인터페이스 분리 원칙 (인터페이스도 단일 책임을 져야한다)
- DIP : 의존성 역전 원칙 (하위의 변경이 상위의 변경까지 요구하지 않도록 인터페이스를 통해 의존성을 끊어버린다)
| 스프링의 코어(핵심)
1. DI, IoC, 컨테이너
- 스프링에서는 컨테이너라는 공간 안에 클래스들을 규격화된 Bean형태로 저장한다.
* 수업에서는 컨테이너를 레고판으로, Bean을 레고에 비유했다.
- 이렇게 규격화된 방식을 사용하게 되면 프로젝트의 개발자가 바뀌어도 작업이 원활하게 이루어질 수 있다.
(1) DI(Dependency Injection) - 의존성 주입
✅ DI 곧, 의존성 주입은 A와 B의 의존 관계를 외부에서 주입해주는 걸 말한다. |
(2) IoC(Inversion Of Control) - 제어의 역전
✅ IoC 곧, 제어의 역전이란, 사용자가 클래스를 생성(제어)하지 않고 스프링 프레임워크가 제어하게 하는 것이다. |
(3) DI 설정 방법의 역사
✅ DI 설정 방법의 역사 1️⃣ XML을 통한 Bean 등록 2️⃣ XML ComponentScan을 통한 Bean 등록 3️⃣ JavaConfig Class를 통한 Bean 등록 4️⃣ JavaConfig Class의 ComponentScan를 통한 Bean 등록 |
- DI 설정방법은 XML을 통할 수도 있고, JavaConfig 클래스를 통할 수도 있는데 후자를 많이 사용한다.
- JavaConfig를 사용하는 방법만 보면, 아래와 같이 @Configuration을 통해 이 클래스가 Di config임을 알려주고,
내부에 사용하려는 각 클래스의 구현체를 @Bean을 통해 전달하는 코드를 담는다.
@Configuration
public class ApplicationConfig{
@Bean
public MyService myService(){
return new MyService();
}
}
- 클래스를 Bean으로 등록할 때에는 일일히 @Bean을 치지 않고 각 클래스에 @component를 하면 Bean으로 등록된다.
- 거기에 플러스로 config 파일에 @ComponentScan(//생략)을 추가하면 스프링이 알아서 의존 관계를 찾아준다.
@Configuration
@ComponentScan(basePackages = "com.sample.myApplication") // 패키지 기준으로 탐색
public class ApplicationConfig{
}
// 또는
@Configuration
@ComponentScan(basePackageClasses = MyApplication.class) // 메인 클래스 기준으로 탐색
public class ApplicationConfig{
}
- main함수에서 설정을 호출할때
// DI config 호출
// 1. xml을 사용할 경우
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2. config클래스를 사용할 경우
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(ApplicationConfig.class);
[ Bean 설정 방법 - 더보기 ]
구분 | 설정 방법 | 설명 |
구현체 지정 | @Primary | 구현체에 직접 지정 |
@Qualifier("beanName") | 사용자인 클래스의 생성자 매개변수에 구현체 지정 | |
Set이나 List로 모두 받기 | 사용자인 클래스의 생성자 매개변수에 Set, List로 지정 | |
프로퍼티 이름을 Bean과 동일하게 | ex. PayByCard --> payByCard | |
Scope | 싱글톤 | default, 한 번 만들어 계속 사용 |
Prototype | Request, Session, WebSocket | |
Profile | @Profile("!production") | 여러 환경(test, stage, open)에서만 동작하는 Bean을 만들 때 클래스 단위나 메소드 단위에 사용 가능 - Dspring.profiles.active = sandbox, beta, production 대체로 !, &, | 와 함께 사용 |
[ Bean 설정 방법 ]
[1] Bean의 구현체가 여러 개인 경우
(1) @Primary : 구현체에 직접 Primary를 단다.
(2) @Qualifer : 서비스 생성자의 매개변수에 자격 요건을 단다.
@Component
public class ConveniencePayService { // 편결이
private final Map<PayMethodType, PaymentInterface> paymentInterfaceMap
= new HashMap<>();
private final DiscountInterface discountInterface;
// 생성자를 통해 결제수단의 종류를 Map으로 저장
public ConveniencePayService(Set<PaymentInterface> paymentInterfaceSet,
@Qualifier("paymentByMethod")
DiscountInterface discountInterface) {
paymentInterfaceSet.forEach(
paymentInterface -> paymentInterfaceMap.put(
paymentInterface.getPayMethodType(),
paymentInterface
)
);
this.discountInterface = discountInterface;
}
}
(3) Set 또는 List로 모두 받는다.
(4) 프로퍼티 이름을 Bean과 동일하게 한다.
@Component
public class ConveniencePayService { // 편결이
private final Map<PayMethodType, PaymentInterface> paymentInterfaceMap
= new HashMap<>();
private final DiscountInterface discountInterface;
// 생성자를 통해 결제수단의 종류를 Map으로 저장
public ConveniencePayService(Set<PaymentInterface> paymentInterfaceSet,
DiscountInterface discountByPayMethod) {
paymentInterfaceSet.forEach(
paymentInterface -> paymentInterfaceMap.put(
paymentInterface.getPayMethodType(),
paymentInterface
)
);
this.discountInterface = discountByPayMethod;
}
}
[2] Scope설정하기
- 싱글톤 : default설정(아무것도 설정X시)
- Prototype : 데이터를 클렌징할 때 사용하며, 매번 새로 Bean을 생성한다.
ㄴ Request/Session/Web Socket
[3] Profile
- 인텔리j의 경우, 실행버튼 좌측에 셀렉박스 선택 - [Edit Configuration...] 에서 개발 환경변수를 입력할 수 있고
- 쉘에 직접 칠 경우, - Dspring.profiles.active = sandbox, beta, production 로 개발 환경변수를 입력할 수 있다.
- 특정 환경에서만 사용할 클래스에 @Profile("test")와 같이 작성하면 된다.
@Component
@Profile("test")
public class DiscountByConvenience implements DiscountInterface{
}
2. 스프링의 부가기능 - Resource, AOP, Validation&Data Binding, SpEL
(1) Resource(외부 자원)
✅ 외부자원은 외부 sftp, http, 파일 등에서 얻는다. (pf. sftp는 ssh의 ftp 버전) ✅ Resource는 인터페이스며 다양한 구현체를 갖고 있다. ✅ ApplicationContext는 ResourceLoader를 상속하며 프로그램이 시작할 때 Resource는 자동 로딩된다. ✅ 단, 추가적으로 파일을 로딩해야하는 경우 @Service와 @Autowired를 통해 직접 로딩할 수 있다. ✅ ApplicationContext는 ResourcePatternResolver도 상속하는데, 위치 지정자 패턴(classPath, file, http)에 따라 Resource 구현체가 자동 선택된다. |
인터페이스 | 구현체 | 메소드 |
Resource(외부자원 가져오기) | UrlResource ClassPathResource FileSystemResource ServletContextResource InputStreamResource ByteArrayResource |
exists() isReadable() isOpen() getFile()..... |
- 추가 파일 로딩
@Service
public calss ResourceService{
@AutoWired
ApplicationContext ctx;
public void setResource(){
Resource myTemplate = ctx.getResource("classPath:some/resource/path/myTemplate.txt");
}
}
- ApplicationContext(스프링의 핵심설정)을 이루는 설정값을 가져오기
// 스프링의 핵심설정을 이루는 설정값을 가져오기
ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml");
ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml");
ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");
Bear bear = (Bear) ctx.getBean("bear");
(2) AOP(Aspect Orient Programing, 관점지향 프로그래밍)
✅ 단순 자바 OOP로는 반복적인 메소드를 관리하기 어려워, AOP를 사용하게 되었다. ✅ AOP로 공통 관심사(로깅, 트랜잭션, 인증)를 여러 메소드 Before/After/Around에 원할 때마다 쉽게 추가할 수 있다. ✅ AOP의 기본 개념 - [ Aspect(관심사) : Pointcut(포인트 선택 방법 - 조건식) + Advice(조언) ] - Join point(연결 포인트) : 프로세스 내의 삽입 지점 - AOP proxy : target object에 advice를 덧붙이기 위해 하는 작업 - Weaving : advice를 비즈니스 로직 코드에 삽입하는 것 ✅ AOP는 AspectJ 라이브러리를 사용해야 제대로 쓸 수 있다. |
* 로깅/트랜잭션이란?
- 간단히 말해 로깅은 로그(log) 곧, 기록을 남기는 것을 말하고,
- 트랜잭션이란 DB관련 작업 단위를 말하는 것으로 DB와의 작업을 한다는 의미이다.
OOP | AOP | |
포커스 | 객체 간의 상호작용 | 관점의 핵심과 부가지점 |
사용 방식 | 객체를 독립적으로 사용하거나 부품처럼 결합 | 관점에 따라 공통 로직의 객체들을 하나로 모듈화 |
- AOP 예시 코드
package org.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect // Aspect 생성
@Component // Aspect Class를 스프링 Bean으로 등록
public class CertainAspect{
@PointCut("execution(public * *(..))") // public 대상 PointCut선언
private void certainAdviceA(){
// 조건식 생성
}
@PointCut("within(com.xyz.myapp.trading..*)") // 특정 패키지 대상 PointCut선언
private void certainAdviceB(){
// 조건식 생성
}
@PointCut("certainAdviceA() && certainAdviceB()") // PointCut결합
private void mergeAdvice(){
// 조건식 생성
}
}
- 미리 정의된 포인트컷의 before/after/around에 advice실행
package org.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect // Aspect 생성
public class CertainAspect{
@Before("com.xyz.myapp.certainPointCutMethod()")
private void certainAdvice(){
}
@AfterReturning("com.xyz.myapp.certainPointCutMethod()")
private void certainAdvice(){
}
@Around("com.xyz.myapp.certainPointCutMethod()")
private void certainAdvice(){
}
}
(3) Validation & Data Binding
- Validation
✅ Validation은 유효성 검증을 말하며, 주로 HTTP Request에서 잘못된 내용을 검증할 때 사용한다. - 데이터 검증 : 필수 데이터 / 문자열 길이 및 숫자형 데이터 범위 / 이메일 및 신용카드 번호 등 형식 확인 - 비즈니스 검증 : 서비스 정책에 따라 데이터 검증 ✅ Validation 방식 1) Java Bean Validation : dto클래스 맴버에 Annotaion(ex. @NotBlank, @Size, @Email...)을 붙이는 방식 2) Spring validator 인터페이스 구현을 통한 validation ✅ Validation 주의사항 : Validation을 멀리 두면 테스트 및 유지보수성이 떨어지므로 가능한 Annotaion방식을 쓰는 게 좋다. * 수업에서의 제안 : 1차로 dto에 Annotation방식 사용, 2차로 비즈니스 검증 후 실패 시 Custom Exception throw |
- Data Binding
✅ Data Binding이란 요청 데이터를 특정 도메인 객체에 저장해 프로그램 Request에 담아주는 걸 말한다. >> ex. 어떤 데이터를 특정 dto객체로 변환하고 싶을 때 ✅ Data Binding 방식 1) Converter<S, T> interface를 구현한 클래스를 bean으로 등록해 사용 2) Formatter<T> interface를 구현한 클래스를 통해 특정 객체를 String, String를 특정 객체로 변환 |
(4) 스프링 표현 언어 (SpEL)
✅ 표현언어(EL)는 간단한 문법으로 필요한 데이터 및 설정값을 얻을 수 있게 하는 언어를 말한다. * 이 부분은 차후에 더 정리할 예정. |
| MVC
- MVC란? Model + View + Controller 의 약자
Model | View | Controller | |
내용 | 로직 내 이동 중인 데이터 (pf. 객체) |
보여지는 화면 | 비즈니스 로직에 따라 처리하는 처리자 |
명칭 | VO(value object), DTO(data transfer object) |
- | - |
- 최근에는 일반적으로 Request와 Response 모두 JSON 타입을 사용한다.
- Spring MVC의 흐름
[1] Dispatcher Servlet이 요청 URL을 받아 HandlerMapping에 전달 * Dispatch : 파견 보내다. rf. 도서관의 사서(dispatcher)는 도서를 요청(request)받았고, 도서 코드 목록(HalderMapping)서 도서를 어떻게 찾을지 본다. [2] HandlerMapping는 요청 URL에 맞는 Controller와 Method 정보를 반환 rf. 도서의 코드 목록에 따르면 해당 도서는 '역사' 카테고리 구역에 있단다. [3] Dispatcher Servlet이 Handler Adapter에게 요청 처리를 위임 rf. 사서는 인턴에게 '역사' 카테고리 책장으로 이동해서, 가나다순으로 진열된 책들을 보면 된다고 알려준다. [4] HandlerAdapter가 Controller와 Method를 실행 rf. 인턴이 찾으려는 도서의 코드와, 사서의 안내를 가지고 '역사' 구역에 가서 도서를 찾는다. [5] Cotroller는 비즈니스 로직을 처리하고, 그 결과를 바탕으로 뷰(ex.JSP)에 전달할 객체를 Model 객체에 저장 rf. 인턴은 찾은 도서를 가져와 책수레에 담는다. [6] Dispatcher Servlet은 view name을 View Resolver에 전달하여 View 객체를 얻음 rf. 사서는 인턴으로부터 "00책 가져왔어?"라고 물어보고, 인턴은 "00책 여기있어요."하고 책수레에 있던 도서를 전달한다. [7] Dispatcher Servlet은 View 객체에 화면 표시를 의뢰 rf. 사서는 책을 요청한 사람에게 책을 전달하기 위해 "00책 요청하신분~?"하고 물어본다. [8] View 객체는 해당하는 뷰(ex.JSP.Thymeleaf)를 호출하며, 뷰는 Model 객체에서 화면 표시에 필요한 객체를 가져와 화면 표시를 처리 rf. 사서는 책을 요청한 사람에게 책을 대여해주고, 반납일에 대해 안내한다. |
| 스프링 프로젝트 생성하기
1. 프로젝트 생성하기
[1] 인텔리J를 통해 바로 Gradle프로젝트 형성 가능 [2] spring initializer를 통한 프로젝트 생성 |
[1] https://start.spring.io/ 에서 프로젝트를 하나 만들어 생성
- 압축파일 해제한 뒤 IDE에 가져와 Build를 보면 아래와 같은데, 알아서 기본 세팅을 해준 걸 볼 수 있다.
/* 플러그인의 의존성(library) 관리 */
plugins {
id 'org.springframework.boot' version '2.7.3'
id 'io.spring.dependency-management' version '1.0.13.RELEASE'
id 'java'
}
group = 'com.zerobase'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
/* 각종 의존성들(libraries)을 어떤 원격 저장소에서 받을 것인지 지정 */
repositories {
mavenCentral() // jcenter로 업로드 설정을 간소화할 수 있다.
}
/* 프로젝트 개발에 필요한 의존성들을 선언하는 곳 */
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
[ Maven Project와 Gradle Project ]
* Gradle이 Maven에 비해 최대 100배가 빠르다. (Maven vs Gradle 바로가기)
- Maven Project : xml 기반
- Gradle Project : 그루비 기반
[2] 인텔리j를 통해 바로 프로젝트 생성
[ 정리 ]
✅ 스프링 프로젝트를 생성할 때에는 spring.io 홈페이지나 IDE를 통해 생성할 수 있다. ✅ Bean 등록 : 각 클래스에 @Component를 쓰면 별도 등록이 필요 없다. ✅ DI Container에 Config 설정하기 : - config클래스에서 @confuration을 통해 각 클래스간 의존 관계를 DI(의존성 주입)하고, - @componentScan을 통해 IoC(제어의 역전) - 자동으로 의존 관계 설정 - 을 한다. ✅ Resource는 ApplicationContext 시작 시 ResourceLoader에 의해 자동 업로드되며, 별도 업로드 또한 가능하다. ✅ AOP는 관점 지향 프로그래밍으로 로깅, 트랜잭션, 인증 등의 반복적인 처리가 필요할 때 AspectJ lib를 통해 구현한다. ✅ Validation은 HTTP request에 대한 데이터 유효성 검증 또는 비즈니스 로직 검증을 하는 것으로, 1차적으로는 dto bean에 Annotation방식으로 데이터 유효성 검증 후, 2차적으로는 validator를 구현한 bean을 통해 비즈니스 로직을 검증하는 방식을 사용할 수 있다. ✅ Data-Binding이란 HTTP request로 받은 데이터를 특정 데이터 폼으로 변경하는 것을 말하는 것으로, Converter나 Formatter 인터페이스를 구현한 Bean을 가져와 사용하는 것으로 구현할 수 있다. ✅ SpEL이란, 스프링 표현언어를 말하는 것으로 간단한 문법을 통해 필요 데이터 및 설정값을 가져올 수 있는 언어를 말한다. |
[ 참고 및 출처 ]
부트캠프 수업 내용 정리
mvc란? https://hanamon.kr/mvc%EB%9E%80-mvc-design-pattern/
트랜잭션이란 https://mommoo.tistory.com/62
spring MVC https://codingnotes.tistory.com/28
프레임워크와 라이브러리, OOP와 AOP https://geminihoroscope.tistory.com/104
'Framework > Spring' 카테고리의 다른 글
[스프링] 프로젝트 전 꼭 알아두면 좋은 것들 (0) | 2022.09.13 |
---|---|
[스프링] 스프링 MVC - 예외처리 (1) | 2022.09.11 |
[스프링] 스프링 MVC - 필터, 인터셉터 (1) | 2022.09.11 |
[스프링] 스프링MVC - HTTP 요청 및 응답 (1) | 2022.09.11 |
[스프링] OOP의 SOLID 원칙과 스프링 (0) | 2022.08.29 |