Layering
실무에서 다루는 복잡한 프로그램들은 우리가 개념을 배울 때 사용하는 예시 프로그램과 같은 간단한 구조로 이루어져있지 않다. 이러한 복잡한 프로그램을 다루는 가장 일반적인 방법은 layer를 분리하는 것이다.
이렇게 Layering을 하는 목적은 추상화에 그 목적이 있다. JSP와 같이 하나의 파일로 여러가지 역할을 처리하면, 너무 많은 역할을 하게 된다. 비즈니스 로직을 호출하는 부분에 변경이 발생해도, UI를 변경할 일이 있어도 모든 경우에 하나의 파일에 접근하여 코드를 수정해야 하는 것이다.
그래서 적절한 Layering은 추상화를 성공시켜 각기 다른 layer끼리는 서로 변경에 영향을 받지 않게 되고, 이것은 유지보수에 큰 이점을 가져다준다.
개발자들은 다양한 프로젝트를 통해서 최선의 패턴을 찾아냈고, 대표적인 예시로 계층 구조가 있다.
<View>
ㆍ Presentation Layer
- UI(사용자 인터페이스)
- resource에 대한 표현들을 담당하는 layer (UI변경에 의해 바뀐다)
- 사용자가 선택할 수 있는 기능들을 표기하고, 기능 요청에 필요한 입력 양식이 있어야 한다.
- 다른 계층의 일을 직접 수행하거나 직접적인 통신이 있으면 안된다.
<Controller>
ㆍ Control Layer
- 프레젠테이션(View)와 비지니스 로직(Model)을 분리하기 위한 컨트롤러를 제공
- 전체 시스템의 설정 상태 유지
- 요청에 따른 적절한 로직의 동작을 결정, 로직과 뷰를 적절하게 연결지어준다.
<Model>
ㆍ Business Logic Layer (Service Layer)
- 핵심 로직이 동작(트랜잭션 처리, 인터페이스 제공 등). 핵심 업무에 대한 동작 방법이 들어가있음
ㆍ Repository Layer (Persistence Layer)
- 데이터 처리를 담당(RDB와 쿼리문을 이용한 통신)
- 데이터의 CRUD연산 수행
- 관계형 정보를 객체화
ㆍ Domain Model Layer(DTO)
- 각 계층 사이에 전달되는 실질적인 비지니스 객체
- DTO(데이터 전송 객체) 형태로 개발자가 만들어서 다양한 계층에서 사용됨
- 서비스(Service)로 로직을 이용한 자동화 가능. 하지만 DTO에 단순히 필드, getter, setter만 두고 사용하는것보다는 actor가 되는 model클래스는 구별해주는게 좋다. 무조건 모든 로직을 Service에 넣는 것은 좋지 않음.
MVC
응용 프로그램을 세 가지의 구성요소(model-view-controller)로 나누는, 이러한 대표적인 소프트웨어 디자인 패턴을 모델-뷰-컨트롤러(MVC) 디자인 패턴이라고 한다. 이 패턴을 올바르게 사용하면 사용자 인터페이스로부터 비즈니스 로직을 분리하여 시각적 요소와 비즈니스 로직이 서로 영향을 끼치지 않게 하여(SoC, 관심의 분리) 유지보수와 기능추가/삭제가 수월해진다. 여기서 비즈니스 로직이란 데이터를 생성, 저장, 수정하는 기능들을 이야기한다. 앞서 언급한 5계층 패턴에 포함되는 모델이다.
Controller
- user로부터의 입력(HTTP request)을 전달받아 적절한 로직이 동작하도록 모델에 지시. 즉, UI로부터 input을 받아서 어떤 service layer api를 호출해서 어떤 UI에 결과를 전달할지를 결정한다.
Model
- 앱이 포함되어야 할 데이터가 무엇인지, 즉 어플리케이션이 무엇을 할 것인지를 정의한다. 컨트롤러의 요청을 받아 Service, Repository등에서 뷰에 필요한 데이터를 영속성 콘텍스트(1차캐시)나 DB에서 조회/변경하여 결과를 뷰에 전달하는 역할을 맡는다.
View
- 모델로부터 데이터를 전달받아 데이터를 사용자(뷰)에게 나타내준다. 뷰가 필요한 데이터들은 모두 모델에서 전달해주기 때문에 뷰는 비즈니스 로직이나 데이터 접근을 몰라도 되고, 화면을 렌더링 하는 일에 집중할 수 있다.
Spring MVC
스프링의 MVC는 기본적으로 톰캣이 시작되며 필터, 디스패쳐가 시작되면서 시작된다.
(1) client의 request가 들어오면 가장 먼저 디스패처 서블릿이 적당한 컨트롤러를 찾아 매핑해준다. (밑에서 자세히 설명)
(2) 컨트롤러는 알맞는 서비스를 실행시킨다
(3) 서비스에서는 실행할 요청의 종류에 따라서 repository를 동작시키는 로직을 적절하게 호출한다.
(4) 그러면 repository에서는 로직에 맞게 쿼리문을 만들어서 영속성 콘텍스트(1차캐시)와 DB를 확인하게 되는데, 1차캐시에 요청 정보가 존재하지 않는다면 Datasource라는 파트가 DB를 조회하고, 1차캐시에 존재한다면 바로 오브젝트를 조회해오는 식으로 동작한다.
(5) 그 후에는 조회한 결과를 repository-service-controller와 같이 역순으로 보내는데, 같은 트랜잭션 내에서 오류가 있다면 서비스는 rollback처리를 해서 프로그램이 잘못 동작하지 않도록 막는다.
(6) 마지막으로 컨트롤러에서 결과를 디스패처 서블릿으로 전송하면 뷰 리졸버를 호출하여 뷰 객체를 만들어 response를 전송한다.
Controller: 요청의 종류에 따라서 DispatcherServlet에 의해 알맞는 컨트롤러가 서비스를 호출한다.
Service: 하나의 트랜잭션(기능)을 담당. 하나의 기능을 위해서 여러 번의 데이터 요청이 있을 수 있는데, 이 요청들에 문제가 발생할 경우 rollback처리를 하고, 아니면 commit하는 역할을 한다.
Repository: 1차캐시나 DB에서 데이터를 CRUD하는 역할을 맡는다.
Dispatcher-Servlet
스프링 MVC에서 request를 받고 response를 전송하는 앞단에는 'DispatcherServlet'이 프론트 컨트롤러의 역할을 하는 패턴으로 구현되어 있다. 실제 동작 순서는 다음과 같다.
1. 핸들러 조회: url에 매핑된 핸들러(컨트롤러)를 조회한다.
2. 핸들러 어댑터 조회: 조회한 컨트롤러를 처리할 수 있는 핸들러 어댑터를 조회한다.
3. 핸들러 어댑터 실행: 조회한 핸들러 어댑터를 실행한다.
4. 핸들러 실행: 실행한 핸들러 어댑터가 실제 핸들러(컨트롤러)를 실행한다
5. ModelAndView 반환: 핸들러가 반환하는 정보를 ModelAndView로 변환하여 반환한다.
6. 뷰 리졸버 호출 : 뷰 리졸버를 실행한다.
7. View 반환: 뷰 리졸버가 렌더링 역할을 하는 뷰 객체를 반환한다.
8. 뷰 렌더링: 뷰를 통해서 렌더링한다.
@Controller
public class Controller {
@GetMapping("url")
public String r(@RequestBody Data data) {
//logic
return "path";
}
}
[1~4] 먼저 클라이언트의 request가 들어오면, DispatcherServlet이라는 프론트 컨트롤러가 URI 매핑된 컨트롤러를 찾고, 요청 주소에서 들어오는 http요청(@RequestBody, @ModelAttribute, @RequestParam 등)의 content-type 미디어 타입을 지원하는지 등의 여부로 적절한 핸들러 어댑터를 찾고 그 어댑터가 그 컨트롤러에서 적절한 비즈니스 로직을 실행한다.
(상단 코드에서는 Data가 요청으로 들어오며, r 메소드 내에서 요청으로 들어온 데이터를 적절하게 가공/활용한다. )
-------------------------------------------------------------------------------------------------------------------------------------------------
[컨트롤러 뒤에서 1차캐시나 DB에서 조회,추가가 이루어지는 등의 작업 결과가 컨트롤러로 반환된다.]
-------------------------------------------------------------------------------------------------------------------------------------------------
[5] 컨트롤러는 비즈니스 로직을 실행한 이후에 처리 결과를 핸들러 어댑터로 반환하고, 어댑터는 반환받은 결과를 ModelAndView형태로 변환하여 DispatcherServlet에게 반환한다.
이 때 ModelAndView에는 컨트롤러에서 처리 후 응답할 뷰와, 뷰에 전달할 값이 저장된다.
(상단 코드에서는 path라는 문자열이 ModelAndView꼴로 변환되어 반환될것이다)
[6~7] DispatcherServlet은 뷰 리졸버에게 ModelAndView객체를 넘기고, 뷰 리졸버가 ModelAndView객체 안에 저장된 뷰 객체를 찾고 suffix, prefix등을 추가하여 물리 주소로 변환하여 뷰 객체를 반환해준다.
만약 @ResponseBody(@RestController) 애노테이션이 존재한다면 뷰 리졸버가 실행되지 않고, 문자열이 화면에 그대로 뿌려진다.
(상단 코드에서는 ModelAndView의 path에 suffix와 prefix를 추가한 물리 주소에 매핑되는 뷰가 반환될 것이다)
[8] 뷰 리졸버가 찾거나 만들어서 반환한 뷰 객체가 클라이언트에 전달되면 response가 종료된다.
그 외에도..
MVP(Model-View-Presenter), MVVM(Model-View-View Model)..
https://eckrin.tistory.com/entry/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-MVC-MVP-MVV
'[ Backend ] > Spring Web' 카테고리의 다른 글
[Spring MVC] 메세지, 국제화 (0) | 2022.07.08 |
---|---|
[Spring MVC] URL 매핑, 데이터 전송/조회 (0) | 2022.02.19 |
[Spring MVC] 서블릿과 스프링, 프론트 컨트롤러 (0) | 2022.02.09 |
[Spring MVC] 웹 서버, WAS, 서블릿 (0) | 2022.02.08 |
[Spring MVC] @~Mapping 사용시 참고 (0) | 2022.02.04 |