Study/Java

[Java] 40. Web Spring framework에서 Controller를 다루는 방법

v명월v 2021. 6. 3. 13:29

안녕하세요. 명월입니다.


이 글은 Web Spring framework에서 Controller를 다루는 방법에 대한 글입니다.


이전에 웹 서비스를 Spring framework를 이용해서 구축하는 방법에 대해서 설명했습니다.

link - [Java] 39. Spring web framework를 이용해서 웹 서비스 프로젝트를 만드는 법


Controller란 클라이언트(Browser)에서 요청이 오면 호스트를 뺀 주소값을 파싱해서 해당 메소드를 호출하는 기능입니다.

이 주소 값으로 파싱하여 메소드를 호출할 때, 가장 쉬운 방법으로는 각각의 페이지마다 메소드를 호출하는 방법이 가장 많이 사용하는 방법이지만, 그외에도 복수의 페이지나 정규식으로 호출하는 방법도 있습니다.또는 호출되는 헤더의 정보에 따라, 파라미터의 값의 따라 호출하는 메소드를 달리 할 수도 있습니다.

@RequestMapping

@RequestMapping는 웹 호출이 오면 주소 값의 의해 탐색이 가능하게 하는 어트리뷰트입니다. 이 어트리뷰트는 클래스와 메서드에서 사용할 수 있습니다.

package controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

// 컨트롤 표시하는 어트리뷰트
@Controller
// 클래스에 RequestMapping는 생략이 가능하다. 생략을 하게 되면 주소에서 호스트를 뺀 루트를 가르킨다.
@RequestMapping(value = "/home")
public class Helloworld {
  // 클래스에서 분류된 아래의 주소를 탐색한다.
  // 이 예제에서는 /home/index.html를 찾게 된다.
  @RequestMapping(value = "index.html")
  public String index(ModelMap modelmap, HttpSession session, HttpServletRequest req, HttpServletResponse res) {
    // view로 보내는 데이터
    modelmap.addAttribute("Data", "Helloworld");
    // view 파일 지정
    return "index";
  }
}

위 예제를 보시면 /home/index.html를 호출하면 위 index함수가 호출되는 것을 확인할 수 있습니다.


그리고 이 RequestMapping은 하나의 페이지 뿐만 아니라 여러개의 호출 주소를 하나의 메서드로 설정할 수도 있습니다.

package controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

// 컨트롤 표시하는 어트리뷰트
@Controller
// 클래스에 RequestMapping는 생략이 가능하다. 생략을 하게 되면 주소에서 호스트를 뺀 루트를 가르킨다.
@RequestMapping(value = "/home")
public class Helloworld {
  // 클래스에서 분류된 아래의 주소를 탐색한다.
  // 이 예제에서는 /home/index.html와 /home/helloworld.html를 찾게 된다.
  @RequestMapping(value = {"index.html", "helloworld.html"})
  public String index(ModelMap modelmap, HttpSession session, HttpServletRequest req, HttpServletResponse res) {
    // view로 보내는 데이터
    modelmap.addAttribute("Data", "Helloworld");
    // view 파일 지정
    return "index";
  }
}

단순하게 주소를 지정하는 것보다 정규식을 통해서도 매핑이 가능합니다.

package controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

// 컨트롤 표시하는 어트리뷰트
@Controller
// 클래스에 RequestMapping는 생략이 가능하다. 생략을 하게 되면 주소에서 호스트를 뺀 루트를 가르킨다.
@RequestMapping(value = "/home")
public class Helloworld {
  // 클래스에서 분류된 아래의 주소를 탐색한다.
  // 이 예제에서는 /home/(i또는 h로 시작하는)html를 찾게 된다.
  @RequestMapping(value = {"i*.html", "h*.html"})
  public String index(ModelMap modelmap, HttpSession session, HttpServletRequest req, HttpServletResponse res) {
    // view로 보내는 데이터
    modelmap.addAttribute("Data", "Helloworld");
    // view 파일 지정
    return "index";
  }
}

그리고 그 외에 호출 메서드 타입 별로 구분할 수도 있습니다.

package controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

// 컨트롤 표시하는 어트리뷰트
@Controller
// 클래스에 RequestMapping는 생략이 가능하다. 생략을 하게 되면 주소에서 호스트를 뺀 루트를 가르킨다.
@RequestMapping(value = "/home")
public class Helloworld {
  // 클래스에서 분류된 아래의 주소를 탐색한다.
  // 이 예제에서는 /home/(i또는 h로 시작하는)html를 찾게 된다.
  // 메서드 타입이 GET 방식만 호출하게 된다. 만약 POST 방식으로 오게 되면 다른 메서드를 호출한다.(지정된 것이 없으면 매핑된 메서드가 없다고 에러가 발생한다.)
  @RequestMapping(value = {"i*.html", "h*.html"}, method = RequestMethod.GET)
  public String index(ModelMap modelmap, HttpSession session, HttpServletRequest req, HttpServletResponse res) {
    // view로 보내는 데이터
    modelmap.addAttribute("Data", "Helloworld");
    // view 파일 지정
    return "index";
  }
}

그 외에도 파라미터, 해더의 값 등등을 구분으로도 설정이 가능합니다.

사양에 따라 구분을 하여 사용하는 경우도 있겠지만, 보통이라면 그렇게까지 구분을 해서 작성하지 않습니다. 너무 잘게 구분해 놓으면 오히려 가독성이 떨어지고 프로그램만 복잡해 질 수 있기 때문입니다. 사실 저도 value, method 이외에는 사용해 본 적이 없네요.

ModelMap

Spring에서 매핑 함수를 만들 때, 딱히 파라미터가 있어도 없어도 상관 없습니다. 매핑은 @RequestMapping의 구분으로 매핑되는 것이기 때문입니다.

그러나 브라우저에서 파라미터 값을 받을 때나 여러가지 세션 정보와 요청 값, 응답 값을 사용해야 하는 경우가 있습니다.

그래서 저는 기본적으로 네개의 파라미터, 즉 ModelMap modelmap, HttpSession session, HttpServletRequest req, HttpServletResponse res를 사용합니다.


먼저 HttpSession는 세션 정보가 있는 파라미터이고 HttpServletRequest는 요청 값, HttpServletResponse는 응답 값이 있는 파라미터 입니다.

이 부분은 기본적으로 Servlet과 같습니다.


여기서 modelmap은 view로 데이터를 넘길 때 사용합니다. return 값으로 view를 매핑합니다만 req나 res로는 값을 넘길 수가 없기 때문입니다.

package controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

// 컨트롤 표시하는 어트리뷰트
@Controller
// 클래스에 RequestMapping는 생략이 가능하다. 생략을 하게 되면 주소에서 호스트를 뺀 루트를 가르킨다.
@RequestMapping(value = "/home")
public class Helloworld {
  // 클래스에서 분류된 아래의 주소를 탐색한다.
  // 이 예제에서는 /home/index.html를 찾게 된다.
  @RequestMapping(value = "index.html")
  public String index(ModelMap modelmap, HttpSession session, HttpServletRequest req, HttpServletResponse res) {
    // view로 보내는 데이터
    modelmap.addAttribute("Data", "Helloworld");
    // view 파일 지정
    return "index";
  }
}
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
  </head>
  <body>
    <!-- modelmap으로 받은 데이터를 JSTL언어로 받는다. -->
    ${Data}
  </body>
</html>

위 예제에서는 index 함수에서 return 값을 index로 했기 때문에 view/index.jsp를 매핑합니다.

index.jsp 파일 안에 JSTL 언어로 Data값을 표시하는 것으로 되어 있습니다. index함수 안에서 modelmap을 이용해서 Data값을 설정합니다.

@RequestParam

우리가 클라이언트(Browser)로부터 데이터를 받는 경우가 있습니다.

GET방식으로는 url주소에서 ?마크로 구분하여 뒤의 값는 QueryString 값을 전달하게 되고, POST방식으로는 form값을 submit하여 프로토콜 헤더에 값을 전달하는 방법이 있습니다.

Spring에서 제시하는 방법으로는 파라미터에 @RequestParam을 이용하여 파라미터를 가져올 수 있습니다.

package controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

// 컨트롤 표시하는 어트리뷰트
@Controller
public class Helloworld {
  // 이 예제에서는 /home/index.html를 찾게 된다.
  @RequestMapping(value = "index.html")
  // 파라미터로 data의 값을 취득한다.
  public String index(@RequestParam String data, ModelMap modelmap, HttpSession session, HttpServletRequest req, HttpServletResponse res) {
    // 파라미터로 받은 값을 modelmap에 설정한다.
    modelmap.addAttribute("Data", data);
    // view 파일 지정
    return "index";
  }
}

그런데 저는 이 방법을 추천하지 않습니다. 왜냐하면 data값이 null이게 되면 에러가 되어 버립니다.

Servlet에서 사용한 HttpServletRequest변수를 통해서 getParameter함수로 값을 통하면 파라미터가 null이라도 함수 호출이 되고 따로 함수 안에서 null처리로 하면 됩니다.

그러나 클라이언트(Browser)로 넘어오는 값이 많을 경우는 getParameter함수로 전부 처리하기에는 소스가 많이 길어지게 됩니다.

그래서 저의 경우는 ModelAttribute 어트리뷰트를 통해서 클래스 형식으로 데이터를 받습니다.

package controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

// 컨트롤 표시하는 어트리뷰트
@Controller
public class Helloworld {
  // 이 예제에서는 /home/index.html를 찾게 된다.
  @RequestMapping(value = "index.html")
  // Node클래스에서 파라미터가 data의 경우는 data변수로 저장, data1의 경우는 data1변수로 저장된다.
  public String index(@ModelAttribute Node node, ModelMap modelmap, HttpSession session, HttpServletRequest req, HttpServletResponse res) {
    // 파라미터로 받은 값을 modelmap에 설정한다.
    modelmap.addAttribute("Data", node.getData() + node.getData1());
    // view 파일 지정
    return "index";
  }
}
// 파라미터를 받기 위한 Bean 클래스
class Node {
  // 파라미터 data를 받음
  private String data;
  // 파라미터 data1를 받음
  private String data1;
  // data 변수의 getter
  public String getData() {
    return data;
  }
  // data 변수의 setter
  public void setData(String data) {
    this.data = data;
  }
  // data1 변수의 getter
  public String getData1() {
    return data1;
  }
  // data1 변수의 setter
  public void setData1(String data1) {
    this.data1 = data1;
  }
}

위 처리를 보니 data의 경우는 데이터가 들어왔기 때문에 처리가 되었습니다만, data1의 경우는 파라미터를 넘기지 않았기 때문에 null이 표시가 되네요.

여기까지 클라이언트(Browser)에서 처리가 오면 Controller로 처리가 되고 view를 파싱해서 최종 결과를 응답하게 됩니다.

그런데 웹 서비스를 만들게 되면 당연히 웹 페이지 요청하면 웹 페이지를 응답하는 게 맞지만, 만약에 ajax의 경우는 어떻게 해야 할까요?

ajax는 응답해야 할 웹페이지가 필요한 것이 아니고, Controller에서 처리하는 값을 json이나 기타 데이터 형식으로 반환해야 합니다.

다음 게시글에서는 Spring framework에서 ajax를 처리하는 방법에 대해 설명하겠습니다.


여기까지 Web Spring framework에서 Controller를 다루는 방법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.