본문 바로가기
[ Developer ]/Spring Framework

[Spring] 스프링 MVC 기본 및 응용 실습 Model View Controller

by 김현섭. 2016. 6. 8.
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
MVC : Model View Controller
  • Model 1 방식의 개발 방법을 사용했을 때 발생되는 단점들을 보완하기 위한 개발 방법
  • Model 2 방식 혹은 MVC라고 부름
  • Controller는 URL 요청에 알맞은 Java Class의 Method를 실행
    • Controller는 Servlet을 축소시킨 개념
  • View는 JSP 자체를 뜻하며 Model 2 방식에서는 Scriptlet을 사용하지 않는다
  • Model은 View로 전달되는 객체들을 의미
  • Model 1 방식의 최대 단점인 유지 보수의 어려움을 쉽게 해결할 수 있다
  • 비즈니스 로직 / 데이터 로직 / View가 각각 분리되어 Pair Programming이 가능

# 처리 구조
  • 서블릿을 생성하는 것이 아니고 Dispatcher Servlet이 모든 요청을 받는다
  • 누가 처리할 지 URL과 매칭되는 Controller를 검색 후 Controller에게 처리 요청한다
  • Servlet 대신에 Controller가 요청을 대신 처리하게 된다

# Controller 작성
@Controller
public class textController {
@RequestMapping("/test")
public String test() {
          return "test/test";
}
}

위와 같이 @Controller를 작성해줘야 요청들을 처리할 수 있다
@RequestMapping으로 URL을 분류할 수 있다

Controller를 이용하기 위해서는 dispatcherServlet.xml에서 bean을 추가 시켜줘야 한다

Maven을 이용하기 위해서는 다운로드 받아야할 것이 있다

Window - Preferences에서 아래의 체크박스를 체크한다


그리고 Window -> Show view -> Other를 들어간다


Other에서 Maven의 Maven Repositories를 선택한다


옆 화면에 아래와 같이 뜨고 Global Repositories에서 Full Index Enabled을 하면 된다


시간은 조금 오래 걸리나 Central을 펼쳤을 때 폴더들이 들어있다면 성공이다


서블릿을 생성하기 위해서 web.xml에 서블릿을 생성한다

web.xml을 켠다


빈 공간에서 Ctrl + Space를 누르면 자동완성이 되고 밑에 내리면 Dispatcher Servlet을 생성하면 된다
그럼 다음과 같이 생성된다


위의 서블릿에서 Location은 applicationContext.xml, URL은 /로 주면 된다
모든 URL에서 요청을 받는다는 뜻이다

location은 현재 web.xml이 위치한 폴더와 경로가 다르다
그렇기 때문에 applicationContext.xml을 src/main/resources에 넣지 않는다
수정이 안되기 때문에 외부로 빼야 한다

기존의 WebContent 안에 있던 jsp, css, js는 브라우저가 접근이 가능했다
하지만 WebContent/WEB-INF는 브라우저가 접근이 불가능 하다
아래와 같이 변경을 한다


그리고 WEB-INF 안에 spring 폴더와 applicationContext.xml을 생성한다
우선 프로젝트를 우 클릭 후 Configure -> Convert to Maven Project를 해준 후 생성한다

그리고 난 후 pom.xml -> Dependencies 탭으로 들어간다

Select Dependency를 클릭한 후 검색 한다


org.springframework로 검색을 한다 이중에 Spring-webmvc를 누르고 OK 한다


그리고 나서 pom.xml에서 보면 추가된 것을 볼 수 있다


그리고 applicationContext.xml에서 Namespaces 탭으로 들어간 후
이 중에서 mvc를 체크하고 Source 탭으로 다시 돌아온다


아래와 같이 mvc가 추가된 것을 볼 수 있다
mvc의 기본 설정들을 모두 불러온 것이다.


mvc에 필요한 모든 것들을 만들어 주기 위해서 <mvc 작성 후 Ctrl+Space를 누르면 아래와 같이 자동완성이 되고
Annotation-driven을 눌러서 작성해주면 된다



위와 같이 작성하면 필요한 것들을 만들어 준 것이다
그리고 생성되지 않은 ViewResolver를 생성해주기 위해서 <bean을 통해서 생성한다


위와 같이 작성하면 ViewResolver가 생성된다
ViewResolver는 RequestDispatcher.forward();와 비슷한 개념이라고 볼 수 있다

forward안에 URL을 주려면 2가지를 추가해야 한다


# Encoding Filter

그리고 web.xml에서 모든 글자 타입을 UTF-8로 바꿔주기 위해 다음 구문을 추가해야 한다


  1.     <filter>
  2.         <filter-name>encodingFilter</filter-name>
  3.         <filter-class>
  4.             org.springframework.web.filter.CharacterEncodingFilter
  5.         </filter-class>
  6.         <init-param>
  7.             <param-name>encoding</param-name>
  8.             <param-value>UTF-8</param-value>
  9.         </init-param>
  10.     </filter>
  11.    
  12.     <filter-mapping>
  13.         <filter-name>encodingFilter</filter-name>
  14.         <url-pattern>/*</url-pattern>
  15.     </filter-mapping>


이전에 viewResolver에서 prefix에 지정했던 /WEB-INF/view를 물리적인 위치를 만들어주기 위해 WEB-INF 안에 view 폴더를 생성한다
그리고 Servlet을 대체할 Class를 생성한다 Servlet을 대체할 Class는 Controller라는 이름을 붙여주고 @Controller를 적어준다



그리고 index 메소드를 적어준다


Controller 식별자를 통해서 들어오고 RequestMapping은 "/home" URL로 들어왔다면 밑에 index() 메소드로 처리를 하게다는 뜻이다
Controller에서 String으로 return 되는 것은 MainPage를 뜻한다
String으로 return 된다면 InternalResource가 가져와서 prefix가 앞으로 붙고 suffix가 끝으로 붙어서
/WEB-INF/view/'mainPage'.jsp가 되어서 메인으로 가는 것이다

그리고 실제로 view 폴더 안에 mainPage.jsp를 생성한다


그리고 난 후 applicationContext.xml에 Controller를 추가해 준다


접속된 화면을 볼 수 있다



RequestMapping을 하나 더 추가해보기 위해서 login으로 들어오는 것을 다른 jsp로 연결해본다

  


view 밑에 login 폴더 안에 login.jsp를 추가해줬다


접속하면 정상적으로 나오는 것을 볼 수 있다

# JSP로 데이터 보내기
이제는 패키지내에 ArticleController를 하나 더 생성한다
데이터를 받기 위해서는 리턴 타입이 달라야 하는데 ModelAndView라고 명시하면 된다


위와 같이 적는다면 /list로 접속한다면 view를 리턴하는 것이다
ModelAndView에서 View는 어떠한 페이지를 보여줄 것인가에 대해 데이터 이고
Model은 어떠한 데이터를 보낼지를 정하면 된다


ViewName으로 URL을 정의해줄 수 있다
article폴더와 list.jsp를 만들어 준다

또한 Controller를 applicationContext.xml에 추가해준다


접속된 화면


단순히 페이지만 보여준다면 String으로 리턴하고
데이터까지 보내서 보여주고 싶다면 ModelAndView로 리턴하면 된다

이제는 데이터를 보내려고 한다


view.addObject는 setAttribute와 같이 첫번째가 Key 두번째가 Value 값이다




위와 같이 EL 문법으로 표기하고 표현된 것을 볼 수 있다

addObject도 갯수 제한없이 보내고 싶은 만큼 보낼 수 있다

 



홈페이지에 잘 표현되는 것을 볼 수 있다

MVC 응용

#doPost, doGet
MVC에서도 Post와 Get 방식으로 받을 수 있다
RequestMapping시 정의를 할 수 있다


위와 같이 정의가 된다면 value는 URL이 되는 것이고 method에서 RequestMethod.GET이라면 GET 방식만 접근이 가능하다


위와 같이 POST 방식도 할 수 있다
home과 같이 get 방식이라면 모두 접근이 가능하고 Post방식이 로그인에 접근하면 에러가 발생한다


위와 같은 에러는 GET 방식으로 들어왔다는 의미이다

# URL Parameter
URL 자체가 파라미터인 경우
이전에 배웠던 방식인 localhost:8080/list?articleId=1을 localhost:8080/list/1이라고 표현이 된다

ArticleController에서 detail로 접근하는 메소드를 정의해본다


detail이란 메소드로 접근할 때 request에서 파라미터를 받아오는게 아니고 int 타입으로 받을 수 있다
@PathVariable만 정의해준면 된다
그리고 RequestMapping에서 {변수명}을 정의해주면 된다


그리고 detail.jsp를 작성한다 


접속한 화면은 다음과 같다



문자 입력 시 에러가 난다

URL Paremeter를 여러 개 보내려면 ,(콤마)를 이용해서 여러 개 보내면 된다

# Http 요청 파라미터
기존 방식과 동일하게 사용이 가능하고
public ModelAndView Test ( @RequestParam("id") String id ) {
}

위와 같이 작성된다면 id로 파라미터가 넘어온다면 String id로 받아진다는 의미이다
그리고 보내지 않은 파라미터나 또한 그럴 시 기본 값을 정의하고 싶다면 옵션을 주면 된다
public ModelAndView Test ( @RequestParam(value="id" required=false, defaultvalue="") String id ) {
}

required=false 는 요청 값이 없을 때 에러를 보내지 않고 null을 리턴
하지마 잘 사용되지 않는다 유지보수 시 에러가 발생하지 않기 때문에 잡기 힘들다

파라미터가 여러 개 라면 콤마를 이용해서 여러 개 작성해주면 된다

하지만 여러 값이 넘어올 때 객체를 정의해주고 객체로도 받을 수 있다
public ModelAndView Test ( 객체Class 객체명 ) {
}

위와 같이 작성한다면 객체가 가진 변수명에 form이 가진 Name으로 매칭되어서 값들이 들어가게 된다


같은 이름으로 여러 값들이 넘어온다면
public void setNames(List<String> names)로도 받을 수 있다


# Redirect
return "redirect:test";
리 다이렉트 처리 시 위와 같이 작성하면 된다

# Session
HttpSession session을 하면 session을 받을 수 있다
public ModelAndView articleList( HttpSession session ) 과 같이 작성하면서 바로 받을 수 있다

# Parameter 받기 실습


login.jsp에서 input을 2개 주고 파라미터를 받으려고 한다
doLogin으로 접근 시 파라미터를 받는데 이전에 request에서 받았던 방식 말고 새로운 방식으로 받아본다
@RequestParam으로 받지만 Parameter Name이 받을 변수명과 같다면 id처럼 ()안에 id를 명시하지 않아도 된다

하지만 @RequestParam과 HttpServletRequest를 같이 써줘도 된다
파라미터를 받는 순서는 중요하지 않다
Dispatcher가 순서를 판단하고 해당 변수에 넣기 때문에 신경쓰지 않아도 된다

# 객체로 받기


LoginVO에 id와 password를 정의하고 객체로 받는 방식이다

이제 LoginVO에 Integer타입의 memberNumber라는 변수를 하나 추가해 주고
입력 창에도 만들어 준다

입력창에 숫자가 아닌 문자를 입력하면 받지를 않고 에러조차 발생하지 않는다

객체에 List로 선언한다면 여러가지 값들도 받을 수 있다

부르는 방법
HttpServletRequest = Request
@RequestParam = RequestParam
VO = Command

Redirect
Redirect를 실습하기 위해 login으로 접근 시 세션을 통해
로그인이 되었다면 home으로 보내고 아닐 시 login으로 redirect 시키는 방식을 해본다


절대경로에는 2가지 방식이 존재한다
다른 도메인일 경우 모든 경로를 다 적어주며 같은 도메인일 경우 접속 URL만 적어주면 된다

redirect시 데이터를 넘겨줄 수 없기 때문에 리턴 타입이 String일 수 밖에 없다
ModelAndView를 사용할 수 없다 (사용은 가능하지만 의미가 없다)
Redirect는 response.snedRedirect와 같이 대량의 데이터를 보낼 수 없다

위와 같은 구문으로 doLogin시 생성한 session을 통해서 로그인 시 home으로 자동으로 
redirect 보내준다

만약 대량의 데이터를 보내고 싶다면 세션을 사용하면 된다

Command 객체 검증 및 에러 메시지
  • 파라미터로 받는 객체
  • 값을 검증하기 위한 Annotation 추가 가능
  • 또한 Controller 수정으로 에러 메시지를 출력 가능
  • 에러 메시지를 출력하는 파라미터 2개는 위치가 붙어 있어야 한다

커맨드 객체를 검증하기 위해서 기존의 form 태그를 form:form으로 대체
form:form을 사용하기 위해서 taglib중 하나인 form을 추가해줘야 한다

form:form 태그안에 들어가는 옵션
  • commandName : form의 ID이며 받을 Command의 클래스명 과 같아야 한다
  • method : form의 Method과 동일
  • action : form의 Action과 동일

또한 커맨드 객체 값 검증하기 위해서 validator를 DispatcherServlet에 추가해줘야 한다

우선 실습을 하기 위해서 LoginVO로 간다
기존의 id, password, memberNumber, enableAutoLogin, hobby를 받는데
@NotNull을 이용해서 검사를 하기로 한다

NotNull을 사용하기 위해서 pom.xml에서 dependencies를 추가한다


그리고 VO에서 @NotNull을 Import 한다


ID와 Password를 NotNull로 체크를 해주고
입력하는 memberNumber에서 Min과 Max를 이용해 본다
Min을 입력 후 빨간줄이 뜨는데 괄호 안에서 value를 이용하면 된다



그리고 IndexController로 가서 수정을 한다
Command 객체인 LoginVO 앞에 @Valid를 통해서 체크를 해준다


Min이나 Max에서처럼 여러가지를 적으려면 value로 조건을 적어줘야 한다
또한 message를 통해서 메시지를 출력해줄 수 있다

문자는 NotNull이 파라미터 자체를 보내므로 Null로 인식이 안되므로 NotEmpty로 바꿔준다



@Valid가 나온다면 그 이후엔 반드시 Errors errors가 등장해야 한다


이 둘의 순서는 중요하다 @Valid 이후에 반드시 Errors가 등장해야 한다
데이터를 보내줘야 하므로 리턴 타입이 ModelAndView로 변경 되어야 한다


errors의 hasErrors로 에러가 발생했는지를 체크해야 한다
발생했다면 view를 다시 login으로 돌려보내고 
아니라면 home으로 보내면 된다


그리고 applicationContext.xml에 가서 추가를 해줘야 한다


위와 같이 추가한다면 Validation Check가 완료된다
또한 pom.xml에서 Dependencies에서 하나를 추가해줘야 한다


Alpha는 테스트 중인 버전이므로 final 버전으로 선택해준다



발생한 에러를 보기위해 login.jsp에서 taglib를 추가한다


추가 후 form 태그를 변경해주면 된다

그리고 에러를 출력해줄 수 있다


errors에서 path로 input의 name과 동일하게 맞춘다

다음과 같은 결과를 볼 수 있다


예외 처리
@ControllerAdvice를 이용한 공통 Exception을 처리
Controller를 하나 추가해서 Exception들을 처리해주는 것이다

기존의 예외처리는 코드를 에러메시지에서 보여주기 때문에 보안적인 문제가 많다
그렇기 때문에 Spring의 예외 처리를 이용해서 다른 페이지를 보여주는 것이다

그러나 기존의 예외 처리에서도 JSP를 통해 다른 페이지를 보여주는 것이 가능하지만
Spring을 이용하는 이유는 어떠한 값을 입력해서 에러가 발생했는지 또한 알려줄 수 있기 때문에
Spring의 예외 처리를 이용한다

흔하게 만날 수 있는 에러가 400, 403, 404, 405, 500 정도가 있다
앞에 있는 4나 5에 따라서 에러의 종류가 다르다
4 : 브라우저의 잘못된 요청 에러
5 : 서버의 에러, Internal Server Error

web.xml에서 에러코드를 정의해서 예외 처리할 수 있다
error-page라고 작성하고 그안에서 다른 태그를 넣을 수 있다
error-code는 에러가 발생하는 코드이고 exception-type은 발생하는 예외의 종류이다 너무 종류가 많아서 error-page로 정의한다


400에러를 잡으려면 다음과 같이 작성하면 된다


위가 한 세트이고 여러가지 에러코드를 적을 수 있다


에러 테스트는 404와 500을 테스트 한다
view 폴더 밑에 error라는 폴더를 생성하고 404.jsp와 500.jsp 2개를 생성한다


그리고 404와 500 jsp에서 내용을 정의한다


그리고 서버를 재시작 후 테스트를 진행해본다
404를 띄우기 위해서 이상한 URL로 접근을 해본다


404.jsp로 정의한 페이지가 나오는 것을 볼 수 있다

500 에러를 보기 위해서 NumberFormatException을 발생시킨다


그렇다면 아래와 같이 500에러도 우리가 생성한 jsp로 가는 것을 볼 수 있다



#Controller로 예외 처리
예외 처리를 Controller로 처리하기 위해서 패키지 내에서 handler 패키지를 생성하고
ExceptionHandler 클래스 파일을 생성한다


ControllerAdvice로 패키지명을 넣으면 해당 패키지에서 발생하는 예외는 모두 한번 ExceptionHandler에게 먼저 오게 된다


ExceptionHandler로 특정 예외를 받을 수 있는데 안에 value는 Class<? extends java.long.Throwable>이 되어 있다
Throwable을 상속받고 있는 모든 클래스가 들어올 수 있다는 것이다


{}는 배열이라는 의미이다


위와 같이 작성하여 발생하면 500.jsp을 열게 끔하고 content로 에러 메시지를 보내본다
그리고 난 뒤 applicationContext.xml에 추가를 해주게 된다


Controller의 기능을 하기 때문에 선언을 해줘야 한다

결과 화면은 다음과 같다


내가 보냄이 뜬 것을 볼 수 있다

웹에서 발생되는 Exception은 RuntimeException으로 뜨기 때문에 Controller에서 RuntimeException으로 잡아주면 된다


하지만 대부분의 예외는 잡히지만 어떠한 에러가 잡히는 지는 알 수 없기 때문에
에러 메시지를 보내줄 수 있다 그렇기 위해서 예외를 파라미터로 받아올 수 있다



결과를 위와 같이 확인할 수 있다

또한 request를 받아와 원하는 정보를 출력할 수도 있다


Referer은 에러가 발생한 페이지를 호출한 직전의 페이지를 나타낸다


Static File
CSS, JS, Image 등의 자원은 파일이 존재하는 URL 자체로 사용되는데 
MVC는 DispatcherServlet으로 인해 원하는 자원들을 얻을 수 없다
때문에 CSS, JS, Image 같은 정적 자원들은 따로 URL을 주어야 하는데
mvc:resources가 지원해 준다

<mvc: resources mapping="/resouces/**" location="/WEB-INF/resources/" />
resources라는 경로로 정적파일들을 지원하겠다는 의미의 설정