본문 바로가기

기타/설치 및 설정

스프링 MVC

728x90
반응형

스프링 MVC

1. Spring MVC

Spring MVC 란

  • Spring 프레임워크에서 제공하는 웹 모듈이다.
  • MVC 는 Model-View-Controller 의 약자로, 기본 시스템 모듈을 MVC 로 나누어 구현되어있다.
    • Model 은 '데이터' 디자인을 담당한다.
      • ex. 상품 목록, 주문 내역 등
    • View 는 '실제로 렌더링되어 보이는 페이지' 를 담당한다.
      • ex. .JSP 파일들이 여기에 해당된다.
    • Controller 는 사용자의 요청을 받고, 응답을 주는 로직을 담당한다.
      • ex. GET 등의 uri 매핑이 여기에 해당된다.
  • 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/

출처 : dailyheumsi.tistory.com/159#recentEntries

728x90
반응형