티스토리 뷰
05. Spring MVC와 모델2방식
이전 글 : http://marshmello.tistory.com/6
저번글에서는 Mybatis에대해서 자세히 알아보고 테스트 코드를 작성해보았습니다.
스프링 MVC는 모델 2 방식 구조를 이용하기 때문에 이론적으로 모델2방식에 대한 이해가 반드시 필요합니다
이번글에서는 모델 2 방식을 사용하는 스프링의 기본 구조를 이해한 후 바로 가장 쉬운 형태의 게시물 관리를 제작하면서 직접 사용해 보도록 하겠습니다.
이글에서는 스프링 MVC의 기본 예제이므로 반드시 연습해보시길 권장합니다.
5.1 모델 2 패턴의 이해
최근 모든 웹 개발은 거의 Model2 방식을 사용한다고 해도 과언은 아닙니다.
모델 2 방식은 흔히 MVC 구조를 응용한 방식이라고 하는데, 가장 핵심적인 내용은 '화면과 데이터 처리를 분리해서 재사용이 가능하도록 하는 구조' 라고 할 수 있습니다.
controller는 모델 계층과 연동해서 필요한 데이터를 처리하고 결과를 뷰로 전송하게 됩니다. 모델 2 구조를 그림으로 표현하면 다음과 같은 형태가 됩니다.
- 모델(Model) : 데이터 혹은 데이터를 처리하는 영역을 의미합니다.
- 뷰(View) : 결과 화면을 만들어 내는 데 사용하는 자원을 의미합니다.
- 컨트롤러(Controller) : 웹의 요청 (request)을 처리하는 존재로 뷰와 모델 사이에서 중간 통신 역할을 합니다.
모델 2에서 모든 요청은 기본적으로 컨트롤러를 호출합니다. 각 컨트롤러는 자신을 호출하는 특정한 URL 경로를 가지고 있습니다. 과거에는 주로 호출 시에 마지막 확장자를 '*.do' 등을 이용하는 방식을 많이 사용했습니다.
모델2 방식 특징
- 개발자와 웹 퍼블리셔의 영역을 분리 할수 있습니다.
- 컨트롤러의 URL를 통해서 뷰를 제어하기 때문에 뷰의 교체나 변경과 같은 유지보수에 유용하게 사용될 수 있습니다.
5.2 모델 2 에서 Front Controller 패턴으로
모텔2방식이 개발자와 웹 퍼블리셔 간의 분업을 이루는데는 성공했지만, 각 컨트롤러 사이의 중복적인 코드의 문제와 개발자의 개발 패턴의 차이 등의 문제로 인해 모델 2 방식은 좀더 강제적인 형태의 Font Controller 방식을 적용하게 됩니다.
아래 그림은 스프링 MVC가 사용하는 구조 입니다.
Front Controller 패턴의 가장 주요한 변화는 전체 로직의 일부만을 컨트롤러가 처리하도록 변경되었다는 것 입니다. 흔히 '위임(Delegation)'이라고 하는데 전체 로직의 일부를 컨트롤러에게 위임하고 모든 흐름의 제어는 앞쪽의 Front Controller가 담당하게 됩니다.
이와 같은 구조를 사용하게 될 경우 개발자가 작성하는 컨트롤러는 전체 로직의 일부분만을 처리하는 형태가 되기 때문에 개발자가 작성해야 하는 전체코드는 줄어들게 됩니다. 또한 모든 컨트롤러는 Front Controller의 일부분을 구현하는 형태이므로 좀더 규격화된 코드를 작성하게 됩니다.
5.3 스프링 MVC의 구조
① 사용자의 모든 요청은 스프링 MVC의 Front Controller에게 전달됩니다.
② 전달된 요청은 적절한 컨트롤러를 찾아서 호출하게 되는데 , 이 때 사용되는 컨트롤러의 작업이 개발자의 몫으 로 남겨진 일입니다. 컨트롤러는 적절한 서비스 객체를 찾아서 호출하고
③ 서비스는 데이터베이스의 작업을 담당하는 DAO(Data Access Object)를 이용해서 원하는 데이터를 요청하게 됩니다.
④ DAO 객체는 MyBatis를 이용하는 Mapper를 통해서 원하는 작업을 수행하게 됩니다.
⑤ 서비스가 처리한 데이터를 컨트롤러에게 전달하게 되면
⑥ 컨트롤러는 다시 스프링 MVC쪽으로 데이터를 전달하게 됩니다.
스프링 MVC가 처리해 주는 작업
- URL를 분석해서 적절한 컨트롤러를 찾는 작업
- 컨트롤러에 필요한 메소드를 호출하는 작업
- 컨트롤러의 결과 데이터를 뷰로 전달하는 작업
- 적절한 뷰를 찾는 작업
개발자가 직접해야 하는 작업
- 특정 URL에 동작하는 컨트롤러를 설계하는 작업
- 서비스 객체의 생성
- DAO 객체의 생성
- 컨트롤러 내에 원하는 결과를 메소르로 설계
- 뷰에서 전달받은 데이터의 출력
5.4 스프링 MVC의 컨트롤러
스프링 MVC를 공부하는데 있어서 필수적인 내용은 컨트롤러를 어떻게 만들고 처리하는가 입니다.
우선 가장 중요한 질문인 스프링 MVC의 컨트롤러가 무엇을 처리해주는가? 에 대해서 다음과 같이 정리할 수 있습니다.
- 파라미터의 수집
웹에서 가장 많이 사용하는 작업은 사용자의 요청(request)에 필요한 데이터를 추출하고 이를 VO(Value Object) 혹은 DTO(Data Transfer Object)로 변환하는 파라미터의 수집 작업입니다. 스프링 MVC의 컨트롤러는 이러한 처리를 자동으로 해주기 때문에 개발 시간을 크게 단축할 수 있습니다.
- 애노테이션을 통한 간편 설정
스프링 MVC의 설정은 크게 XML과 애노테이션을 사용할 수 있지만 애노테이션을 사용하는 경우가 더 많습니다.
애노테이션을 사용하기 때문에 클래스나 메소드의 선언에 필요한 애노테이션을 추가하는 작업을 통해서 요청 (request)이나 응답(response)에 필요한 모든 처리를 완료할 수 있습니다.
- 로직의 집중
기존의 모델2는 특정한 URL마다 컨트롤러를 개발하는 경우가 많았지만 스프링 MVC 컨트롤러의 경우 각 메소드마다 필요한 애노테이션을 설정할 수 있기 때문에 메소드를 하나의 컨트롤러에 집중해서 작성할 수 있습니다.
- 테스트의 편리함
스프링은 테스트 모듈을 사용해서 스프링 MVC로 작성된 코드를 WAS의 실행없이도 테스트 할 수 있는 편리한 방법을 제공합니다.
스프링 MVC 컨트롤러는 기존 Java 코드와 다른점은 다음과같습니다.
1. 스프링 MVC 컨트롤러는 상속이나 인터페이스를 구현하지 않아도 됩니다. 기존의 프레임워크들과 달리 스프링 MVC에서 컨트롤러 작성 시 아무런 제약이 없습니다. 그대신 해야 하는작업은 @Controller 라는 애노테이션에 대한 추가 작업입니다.
2. 메소드의 파라미터와 리턴 타입에 대한 제약이 없습니다. 클래스가 특정 부모 클래스나 인터페이스가 없늬 메소드에 대한 제약도 존재하지 않습니다. 파라미터 타입과 리턴 타입에 대한 제약이 없는 관계로 기존보다 훨씬 자유로운 코트를 만들 수 있습니다.
3. 스프링 MVC가 제공하는 유용한 클래스들이 존재합니다. 스프링 MVC의 경우 다양한 클래스를 이용해서 필요한 작업을 수월하게 진행 할 수 있습니다. 예를 들어 파일 업로드 처리나 유효성 검사등을 제공하고 있기 때문에 개발자는 피룡한 클래스를 이용해서 빠른 시간 내에 개발 할 수 있습니다.
5.4.1 servlet-context.xml
컨트롤러의 개발과 관련해서 가장 먼저 알아야할 내용은 스프링 MVC 컨트롤러가 어떤 설정을 통해서 동작하는지에대한 이해입니다.
MyBatis와의 연동을 위해서 작성해 본 프로젝트의 servlet-context.xml을 보면 스프링 MVC 컨트롤러에 실행에 있어서 가장 중요한 정보 몇가지를 볼수 있습니다.
appServlet 폴더내에 존재하는 servlet-context.xml은 스프링 MVC 관련 설정만을 분리하기 위해서 만들어진 파일입니다.
<annotation-driven>의 설정은 클래스 선언에 애노테이션을 이용해서 컨트롤러에 작성할 수 있다는 선언입니다.
InternalResourceViewResolver 부분은 뷰를 어떻게 처리하는가에 대한 설정입니다. 이때 주목해야하는 경로가 'WEB-INF/views'라는 경로입니다. 'WEB-INF'는 절대로 브라우저에서 접근 할 수 없는경로이기 때문에 컨트롤러의 호출이 우선되는 모델 2 방식에 맞는 구조가 됩니다.
<resources>는 웹에서 이미지나 CSS, javaScript 파일과 같이 고정된 자원들의 위치를 의미합니다.
<component-scan> 의미도 상당히 중요한 의미를 가지는데 component-scan은 base-package 속성 값에 해당하는 패키지 내부의 클래스들을 조사한다는 뜻입니다.
이는 <annotation-driven>과 같이 결합해서 해당 패키지에 애노테이션 처리가 컨트롤러를 작성만 해주면 자동으로 인식되게합니다.
5.4.2 스프링 MVC에서 많이 사용하는 애노테이션의 종류
스프링 MVC에서는 컨트롤러가 상속 등의 전통적인 기법을 사용하지 않는 대신에 애노테이션으로 많은 일을 처리합니다. 주로 많이 사용하는 애노테이션은 다음과 같습니다.
annotation |
설명 |
사용 |
@Controller |
Spring MVC의 Controller 객체임을 명시하는 애노테이션 |
클래스 |
@RequestMapping |
특정 URL에 매칭되는 클래스나 메소드임을 명시하는 애노테이션 |
클래스, 메소드 |
@RequestParam |
요청(request)에서 특정한 파라미터의 값을 찾아낼때 사용하는 애노테이션 |
파라미터 |
@RequestHeader |
요청(request)에서 특정 HTTP 헤더 정보를 추출 할 때 사용 |
파라미터 |
@PathVariable |
현재 URL에서 원흐는 정보를 추출할 때 사용하는 애노테이션 |
파라미터 |
@CookieValue |
현재 사용자의 쿠키가 존재하는 경우 쿠키의 이름을 이용해서 쿠키의 값을 추출 |
파라미터 |
@ModelAttribute |
자동으로 해당 객체를 뷰까지 전달하도록 만드는 애노테이션 |
메소드, 파라미터 |
@SesseionAttribute |
세션상에서 모델의 정보를 유지하고 싶은 경우에 사용 |
클래스 |
@InitBinder |
파라미터를 수집해서 객체로 만들 경우에 커스터마이징 |
메소드 |
@ResponseBody |
리턴 타입이 HTTP의 응답 메시지로 전송 |
메소드, 리턴타입 |
@RequestBody |
요청(request) 문자열이 그대로 파라미터로 전달 |
파라미터 |
@Repository |
DAO 객체 |
클래스 |
@Service |
서비스 객체 |
클래스 |
.
.
.
.
외워야 할게 너~~무 많아보이죠?
지금은 읽어보는 정도로 눈에 익힌 후 넘어가고,
코딩을 하며 여러번 애노테이션을 사용하다보면 자동으로 외워집니다.
Sample Code를 직접 타이핑 해봅시다.
리턴타입이 void 인 경우
src/main/java 내에 SampleController.class를 만듭니다.
코드를 아래와 같이 작성합니다. (직접 타이핑해보세요!)
클래스 선언에 사용된 @Controller 애노테이션 설정은 가장 중요한 클래스를 컨트롤러로 설정하게 하는 애노테이션입니다.
각 메소드에는 @RequestMapping을 이용해서 특정한 URL 경로에 해당하는 메소드가 실행됩니다.
Run on Server를 사용해서 Tomcat 서버를 이용하여 프로젝트를 실행하면 다음과 같이 확인할 수 있습니다.
Tomcat v.8.x Server at localhost 선택 후 Finish
첫실행 화면
URL 끝에 doA 입력
doB 입력
현재 메소드의 리턴 타입이 void 인 경우 스프링 MVC는 현재 경로에 해당하는 jsp파일을 실행하게 됩니다.
리턴타입이 String인 경우
컨트롤러에서 메소드의 리턴 타입이 문자열인 경우라면 결과는 문자열 .jsp 파일을 찾아 실행하게 됩니다.
SampleController.class에 아래 메소드를 추가합니다.
실행하게 되면 아래와같이 로그가 출력됩니다.
메소드에 선언된 @RequestMapping은 URL이 '/doC'인 경우에 동작함을 의미합니다.
doC() 메소드 내의 파라미터에 사용된 @ModelAttribute("msg")는 요청(request) 시 'msg' 이름의 파라미터를 문자열로 처리해 주고 뷰에 전달되도록 합니다. 이 작업이 자동으로 이루어지기 때문에 개발자는 별도의 코드를 작성할 필요가 없습니다.
doC() 메소드의 리턴 값으로 사용된 'result'는 결과적으로 'WEB-INF/views/result.jsp' 파일을 찾아 실행됩니다.
'WEB-INF/views/' 아래에 result.jsp 파일을 생성하여 아래와 같이 작성합니다.
코드를 보면 JSP의 EL 을 사용해서 ${msg}를 출력하는 부분이 보입니다. Spring MVC의 @ModelAttribute는 자동으로 해당 객체를 뷰까지 전달합니다.
브라우저에서 실항할때 아래와 같이 'msg' 파라미터를 같이 전달해주면 결과 화면에 출력되는 것을 볼 수 있습니다.
URL : http://localhost:8080/example/doC?msg=marshmello
컨트롤러를 제작하면서 가장 많이 하는 작업은 다른 객체의 도움을 받아 만들어진 데이터를 뷰로 전달하는 일을 하는 것입니다. 이떄는 스프링 MVC의 Model 객체를 사용해서 간편하게 처리할 수 있습니다.
만들어진 결과 데이터를 전달해야하는 경우
아래와 같이 src/main/java 내에 간단히 패키지와 클래스를 작성해 봅시다.
domain.프로젝트명.VO 패키지를 만든 후 ProductVO 클래스를 만듭니다.
SampleController 클래스에 doD() 메소드를 아래와같이 작성합니다.
doD() 메소드의 선언에 Model이라는 클래스를 파라미터로 사용하는데 Model 클래스는 import 구문에서 보듯이
스프링 MVC에서 기본적으로 제공되는 클래스입니다. 이클래스의 용도는 뷰에 원하는 데이터를 전달하는 일종의 컨테이너나 상자의 역할을 한다고 생각하면 됩니다.
doD() 메소드의 내부에서 ProductVO 클래스의 객체를 하나 생성하는 것이 보입니다. 이 객체를 Model이라는 객체를 이용해서 필요한 데이터를 담은 후 뷰로 전달하게 됩니다.
ProductVO 클래스의 객체 생성 이후에 addAttribute()라는 메소드를 이용해서 ProductVO 객체를 보관시키는 것을 볼 수 있습니다. addAttribute는 크게 두가지의 형태로 사용됩니다.
- addAttribute("이름", 객체)
객체에 특별한 이름을 부여해 뷰에서 이름값을 이용해서 객체 처리
- addAttribute(객체)
이름을 지정하지 않는 경우에는 자동으로 저장되는 객체의 클래스명 앞글자를 소문자로 처리한 클래스명을 이름으로 간주
위 코드 경우 ProductVO를 이름 없이 저장했기 때문에 뷰에서는 productVO가 됩니다.
뷰(productDetail.jsp)의 코드는 다음과같습니다.
실행 후 화면
URL : http://localhost:8080/example/doD
리다이렉트를 해야하는 경우
가끔 특정한 컨트롤러의 로직을 처리할 때 다른 경로를 호출해야 하는 경우가 있습니다.
이경우에는 스프링 MVC의 특별한 문자열인 'redirect:'를 이용하는데 ':'을 사용하는 것을 주의할 필요가 있습니다.
리다이렉트를 하는 경우 RedirectAttributes라는 클래스를 파라미터로 같이 사용하게 되면 리다이렉트 시점에 원하는 데이터를 임시로 추가해서 넘기는 작업이 가능하게 됩니다.
SampleController 클래스에 아래의 메소드를 추가합니다.
doE() 메소드의 URL는 '/doE'로 외부에서 호출하도록 되어있습니다. 메소드의 파라미터 선언에 RedirectAttributes를 파라미터로 사용하는데 주목할 필요가있습니다. doE() 메소드의 결과로 리다이렉트 시점에 문자열 정보를 하나더 전달하고 싶었기 때문에 'msg'라는 이름의 데이터를 추가했습니다. 이때 addFlashAttribute()는 임시데이터를 전달합니다.
마지막 반환 리턴값은 'redirect:...'로 시작하는 문자열을 반환하기 때문에 스프링 MVC는 브라우저에 다시 '/doF'를 호출하게 됩니다.
RedirectAttributes를 이용하는 경우에 URL에 보이지 않게 데이터를 전달합니다.
스프링 MVC는 'redirect:..'외에도 'forward:..'역시 같이 지원합니다.
JSON 데이터를 생성하는 경우
스프링 MVC의 장점 중 하나는 최근 프로그래밍에서 많이 사용하는 JSON(JavasScript Object Notation)데이터에 대한 처리를 너무나 간단하게 처리할 수 있다는 것입니다.
이를 위해서 pom.xml을 사용해서 jackson-databind 라이브러리를 추가해야합니다.
Maven URL : https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind/2.5.4
스프링 MVC의 컨트롤러에서 JSON데이터를 생성하기 위해서 사용자는 적절한 객체를 반환해주고 @ResponseBody 애노테이션을 추가해주는 작업만 하면 됩니다.
예제코드를 다음과 같이 작성합니다.
메소드의 선언에 사용된 리턴 타입을 보면 특이하게도 @ResponseBody 애노테이션이 사용된 것이 보입니다. 리턴 타입 역시 일반 객체임을 알 수 있습니다. 메소드의 실행 결과로 일반 객체를 리턴하고 있는데 이것만으로 JSON 처리가 끝나게 됩니다.
브라우저에서 '/doJSON'을 호출해보면 다음과 같은 결과를 확인 할 수 있습니다.
실행은 반드시 STS 내에 내장된 브라우저가 아닌 일반 브라우저로 확인해야 합니다. STS 내장 브라우저의 경우 JSON 데이터를 처리할 수 없기 떄문에 자동으로 .json 파일이 다운로드 됩니다.
스프링 MVC가 특별하게 느껴지는 이유 중 하나는 spring=test 모듈을 통해서 별도의 WAS 구동 없이도 컨트롤러를 테스트 하는 작업이 가능하기 때문입니다.
스프링 3.2버전 부터는 jUnit만을 사용해서 스프링 MVC에서 작성된 컨트롤러를 테스트할수 있는데, WAS 에서 테스트 해보는것이 어려운 경우 아주 유용하게 사용할 수 있습니다.
최근의 Spring Boot는 별도의 WAS 없이도 내장된 서버를 이용해서 프로그램을 실행할 수 있는 환경을 제공하고 , 테스트 역시 간단히 제작할수 있습니다.
spring-test를 사용해서 실행할 때 가능하면 WAS의 Servlet 스펙 버전을 일치 시켜 테스트 하는 것이 좋습니다. 예를 들어 Tomcat을 이용하면 Servlet 스펙 3.1을 이용한다면 테스트 환경도 같이 맞추는 것이 좋습니다.
스프링 MVC를 테스트하기 위해서는 pom.xml의 javax.servlet 라이브러리의 버전을 변경해야만 올바르게 실행 가능합니다.
컨트롤러의 테스트 코드 작성은 test 폴더를 이용해서 작업해야 합니다. WAS를 실행하지 않고 컨트롤러를 테스트하라면 pom.xml의 servlet 버전을 높여줘야만 합니다.
Maven URL : https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api/3.1.0
수정 전
수정 후 (<scope>태그 삭제 해야 합니다.)
테스트 코드의 작성 시 스프링에서 제공하는 애노테이션을 사용할 필요가 있습니다. 테스트 경로는 'src/test/java' 밑에 SampleControllerTest로 클래스를 생성합니다.
아래와 같이 코드를 작성합니다.
import 정확하게 안하면 에러납니다 확인 후 정확히 import 하십시오
테스트 클래스의 선언부에서는 @WebAppConfiguration 애노테이션을 사용하는데 이것이 기존의 스프링과 스프링 MVC를 테스트하는데있어서 가장 큰 차이입니다.
MockMvc는 브라우저에서 요청과 응답을 의미하는 객체로 간주하면 됩니다. 매번 테스트를 진행할때마다 가상의 요청과 응답을 처리하기 위해서 setup() 메소드에서는 @Before 애노테이션으로 처리되어 매번 테스트 메소드의 실행 전에 MockMvc 객체를 만들어내게 됩니다.
testDoA() 를 보면 mockMvc를 사용해서 perform()이라는 메소드를 실행하게 되는데 이때 get(), post()등을 이용해서 GET 방식이나 POST 방식의 호출을 사용하게 됩니다.
위에 테스트 코드가 정상적으로 실행되면 아래와 같이 로그들이 출력되는 것을 볼 수 있습니다.
Console
INFO : org.springframework.mock.web.MockServletContext - Initializing Spring FrameworkServlet '' INFO : org.springframework.test.web.servlet.TestDispatcherServlet - FrameworkServlet '': initialization started INFO : org.springframework.test.web.servlet.TestDispatcherServlet - FrameworkServlet '': initialization completed in 93 ms INFO : com.marshmellow.example.SampleControllerTest - setup....................... WARN : org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/doA] in DispatcherServlet with name '' INFO : org.springframework.web.context.support.GenericWebApplicationContext - Closing org.springframework.web.context.support.GenericWebApplicationContext@598067a5: startup date [Wed Oct 18 14:41:09 KST 2017]; root of context hierarchy |
spring-test 모듈을 사용하는 이유
- 웹 페이지를 테스트하려면 매번 입력 항목을 입력해서 제대로 동작하는지를 확인하는데, 이때 여러번 웹 페이지에서 입력하는것보다 테스트 코드를 통해서 처리하는것이 개발시간을 단축시킬수 있습니다.
- JSP 등에서 발생하는 에러를 해결하는 과정에서 매번 WAS에 만들어진 컨트롤러 코드를 수정해서 배포하는 작업은 많은 시간을 소모하게 됩니다.
- 컨트롤러에서 결과 데이터만을 확인할 수 있기 때문에 문제의 발생 시 원인을 파악할 수 있는 시간이 절약됩니다.
수고하셨습니다.
'Spring' 카테고리의 다른 글
07. 스프링(Spring) 개발 - 프로젝트 생성 및 시작 (0) | 2017.10.25 |
---|---|
06. Spring + MyBatis 연동 (2) | 2017.10.18 |
04. Spring + MyBatis + MySQL 설치 및 연동 (2) | 2017.10.07 |
03. [Spring] MySQL 설치 및 설정 (2) | 2017.09.29 |
02. 스프링 프로젝트 생성 및 구조 (0) | 2017.09.28 |
- Total
- Today
- Yesterday