스프링 프레임워크(Spring Framework)
웹 개발에 필요한 다양한 기능을 제공해 주는 자바 기반의 프레임워크이다.
여기서 핵심은 바로 자바 언어를 기반으로 만들어졌다는 것인데 자바 언어는 객체지향 언어이므로 스프링 프레임워크는 결국 객체지향 언어가 가진 강력한 특징을 살려주는 프레임워크인 것이다.
좋은 객체 지향 애플리케이션을 개발할 수 있게 도와주는 프레임워크이다.
그렇다면 프레임워크와 라이브러리의 차이가 뭘까?
프레임워크와 라이브러리의 차이
- 프레임워크(Framework)
프레임워크는 하나의 소프트웨어로 프로그램을 개발하기 위해 필요한 것을 생성 및 관리해 주고 전체적인 구조나 뼈대 혹은 틀을 제공함으로써 프로그램 개발을 보다 간편하게 할 수 있도록 도와준다.
- 라이브러리(Library)
라이브러리는 프로그램을 개발하는데 있어 개발자의 필요에 의해서 직접 호출하여 사용하는 도구들의 집합을 의미한다.
프레임워크와 라이브러리의 가장 명확한 차이는 제어의 역전(IoC) 즉, 제어의 흐름을 누가 관리하는지에 차이가 존재한다.
개발자가 프로그램을 개발하면서 필요한 기능이 있으면 능동적으로 라이브러리를 호출하여 사용하면서 전체적인 흐름을 제어하게 되지만 프레임워크는 이미 정해진 틀 안에서 프로그램을 개발하게 된다.
좋은 객체 지향
좋은 객체 지향이라는 것은 유연하고 변경에 용이하다는 뜻이다.
유연하고 변경에 유리하다는 것은 컴포넌트를 쉽고 유연하게 변경하면서 개발할 수 있는 방법을 의미하고 이것을 가능하게 해주는 것이 바로 객체 지향 개념 중 다형성이다.
객체를 설계할 때 역할과 구현을 명확히 분리하면 내부적인 구현이 변경되어도 상위 역할만 잘하고 있으면 클라이언트에 영향을 주지 않게 된다.
역할과 구현을 분리했기 때문에 상위 역할의 변경 없이 새로운 기능을 제공할 수 있고 다른 기능으로 바꿀 수 있는 대체 가능성이 생기기 때문에 유연하고 변경에 용이해진다.
- SRP(단일 책임 원칙)
단일 책임 원칙은 하나의 클래스(객체)는 단 하나의 책임만 가져야 한다는 원칙으로 여기서 책임은 하나의 기능을 의미한다.
하나의 클래스가 여러 개의 기능을 가지고 있다면 특정 기능 하나를 변경하거나 수정하게 된다면 다른 기능에게 영향을 끼칠 수 있다.
- OCP(개방-폐쇄 원칙)
개방 폐쇄 원칙은 클래스(객체)가 확장에는 열려있어야 하며, 수정에는 닫혀있어야 한다는 의미이다.
쉽게 생각해보면생각해 보면 하나의 추상 클래스를 만들고 해당 클래스를 상속받는 또 다른 클래스를 만드는 것을 생각해 보면 된다. 그렇게 되면 상속받은 클래스는 개발자가 원하는 방향으로 만들어서 사용할 수 있게 되고, 추가적인 요구사항이 발생했을 시 유연하게 추가할 수 있다.
추가적인 요구사항으로 인하여 새로운 기능을 만든다면 객체를 직접적으로 수정하는일 없이 기능을 추가할 수 있다.
- LIP(리스코프 치환 원칙)
리스코프 치환 원칙은 자식 타입이 언제나 부모 타입으로 교체할 수 있어야 한다는 원칙이다.
의미를 자세히 보면 자바에서 객체 지향을 공부하면서 알게 되는 다형성의 특징과 같다는 것을 알 수 있다. 다형성도 마찬가지로 부모 클래스를 상속받은 자식 클래스는 자신의 타입을 부모 타입으로 바꿔서 사용할 수 있으므로 부모 타입으로의 교체가 이뤄진다.
- ISP(인터페이스 분리 원칙)
인터페이스 분리 원칙은 여러 인터페이스를 각 사용에 맞게 잘게 분리해야한다는 원칙이다.
인터페이스를 주로 사용하는 클라이언트를 기준으로 분리하여 해당 클라이언트의 목적과 용도에 적합한 인터페이스만 제공될 수 있게 설계가 이뤄져야 된다.
- DIP(의존 역전 원칙)
의존 역전 원칙은 구현 클래스에 의존하지 않고, 인터페이스 혹은 상위 클래스에 의존하는 원칙이다.
만약 구현 클래스에 의존하게 된다면 어떠한 요구사항이 생겼을 때 변경하기 어려워진다. 반면에 인터페이스나 상위 클래스 혹은 추상 클래스에 의존하게 된다면 원하는 기능을 추가하거나 요구사항을 유연하게 추가할 수 있다.
스프링에서 중요한 개념
IoC(Inversion of Control, 제어 역전)
앞에서 프레임워크와 라이브러리의 차이에 관해서 설명할 때 나와있었지만 스프링에서 중요한 개념 중 하나는 제어의 역전이다.
자바를 공부하면서 아마도 가장 많이 사용하는 문법은 객체를 생성하는 문법일 것이다.
User user = new User();
보통 객체를 생성한다고 하면 위의 코드와 같이 new 연산자를 통해서 User의 생성자를 호출해 객체를 생성하게 된다.
개발하면서 흔히 쓰이는 이 문법을 사용하면 개발자가 직접 객체를 생성하고 관리함으로 프로그램의 제어권을 개발자가 갖게 된다. 하지만 스프링에서는 이러한 제어권이 개발자가 아니라 스프링 프레임워크에서 갖게 되는데 어떻게 이것이 가능할까?
스프링이 동작하는 과정
스프링을 실행하게 되면 먼저 DispatcherServlet이라는 객체가 생성되고 Component-Scan이 xml 파일에서 지정한 특정 경로를 따라가서 @Component를 가지고 있는 클래스들을 모두 객체로 생성해 준다.
이렇게 생성된 객체들은 Spring Container라는 곳에서 Bean으로 등록되고 개발자가 아닌 스프링이 자체적으로 관리해 준다.
스프링이 동작하는 과정을 쭉 보면 기존에 개발자가 사용했던 new 연산자를 통한 객체 생성이 아닌 스프링에서 자동으로 객체를 생성 및 관리하게 된다는 것을 알 수 있다. 이를 통해서 프로그램의 전체적인 제어 흐름을 개발자가 아닌 스프링이 대신하게 되는데 이것이 바로 스프링의 핵심 개념 중 하나인 IoC 즉, 제어의 역전을 뜻한다.
DI(Dependency Injection, 의존성 주입)
private final MovieService movieService;
@RequestMapping(method = RequestMethod.POST, value = "/create")
public ResponseEntity<Object> create(MovieDto movieDto) {
movieService.create(movieDto);
return ResponseEntity.ok().body("생성");
}
의존성 주입은 스프링에서 중요한 개념 중 하나로 먼저 의존이라는 것을 살펴보면 위의 코드에서 만약 movieService가 변경된다면 create()에서 사용하는 movieService.create()를 사용하지 못하게 된다. 이처럼 어떤 대상이 변하면 그것이 다른 곳에서 영향을 미치는 것을 의존한다고 얘기한다.
public interface MovieRepository {...}
public class JdbcMovieRepository implements MovieRepository {...}
public class MyBatisMemberRepository implements MovieRepository{...}
만약 위의 코드와 같이 MovieRepository를 구현하는 두 개의 클래스가 있다고 가정해 보자.
public class MovieService {
private MovieRepository movieRepository;
public MovieService() {
this.movieRepository = new JdbcMemberRepository();
//this.movieRepository = new MyBatisMemberRepository();
}
}
의존성을 사용하지 않는다면 각각의 객체를 생성하여 필요에 따라서 movieRepository 변수에 저장해야 된다.
만약 기존의 객체를 사용하지 않고 새로운 JpaMovieRepository를 생성하여 사용한다면 다시 new 연산자를 통해서 새로운 객체를 만들어 변수에 저장해야 되는 문제가 발생하게 된다.
그렇다면 위의 코드에서 의존성을 주입하게 된다면 어떻게 될까?
private MovieRepository movieRepository;
public MovieService(MovieRepository movieRepository) {
this.movieRepository = movieRepository;
}
의존성을 주입한 코드를 살펴보면 개발자가 직접 new 연산자를 통해 객체를 생성하지 않고 외부에서 객체를 받아서 주입해주고 있다.
그러면 외부에서 어떻게 객체를 받아와서 movieRepository에 주입되는 걸까?
DI 전에 언급했던 IoC에서 그림을 보면 제어의 역전이 일어나 스프링에서 객체를 생성하고 스프링 컨테이너라는 곳에서 관리를 하게 된다. 만약 프로그램이 실행되고 위의 예시 코드에서 생성자가 실행된다면 MovieRepository 객체를 스프링(외부)에서 가져와 주입시켜 주게 된다.
외부에서 주입이 가능한 이유는 MovieService와 MovieRepository가 각각 @Service, @Repository를 갖고 있기 때문에 스프링에서 해당 클래스들의 객체를 생성하여 관리를 해주고 있기 때문이다.
IoC에서 사용했던 그림에서 DI를 적용하는 그림을 추가해 봤다. 확실히 그림을 그려보면서 이해하는 방법이 제일 빠른 것 같다.
DispatcherServlet
스프링은 클라이언트에게 HTTP 요청을 받으면 DispatcherServlet이라는 곳을 통하게 된다. 모든 HTTP 요청을 받아들이는 DispatcherServlet은 FrontController라고 불리기도 한다.
디스패처 서블릿을 통해서 HTTP 요청이 들어오면 아래와 같은 순서로 처리되게 된다.
1. 클라이언트에서 디스패처 서블릿으로 HTTP 요청을 보낸다.
2. 받은 HTTP 요청을 핸들러 매핑으로 가서 해당 요청을 처리할 수 있는 컨트롤러를 찾는다.
3. 찾은 컨트롤러를 다시 디스패처 서블릿으로 보내준다.
4. 디스패처 서블릿으로 받은 컨트롤러를 실행한다.
5. 컨트롤러에서 요청 처리가 끝나고 뷰를 다시 디스패처 서블릿으로 보내준다.
6. 디스패처 서블릿으로 받은 뷰를 뷰 리졸버 클래스를 통해서 맞는 뷰 파일을 찾는다.
7. 뷰 리졸버에서 찾은 뷰 파일을 다시 디스패처 서블릿으로 보내준다.
8. 디스패처 서블릿으로 들어온 뷰를 실행시켜 클라이언트에 화면으로 보여준다.
HTTP 요청을 위와 같은 과정을 거치면서 처리하는 패턴을 MVC 패턴이라고 하며 이러한 틀을 스프링에서 만들어 놓고 개발자에게 제공해 주기 때문에 Spring MVC Framework라고 불린다.
'스프링' 카테고리의 다른 글
[Spring] - 스프링 부트 프로젝트 배포하기 (0) | 2024.01.10 |
---|---|
[Spring] - 소셜 로그인 (1) | 2024.01.04 |
[Spring] - Spring Security (0) | 2023.12.25 |
[Spring] - 레이어드 아키텍처 (0) | 2023.12.21 |