스프링 MVC의 Controller

cy8erpsycho·2023년 8월 23일
0

스프링

목록 보기
8/29
post-thumbnail

스프링 MVC의 Controller


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

다른 프레임워크들과 달리 스프링 MVC는 어노테이션을 중심으로 구성되기 때문에 각 어노테이션의 의미에 대해 주의하며 학습하자.

@Controller, @RequestMapping

@Controller

해당 클래스의 인스턴스를 스프링의 빈으로 등록하고 컨트롤러로 사용하여 <component-scan>과 같이 활용된다.
다시 말하면, SampleController의 클래스 선언부에는 @Controller라는 스프링MVC에서 사용하는 어노테이션을 적용하고 있다. 작성된 SampleController 클래스는 위의 그림과 같이 자동으로 스프링의 객체(Bean)로 등록되는데 servlet-context.xml에 그 이유가 있다.

위의 servlet.xml에는 <context:component-scan> 이라는 태그를 이용해서 지정된 패키지를 조사하도록 되어있다. 해당 패키지에 선언된 클래스들을 조사하면서 스프링에서 객체(Bean) 설정에 사용되는 어노테이션들을 가진 클래스들을 파악하고 필요하다면 이를 객체로 생성해서 관리하게 된다.

<context:component-scan>

<context:component-scan> 태그는 Spring Framework에서 사용되며, 이 태그를 통해 지정된 패키지 내의 클래스들을 스캔하여 Spring 컨테이너가 관리할 수 있는 빈(Bean)으로 등록합니다.

패키지를 지정할 때 base-package 속성을 사용하는데, 이는 스캔을 시작할 패키지의 시작 위치입니다. 위 예시에서는 com.zerock.controller 패키지를 시작점으로 설정하였습니다.

자세히 설명하면 다음과 같습니다:

  1. 패키지 스캔: <context:component-scan> 태그가 선언된 패키지(com.zerock.controller와 그 하위 패키지들) 내의 모든 클래스를 스캔합니다.

  2. 어노테이션 탐지: 스캔 중인 클래스들 중에서 Spring이 제공하는 특별한 어노테이션(@Controller, @Service, @Repository, @Component 등)이 선언된 클래스를 찾습니다.

  3. 빈 생성과 등록: 해당 어노테이션을 가진 클래스를 찾으면, 이 클래스의 인스턴스를 생성하고 이를 Spring의 애플리케이션 컨텍스트에 빈(Bean)으로 등록합니다. 이렇게 등록된 빈은 애플리케이션 전체에서 사용할 수 있게 됩니다.

  4. 의존성 주입: 또한, 이 클래스들은 필요한 경우 다른 빈과 자동으로 연결될 수 있습니다. 이를 의존성 주입(Dependency Injection)이라고 합니다.

이러한 과정을 통해 Spring은 설정을 크게 단순화하고, 개발자가 직접 객체를 생성하고 관리하는 번거로움을 줄여줍니다. 그 결과 개발자는 비즈니스 로직에 더 집중할 수 있게 됩니다.

클래스 스캔

"스캔 중인 클래스"라는 표현은 Spring Framework가 base-package 속성에서 지정한 패키지와 그 하위 패키지에 있는 Java 클래스 파일들을 탐색하면서 어노테이션 등을 확인하는 것을 의미합니다. 이 때 클래스는 컴파일된 .class 파일들을 대상으로 합니다.

Java에서 소스 코드는 .java 파일에 작성되며, 이 파일은 컴파일 과정을 거쳐 .class 파일로 변환됩니다. 이 .class 파일은 바이트 코드로 되어 있어, JVM(Java Virtual Machine)에서 실행할 수 있습니다.

Spring은 이러한 .class 파일들 중에서 특정 어노테이션(@Controller, @Service, @Repository, @Component 등)이 붙어 있는 클래스를 찾아 그것을 Spring 컨테이너에서 관리하는 빈(Bean)으로 등록합니다.

따라서 "스캔 중인 클래스"라고 할 때는, 이런 컴파일된 .class 파일들을 의미하는 것입니다.

@RequestMapping

특정한 URI에 대한 처리를 해당 컨트롤러나 메서드에서 처리한다. @RequestMapping은 현재 클래스의 모든 메서드들의 기본적인 URL 경로가 된다. 예를 들어, SampleController 클래스를 다음과 같이 '/sample'이라는 경로로 지정하면

  • /sample/aaa
  • /sample/bbb

위와 같은 URL은 모두 SampleController에서 처리된다.
@RequestMapping 어노테이션은 클래스의 선언과 메서드 선언에 사용할 수 있다.

package com.zerock.controller;

import java.text.SimpleDateFormat;

import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
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.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.zerock.domain.SampleDTO;
import com.zerock.domain.TodoDTO;

import lombok.extern.log4j.Log4j;

@Controller
@RequestMapping("/sample")
@Log4j
public class SampleController {
	/*
	 * private static final Logger logger =
	 * LoggerFactory.getLogger(SampleController.class);
	 */
	
	@InitBinder()
	public void initBinder(WebDataBinder binder) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(sdf, false));
	}
	
	
	@GetMapping("/ex03")
	public String ex03(TodoDTO todo, @ModelAttribute("page") int page) {
		//logger.info("DTO 확인 : " + todo);
		log.info("DTO 확인 : " + todo);
		log.info("page:" + page);
		return "sample/ex03";
	}
	
	@GetMapping("/ex06")
	public @ResponseBody SampleDTO ex06() {
		log.info("ex06 실행");
		SampleDTO dto = new SampleDTO();
		dto.setName("홍길동");
		dto.setAge(1);
		return dto;
	}

}


@RequestMapping의 변화

  • 스프링 4.3 전까지는 @RequestMapping( method =‘get’) 방식으로 사용
  • 스프링 4.3이후에는 @GetMapping, @PostMapping등으로 간단히 표현 가능

아래와 같이 비교해서 학습하는 것이 좋다.

@RequestMapping은 GET, POST 방식 모두를 지원해야 하는 경우에 배열로 처리해서 지정할 수 있다. 일반적인 경우에는 GET, POST 방식만을 사용하지만 최근에는 PUT, DELETE 방식 등도 사용하는 추세이다. @GetMapping의 경우 오직 GET 방식에만 사용할 수 있으므로, 간편하지만 기능에 대한 제약은 많은 편이다.



Controller의 파라미터 수집

Controller를 작성할 때 가장 편리한 기능은 파라미터가 자동으로 수집되는 기능이다. 이 기능을 이용하면 매번 request.getParameter()를 이용하는 불편함을 없앨 수 있다.

  • 스프링 MVC의 컨트롤러는 메서드의 파라미터를 자동으로 수집, 변환하는 편리한 기능을 제공한다.
  • Java Beans 규칙에 맞게 작성되어야 한다.
    • 생성자가 없거나 빈 생성자
    • 올바른 규칙으로 만들어진 Getter/Setter

SamplerController의 메서드가 SampleDTO를 파라미터로 사용하게되면 자동으로 setter 메서드가 동작하면서 파라미터를 수집하게 된다.

프로젝트를 실행할 때 기본경로는 '/controller'라는 경로로 동작하므로, 이를 '/'로 동작하도록 변경해서 실행해야 한다.

파라미터의 수집과 변환

Controller라 파라미터가 수집하는 방식은 파라미터 타입에 따라 자동으로 변환하는 방식을 이용한다.

	@GetMapping("/ex01")
	public String test(@RequestParam("name") String n1, 
    @RequestParam("age") int age, Model model) {		
		System.out.println("name : " + n1 + ", age : " + age);
		logger.info("name : " + n1 + ", age : " + age);
		model.addAttribute("name", n1);
		model.addAttribute("age", age);
		return "ex01";
	}

@RequestParam("age") int age 구문은 Java의 Spring Framework에서 HTTP 요청의 파라미터를 메서드의 인자로 바인딩할 때 사용됩니다. 이 구문을 사용하면, Spring은 자동으로 "age" 라는 키를 가진 HTTP 요청 파라미터를 찾아 그 값을 메서드의 age 인자에 할당합니다. 이때 타입 변환이 필요하다면, Spring이 그것을 알아서 처리해 줍니다.

예를 들어, HTTP 요청이 http://example.com/someEndpoint?age=30 이라고 가정해 봅시다. 이때 age 파라미터의 값은 "30"인데, 이것은 문자열입니다. 하지만 @RequestParam("age") int age 라고 선언되어 있으므로, Spring은 이 문자열을 자동으로 int 타입으로 변환하여 age 변수에 저장합니다.

이러한 자동 형변환이 가능한 이유는 Spring Framework의 데이터 바인딩 기능 때문입니다. 이 기능을 통해 프레임워크는 요청 파라미터, 헤더, 바디 등의 정보를 메서드의 인자로 적절하게 변환해줍니다.

data validation

  1. 프론트에서 백으로 보내기전에 검사(주로 자바스크립트 이용해서 검사)
  2. 서버로 넘어왔을때, 데이터 검증
  3. DB에서 검증(거의 안함 서버로 넘어왔을때 거의 처리함)

http
300번대 응답 : 페이지를 다른곳으로 이동시키는 목적ㄱㄱㄱㄱㄱ
400번대 오류 : 주로 클라이언트에서 발생
500번대 오류 : 요청은 제대로 됐으나 서버 내부에서 발생

Redirect : A라는 요청을 했는데 B로 바꿔주는 것. 리디렉트가 일어나면 요청과 응답이 2번일어난다.

RedirectAttribute

  • 화면에 한번만 전달되는 파라미터를 처리하는 용도
  • 내부적으로 HttpSession객체에 담아서 한번만 사용된 후 폐기

Controller의 리턴타입

  • String: jsp를 이용하는 경우에는 jsp 파일의 경로와 파일이름을 나타내기 위해서 사용
  • void: 호출하는 URL과 동일한 이름의 jsp를 의미
  • VO, DTO 타입: 주로 JSON 타입의 데이터를 만들어서 반환하는 용도로 사용 (추가적인 라이브러리 필요).
  • ResponseEntity 타입: response할 때 Http 헤더 정보와 내용을 가공하는 용도로 사용 (추가적인 라이브러리 필요).
  • Model, ModelAndView: Model로 데이터를 반환하거나 화면까지 같이 지정하는 경우에 사용 (최근에는 많이 사용하지 않습니다.).
  • HttpHeaders: 응답에 내용 없이 Http 헤더 메시지만 전달하는 용도로 사용

WEB-INF : 상위 디렉토리로 이동하지 못하게 방화벽역할을 한다.


주로 리턴타입으로 String, DTO ResponseEntity을 사용한다.

void 타입

메소드의 리턴 타입을 void로 지정할 때, 일반적인 경우에는 해당 URL의 경로를 그대로 jsp 파일의 이름으로 사용하게 된다.

'/sample/ex05'를 호출하면 위와 같은 결과를 보게 된다.

servlet-context.xml의 설정과 맞물려 URL경로를 View로 처리하기 때문에 생기는 결과이다.

String 타입

  • 상황에 따라 다른 화면을 보여줄 필요가 있을 경우에 유용하게 사용
  • String 타입에는 다음과 같은 특별한 키워드를 붙여서 사용할 수 있음
    - redirect: 리다이렉트 방식으로 처리하는 경우
    - forward: 포워드 방식으로 처리하는 경우

return에는 servlet-context.xml의 설정을 확인해서 반환할 파일의 경로를 써주도록 한다. ex03의 경우 sample 폴더 안에 위치하므로, return 에도 그대로 넣어주도록 한다.

객체 타입

💡@ResponseBody

@ResponseBody는 Spring Framework에서 사용되는 어노테이션입니다. 이 어노테이션은 컨트롤러 메소드가 반환하는 객체를 HTTP 응답 본문으로 직접 전송하려고 할 때 사용됩니다.

다시 말해, @ResponseBody를 사용하면 반환된 객체가 ViewResolver를 통해 뷰로 해석되는 것을 피하고, 대신 해당 객체는 HTTP 응답 본문으로 직접 변환됩니다. 변환 방법은 주로 Jackson 라이브러리와 같은 JSON 변환 라이브러리를 사용하여 JSON 형식으로 변환됩니다. 만약 해당 라이브러리가 클래스 경로에 포함되어 있지 않다면, 이 변환이 실패하게 됩니다.

예제에서, ex06 메소드는 SampleDTO 객체를 반환하며, 이 객체는 @ResponseBody 어노테이션의 영향으로 JSON 형태로 직접 변환되어 클라이언트에게 응답으로 전송됩니다. 따라서 클라이언트는 다음과 같은 JSON 응답을 받게 될 것입니다:

{
  "name": "홍길동",
  "age": 1
}

@ResponseBody는 RESTful 웹 서비스를 구현할 때 흔히 사용됩니다.


객체를 문자열로 어떻게 바꿀까?

스프링에서는 객체를 문자열을 바꿀떄 json을 기본으로 채택하고있음

위와 같이pom.xml에 dependency 추가후 maven update한다.

그 후 다시 해당 URL에 요청을 한 결과는 다음과 같다.

json에서 중요한것은 key:value의 형태로 문자열을 출력한다는 것이다.

ResponseBody에 직렬화된 데이터가 전달됨.

ResponseEntity 타입

  • HTTP헤더 정보와 추가적인 데이터를 전달할 때 사용

파일 업로드 처리

GET /controller/sample/ex03?Name=%ED%99%8D%EA%B8%B8%EB%8F%99&age=11&date=2023-08-22&page=1 HTTP/1.1 URL encoded 방식 &으로 구분후 =으로 구분하고 key value 해석

파일 전송할때 : multi part form data 분석해서 파일이 제대로 업로딩이 될 수 있도록 했다.

servlet-context.xml 설정

  • multipartResolver라는 이름으로 스프링 빈 설정

파일업로드를 위한 HTML

  • <form>태그 내 enctype="multiPart/form-data">

업로드되는 파일의 처리

  • MultipartFile을 이용해서 처리


컨트롤러의 예외(Exception) 처리

  • @ExceptionHandler와 @ControllerAdvice를 이용한 처리
  • @ResponseEntity를 이용하는 예외 메시지 구성

@ControllerAdvice

  • 예외처리와 원래의 컨트롤러가 혼합된 형태의 클래스가 작성되는 방식
  • @ExceptionHandler는 해당 메서드가 ( )들어가는 예외 타입을 처리한다는 것을 의미

CommonExceptionAdvice

package com.zerock.exception;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import lombok.extern.log4j.Log4j;

@ControllerAdvice
@Log4j
public class CommonExceptionAdvice {
	
	@ExceptionHandler(Exception.class) // 일반적인 예외를 모두 처리하겠다
	public String except(Exception e, Model model) { // view에 exception에 대한 내용을 전달하기 위해
		log.error("오류 핸들러 : " + e.getMessage());
		model.addAttribute("exception", e);
		return "error_page";
	}
}

error_page.jsp
servlet-context.xml

0개의 댓글