스프링 MVC
1. Spring MVC
Spring MVC 란
- Spring 프레임워크에서 제공하는 웹 모듈이다.
- MVC 는 Model-View-Controller 의 약자로, 기본 시스템 모듈을 MVC 로 나누어 구현되어있다.
- Model 은 '데이터' 디자인을 담당한다.
- ex. 상품 목록, 주문 내역 등
- View 는 '실제로 렌더링되어 보이는 페이지' 를 담당한다.
- ex. .JSP 파일들이 여기에 해당된다.
- Controller 는 사용자의 요청을 받고, 응답을 주는 로직을 담당한다.
- ex. GET 등의 uri 매핑이 여기에 해당된다.
- Model 은 '데이터' 디자인을 담당한다.
- Spring MVC 모듈을 사용하여, 백엔드 프로그래밍의 기본 프레임워크를 잡는다.
- Web 서버에 특화되어 만들어진 모듈이라, 개발자가 해야할 영역을 더 적게 만들어준다.
- 즉 기존에 Spring 보다 더 깔끔하고 간편하게 개발 가능.
- 별다른 말이 없으면 MVC Model 2 아키텍처를 사용하는 것으로 생각한다.
출처 : 부스트코스( https://www.edwith.org/boostcourse-web/lecture/16762/ )
기본 동작 흐름
출처 : 부스트코스(https://www.edwith.org/boostcourse-web/lecture/16762/)
전체 흐름은 다음과 같다.
요청 -> 프론트 컨트롤러 -> 핸들러 매핑 -> 핸들러 어댑터 -> 컨트롤러 -> 로직 수행(서비스) -> 컨트롤러 -> 뷰 리졸버 -> 응답(jsp, html)
- 컨트롤러 중에서도, 맨 앞단에서 유저의 유청을 받는 컨트롤러를 프론트 컨트롤러라고 한다.
- DispatcherServlet 객체가 이 역할을 한다.
- 본격적으로 로직에 들어오기 전에, 요청에 대한 선처리 작업을 수행한다.
- ex. 지역 정보 결정, 멀티파트 파일 업로드 처리 등
- 프론트 컨트롤러는 요청을 핸들러 매핑을 통해 해당 요청을 어떤 핸들러가 처리해야하는지를 매핑한다.
- HandlerMapping 객체가 핸들러 매핑에 대한 정보를 담고있다.
- 이렇게 매핑된 핸들러를 실제로 실행하는 역할은 핸들러 어댑터가 담당한다.
- HandlerAdapter 객체가 이 역할을 한다.
- 컨트롤러는 해당 요청을 처리하는 로직을 담고있다.
- 보통 요청의 종류 혹은 로직의 분류에 따라 내부적으로 Service 단위로 나누어 모듈화 한다.
- 각 서비스에서는 DB 접근할 수 있는 Repository 객체를 이용하여 데이터에 접근할 수 있다.
- 컨트롤러는 서비스에서의 로직 처리 후, 결과를 뷰 리졸버를 거쳐 뷰 파일을 렌더링하여 내보낸다.
- ViewResolver 객체가 이 역할을 한다.
2. Spring MVC 구현
1) DispatcherServlet 을 프론트 컨트롤러로 세팅
<!-- web.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<display-name>Spring JavaConfig Sample</display-name>
<servlet>
<!-- 2. 해당 서블릿의 구현체는 DispatcherServlet 로 정의-->
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 3. contextClass는 AnnotationConfigWebApplicationContext 를 사용-->
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<!-- 4. context 에 대해 따로 설정해둔 클래스의 위치를 파라미터로 줌. 여기서는 사용자가 정의한 WebMvcContextConfiguration 을 사용-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>kr.or.connect.mvcexam.config.WebMvcContextConfiguration</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!-- 1. / 로 들어오는 요청은 mvc 라는 이름의 servlet 이 처리-->
<servlet-name>mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
// WebMvcContextConfiguration.java package org.example.guestbook.config; ...
// 설정 파일임을 Spring이 알게함.
@Configuration
// Web에 필요한 빈들을 대부분 자동으로 설정. 주로 아래처럼 커스텀으로 설정해야할 때, WebMvcConfigurerAdapter 를 상속받아 클래스로 구현
@EnableWebMvc
// 해당 패키지에 정의된 클래스중 컴포넌트들을 빈으로 등록해놓음.
// @Controller, @Service, @Repository, @Component 가 달린 객체를 찾음.
@ComponentScan(basePackages = { "kr.or.connect.mvcexam.controller" })
public class WebMvcContextConfiguration extends WebMvcConfigurerAdapter {
// 자바 파일이 아닌, Resource 파일들에 대한 url 요청이 왔을 경우, 해당 경로에서 찾을 수 있게 설정
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/META-INF/resources/webjars/").setCachePeriod(31556926);
registry.addResourceHandler("/css/**").addResourceLocations("/css/").setCachePeriod(31556926);
registry.addResourceHandler("/img/**").addResourceLocations("/img/").setCachePeriod(31556926);
registry.addResourceHandler("/js/**").addResourceLocations("/js/").setCachePeriod(31556926);
}
// default servlet handler를 사용하게 함
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
// / 로 접근시 main 템플릿(jsp) 로 가게함.
@Override
public void addViewControllers(final ViewControllerRegistry registry) {
System.out.println("addViewControllers가 호출됩니다. ");
registry.addViewController("/").setViewName("main");
}
// 렌더링되는 view 파일들의 경로와 확장자명 설정
@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp");
return resolver;
}
}
2) 이 외 전반적인 모듈 구성
config/*
- 각종 설정 클래스 파일들을 담고 있음.
- 클래스 앞에 @Configuration 이 붙음.
- WebMvc 설정 관련 Config 를 제외하고 나머지는 모듈화한 뒤 Application 에서 모두 import
- @Controller 관련 빈들은 WebMvcContextConfiguration 에서 @ComponentScan 으로 찾아줘야하고,
@Service, @Service, @Component 관련 빈들은 ApplicationConfig 에서 찾아줘야 함. - 예를 들어 ApplicationConfig 는 다음과 같음.
@Configuration @ComponentScan(basePackages = {"org.example.guestbook.dao", "org.example.guestbook.service"}) @Import({DBConfig.class}) public class ApplicationConfig { }controller/*
- 각종 컨트롤러 클래스 파일들을 담고 있음.
- 클래스 앞에 @Controller 가 붙음.
- 각 컨트롤러 코드는 URI 매핑을 담당.
- Serivce 인스턴스를 가져와 로직을 실행하고, View 단에 나가기 전후 작업을 담당
- 예를 들어 GuestbookController 는 다음과 같음.
@Controller public class GuestbookController {
// @Autowired 를 통해 스프링이 관리하는 빈을 가져올 수 있음.
@Autowired GuestbookService guestbookService; @GetMapping(path="/list")
public String list(@RequestParam(name="start", required = false, defaultValue = "0") int start, ModelMap model) {
// start로 시작하는 방명록 목록 구하기
List<Guestbook> list = guestbookService.getGuestbooks(start);
...
model.addAttribute("list", list);
// list.jsp 로 렌더링
return "list";
}
@PostMapping(path="/write")
public String write(@ModelAttribute Guestbook guestbook, HttpServletRequest request) {
String clientIp = request.getRemoteAddr();
guestbookService.addGuestbook(guestbook, clientIp);
return "redirect:list";
}
}
- DB 에 대해 접근할 때 사용하는 클래스 파일들을 담고 있음.
- 클래스 앞에 @Repository 가 붙음.
- 실제 dao 클래스와, 사용할 SQL 만을 담고있는 클래스가 따로 모듈화해서 사용함.
- 예를 들어 GuestbookDao 와 GuestbookDaoSqls 는 다음과 같이 생음.
// GuestbookDao.java
import static org.example.guestbook.dao.GuestbookDaoSqls.*;
@Repository
public class GuestbookDao {
private NamedParameterJdbcTemplate jdbc;
private SimpleJdbcInsert insertAction;
private RowMapper<Guestbook> rowMapper = BeanPropertyRowMapper.newInstance(Guestbook.class);
public GuestbookDao(DataSource dataSource) {
this.jdbc = new NamedParameterJdbcTemplate(dataSource);
this.insertAction = new SimpleJdbcInsert(dataSource) .withTableName("guestbook") .usingGeneratedKeyColumns("id");
}
public List<Guestbook> selectAll(Integer start, Integer limit) { ... } public Long insert(Guestbook guestbook) {
...
}
public int deleteById(Long id) {
...
}
...
}
// GuestbookDaoSqls.java
public class GuestbookDaoSqls {
public static final String SELECT_PAGING = "SELECT id, name, content, regdate FROM guestbook ORDER BY id DESC limit :start, :limit";
public static final String DELETE_BY_ID = "DELETE FROM guestbook WHERE id = :id";
public static final String SELECT_COUNT = "SELECT count(*) FROM guestbook";
}
- 데이터를 모델링한 클래스 파일들을 담고있음.
- 필드(프로퍼티)와 Getter, Setter 를 가짐.
- 예를 들어 Guestbook 는 다음과 같이 같음.
public class Guestbook { private Long id; private String name; private String content; private Date regdate; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } ... }service/*
public class Guestbook {
private Long id;
private String name;
private String content;
private Date regdate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
...
}
- 서비스 로직을 담는 클래스 파일들을 담고있음.
- 클래스 앞에 @Service 가 붙음.
- Interface로 핵심 로직 먼저 정의한 후, 클래스로 구현.
- 필요한 경우, Dao 를 직접 사용하는 클래스임.
- 예를 들어 GuestbookServiceImpl 는 다음과 같음
@Service
public class GuestbookServiceImpl implements GuestbookService {
@Autowired
GuestbookDao guestbookDao;
@Autowired
LogDao logDao;
@Override
@Transactional
// 기본 값 read only transaction 적용
public List<Guestbook> getGuestbooks(Integer start) {
return guestbookDao.selectAll(start, GuestbookService.LIMIT);
}
@Override
@Transactional(readOnly = false)
public int deleteGuestbook(Long id, String ip) {
int deleteCount = guestbookDao.deleteById(id);
Log log = new Log();
log.setIp(ip);
log.setMethod("delete");
log.setRegdate(new Date());
logDao.insert(log);
return deleteCount;
}
...
}
- 렌더링 되는 뷰 관련 파일(.jsp)들을 담고있음.
- 이 파일들은 뷰 리졸버를 거쳐, 최종적으로 렌더링 되기 직전의 파일임.
3. 레이어드(Layered) 아키텍처
여러 모듈들을 일종의 레이어 단위로 나누는 설계 방식을 말한다.
크게 다음 레이어들로 구성한다.
- Presentation Layer
- 화면 조작 또는 사용자 입력을 처리하는 레이어
- Service Layer (Domain)
- 비즈니스와 관련된 도메인 로직을 처리하는 레이어
- 하나의 비즈니스 로직은 하나의 트랜잭션 단위로 동작.
- Repository Layer (Data source)
- 도메인에서 필요로 하는 데이터를 조작하기 위한 레이어
출처 : https://anchormen.nl/blog/big-data-services/spring-boot-tutorial/
'기타 > 설치 및 설정' 카테고리의 다른 글
[Spring] 스프링 개발환경 설정 (0) | 2021.04.25 |
---|---|
Spring 프로젝트 초기 설정하기 (0) | 2021.04.25 |
JAVA 1.8 다운로드 및 설치 (0) | 2021.04.24 |
Python / Visual Studio Code 개발 환경 만들기 (0) | 2021.04.20 |
[유틸리티] Notepad++ 플러그인 소개 (0) | 2021.04.20 |