스프링 MVC란
스프링 MVC는 애플리케이션을 Model-View-Controller 형식으로 관심사를 분리하여 각 계층을 모듈화 한 것을 말합니다.
스프링 mvc는 이런 mvc를 프레임워크 형태로 제공하고 있습니다.
1 | View |
|
2 | Controller | ● 역할: 사용자의 요청을 처리하고 적절한 모델 데이터를 생성한 후, 이를 뷰에 전달하는 역할을 합니다. 요청을 받고, 그에 대한 비즈니스 로직을 실행한 후, 결과 데이터를 담아 뷰로 넘겨줍니다. 주로 @Controller 또는 @RestController로 정의되며, URL 매핑을 통해 특정 요청을 처리합니다. ● 예시: 사용자가 특정 페이지를 요청하면 그에 맞는 데이터를 불러와 뷰에 전달하는 작업. |
3 | Model | ● 애플리케이션의 데이터와 비즈니스 로직을 담당, ● 데이터베이스나 다른 외부 리소스에서 가져온 데이터를 표현하며, 사용자의 요청에 따라 데이터를 처리하거나 조작합니다. ● 데이터 객체(POJO), DAO (Data Access Object) 클래스 등이 속할 수 있습니다. |
스프링 MVC 동작 원리
- 클라이언트 요청
- 사용자가 웹 브라우저에서 URL을 입력하거나 버튼을 클릭하면, HTTP 요청이 발생합니다.
- DispatcherServlet (프론트 컨트롤러)
- DispatcherServlet은 모든 요청을 가장 먼저 받는 프론트 컨트롤러 역할을 합니다.
- 클라이언트의 요청을 적절한 컨트롤러로 전달하고, 컨트롤러의 처리 결과를 다시 받아 클라이언트에게 반환하는 흐름을 제어합니다.
- 핸들러 매핑 (Handler Mapping)
- DispatcherServlet은 요청 URL을 분석하여, 해당 요청을 처리할 컨트롤러를 찾습니다.
- HandlerMapping이 이 작업을 담당하며, URL과 컨트롤러 메서드 사이의 매핑을 관리합니다.
- 컨트롤러 (Controller)
- 매핑된 컨트롤러가 요청을 받아 비즈니스 로직을 처리합니다.
- 필요한 데이터를 가져오거나 조작한 후, 모델에 데이터를 담고, 뷰 이름을 반환합니다.
- 모델 (Model)
- 모델은 컨트롤러가 처리한 데이터(예: 데이터베이스에서 조회한 값)를 저장하는 역할을 합니다.
- 이 데이터는 뷰로 전달되어 화면에 출력될 수 있도록 합니다.
- 뷰 리졸버 (View Resolver)
- 컨트롤러가 반환한 뷰 이름을 기반으로, 어떤 화면을 렌더링할지 결정합니다.
- ViewResolver는 뷰 이름을 실제 뷰(JSP, HTML, Thymeleaf 등)로 변환하는 역할을 합니다.
- 뷰 (View)
- ViewResolver가 선택한 뷰가 모델 데이터를 받아 HTML과 같은 사용자 인터페이스로 렌더링합니다.
- 이 뷰는 최종적으로 사용자에게 응답으로 전송됩니다.
- 클라이언트에게 응답
- 최종적으로 DispatcherServlet은 생성된 뷰를 클라이언트에게 응답으로 반환합니다
주요 구성 요소
- DispatcherServlet: 프론트 컨트롤러로 모든 요청을 중앙에서 처리.
- Handler Mapping: URL 요청을 적절한 컨트롤러와 매핑하는 역할.
- Controller: 요청을 받아 비즈니스 로직을 처리하고 결과를 반환.
- Model: 컨트롤러에서 처리된 데이터가 저장되는 공간.
- ViewResolver: 뷰 이름을 물리적인 뷰 파일로 변환하는 역할.
- View: 클라이언트에게 데이터를 보여주기 위한 UI 부분.
스프링 MVC 라이브러리 프로젝트에서 web.xml에 다음과 같은 설정을 해준다.
<servlet>
<servlet-name>dispather</servlet-name>
<servlet-class>org.springframework.web.servlet.DispathcerSerlvet</servlet-class>
</servlet>
//사용자가 요청을 할 수 있게 매핑 설정
//모든 요청이 여기로 들어올 것
<servlet-mapping>
<url-pattern>/</url-pattern>
</servlet-mapping>
여기서 url-mapping하는 부분을 따로 dispather-servlet.xml 파일을 생성하여 여기서 따로 관리를 하겠습니다.
src/main/webapp/WEB-INF 경로에 해당 파일을 생성해줍니다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/shcema/beans/spring-beans.xsd">
//id: controller의 요청 URL, class : controller의 class파일 경로
<bean id="/index" class="패키지명.IndexController" />
</beans>
IndexController도 생성해줍니다.
public class IndexController implements Controll { //Controll 인터페이스 구현
//요청에 대한 핸들러 메서드를 구현
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//dispatcher에게 담아서 줄 ModelAndView 객체 생성
ModelAndView mv = new ModelAndView();
mv.addObject("data", "Hello"); // "data"라는 이름으로 "Hello"라는 문자열데이터 값을 보냄
mv.setViewName("index.jsp"); //view페이지의 이름
return mv;
}
}
사용자의 서버로의 요청은 Controller가 받게 해야한다. 바로 view페이지로의 요청은 차단되어야 한다.
원래는 서비스로직과 view페이지가 하나로 합쳐진 것을 기능과 관심사별로 계층을 분리했지만 일련의 하나의 동작으로 구성되어야 한다.
index.jsp를 직접 요청할 수 없게 보통은 WEB-INF파일에 jsp파일들을 위치시킨다.
WEB-INF아래에 있는 파일들은 사용자가 직접 요청 할 수 없기때문에이다. 서버에서는 요청해서 반환시켜 줄 수 있다.
그래서 Controller에서 view를 반환하는 부분을
mv.setViewName("/WEB-INF/index.jsp"); //view페이지의 이름 , 맨앞에 / 를 추가했으므로 루트경로의 절대경로를 통해 찾는다.
이렇게 수정한다. 이렇게 해서 view는 컨트롤러를 통해서만 요청 할 수 있게 된다.
ViewResolver 사용하기
mv.setViewName("/WEB-INF/index.jsp"); 이런식으로 모든 경로와 파일확장자까지 쓰는건 귀찮고 내용이 길어지기 때문에 생략을 해야합니다.
/WEB-INF 와 .jsp 반복되는 형태이기 때문에 이것들은 따로 설정을 하고 재사용성을 높히게 할 수 있습니다.
dispatcher-servlet.xml에 다음 내용을 추가합니다.
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
이 내용을 추가하면
이제 view를 반환할때 mv.setViewName("index"); 로 변환해서 사용할 수 있게 되었습니다.
데이터 서비스 클래스 준비하기 (Service)
이제는 어떠한 service클래스를 만들어서 (service클래스 소스는 생략) 이것또한 bean으로 관리 할 수 있게 되어야 합니다.
그래야 컨트롤러 계층에서 전역적으로 사용을 할 수 있고 관리가 쉬어집니다. service클래스를 만들었다는 가정하에
dispatcher-servlet.xml 파일에 다음과 같은 내용을 추가합니다.
<bean id="indexService" class="패키지명.IndexService"> 이 내용 또한 빈즈 태그안에 포함하고 그 후 IndexController에 주입을 해야 하기 때문에
<bean id="/index" class="패키지명.IndexController" /> 또한 변경합니다
<bean id="/index" class="패키지명.IndexController" />
<property name=" indexService " ref="indexService" /> //해당 서비스의 빈 id를 ref에 넣어주면된다.
</bean>
그 후 IndexController는 아래와 같이 수정될것입니다. (서비스 계층을 컨트롤러에 주입)
public class IndexController implements Controll { //Controll 인터페이스 구현
private IndexService indexservice;
public void setIndexService(IndexService indexService){ //indexService는 설정에서 서비스클래스의 빈의 아이디
this.indexservice = indexService;
}
//요청에 대한 핸들러 메서드를 구현
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//dispatcher에게 담아서 줄 ModelAndView 객체 생성
ModelAndView mv = new ModelAndView();
mv.addObject("data", "Hello"); // "data"라는 이름으로 "Hello"라는 문자열데이터 값을 보냄
mv.setViewName("index.jsp"); //view페이지의 이름
return mv;
}
}
이렇게 이제 컨트롤러에서 해당 서비스클래스를 이용가능하고 의존성이 주입되었습니다.
서비스 객체 분리하기
이렇게 컨트롤러와 서비스 객체 사이에 추상화된 인터페이스를 사이에 두고 주입이 된다면 추후에 구현체(Service객체)
의 교체가 원할해진다. (Controller에 대한 수정이 필요없다. --> 결합력이 약해진다.)
'Spring' 카테고리의 다른 글
[Spring] IOC , DI (0) | 2025.04.02 |
---|---|
Spring Security (0) | 2025.02.24 |
Component (0) | 2024.10.15 |
스프링 어노테이션을 활용한 DI (3) | 2024.10.12 |
Spring framework 설정 (0) | 2024.10.09 |