본문 바로가기

스프링 Spring

21. 04. 02.

XML 매퍼와 같이 쓰기

  • XML 파일 위치: src/main/resources에 패키지명으로 폴더들을 만든 후 (가급적이면) Mapper 인터페이스 이름으로 작성. (XML File)

TimeMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="site.levinni.mapper.TimeMapper">
    <select id="getTime2" resultType="string">
        SELECT SYSDATE FROM DUAL
    </select>
</mapper>
  • dtd는 구글링 해서 가져온 것!

TimeMapper.java에 메서드 추가.
String getTime2();

TimeMapperTest.java에 테스트 코드 추가.
@Test public void testGetTime2() { log.info(mapper.getTime2()); }

@select와 같은 결과를 볼 수 있음. 쿼리문이 길다면 어노테이션 대신 XML을 쓰는 것도 좋은 방법!


5. 스프링 MVC의 기본 구조

5.1 스프링 MVC 프로젝트의 내부 구조

스프링 MVC 프로젝트를 사용한다는 것은 내부적으로는 root-context.xml(일반 Java 영역)과 servlet-context.xml(Web 관련 영역)을 같이 연동해서 구동한다는 것.

5.2 예제 프로젝트의 로딩 구조

프로젝트의 구동 순서
web.xml(Tomcat 구동 관련 설정)에서 시작함. -> root-context.xml이 처리되면 파일에 있는 Bean 설정들이 동작함. Bean들은 스프링의 영역(context) 안에 생성되고, 객체들 간의 의존성이 처리 됨. -> 스프링 MVC에서 사용하는 DispatcherServlet(서블릿과 관련된 설정)이 동작함.

DispatcherServlet 클래스는 스프링 MVC 구조에서 가장 핵심적인 역할을 함!

5.3 스프링 MVC의 기본 사상

Servlet/JSP에서는 HttpServletRequest/HttpServletResponse를 이용해 요청받은 정보를 처리했는데, 스프링 MVC를 쓰면 개발자들이 직접 Servlet/JSP의 API의 사용하는 상황이 현저히 줄어듦. 스프링이 중간에 연결 역할을 하기 때문!

5.4 모델 2와 스프링 MVC

request를 jsp가 받느냐, servlet이 받느냐에 따라 모델1과 모델2로 나뉨.


모델2 기본 구조



스프링 MVC 기본 구조
<출처: 코드로 배우는 스프링 웹 프로젝트>

모든 Request는 DispatcherServlet을 통하도록 설계되는데, 이런 방식을 Front-Controller 패턴이라고 함.


6. 스프링 MVC의 Controller

  • HttpServletRequest, HttpServletResponse를 거의 사용할 필요 없이 기능 구현
  • 다양한 타입의 파라미너 처리·리턴 타입 사용 가능
  • GET, POST 등 전송 방식에 대한 처리를 어노테이션으로 처리 가능
  • 상속/인터페이스 방식 대신 어노테이션만으로도 설정 가능

6.1 @Controller, @RequestMapping

SampleController.java

public class SampleController {

    @RequestMapping
    public void basic(HttpServletRequest req) {
        log.info("basic .....................");
        req.setAttribute("value", "hello world");
    }
}


  • 메서드 이름으로 된 jsp 파일을 자동으로 찾으려고 함.
  • basic.jsp에 <h1>${value}</h1> 작성, req.setAttribute를 통해 브러우저에 나타낼 수 있음.


6.2 @RequestMapping의 변화

몇 개의 속성을 추가할 수 있는데, 가장 많이 사용하는 건 method 속성. (GET, POST 방식 구분할 때)

SampleController.java

  package site.levinni.controller;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import lombok.extern.log4j.Log4j;
import site.levinni.domain.SampleDTO;
import site.levinni.domain.SampleDTOList;
import site.levinni.domain.TodoDTO;

@Controller
@RequestMapping("sample/*")
@Log4j
public class SampleController {

    @RequestMapping
    public void basic(HttpServletRequest req) {
        log.info("basic .....................");
        req.setAttribute("value", "hello world");
    }

    @RequestMapping(value = "basicGet", method = { RequestMethod.GET, RequestMethod.POST })
    public void basicGet() {
        log.info("basic get.....................");
    }

    @GetMapping("/basicGet2")
    public void basicGet2() {
        log.info("basic only get.....................");
    }
}
  • 스프링 4.3 버전부턴 @RequestMapping을 줄여서 @GetMapping, @PostMapping을 쓸 수 있음.
  • GET, POST 방식 모두를 지원해야 할 경우 배열로 지정할 수 있음.
  • GET, POST, PUT, DELETE 네 가지 방식을 REST라고 함.

6.3 Controller의 파라미터 수집

파라미터가 자동으로 수집되는 기능 덕분에 request.getParameter()를 매번 써야 하는 불편함을 없앨 수 있음.

SampleDTO.java

package site.levinni.domain;

import lombok.Data;

@Data
public class SampleDTO {
    private String name;
    private int age;
}

SampleController.java

public class SampleController {

    @RequestMapping
    public void basic(HttpServletRequest req) {
        log.info("basic .....................");
        req.setAttribute("value", "hello world");
    }

    @RequestMapping(value = "basicGet", method = { RequestMethod.GET, RequestMethod.POST })
    public void basicGet() {
        log.info("basic get.....................");
    }

    @GetMapping("/basicGet2")
    public void basicGet2() {
        log.info("basic only get.....................");
    }

    @GetMapping("ex01")
    public String ex01(SampleDTO dto, Model model) {
        model.addAttribute("dto", dto);
        log.info(dto);
        return "sample/basic";
    }
        @GetMapping("ex02")
    public void ex02(String name, @RequestParam("age") int myAge) {
        log.info(name);
        log.info(myAge);
    }
}
  • SampleDTO를 파라미터로 쓰면 자동으로 setter가 동작하면서 파라미터를 수집하게 됨. (자동으로 타입을 변환해서 처리함!)



basic.jsp에 <h1>${dto.name}</h1> <h1>${dto.age}</h1> 추가한 것.
파라미터는 쿼리스트링에 들어간다!
콘솔에도 로그 잘 찍힘.

✅리스트, 배열 처리

같은 name의 파라미터가 여러 개 전달되는 경우, ArrayList<>나 String[] 등을 이용해서 처리 가능.

form.jsp (src/main/webapp/WEB-INF/views)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form method="post">
    <h2>취미를 고르세요</h2>
    <ul>
        <li><label><input type="checkbox" name="hobby" value="fishing">낚시</label></li>
        <li><label><input type="checkbox" name="hobby" value="game">게임</label></li>
        <li><label><input type="checkbox" name="hobby" value="reading">독서</label></li>
        <li><label><input type="checkbox" name="hobby" value="programming">개발</label></li>
    </ul>
    <button>전송</button>
</form>
</body>
</html>

SampleController.java

public class SampleController {

    @RequestMapping
    public void basic(HttpServletRequest req) {
        log.info("basic .....................");
        req.setAttribute("value", "hello world");
    }

    @RequestMapping(value = "basicGet", method = { RequestMethod.GET, RequestMethod.POST })
    public void basicGet() {
        log.info("basic get.....................");
    }

    @GetMapping("/basicGet2")
    public void basicGet2() {
        log.info("basic only get.....................");
    }

    @GetMapping("ex01")
    public String ex01(SampleDTO dto, Model model) {
        model.addAttribute("dto", dto);
        log.info(dto);
        return "sample/basic";
    }
        @GetMapping("ex02")
    public void ex02(String name, @RequestParam("age") int myAge) {
        log.info(name);
        log.info(myAge);
    }
    @GetMapping("form")
    public String form() {
        return "form";
    }

    @PostMapping("form")
    public String form2(@RequestParam("hobby") String[] hobby) {
        log.info(Arrays.toString(hobby));
        return "redirect:form";
    }
}
  • (List<String>hobby)도 됨.
  • return에 redirect 빼먹지 말자!


checkbox 선택 후 전송 button 눌렀을 때 찍힌 로그.

✅객체 리스트

전달하는 데이터가SampleDTO같이 객체 타입이고, 여러 개를 처리해야 할 때?

package site.levinni.domain;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import lombok.Data;

@Data
public class SampleDTOList {
    @Autowired
    private List<SampleDTO> list;
}
  • new 연산자를 쓰지 않고 할 수 있음!!

SampleController.java

public class SampleController {

    @RequestMapping
    public void basic(HttpServletRequest req) {
        log.info("basic .....................");
        req.setAttribute("value", "hello world");
    }

    @RequestMapping(value = "basicGet", method = { RequestMethod.GET, RequestMethod.POST })
    public void basicGet() {
        log.info("basic get.....................");
    }

    @GetMapping("/basicGet2")
    public void basicGet2() {
        log.info("basic only get.....................");
    }

    @GetMapping("ex01")
    public String ex01(SampleDTO dto, Model model) {
        model.addAttribute("dto", dto);
        log.info(dto);
        return "sample/basic";
    }
        @GetMapping("ex02")
    public void ex02(String name, @RequestParam("age") int myAge) {
        log.info(name);
        log.info(myAge);
    }
    @GetMapping("form")
    public String form() {
        return "form";
    }

    @PostMapping("form")
    public String form2(@RequestParam("hobby") String[] hobby) {
        log.info(Arrays.toString(hobby));
        return "redirect:form";
    }
        @GetMapping("ex02Bean")
    public void ex02Bean(SampleDTOList list) {
        log.info(list);
    }
}


[]가 특수문자이기 때문에 400 error가 발생했는데, []를 %5B와 %5D로 바꾸기 위해 크롬 관리자 도구(f12)에서 encodeURI 이용.

✅InitBinder

binding : 파라미터 수집과 같은 말.
경우에 따라선 파라미터를 직접 변환해서 처리해야 하기도 함.

TodoDTO.java

package site.levinni.domain;

import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.Data;

@Data
public class TodoDTO {
    private String title;
    private Date date;
}

SampleController.java

public class SampleController {
     @InitBinder
     public void iniBinder(WebDataBinder binder) {
     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
     binder.registerCustomEditor(Date.class, new CustomDateEditor(sdf,
     false));
     }

         @GetMapping("ex03")
    public void ex03(TodoDTO dto) {
        log.info(dto);
    }
}

http://localhost:8080/sample/ex03?title=%EC%A0%9C%EB%AA%A9%EC%9E%84?date=2021-04-02


✅DateTimeFormat

@InitBinder 안 쓰고 하는 법.

TodoDTO.java

package site.levinni.domain;

import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.Data;

@Data
public class TodoDTO {
    private String title;
    @DateTimeFormat(pattern="yyyy-mm-dd")
    private Date date;
}

6.4 Model이라는 데이터 전달자

Model 객체는 JSP에 컨트롤러에서 생성된 데이터를 담아서 전달하는 역할을 함.
모델 2 방식의 request.setAttribute()와 유사한 역할!

    @GetMapping("ex04")
    public void ex04(SampleDTO dto, @ModelAttribute("page") int page) {
        log.info(dto);
        log.info(page);

        throw new RuntimeException("던짐");
    }
    // @GetMapping("ex04")
    // public void ex04(SampleDTO dto, int page, Model model) {
    // log.info(dto);
    // log.info(page);
    // model.addAttribute("page", page);
    // } 이게 불편하니깐 위처럼 어노테이션 쓴 것.

ex04.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>sapleDTO : ${sampleDTO}</h1>
<h1>page : ${page}</h1>
</body>
</html>

6.5 Controller의 리턴 타입

    @GetMapping("ex04Test")
    public @ResponseBody SampleDTO ex04Test(SampleDTO dto) {
        return dto;
    }

    @GetMapping("ex04Test2")
    public ResponseEntity<SampleDTO> ex04Test2(SampleDTO dto) {
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

        @GetMapping("ex07")
    public ResponseEntity<String> ex07() {
        log.info("ex07............");
        String msg = "{\"datas\" : [" + "{\"name\" : \"홍길동\"}, " + "{\"name\" : \"홍길동\"} " + "]}";
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-type", "application/json;charset=UTF-8");
        return new ResponseEntity<String>(msg, headers, HttpStatus.OK);
    }

✅파일 업로드 처리

  • commons-fileupload 라이브러리 추가
  • d드라이브에 upload/tmp 생성
  • wervlet-context에 Bean 생성

SampleController.java 일부

    @GetMapping("exUpload")
    public void exUpload() {
        log.info("exUpload.....................");
    }

    @PostMapping("exUploadPost")
    public String exUploadPost(List<MultipartFile> files) {
        files.forEach(file -> {
            log.info("-----------------------------");
            log.info(file.getOriginalFilename());
            log.info(file.getSize());
        });
        return "redirect:exUpload";
    }


6.6 Controller의 Exception 처리

'스프링 Spring' 카테고리의 다른 글

21. 04. 06.  (0) 2021.04.06
Part3. 기본적인 웹 게시물 관리 21. 04. 05.  (0) 2021.04.06
21. 04. 01.  (0) 2021.04.01
Spring JDBC 21. 03. 31.  (0) 2021.03.31
AOP 끝 21. 03. 31.  (0) 2021.03.31