본문 바로가기
Programming/Spring Boot

Spring MVC 01_기본

by yoon9i 2024. 7. 2.

14. Spring MVC

1> MVC 아키텍쳐
가. 서블릿/JSP MVC 아키텍쳐(Model 2 Architecture)
- Model 1 은 jsp 만 가지고 사용하는데 현재는 사용하지 않음.

                  요청          (Controller)                   (Model)
    웹브라우저 ----------------> 서블릿(개발자가 구현) ---------> Service ---------> DAO ---------> DB
                                ListServlet(/list): 데이터 가져오는 담당
                                AddServlet(/add): 데이터 저장 담당
                                ....
                                  |
                                  | <== 요청위임(redirect/foward)
                                 ▽
                                 jsp(View)
  
  TestServlet 까지 올려면 요청 URL?
  http://서버IP:port번호/context명/서블릿매핑
  http://localhost:8090/context명/list ---> ListServlet
  http://localhost:8090/context명/add ---> AddServlet
  ==> 서블릿까지만 오면 처리가능

  TestServlet 했던 주요한 작업들?
  - 요청처리
    ex> request.getParameter("name");
        ...
  - 응답처리
    ex> 직접 응답 또는 jsp 위임작업

  * 요청위임
  (1) redirect 방식
  - HttpServletResponse 이용
  - 문법:

      public void doGet(HttpServletRequest request, HttpServletResponse response) {
          response.sendRedirect("타켓");
      }

                      1> 요청(/xxx)
      웹브라우저 ------------------>    MainServlet("/xxx") 2> doGet실행
                                                     HttpServletRequest request(100번지) 생성
                                                     request(100번지).setAttribute("xxx","홍길동")

                                                  3>
                                                  response.sendRedirect(" a.jsp");
                         <-------------------
                4>응답(서블릿이 응답)

                  5> 재요청(a.jsp)
                ------------------>  a.jsp   HttpServletRequest request(200번지)
                <------------------          request.getAttribute("xxx"); null 반환
                6> 응답

        - url이 변경됨. ( /xxx  --> a.jsp )
      - 서블릿에서 request scope에 저장된 데이터는
        리다이렉트된 jsp에서 참조 불가 (null 반환)
        따라서 session scope 또는 application scope를 사용해야 된다.   

  (2) foward 방식
  - HttpServletRequest 이용
  - 문법:

      public class MainServlet extends HttpServlet {
          public void doGet(HttpServletRequest request, HttpServletResponse response) {
              RequestDispatcher dis = request.getRequestDispatcher("타켓");
              dis.forward(request, response);
          }
      }

      public class MainServlet extends HttpServlet {
          public void doGet(HttpServletRequest request, HttpServletResponse response) {
              RequestDispatcher dis = request.getRequestDispatcher("a.jsp");
              dis.forward(request, response);
          }
      }

                  1> 요청                   서버
      웹브라우저 --------------------------------> MainServlet("/xxx")     2> doGet 실행
                                                                                                             |                      HttpServletRequest request 생성
                                                                                                             |                      request.setAttribute("xxx", "홍길동");
                                                                                                             |
                                                                                                             | 3> 포워드 위임(요청)

                                                                                                                        - 1번에서 만든 request 를 재사용.
                                                                                                             |    request.getRequestDispatcher("a.jsp");
                                                                                                             |    dis.forward(request, response);
                <----------------------------------- a.jsp
      
      ==> forward 방식은 서블릿을 요청할 때 생성했던 request 를 jsp 로 위임(요청)할 때도 재사용한다.
          따라서, 하나의 request로 서블릿도 요청하고 jsp 도 요청하는 방식으로서 request를 
          확장시킬수 있는 개념이다.
      ==> 서블릿에서 request.setAttribute(key,value) 로 저장된 데이터를
          JSP 에서 request.getAttribute(key) 하면 value 를 얻을 수 있다.
    ==> 브라우저 url 변경 안됨
        서블릿맵핑값으로 계속 유지됨.

나. Spring MVC 아키텍쳐

                  요청
    웹브라우저 ----------------> 서블릿(spring 제공) ---------> Controller ---------> Service ---------> Repository ---------> DB
                                DispatcherServlet(/)
                                Mapping servlets: dispatcherServlet urls=[/]          
                                
                                                              /list 역할

                                                              /add 역할

                                                   <---------
                                                    jsp + jsp 에서 보여줄 데이터(응답처리)

                                        |
                                        |<== 요청위임(redirect/forward)
                                       jsp


  DispatcherServlet 까지 올려면 요청 URL?
  httP://서버IP:port번호/context명/서블릿매핑
  http://localhost:8090/context명/

  ==> 404이기 때문에 White Label page 가 보임.

  Controller 까지 올려면 요청 URL?(*)
  httP://서버IP:port번호/context명/서블릿매핑/요청맵핑값
  http://localhost:8090/context명/list ---> Controller
  http://localhost:8090/context명/add ---> Controller
  ==> Controller 까지 와야지 처리가능

  DispatcherServlet 에서는
  이전 ListServlet 에서 했던 요청처리 및 응답처리를
  직접 구현할 수 없다.
  따라서 다른 클래스(Controller)를 이용해서 이전 서블릿에서
  했던 작업들을 구현한다.

2> Spring MVC 실습
가. 의존성 실습

  <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>

나. port 번호 및 context 명 변경

port 번호 및 context 명 변경

  # application.properties
  # tomcat port 번호 변경
  server.port=8090

  # context 명 변경
  server.servlet.context-path=/app

요청은 http://localhost:8090/app/ 이고 whitelabel error page 보임.

  /app : context 명
  /    : 서블릿맵핑명(/app 뒤의 /)

다. Controller 작성
- 이전 서블릿/jsp 버전의 서블릿 역할 담당.(요청처리 및 응답처리)
- POJO 기반
- 빈으로 자동생성될 수 있도록 권장 패키지 구조를 준수 + @Controller 로 지정.
- 요청 url과 일치하는 요청맵핑값을 설정
  반드시 유일한 값으로 지정해야한다.
  ex>
      http://localhost:8090/app/요청맵핑값
      http://localhost:8090/app/list <== list 역할의 메서드 실행
      http://localhost:8090/app/add <== add 역할의 메서드 실행

  문법:
      
      // list 역할 가정
      @RequestMapping("/list") // `/list` 값이 요청맵핑값이다.
      public String 메서드명([변수, ...]) {

        return "(보여줄 jsp정보)"; // 기본적으로 forward 방식으로 jsp 에 위임한다.
      }

      // add 역할 가정
      @RequestMapping("/add") // `/add` 값이 요청맵핑값이다.
      public String 메서드명([변수, ...]) {

        return "(보여줄 jsp정보)";
      }

- Controller 의 메서드 리턴타입은 다양한 타입 지정 가능.
ex> String, void, DeptDTO, List<DeptDTO>, ...
    만약 String 이면 View 정보가 된다. (JSP 정보.)

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

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


라. jsp 저장
- jsp 및 css/js/image 등을 저장하기 위해서는
  기본적인 웹 어플리케이션 디렉토리 구조가 있음.

      src
     main
        java       ( *.java 가 저장됨 )
          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(타임리프) 권장.
  https://www.thymeleaf.org/

- spring boot의 웹 디렉토리 구조는 다음과 같다.

  src/main/resources
    ㄴ META-INF
          ㄴ resources
                ㄴ WEB-INF
                      ㄴ views(폴더)
                            ㄴ *.jsp <== WEB-INF 에 저장한 이유는 웹브라우저에서 직접 접근불가.
                                         Controller 가 알려준 jsp 정보를 가진 DispatcherServlet만 접근가능.

  <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>
  <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 Marketplace > 'eclipse web' 검색 > 
Eclipse Enterprise java and Web Developer tools 3.33 설치하기

package com.exam;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
	
	
}
package com.exam.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MainController {
	
	Logger logger = LoggerFactory.getLogger(getClass());
	
	// 요청 url 문법? http://localhost:8090/app/요청맵핑값 (*)
	
	// 요청 url? http://localhost:8090/app/list
	@RequestMapping("/list")
	public String list() { // String 값은 view 정보
		
		logger.info("logger: MainController:{}", "/list요청");
		return "/WEB-INF/views/hello.jsp"; // jsp 정보 (기본적으로 forward 방식 - url 이 변경이 안됨.)
	}
	
	// url 변경 -> redirect 방식
	// url 변경 x -> forward 방식
	
	// 요청 url? http://localhost:8090/app/add
	@RequestMapping("/add")
	public String add() {
		
		logger.info("logger: MainController:{}", "/add요청");
		return "/WEB-INF/views/hello.jsp"; // jsp 정보
	}
}
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <p>안녕하세요</p>
    <p>Hello</p>
</body>

</html>
# application.properties
logging.level.org.springframework=info

# tomcat port 번호 변경
server.port=8090

# context 명 변경
server.servlet.context-path=/app
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
	http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.18</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.exam</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
	</properties>
	<dependencies>
		
<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>
<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>


		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>