코딩하는 문과생

[Spring] Spring 프레임워크(2) - Layer와 MVC패턴 본문

웹 프로그래밍/Spring

[Spring] Spring 프레임워크(2) - Layer와 MVC패턴

코딩하는 문과생 2020. 3. 8. 14:35

[계층화 아키텍처]

효율적인 개발과 유지보수를 위해 계층화하여 개발

각 레이어는 독립된 R&R을 가진다.

  • 프레젠테이션(화면) 영역: 사용자와 상호작용을 담당, 사용자의 요청을 분석/응답

  • 비즈니스 영역: 기능을 수행, 트랜잭션 수행

  • 데이터 영역: 데이터의 저장과 조회를 담당, 주로 데이터베이스와 연동하여 작업

 

[MVC패턴]

프레젠테이션쪽을 세분화한 패턴

  • View - 화면

  • Controller - View와 Model의 바인딩과 제어를 담당, 사용자의 요청을 처리

  • Model - 화면에 뿌려질 데이터(데이터의 저장과 처리)

[컴포넌트 자동등록]

1. 어노테이션을 사용

@Component

  @Controller - 프레젠테이션 영역

  @Service - 비즈니스 영역

  @Repsitory - 데이터 영역

 

2. <context:component-scan base-package="패키지 명" />

어노테이션이 있는 클래스를 스캔한다.

Bean이 될 수 있는 모든 컴포넌트들을 자동으로 찾아 Bean Container에 등록

 

3. @Autowired를 이용해 Component간 의존관계 표현

 

 

ex. component layer

- Article.java

package kr.co.acomp.hello.vo;

public class Article {
	private int articleId;
	private String author;
	private String title;
	private String content;

	public Article() {
		
	}
	
	public Article(int articleId, String author, String title, String content) {
		this.articleId = articleId;
		this.author = author;
		this.title = title;
		this.content = content;
	}

	public int getArticleId() {
		return articleId;
	}

	public void setArticleId(int articleId) {
		this.articleId = articleId;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	@Override
	public String toString() {
		return "Article [articleId=" + articleId + ", author=" + author + ", title=" + title + ", content=" + content
				+ "]";
	}
}

-spring-context.xml 추가

<!-- 해당 패키지 하위에 있는 컴포넌트를 스캔한다. -->
<context:component-scan base-package="kr.co.acomp.hello"/>

- ArticleDAO.java

package kr.co.acomp.hello.dao;

import org.springframework.stereotype.Repository;

import kr.co.acomp.hello.vo.Article;

@Repository
public class ArticleDAO {
	
	public void insertArticle(Article article) {
		System.out.println("insert OK...");
	}
	
}

- BbsService.java

package kr.co.acomp.hello.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import kr.co.acomp.hello.dao.ArticleDAO;
import kr.co.acomp.hello.vo.Article;

@Service
public class BbsService {
    //Auto DI를 위한 어노테이션
	@Autowired
	private ArticleDAO articleDAO;
	
	public void registArticle(Article article) {
		articleDAO.insertArticle(article);
	}
	
}

-HelloMain.java

package kr.co.acomp.hello;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import kr.co.acomp.hello.service.BbsService;
import kr.co.acomp.hello.vo.Article;

public class HelloMain {

	public static void main(String[] args) {
		AbstractApplicationContext ctx = 
				new ClassPathXmlApplicationContext("/spring-context.xml");

		BbsService service = ctx.getBean("bbsService", BbsService.class);
		service.registArticle(new Article());
        //insert OK~
	}
}

[MVC패턴 - 모델2 아키텍처]

비즈니스 로직과 프레젠테이션을 분리하기 위함

  • 컨트롤러: 요청 처리 및 흐름 제어 담당(프론트 컨트롤러- 모든 요청을 받는다, 애플리케이션 컨트롤러 - @Controller)

  • 모델: 비즈니스 로직 및 데이터 처리 담당

  • 뷰: 모델이 처리한 결과 데이터의 화면 생성 담당

서버 템플릿 기술: Thymeleaf

프론트 컨트롤러: 클라이언트가 보낸 요청을 맏아 공통 작업을 먼저 수행, 적절한 세부 컨트롤러에게 작업을 위임, 마지막 뷰를 생성

 

[Spring MVC]

DI, AOP 지원, 웹개발을 위한 MVC프레임워크를 제공

프론트 컨트롤러(DispatcherServlet클래스 - 모든 요청을 받아서 처리, 예외 발생 시 일관된 방식으로 처리) 지원

 

- 구성요소

  • DispatcherServlet

  • HandlerMapping - DispatcherServlet이 어떤 Controller를 호출해야 되는 지 알려준다.

  • Controller - 요청처리,  @Controller를 우리는 개발한다.

  • ModelAndView - 화면과 데이터에 대한 정보를 보유한 객체

  • ViewResolver - 어떤 뷰를 선택할 지 도와준다.

  • View - 화면 정보만 보유한 객체

web.xml에서 DispatcherServlet설정과 filter, encoding작업이 필요하다.

ex. 설정

- pom.xml 추가

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>4.3.9.RELEASE</version>
</dependency>

-web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <!-- 모든 요청에 대해 DispatcherSevlet을 통과해라 -->
  <servlet>
  	<servlet-name>dispatcherServlet</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<init-param>
  		<param-name>contextConfigLocation</param-name>
  		<param-value>/WEB-INF/spring/servlet-context.xml</param-value>
  	</init-param>
  </servlet>
  <servlet-mapping>
  	<servlet-name>dispatcherServlet</servlet-name>
  	<url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

-servlet-context.xml 생성

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

	<context:component-scan base-package="kr.co.acomp.hello"></context:component-scan>

	<mvc:annotation-driven/>
	
	<bean id="viewResolver" 
	class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
</beans>

[Controller]

  • 사용자 요청(url) 기준으로 컨트롤러의 특정 메소드를 찾는다.

  • 요청 파라미터가 있으면 처리하고

  • 비즈니스 처리를 위해서 서비스(Service) 컴포넌트를 주입받는다.

  • 실행된 결과를 전달받아 DispatcherServlet에게 반환

 

- 주요 어노테이션

@Controller

@RequestMapping: 클래스 레벨과 메소드 레벨에서 정의하는 방법이 있다.

@Autowired

 

- ModelAndView에는 2가지가 저장된다.

1. ViewName 2. 담은 데이터(key:value, 이 값은 jsp파일에서 ${...}형식으로 받아올 수 있다.)

 

- 리다이렉트와 포워드, /로 시작하면 절대경로, 없다면 현재 경로 기준

  • return "redirect:/bbs/list"

  • return "forward:/bbs/list"

 

 

ex. controller 

- BbsController.java

package kr.co.acomp.hello.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import kr.co.acomp.hello.service.BbsService;
import kr.co.acomp.hello.vo.Article;

@Controller
@RequestMapping("/bbs")
public class BbsController {
	
	@Autowired
	private BbsService bbsService;
	
	@RequestMapping("/write")
	public String write() {
		bbsService.registArticle(new Article());
		
		return "write_ok";
	}
}

-write_ok.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
	<h1>write ok...</h1>
</body>
</html>

- BbsController.java 수정

@Controller
@RequestMapping("/bbs")
public class BbsController {
	
	@Autowired
	private BbsService bbsService;
	
	@RequestMapping("/write")
	public String write(@RequestParam("author") String author) {
		bbsService.registArticle(new Article());
		System.out.println(author);
		return "write_ok";
	}
}

파라미터를 전달하면
요청url과 맵핑된 메소드에서 파라미터로 받는다.


[Http 파라미터 처리]

-@RequestParam

required=false는 요청 값이 없을 때 에러를 보내지 않고 null을 리턴

required=false, defaultValue=""는 null인경우 값을 지정할 수 있다.

 

-파라미터가 많아지는 경우

: Command객체를 이용해 폼을 전송한다.

ex. public ModelAndView test9(Member member) {...

  • Command객체는 자동으로 View의 Model로 바로 등록

  • 타입 자동 변환 기능

  • name이 동일한 input값들을 List로 처리가능

 

 

ex. get방식과 post방식

package kr.co.acomp.hello.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import kr.co.acomp.hello.service.BbsService;
import kr.co.acomp.hello.vo.Article;

@Controller
@RequestMapping("/bbs")
public class BbsController {
	
	@Autowired
	private BbsService bbsService;
	
	@RequestMapping(value="/write", method=RequestMethod.POST)
    	//@PostMapping("/write")
	public String doWrite() {
		bbsService.registArticle(new Article());
		System.out.println("post request...");
		return "write_ok";
	}
	
	@RequestMapping("/write")
   	//@GetMapping("/write")
	public String write() {
		bbsService.registArticle(new Article());
		System.out.println("get request...");
		return "write_ok";
	}
}

 

ex.@Pathvariable

- BbsController.java 추가

	@GetMapping("/{articleId}")
	public String viewDetail(@PathVariable String articleId) {
		System.out.println("글 번호는: " + articleId);
		return "write_ok";
	}

 

ex. command객체

- BbsController.java 수정

	@PostMapping("/write")
	//스프링은 파라미터 내 객체를 Command객체로 판단한다.
	public String doWrite(Article article) {
		bbsService.registArticle(article);
		System.out.println("post request...");
		return "write_ok";
	}

-ArticleDAO 수정

	public void insertArticle(Article article) {
		System.out.println(article);
	}
	

결과

 

ex. ModelAndView로 jsp에 데이터 전달

-BbsController.java수정

	@PostMapping("/write")
	//스프링은 파라미터 내 객체를 Command객체로 판단한다.
	public ModelAndView doWrite(Article article) {
		bbsService.registArticle(article);
		System.out.println("post request...");
		return new ModelAndView("write_ok").addObject("article", article);
	}

-write_ok.jsp

<body>
	<h1>write ok...</h1>
	<ul>
		<li>${article.articleId }</li>
		<li>${article.author }</li>
		<li>${article.title }</li>
		<li>${article.content }</li>
	</ul>
</body>

결과 OK