mojo's Blog
스프링 프레임워크 개요 본문
스프링 프레임워크와 스프링 부트
※ 스프링 프레임워크란?
스프링 프레임워크는 자바 기반의 오픈소스 프레임워크로 Java EE에서 요구하는 수준의 복잡한 기능을 Java EE를 사용하지 않고 구현하기 위해 시작하였다.
스프링 프레임워크는 높은 수준의 스프링 기반 애플리케이션을 손쉽게 만들어주는 스프링 프로젝트 중 하나로, 20개 이상의 서로 다른 모듈로 구성된다.
스프링 프레임 워크의 주요 특징은 다음과 같다.
- 경량 컨테이너: 객체 생성, 소멸과 같은 생명 주기를 관리하며 스프링 컨테이너로부터 필요한 객체를 얻을 수 있다.
- 제어의 역행 지원: 메서드나 객체의 호출 제어권이 사용자가 아니라 프레임워크에 있어서 필요에 따라 스프링에서 사용자의 코드를 호출한다.
- 의존성 주입 지원: 각각의 계층이나 서비스 간에 의존성이 존재할 경우 프레임워크가 서로 연결해준다.
- 관점 지향 프로그래밍 지원: 트랜잭션이나 로깅, 보안과 같은 여러 모듈에서 공통적으로 사용하는 기능의 경우 해당 기능을 분리하여 관리할 수 있다.
스프링의 주요 프로젝트로는 다음과 같다.
- 스프링 부트: 보다 간편하게 스프링 프로젝트를 시작할 수 있도록 해준다. 특히 마이크로서비스 개발 및 웹 애플리케이션 개발에 적합하다.
- 스프링 데이터: 데이터베이스 연동을 위한 보다 편리한 개발을 지워한다. 전통적인 JDBC에서부터 JPA까지 모두 지원하며, Redis, MongoDB와 같은 NoSQL을 위한 전용 모듈도 지원한다. 또한 Spring Data Rest는 데이터베이스와 연동되는 Rest API 개발의 대부분을 자동화해준다.
- 스프링 배치: 대량의 데이터를 일괄 처리하기 위한 솔루션으로 대형 시스템에서 주로 사용한다. 편리한 설정과 데이터 작업 시 발생하는 문제에 대한 대응 및 관리 기능이 뛰어나다.
- 스프링 시큐리티: 보안과 관련된 여러 기능을 통합 제공한다. 모든 웹 애플리케이션이나 서비스 개발에 필요한 사용자 인증과 권한 부여를 위한 기본 인프라를 제공하며 OAuth2를 통한 인증도 지원한다.
※ 스프링 부트란?
스프링 부트는 스프링 프레임워크 기반의 프로젝트를 빠르고 간편하게 시작할 수 있게 해준다.
스프링 프레임워크는 방대한 구성요소를 가지고 있으며 프로젝트를 시작하기 위해 여러 설정과 필요한 구성요소를 개발자가 직접 정의해주어야 하기 때문에 시작 단계에서부터 많은 어려움을 겪을 수 있다.
스프링 부트는 스프링에 대한 전문적인 지식이 없어도 특정 목적의 개발환경과 프로젝트 구조를 자동으로 생성하고 바로 사용할 수 있도록 도와준다.
또한 톰캣, 제티와 같은 WAS도 내장하고 있으며, 별도의 웹 서버 설치나 실행 없이 스프링 부트 애플리케이션을 실행하는 것으로 웹 애플리케이션의 구동이 가능하다.
스프링 부트 2.0에서부터 기존 구조에서 많은 개선이 있었는데, 가장 큰 차이점으로 리액티브 스택과 서블릿 스택을 구분하고 있다는 점을 꼽을 수 있다.
▶ 리액티브 스택
새로운 비동기 논블로킹 I/O 구조를 사용하며 멀티코어 시스템의 장점을 살리고 대규모 사용자 접속을 처리하는데 유용한 구조로 설계되어 있다.
▶ 서블릿 스택
기존과 같이 서블릿 API에 기반한 동기 방식의 블로킹 I/O 구조를 사용하며 하나의 요청은 하나의 스레드로 처리한다.
※ 리액티브 프로그래밍이란?
변화에 반응하는 프로그램 모델을 말하며 기본적으로 비동기 I/O 기반으로 데이터 흐름과 변화 전파에 중점을 둔 프로그래밍 패러다임을 의미한다.
React, RxJava와 같이 최근 유행하고 있는 각종 프레임워크와 라이브러리의 기본 철학으로, 기존의 프로그램이 작성된 순서에 따라 진행되는 것에 비해 리액티브 프로그래밍은 데이터의 흐름을 먼저 정의하고 데이터가 변경되었을 때 연관된 함수나 수식이 업데이트되는 방식으로 이해할 수 있다.
리액티브 프로그래밍 기법을 통해 개발의 효율을 높이며, 적은 스레드 개수로 더 많은 부하를 처리할 수 있기 때문에 리액티브 프로그래밍이 주목을 받게 된다.
IoC, DI, AOP
IoC, DI, AOP는 스프링 프레임워크의 특징이며, 스프링 프레임워크의 구조를 이해하기 위해 반드시 알아야 하는 중요 개념이라고 할 수 있다.
※ IOC란?
Inversion of Control 으로 '제어의 역행'이라고 한다.
기존의 프로그램은 main()처럼 프로그램이 시작되는 곳에서 필요한 객체를 생성하고, 생성된 객체의 메서드를 호출하는 흐름을 가진다.
이와 달리 IOC는 작업을 수행하는 쪽에서 객체를 생성하는 일반적인 개념을 뒤집은 것으로, 프로그램 제어를 자신이 아닌 다른 곳에 위임하는 것을 말한다.
즉 컨테이너에 객체 생성과 공급을 위임하는 형태이다.
IOC 특징으로 다음과 같다.
- IOC에서는 객체가 자신이 사용할 객체를 사용하거나 선택하지 않는다.
- 객체는 자신이 어떻게 생성되고 어떻게 사용되는지 알 수 없다.
- 모든 객체는 제어 권한을 위임받은 특별한 객체(컨테이너)에 의해 만들어지고 사용된다.
스프링의 경우 스프링 컨테이너에서 객체를 생성하고 공급하는 역할을 담당한다.
또한 스프링 빈은 스프링 컨테이너에 의해 관리되는 스프링 객체를 말하는 것으로, 기본적으로 XML 기반의 설정 파일을 통해 객체와 관계를 정의하지만 지금은 주로 애너테이션을 통해 관련 설정을 한다.
※ DI란?
IoC를 시스템적으로 구현하는 방법으로 DI와 DL이 있다.
DI(Dependency Injection) : 클래스 간의 의존관계를 Bean 설정에 기반해 컨테이너가 자동으로 연결해주는 방식이다.
DL(Dependency Lookup) : 저장소에 저장되어 있는 Bean에 접근하기 위해 개발자가 컨테이너에서 제공하는 API를 이용해 Bean을 찾는 방식이다.
따라서 DL은 컨테이너에 대한 의존성이 커지고 불필요한 코드 사용이 증가하므로 DI 방식이 좋다고 할 수 있다.
기본적으로 스프링 프레임워크에서는 DI를 사용한다.
※ AOP란?
Aspect-Oriented Programming는 관점 지향 프로그래밍이라고도 하며 스프링 프레임워크의 핵심 요소 중 하나이다.
관점 지향 프로그래밍이란 횡단 관심사의 분리를 허용함으로써 모듈성을 증가시키는 것이 목적이다.
따라서 코드 자체를 수정하지 않는 대신 기존 코드에 추가 동작인 어드바이스를 정의하여 추가된 기능이 실행된다.
어느 코드가 포인트컷(ex : 예를 들어 특정 이름으로 시작하는 메서드)을 통해 수정되는지를 별도로 지정하는 구조를 가지고 있다.
- 횡단 관심사 : 여러 프로그램에 흩어져서 존재하는 공통 기능으로 이해할 수 있다. 꼭 필요한 기능인데 해당 기능을 사용하기 위해 동일하거나 비슷한 코드가 기계적으로 특정 위치에 들어가는 것이 해당한다. 대표적으로 인증과 로깅이 있다.
AOP는 실제 프로그램을 개발할 때 예를 들어 뉴스와 게시판 기능을 제공하는 서비스가 있다고 할 때 다음과 같이 적용할 수 있다.
① 뉴스와 게시판은 독립된 기능이다.
② 두 모듈은 모두 사용자 인증을 통해 로그인 여부를 체크해야 한다.
③ 어떤 뉴스 기사가 클릭되었는지 어떤 게시글이 클릭되었는지 로그를 기록해야 한다.
④ 이때 모듈은 달라도 두 모듈은 사용자 인증과 로그를 처리하기 위한 코드가 구현되어야 한다.
⑤ 이러한 구현의 추가는 코드에서 핵심 기능과 뒤섞이고 단순한 중복 코드를 생산한다.
⑥ AOP는 핵심 기능과 횡단 관심사를 분리하고 코드를 일일이 구현하는 대신 어드바이스로 정의하고 코드의 특정 위치에 실행하기 위한 포인트 컷을 정의한다.
AOP는 인증, 로깅, 트랜잭션 등 전체 시스템에 공통적으로 필요한 기능에 적용된다.
WebMVC와 RestController 모듈
스프링 프레임워크의 대표적 적용 분야인 웹 개발에 사용할 수 있는 두 가지 모듈로는 WebMVC와 RestController가 있다.
※ WebMVC란?
이전에 공부한 MVC 기반 웹 애플리케이션 개발과 구조적으로 거의 동일하다.
특히 컨트롤러 부분을 제외하면 모델, 뷰는 기존의 코드를 그대로 사용할 수 있을 만큼 유연한 구조를 가지고 있다.
■ 컨트롤러
스프링을 사용하게 되면 직접 구현할 필요 없이 특정 요청에 대한 처리 메서드만 작성하면 된다.
따라서 불필요한 중복 구현을 하지 않아도 되고, 개발자가 보다 나은 컨트롤러 구조를 만들기 위해 고민할 필요가 없다.
컨트롤러 구현하기
@Controller
@RequestMapping("/web")
public class DemoWebController {
}
- @Controller: MVC 컨트롤러로 동작할 클래스를 의미한다.
- @RequestMapping(): 서블릿의 @WebServlet의 request mapping 속성과 같다.
예를 들어 GET/web/hello 요청에 동작하는 메서드는 다음과 같다.
@GetMapping("/hello")
public String hello(Model model) {
model.attribute("msg", "hello World");
return "hello";
}
- hello() 메서드의 인자인 Model 객체는 뷰에 전달되는 객체로 컨트롤러에서 뷰로 전달할 값이 있다면 model.addAttribute()로 전달한다. JSP에서 request.setAttribute()와 같은 개념이다.
- return 타입은 String이고, 값은 단순한 문자열이 아니라 뷰의 이름이 들어간다. 즉 JSP, 타임리프 파일의 이름을 확장자 없이 이름만 사용한다.
- 여기서 hello.jsp를 호출하게 되고 실제 파일의 위치는 스프링 설정에 따른다.
■ 모델
모델 부분은 기존 코드를 그대로 사용할 수 있으며, 스프링의 JDBCTemplate을 사용해 코드를 간결하게 개선할 수 있다.
Spring Data JPA를 적용할 경우 데이터베이스와 관련된 코드의 많은 부분을 스프링에 넘길 수 있어 보다 간편하게 데이터베이스 관련 작업도 처리할 수 있다.
모델 구현하기
@Autowired
ProductDAO dao;
@GetMapping("/productlist")
public String getProducts(Model model) {
model.addAttribute("products", dao.getDatas());
return "product_list";
}
@PostMapping("/productadd")
public String addProduct(Product p) {
dao.addProduct(p);
return "redirect:/web/productlist";
}
- @Autowired로 스프링 컨테이너로부터 productDAO 객체를 전달받아 요청 메서드에서 사용하게 된다. getProducts() 에서는 dao.getDatas() 메서드로 데이터를 받아 model 객체에 넣은 후 뷰 페이지를 리턴한다.
- addProduct() 에서는 인자로 받은 product 객체를 dao.addProduct()로 저장한 뒤 목록 페이지로 이동한다.
DAO 자체는 기존 코드를 그대로 사용해도 되지만 스프링 JDBCTemplate을 사용하는 경우 다음과 같이 코드가 간략화될 수 있다.
public void addProduct(Product p) {
String SQL = "insert into product(name, price) values(?,?)";
jdbcTemplateObject.update(SQL, p.name, p.price);
}
■ 뷰
JSP를 그대로 사용할 수 있으며 EL과 JSTL 기반이라면 구조적으로 나쁘지 않기 떄문에 많은 경우 JSP를 뷰로 사용하고 있다.
다만 JSP의 커스텀 태그나 이를 이용한 JSTL, JSP 액션 태그 사용으로 인해 웹 페이지 자체의 독립적 개발이나 테스트 등이 어려워지는 문제가 존재한다.
스프링 프레임워크의 경우 자바 기반 프레임워크이지만 JSP에 대한 종속은 없으며 타임리프, JSP, 프리마커 등 다양한 템플릿 엔진과의 연동을 지원하고 있다.
JSP 사용은 별도의 설정이 필요하지만 타임리프의 경우 'pom.xml'에 추가만 하면 바로 사용이 가능하다.
뷰 구현하기
뷰의 경우 JSP를 사용한다면 기존과 동일하게 EL, JSTL로 구성된 페이지를 만들면 된다.
만일 타임리프를 사용할 경우 다음과 같이 작성 가능하다.
<!DOCTYPE html>
<html>
...
<body>
MSG : <span data-th-text="${msg}"> hello </span>
</body>
</html>
JSTL 대신에 data-th-xxx 형식으로 로직이나 반복 처리등을 구현할 수 있다.
서버를 거쳐야만 완전하게 화면이 보이는 부분은 JSP와 다르지 않지만 기본 구조 자체가 표준 HTML 형식이므로 화면 디자인 과정에서 별도의 서버 실행 없이 페이지를 디자인할 수 있다는 장점이 있다.
※ RestController란?
스프링에서 REST API 구현을 위해 사용하는 모듈이다.
스프링에서는 JAX-RS를 사용할 수도 있고, RestController를 사용할 수도 있는데 RestController가 좀 더 편하고 간결하다.
기본적인 REST API 개발 구조나 원리는 동일하며 사용하는 애너테이션 등에서만 차이가 있다.
RestController는 메서드에 따른 요청을 구분하고, 요청 또는 경로 파라미터에 따라 파라미터를 제공받을 수 있다.
또한 JSON 규격으로 전달되는 데이터를 자바 객체로 매핑할 수 있으며 리턴 타입으로 사용하면 자동으로 JSON으로 변환되는 점도 JAX-RS와 동일하다.
@RestController
@RequestMapping("/api")
public class DemoRestController {
@GetMapping("/hello")
public String hello(@RequestParam(value="msg", required=false) String msg) {
return msg;
}
@GetMapping("/hello/{msg}")
public String hello2(@PathVariable String msg) {
return msg;
}
}
- @RestController로 REST API를 위한 컨트롤러 클래스라는 것을 스프링 컨테이너에 전달한다.
- @RequestMapping은 API의 호출의 시작점이다.
- hello() 메서드는 /api/hello?msg=Welcome 형식으로 요청이 이루어지고, hello2() 메서드는 /api/hello/Welcome 형식의 요청에 동작한다.
스프링 빈 선언과 오토와이어링
스프링 빈은 스프링 컨테이너에 의해 관리되는 자바 객체로 빈 등록은 설정 xml, 애너테이션, 설정 클래스를 이용해 등록할 수 있다.
이렇게 등록한 스프링 빈은 오토와이어링을 통해 공급된다.
이 두 가지 개념은 프레임워크로 개발하기 위해서 꼭 알아야 한다.
※ 스프링 빈
스프링 부트의 경우 애너테이션을 통한 빈 등록을 기본으로 한다.
@Component, @Service, @Controller, @Repository, @Bean 등으로 필요한 Bean을 등록하고, 필요한 위치에서 @Autowired를 통해 주입받아 사용하는 것이 일반적이다.
그림과 같이 @Service, @Controller, @Repository는 모두 @Component를 상속받으며 해당 애너테이션으로 등록된 클래스는 스프링 컨테이너에 의해 자동으로 생성되어 스프링 Bean으로 등록된다.
- Bean: 개발자가 컨트롤할 수 없는 외부라이브러리를 Bean으로 등록할 때 사용한다(설정 클래스에서 사용).
- Component: 개발자가 직접 만든 클래스를 Bean으로 등록할 때 사용한다(선언된 Class를 Bean으로 등록).
- @Controller, @Service, @Repository: @Component와 기본적으로 동일하며 역할에 따라 다른 이름을 사용한 것이다.
빈 등록
가장 기본 애너테이션인 @Component를 사용하는 경우를 보도록 한다.
게임에서 사용될 무기 클래스를 대표할 수 있는 타입으로 Weapon 인터페이스를 정의해본다.
package com.example.demo;
public interface Weapon {
void fire();
}
그리고 Weapon 인터페이스를 구현하는 ShotGun 클래스를 스프링 빈으로 등록하기 위하여 @Component 애너테이션을 사용한다.
@Component
public class ShotGun implements Weapon {
private String model = "Basic ShotGun";
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
@Override
public void fire() {
System.out.println(model + " fire!!!");
}
}
설정 클래스를 사용하는 경우 다음과 같이 별도의 클래스를 만들고 @Configuration으로 설정 클래스임을 알리고 @Bean 애너테이션을 Weapon 객체를 등록하는 메서드에 붙여주면 된다.
이때 해당 메서드의 리턴이 빈으로 등록된다.
@Configuration
public class BasicConfiguration {
@Bean
public Weapon superShotGun() {
ShotGun sg = new ShotGun();
sg.setModel("Super ShotGun");
return sg;
}
}
※ 오토와이어링
빈이 등록되었다면 컨트롤러, DAO 등의 구현 클래스에서 해당 객체가 필요할 때 @Autowired 애너테이션을 통해 객체를 참조할 수 있다.
이때 필요한 객체를 오토와이어링 하는 방법은 필드, 생성자, setter 주입 세 가지 유형이 있다.
♣ 필드 주입
말 그대로 클래스의 필드에 @Autowired를 사용하는 방식이다.
class Game {
@Autowired
private Weapon w;
public gameRun() {
w.fire();
}
}
순환 의존성 문제가 발생할 수 있으며 final로 지정할 수 없어 불변 객체를 만들 수 없다는 문제점이 존재한다.
♣ 생성자 주입
객체가 생성될 때 호출되는 생성자에 참조할 클래스를 인자로 받아 필드에 매핑하는 방식이다.
생성자가 하나인 경우 @Autowired를 사용하지 않아도 된다.
다만 생성자에 참조할 객체가 늘어나게 되면 코드 개선을 고려할 수 있다.
class Game {
private final Weapon w;
@Autowired
public Game(Weapon w) {
this.w = w;
}
public gameRun() {
w.fire();
}
}
참조 객체를 final로 받아 사용할 수 있어 보다 안전하다.
일반적으로 권장되는 방식이다.
♣ Setter 주입
setter 메서드를 통해 빈을 주입하는 방식이다.
가급적 메서드 이름은 set 타입 이름 규칙을 따르는 것이 좋다.
보통 생성자에 과도하게 의존성 주입이 되는 것을 보완하기 위해 사용하거나 의존성이 선택적인 경우에 권장된다.
필드 주입과 마찬가지로 final을 사용할 수 없다.
class Game {
private Weapon w;
@Autowired
public setWeapon(Weapon w) {
this.w = w;
}
public gameRun() {
w.fire();
}
}
'Spring' 카테고리의 다른 글
회원 관리 - 백엔드 개발 (0) | 2022.01.12 |
---|---|
스프링 웹 개발 기초 (0) | 2022.01.11 |
IntelliJ로 프로젝트 생성 및 view 환경설정 (0) | 2022.01.10 |
스프링 기반 뉴스 기사 관리 웹 서비스 (0) | 2022.01.07 |
스프링 MVC 컨트롤러 및 RestController 구현 (0) | 2022.01.07 |