[신세계I&C KDT][Spring Boot] #38 Spring MVC (0605, 0610)

박현아·2024년 6월 5일
0

신세계아이앤씨 KDT

목록 보기
42/42

Spring MVC

1. MVC 아키텍쳐

1) 서블릿 / JSP MVC 아키텍쳐

웹브라우저 ---> 서블릿 (개발자가 구현) ---> Service --> DAO --> DB
			   ListServlet (/list)
               AddServlet (/add) 등등 각 기능에 맞는 서블릿을 만들었었음
					|
                    | 요청 위임 (redirct/forward)
                   jsp

cf) 요청 위임

  • redirect 방식
    HttpServletResponse 이용
public void doGet (HttpServletRequest request, HttpServletResponse response) {
	response.sendRedirect("타겟");
}
  • forward 방식
    HttpServletRequest 이용
public void doGet (HttpServletRequest request, HttpServletResponse response) {
	RequestDispatcher dis = request.getRequestDispatcher("타겟");
	dis.forward(request, response);
}

2) Spring MVC 아키텍쳐

웹브라우저 ---> 서블릿 (Spring 제공) ---> Controller --> Service --> Repository --> DB
			  DispatcherServlet (/)		/list 역할
              							/add 역할
                                  <---
                                  jsp + jsp에서 보여줄 데이터 (응답처리)
                      |
                      | 요청 위임 (redirct/forward)
                     jsp

2. Spring MVC 실습

1) 의존성 설정

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

2) port 번호 및 context명 변경

  • application.properties
    - tomcat port 번호 변경
    예> server.port=8090
    - context명 변경
    예> server.servlet.context-path=/app

  • 요청은 : http://localhost:8090/app/ 이고 white label page 보임
    /app : context명
    / : 서블릿맵핑명

3) Controller 작성

  • 이전 서블릿/jsp 버전의 서블릿 역할 담당 (요청처리 및 응답처리)
  • POJO 기반
  • 빈으로 자동 생성될 수 있도록 권장 패키지 구조를 준수 + @Controller로 지정
  • 요청 url과 일치하는 요청 맵핑값을 설정
    반드시 유일한 값으로 설정하기
    예> http://localhost:8090/app/list ==> list 역할의 메서드 실행
    http://localhost:8090/app/add ==> add 역할의 메서드 실행
  • 문법
@RequestMapping("/list ")
public String list([변수,...]) {
	return "보여줄 jsp 정보"; // 기본적으로 forward 방식으로 jsp에게 위임한다
}

@RequestMapping("/add")
public String add([변수,...]) {
	return "보여줄 jsp 정보";
}
  • Controller의 메서드 리턴 타입은 다양한 타입 지정 가능
    예> String, void, DeptDTO, List< DeptDTO>,...
    만약 String이면 View 정보를 의미한다 (JSP 정보)

  • Controller의 메서드 파라미터도 다양한 타입 지정 가능

  • Controller의 @RequestMapping에 설정하는 요청맵핑값은 다양한 포맷 지정이 가능하다
    예> /list, /list, /list/, /list/**

4) jsp 저장

  • jsp 및 css/js/image 등을 저장하기 위해서는 기본적인 웹 어플리케이션 디렉터리 구조가 있음
	webapp (*****)
		 META-INF
		    MANIFEST.MF
		 WEB-INF
		    lib
		    web.xml
		    
		    hello.html (정적)
		    test.css
		    test.js
		    test.png
		    world.jsp (동적)

그런데 Spring boot에서는 기본적으로 웹 어플리케이션 디렉터리 구조를 제공 안 함. spring boot는 jsp를 권장 안 하기 때문 (대신 thymeleaf 권장)

(1) spring boot의 웹 디렉토리 구조

src/main/resources
	META-INF
    	resources
        	WEB-INF
            	views (폴더)
                	*.jsp

==> jsp 를 WEB-INF 안에 저장한 이유는 웹브라우저에서 직접 접근이 불가하기 때문에
==> Controller가 알려준 jsp를 가진 DispatcherServlet만 접근이 가능하다

(2) jsp 의존성 설정

# JSP 의존성 설정
<dependency>
	<groupId>org.apache.tomcat.embed</groupId>
	<artifactId>tomcat-embed-jasper</artifactId>
	<scope>provided</scope>
</dependency>
<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>jstl</artifactId>
</dependency>
  • jsp 위한 이클립스 플러그인 설치하기
    help > Eclipse market place > 'eclipse web' 검색 > Eclipse Enterprise java and web developer tools 3.33 설치하기

5) Controller에서 JSP 선택하기 위한 prefix와 suffix 설정

  • 현재 코드
    return "/WEB-INF/views/hello.jsp";
    경로가 길고 확장자가 jsp로 고정되어 있음

  • 수정 코드
    return "hello";
    파일명만 지정
    추가로 application.properties에 파일 경로와 확장자 정보를 알려줌

6) jsp에서 정적 파일 사용하기 (js, css, image)

  • src/main/resources/static 폴더에 저장하기

(1) static 폴더 작성

      src/main/resources/
	                 static
			       			js
			         			test.js
			       			css
			         			test.css
			       			images
			         			logo_daum.png

(2) jsp에서 사용

<link rel="stylesheet" href="css/test.css">
<script src="js/test.js"></script>
<img src="images/logo_daum.png" width="100" height="100">

3-1. 요청 맵핑값의 패턴 1

  • @RequestMapping("요청맵핑값")

  • 예>

  @RequestMapping("/list")
  @RequestMapping(value = {"/list2","/listAll"})
  @RequestMapping("/update*")
  @RequestMapping("/delete/*")
  @RequestMapping("/add/**")
  @RequestMapping("/find/**/board") <== 패턴 에러
  
  해결: spring.mvc.pathmatch.matching-strategy=ant-path-matcher 붙이기

https://docs.spring.io/spring-boot/docs/2.7.18/reference/html/web.html#web.servlet.spring-mvc.content-negotiation

3-2. 요청 맵핑값의 패턴 2

  • @RequestMapping("요청맵핑값") 의 클래스 레벨 사용가능

  • 현재
    ==> 회원정보보기/ 회원생성하기 / 회원수정하기/ 회원삭제하기
    @RequestMapping("/member")
    @RequestMapping("/member/list")
    @RequestMapping("/member/add")
    @RequestMapping("/member/update")
    @RequestMapping("/member/delete")

  • 적용 (/member가 중복되므로 /member를 클래스 레벨에서 사용할 수 있다)

@RequestMapping("/member")
public class MainController2{

     @RequestMapping("/list")
	 public String list(){}

	 @RequestMapping("/add")
	 public String add(){}

	 @RequestMapping
	 public String default(){}
}

3-3. 요청 맵핑값의 패턴 3

  • GET | POST 방식 명시 가능
  • 예> 로그인 (회원가입) 작업은 반드시 2번 요청해야 한다.
    -> 첫 번째 요청은 화면 요청 (GET 방식), 두 번째 요청은 실제 로직 요청 (POST 방식)
  • 현재
@RequestMapping("/loginForm") <== 화면 요청, GET 방식 이용이 일반적
     public String loginForm(){}

@RequestMapping("/login") <== 로직 요청, POST 방식 이용이 일반적
     public String login(){}
  • 적용
    => 요청 맵핑값은 /login으로 동일하지만 method 방식이 달라서 요청에 대한 식별이 가능하다
// 화면 요청, GET 방식 이용이 일반적
@RequestMapping(value="/login", method=RequestMethod.GET)
     public String loginForm(){}

// 로직 요청, POST 방식 이용이 일반적
@RequestMapping(value="/login", method==RequestMethod.POST)
     public String login(){}
  • Spring Framework 4.3 이후에 어노테이션이 추가됨
 @RequestMapping(value="/login", method=RequestMethod.GET)
 		==>@GetMapping("/login")

@RequestMapping(value="/login", method==RequestMethod.POST)
	    ==>@PostMapping("/login")

====> PRG (Post-Redirect-Get) 패턴을 적용해서 최종적인 작업은 post가 아닌 get로 끝나게 처리하기 !!!!!!!!

// 화면 요청
	@GetMapping("/login")
	public String list() {
		return "loginForm";
	}
	
// 로직 요청
	@PostMapping("/login")
	public String add() {
		return "redirect:xxx"; 
	}
	
// PRG 패턴 적용
	@GetMapping("/xxx")
	public String hello() {
		return "hello"; 
	}

4-1. 요청 파라미터 얻기 1 - 단일값 (name 하나 value 하나)

  • 이전 서블릿/jsp의 request.getParameter("name") 역할
  • POST 요청시 한글 처리를 명시적으로 구현함
  • Spring MVC에서는 POST 요청시에도 한글 처리가 자동으로 지원됨

방법 1)

@PostMapping("/login")
public String add(HttpServletRequest request) {
String userid = request.getParameter("userid");
String passwd = request.getParameter("passwd");
}

방법 2) ⭐

@PostMapping("/login")
public String login(@RequestParam("userid") String userid,
			    @RequestParam("passwd") String passwd) {
		
		logger.info("logger:userid={},passwd={}", userid, passwd);
}        

방법 3)

@PostMapping("/login")
public String login(@RequestParam(name = "userid2", 
	                                  required = false, 
	                                  defaultValue = "aaa") String userid,
			            @RequestParam("passwd") String passwd) 

방법 4) name값과 파라미터 변수가 동일하면 name값 생략 가능

 @PostMapping("/login")
public String login(@RequestParam String userid,
			            @RequestParam("passwd") String pw) 

방법 5) DTO에 직접 저장 ⭐

(단, name 값과 DTO의 변수명이 동일한 경우)

@PostMapping("/login")
public String login(LoginDTO dto) {
	logger.info("logger:userid={},passwd={}", dto.getUserid(), dto.getPasswd() );
}

방법 6) Map 계열로 저장

@PostMapping("/login")
	public String login(@RequestParam Map<String, String> map) {
		logger.info("logger:Map={}", map );
}

4-2. 요청 파라미터 얻기 2 - 멀티값 (name 하나 value 여러 개)

: 이전 서블릿/jsp의 request.getParameterValues("name") 역할

방법 1) @RequestParam + 배열로 처리

@PostMapping("/member1")
public String member1(    @RequestParam("userid") String userid,
			             @RequestParam("passwd") String passwd, 
			             @RequestParam("email")  String[] email
			            )

방법 2) @RequestParam + List로 처리

@PostMapping("/member2")
public String member2(    @RequestParam("userid") String userid,
			             @RequestParam("passwd") String passwd, 
			             @RequestParam("email") List<String> email
			            ) 

방법 3) DTO ⭐

public class MemberDTO {

	String userid;
	String passwd;
	List<String> email; // String [] email 가능

     @PostMapping("/member")
	public String member(MemberDTO dto) {
		
		logger.info("logger:MemberDTO:{}", dto);

5. 요청 헤더값 얻기

==> 헤더의 name값은 기본적으로 대소문자 구별 안 함

  • 모든 요청헤더값 얻기
	@GetMapping("/header")
	public String header(HttpServletRequest request) {
	  Enumeration<String> names = request.getHeaderNames();
		
	    while(names.hasMoreElements()) {
	    	String name = names.nextElement();
	    	String value = request.getHeader(name);
	    	logger.info("logger:header name:{}, header value:{}", name, value);
	    }
		return "hello";
	}
  • 특정 헤더값 얻기
	@GetMapping("/header2")
	public String header2(@RequestHeader("user-agent") String x,
			      @RequestHeader("Accept-language") String x2,
			      @RequestHeader("Accept") String x3
			               ) {
		logger.info("logger:user-agent:{}, accept-language:{}, accept:{}", x, x2, x3);
	 
		return "hello";
	}

6. 쿠키값 얻기

  • 전체 쿠키 얻기
	@GetMapping("/get-cookie1")
	public String get_cookie1(HttpServletRequest request) {
			
		Cookie [] cookies = request.getCookies();
		for (Cookie c : cookies) {
			String name = c.getName();
			String value = c.getValue();
			logger.info("logger:쿠키 name:{}, 쿠키 value:{}", name, value);
		}
		return "hello";
	}
  • 특정 쿠키값 얻기
    @GetMapping("/get-cookie")
	public String get_cookie(@CookieValue("userid") String userid) {
		logger.info("logger:쿠키 name:userid, 쿠키 value:{}", userid);
		return "hello";
	}

0개의 댓글