티스토리 뷰

study/Spring

chap 11 - MVC 1 (1)

xoxowo 2023. 2. 6. 23:59

초보 웹 개발자를 위한 스프링 5 프로그램 입문을 보고 복습 겸 개인 학습 정리입니다. (windows 기준)

 

아직... 멀었다....

 

 

chap11에서는 앞전에 간략하게 배운 ^^.. 스프링 MVC를 이용해서 본격적이지만 아주 간단한 회원 가입 예제를 만들 예정이다.

@RequestMapping, 요청 파라미터 접근, 리다이렉트 등을 이용한 가장 기본적인 컨트롤러와 뷰 구현방법을 배우도록 하자.

 

책에 나와있는 순서대로 복습하면서 정리하는데 chap11이 길어서 나눠서 글을 올린다.

 

예제 프로젝트 만들기 (메이븐 기준)  예제 코드

sp5-chap11 프로젝트 폴더 생성

프로젝트(sp5-chap11) 하위 폴더로 아래 폴더 생성

- src/main/java

- src/main/java/webapp

- src/main/java/webapp/WEB-INF

- src/main/java/webapp/WEB-INF/view

+ step.jsp 파일 복붙

+ chap 08 spring 패키지의 파일 복붙하기

+ 데이터 베이스 생성

sp5-chap11 폴더 내부에 pom.xml 생성

+ pom.xml 파일에 <packaging> war 값 추가, 서블릿 3.1, JSP 2.3, JSTL 1.2 dependency 추가

 war : web application archive를 의미

이클립스에서 sp5-chap11 폴더에 생성한 메이븐 프로젝트 import

 

 

1. 스프링 MVC를 위한 기본 설정 파일 작성

chap11/src/main/java/config/MvcConfig.java

package config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
        configurer.enable();
    }
    // jsp 경로 추가
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry){
        registry.jsp("/WEB-INF/view/", ".jsp");
    }
}

 

2. contextConfiguration 초기화 파라미터 추가한 web.xml 작성 (접은 글로 대체)

 chap11/src/main/webapp/WEB-INF/web.xml

더보기
<?xml version="1.0" encoding="UTF-8"?>

 

    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
    version="3.1" metadata-complete="true">

 

    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </init-param>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                config.MemberConfig
                config.MvcConfig
                config.ControllerConfig
            </param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

 

    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

 

    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>
            org.springframework.web.filter.CharacterEncodingFilter
        </filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

 

</web-app>

 

 

요청 매핑 애노테이션을 이용하여 경로 매핑하기

- 특정 요청 URL을 처리할 코드

- 처리 결과를 HTML과 같은 형식으로 응답하는 코드

 

@Controller 애노테이션을 사용한 컨트롤러 클래스를 이용해서 구현한다.

요청 매핑 애노테이션에는 @RequestMapping, @GetMapping, @PostMapping 등이 있다.

 

 

회원가입처럼 여러 단계를 거쳐 하나의 기능으로 완성되는 경우

 

예시 1

컨트롤러 클래스를 한 개만 만들고 세 개의 메서드에서 각 요청 경로를 처리하도록 구현할 수 있다.

클래스 내부의 각 메서드에 @RequestMapping 애너테이션을 붙이고 각 단계에 맞게 경로를 설정 ("/register/step~")해준다.

@Controller
public class RegistController{

	@RequestMapping("/register/step1")
	public String handleStep1(){
		return "register/step1";
	}
	@RequestMapping("/register/step2")
	public String handleStep2(){
		...
	}
	@RequestMapping("/register/step3")
	public String handleStep3(){
		...
	}
}

예시 2

공통되는 경로("/register")를 담은  @RequestMapping 애노테이션을 클래스에 적용하고 내부 메서드는 나머지 경로를 값으로 갖는 요청 매핑 애노테이션을 적용할 수 있다.

 

※ 스프링 MVC는 클래스에 적용한 요청 매핑 애노테이션의 경로("/register")와 메서드에 적용한 요청 매핑 애노테이션("/step~")의 경로를 합쳐서 경로를 찾기 때문에 아래 handleStep1() 메서드가 처리하는 경로는 "/register/step~"이 된다.

@Controller
@RequestMapping("/register")
public class RegistController{

	@RequestMapping("/step1")
	public String handleStep1(){
		return "register/step1";
        }
	@RequestMapping("/step2")
	public String handleStep2(){
		...
        }
}

 

3. 회원가입 첫 과정인 약관을 보여주는 요청 경로를 처리하는 컨트롤러 클래스 만들기

 chap11/src/main/java/controller/RegisterController.java

package controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class RegisterController {
    @RequestMapping("/register/step1")
    public String handleStep1(){
        return "register/step1";
    }
}

4. 만든 컨트롤러 클래스를 빈으로 등록하기

 chap11/src/main/java/config/ControllerConfig.java

package config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import controller.RegisterController;

@Configuration
public class ControllerConfig {
    @Bean
    public RegisterController registerController(){
        return new RegisterController();
    }
}

 

 

5. 서버 실행

이제 서버를 실행해 보자. (미리 톰캣😺 서버 설치 및 설정 완료)

[Run As] → [Run on Server] → [톰캣😺 서버 실행] 

위의 모든 코드를 올바르게 작성했다면 아래와 같은 결과가 브라우저에 출력된다.

 

📌GET과 POST 구분하기 @GetMapping, @PostMapping

주로 어떤 폼을 전송할 땐 POST 방식을 사용하는데 스프링 MVC는 별도 설정이 없으면 GET, POST 상관없이 @RequestMapping에 지정한 경로와 일치하는 요청을 처리한다.

 

따라서 POST방식 요청만 처리하고 싶으면 @PostMapping 애노테이션을 사용해서 제한할 수 있다.

※ 이 방식은 스프링 4.3 버전에서 추가된 것 이전엔 @RequestMapping의 method 속성을 이용해서 처리

 

 

 

요청 파라미터 접근

예를 들어 회원가입 약관 동의 시 값이 true 인 'agree' 요청 파라미터의 값은 POST방식으로 전송한다.

따라서 폼에서 지정한 agree 요청 파라미터의 값을 이용해 약관 동의 여부를 확인할 수 있다.

 

컨트롤러 메서드에서 요청 파라미터를 사용하는 방법은 크게 2가지가 있다.

- HttpServletRequest를 직접 이용

- RequestRaram 애노테이션을 사용 (← 요청 파라미터 개수가 적을 때 사용)

 

HttpServletRequest 사용 예시

→ 컨트롤러 처리 메서드의 파라미터로 HttpServletRequest을

import javax.servlet.http.HttpServletRequest;

@Controller
public class RegisterController {
    @RequestMapping("/register/step1")
    public String handleStep1(){
        return "register/step1";
    }
    // HttpServletRequest의 getParameter()메서드 사용하여 파라미터 값을 구한다.
    @PostMapping("/register/step2")
    public String handleStep2(HttpServletRequest request){
    String agreeParme = request.getParameter("agree");
    if (agreeParem == null || !agreeParam.equals("true")){
    	return "register/step1";
    }
    return "register/step2";
}

 

RequestRaram 사용 예시

속성을 부여하여 agree 요청 파라미터 값을 읽어와 agreeVal 파라미터에 할당한다. 요청 파라미터 값이 없으면 "fales" 문자열을 값으로 사용한다. 

import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class RegisterController {
    @RequestMapping("/register/step1")
    public String handleStep1(){
        return "register/step1";
    }
	
    @PostMapping("/register/step2")
    public String handleStep2(@RequestParam(value="agree", defaultValue="false")Boolean agreeVal){
    if (!agree){
    	return "register/step1";
    }
    return "register/step2";
}

 

@RequestRaram의 속성 

속성 타입 설명
value String HTTP 요청 파라미터의 이름을 지정한다
required boolean 필수 여부를 지정한다.
이 값이 true이면서 해당 요청 파라미터 값이 없으면 익셉션 발생. 기본값 true
defaultValue Stirng 요청 파라미터가 값이 없을 때 사용할 문자열 값 지정. 기본값 없음

 

 

6. @RequestRaram을 사용하여 약관 동의 화면의 다음 요청을 처리하는 코드(step2)를 추가

handleStep2 메서드의 agree 요청 파라미터 값이 true가 아니면 "register/step1" 뷰로 리턴

true 이면 "register/step2"로 리턴한다.

 

 chap11/src/main/java/controller/RegisterController.java

package controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class RegisterController {
    @RequestMapping("/register/step1")
    public String handleStep1(){
        return "register/step1";
    }

    @PostMapping("/register/step2")
    public String handleStep2(@RequestParam(value="agree", defaultValue="false")Boolean agree){
    if (!agree){
    	return "register/step1";
    }
    return "register/step2";
    }
}

 

7. 서버 실행

[Run As] → [Run on Server] → [톰캣😺 서버 실행] 

 

※ 영상 끝부분에 나오는 것처럼 만약 (http://localhost:8090/sp5-chap11/register/step2) 해당 경로를 직접 주소창에 입력하여 들어간다면 GET요청이기 때문에 아래와 같이 오류 화면이 출력된다.

8. GET 요청에 대한 리다이렉트 처리를 추가하자

 chap11/src/main/java/controller/RegisterController.java

package controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class RegisterController {

	... 생략
    
    @GetMapping("/register/step2")
    public String handleStep2Get(){
    	return "register/step1";
    }
}

 

9. 리다이렉트 처리 후 다시 확인해 보자

Get 요청 리다이렉트 처리

 

 커맨드 객체를 이용한 요청 파라미터 사용하기

 

step2.jsp에서 생성하는 폼은 다음 파라미터를 이용해서 정보를 서버에 전달한다.

- email

- name

- password

- conflirmPassword

 

String email = request.getPrameter("email")로 받게 되면 파라미터 개수가 증가할 때마다 코드 길이가 길어진다는 단점이 있다.

이러한 불편함을 줄이기 위해 스프링은 요청 파라미터 값을 커맨드(command) 객체에 담아주는 기능을 제공한다.

 

 

10. 커맨드를 이용하여 RegisterController클래스의 step3에 대한 코드 설정을 추가한다.

코드를 보면 handleStep3 메서드 파라미터 값에 (RegisterRequest regReq)가 들어가 있는데,

이 RegisterRequest 클래스 내부 코드를 보면 setName(), setEmail() 등등 세터메서드가 있다. 

스프링은 이 세터메서드를 사용해서 email, name password, confirmpassword 요청 파라미터를 커맨드 객체에 복사한 후 regReq파라미터로 전달한다. 

 

 즉, 스프링 MVC가 handleStep3() 메서드에 전달할 RegisterRequest 객체를 생성하고 이 객체의 세터 메서드를 이용해 일치하는 파라미터 값을 전달하는 것

 

 chap11/src/main/java/controller/RegisterController.java

@Controller
public class RegisterController {
	
	private MemberRegisterService memberRegisterService;
	
	public void setMemberRegisterService(MemberRegisterService memberRegisterService) {
		this.memberRegisterService = memberRegisterService;
	}
    
    ... 생략
    
    @PostMapping("/register/step3")
        public String handleStep3(RegisterRequest regReq){
            try {
                memberRegisterService.regist(regReq);
                return "register/step3";
            }catch (DuplicateMemberException ex) {
                return "register/step2";
            }
        }
}

 

11.RegisterController클래스는  MemberRegisterService 타입 빈을 의존하므로 ControllerConfig 클래스에 의존 주입을 설정한다. 

 chap11/src/main/java/config/ControllerConfig.java

@Configuration
public class ControllerConfig {
	// 코드 추가
    @Autowired
    private MemberRegisterService memberRegSvc;

    @Bean
    public RegisterController registerController(){
        RegisterController controller = new RegisterController();
        controller.setMemberRegisterService(memberRegSvc);
        return controller;
    }
}

 

 

12. 서버 실행

[Run As] → [Run on Server] → [톰캣😺 서버 실행] 

만약 이미 가입한 회원(중복 email)이라면 가입이 되지 않고 회원 정보 입력화면에서 멈춘다.(?)

 

회원가입이 완료되면 다음과 같이 가입 시 입력한 이름(회원의 이름)이 뜨는데 커맨드 객체를 사용하여 정보를 표시한 것 (step3.jsp에서 설정)

 

스프링 MVC는 커맨드 객체의 클래스 이름과 동일한 속성 이름을 사용해서 커맨드 객체를 뷰에 전달하는데, 커맨드 객체의 클래스 이름이 RegisterRequest인 경우 JSP코드는 registerRequest라는 이름을 사용해서 커맨드 객체에 접근할 수 있다.

파라미터로 사용한 커맨드 객체 (RegisterRequest regReq) → step3.jsp에 설정한 ${registerRequest.name}

 

 

13. 컨트롤러 구현 없는 경로 매핑

회원 가입 완료 후 [첫 화면으로 이동] 버튼을 눌렀으나 메인 페이지를 만들어 놓지 않아서 페이지로 이동할 수 없었다.

단순히 이동할 수 있는 링크만 제공하는데 아래 코드와 같이 경로를 연결해 주면 단순하지만 굳이 이렇게 코드를 매번 작성해야 하는 번거로움이 발생할 수 있다.

@Controller
public class MainController {
	@RequestMapping("/main")
    public String main() {
    	return "main";
    }
}

 

WebMvcConfigurer 인터페이스의 addViewControllers() 메서드를 사용하여 경로를 연결할 수 있다.

MvcConfig에 addViewControllers() 메서드를 사용하여 main 요청 경로로 연결하자.

 chap11/src/main/config/MvcConfig.xml

... import 생략...

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
        configurer.enable();
    }
    
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry){
        registry.jsp("/WEB-INF/view/", ".jsp");
    }
    
    @Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/main").setViewName("main");
	}
}

 

view 폴더에 main.jsp도 추가해 준다. (접은 글로 대체)

 chap11/src/main/webapp/WEB-INF/view/main.jsp

더보기

<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<! DOCTYPE html>
<html>
<head>
    <title> 메인 </title>
</head>
<body>
    <p> 환영합니다. </p>
    <p><a href="<c:url value="/register/step1" />">[회원 가입하기]</a>
</body>
</html>

 

14. 서버 실행

[Run As] → [Run on Server] → [톰캣😺 서버 실행] 

main경로를 설정해 주었기 때문에 http://localhost:8090/sp5-chap11/main 경로로 접속했을 때 아래와 같이 메인 페이지를 볼 수 있다.

 

 

마찬가지로 main에서 회원 가입 → 가입 완료 후 → 메인화면으로도 돌아가는 것을 확인할 수 있다.

 

 

'study > Spring' 카테고리의 다른 글

chap 12 - MVC 메세지 처리  (0) 2023.02.13
chap 11 - MVC 1 (2)  (0) 2023.02.10
chap 10 - 스프링 MVC 프레임워크 동작 방식  (0) 2023.01.30
chap 09 - 스프링 MVC 설정  (0) 2023.01.30
Tomcat 설치  (0) 2023.01.25
댓글