[TIL] 240417

Geehyun(장지현)·2024년 4월 17일

TIL

목록 보기
63/70
post-thumbnail

Today

  • @RequestMapping과 파생 어노테이션

    • @RequestMapping(value="경로")
      : 기본적으로 GET+POST 둘 다 처리 (Class, Method용)
      두번째 인자로 GET으로만 받을건지, POST로만 받을건지 지정 가능 (생략가능)
      => @RequestMapping(value="경로", method=RequestGET)
      => @RequestMapping(value="경로", method=RequestPOST)
      => @RequestMapping(value="경로", method={RequestGET, RequestGet})
    • @GetMapping("경로")
      : GET 요청만 처리 (Method용)
    • @PostMapping("경로")
      : POST 요청만 처리 (Method용)
    • 모두 경로를 처리하기 위한 용도로 사용
    • 스프링에서 가장 많이 사용하는 어노테이션
    • class와 method 둘다에 사용할 수 있는데 class매핑정보 + method매핑정보 합쳐서 경로가 결정된다(둘 중하나만 적어도 되고 그럴 경우 하나만 적은 그 경로로 지정된다

      선생님 TIP
      Controller는 들어왔는데, 그 안에서 view를 못찾는 경우와 Controller 자체를 찾지 못한 경우를 잘 구분할 줄 알아야함. (둘 다 404오류 발생 함)
      => Controller 자체를 못찾았을 때
      => Controller는 찾았으나 그 안에서 view페이지를 찾지 못했을 때

  • spring에서 주로 사용되는 어노테이션

    • Controller 선언부에 주로 사용되는 어노테이션
      • @Controller
      • @RestController
      • @RequestMapping
    • 메소드 선언부에 사용하는 어노테이션
      • @RequestMapping
      • @GetMapping / @PostMapping
      • @ResponseBody
    • 메소드의 파라미터에 사용하는 어노테이션
      • @RequestParam : Request에 있는 파라미터 받아서 처리하기 위해 사용 ( => spring의 파라미터 자동 수집)
      • @PathVariable : url 경로의 일부분을 변수로 처리하기 위해 사용
      • @ModelAttribute : spring에서는 request.setAttribute이 방식이 아니라 model에 포함해서 파라미터를 전달하는 식으로 사용함. 그 때 사용되는 어노테이션
    • 기타
      • @SessionAttribute
      • @Valid
      • @RequestBody : Rest방식에서 문자열이나, JSON 데이터 그대로 전송할 때 사용
  • Spring의 parameter 자동수집
    Spring은 사용자의 request에서 넘어온 parameter를 자동으로 수집함(별다른 처리 없이도 알아서)

    • Controller 내 메서드(parameter에 대한 별다른 처리 없이 그냥 찍음)

      @GetMapping("/view")
        public void view(int idx, String name, int age) {
            log.info("---------------------");
            log.info("BbsController => view()");
            log.info("idx : " + idx);
            log.info("name : " + name);
            log.info("age : " + age);
            log.info("---------------------");
        }
    • GET방식으로 Parameter를 냅다 보내봄

    • spring에서 알아서 자동수집해서 바로 사용 가능

      💡 단, 이렇게 기본으로 사용할 때는 parameter값이 안들어올때 500 에러가 발생함.

      이 때 사용할 수 있는게 @RequestParam어노테이션
      @RequestParam은 자동수집한 값에 대해 name을 대입하고 기본값을 지정해줄 수 있음

      @GetMapping("/view")
          public void view(@RequestParam(name="idx", defaultValue = "0") int idx,
                           @RequestParam(name="name", defaultValue = "")String name,
                           @RequestParam(name="age", defaultValue = "0")int age) {
              log.info("---------------------");
              log.info("BbsController => view()");
              log.info("idx : " + idx);
              log.info("name : " + name);
              log.info("age : " + age);
              log.info("---------------------");
          }

      이렇게 사용하면 매개변수로 입력한 Parameter 중 안들어오는것이 있더라도 입력한 기본값으로 받아 처리할 수 있음.

      💡 기본 자료형이 아닌 Parameter 받기

      1. Formatter 구현하기
        spring에서는 Formatter 인터페이스를 제공하고 있다! (parse, print 메서드를 갖고있음)
        이를 내가 원하는 방식으로 구현하면 됨

        package org.fullstack4.springmvc.controller.Formatter;
        
        import org.springframework.format.Formatter;
        
        import java.text.ParseException;
        import java.time.LocalDate;
        import java.time.format.DateTimeFormatter;
        import java.util.Locale;
        
        public class LocalDateFormatter implements Formatter<LocalDate> {
            @Override
            public LocalDate parse(String text, Locale locale) throws ParseException {
                return LocalDate.parse(text.trim(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            }
        
            @Override
            public String print(LocalDate object, Locale locale) {
                return DateTimeFormatter.ofPattern("yyyy-MM-dd").format(object);
            }
        }
      2. spring이 자동으로 끌어쓸수있도록 Bean으로 등록

        <!-- servlet-context.xml 의 일부! -->
            <!-- formatter 추가하는 부분 -->
            <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
                <property name="formatters">
                    <set>
                        <bean class="org.fullstack4.springmvc.controller.Formatter.LocalDateFormatter" />    
                    </set>
                </property>
            </bean>
            <!-- 만든 formatter가 더 있다면 <set>안에 쭉 넣어주면 됨 -->
            <mvc:annotation-driven conversion-service="conversionService" />
            <!-- 위에 열심히 만든거 어노테이션으로 사용하겠다는 의미 -->

        이렇게 등록해놓으면 spring이 알아서 Formatting이 필요할 때 알아서 잘 씀 (제대로 실습해보면 좋을듯)

      💡 와 그러면...파라미터 100개 받아야하면 @ReauestParameter 100번해야하나요?
      그럴땐 DTO객체를 만들어서 dto를 매개변수로 넣어주면 해당 dto의 필드명을 보고 spring이 알아서 수집하게 할 수 있다

      @PostMapping("/regist")
          public void registPost(BbsDTO dto,
                                 Model model) {
              log.info("---------------------");
              log.info("BbsController => registPost()");
              log.info("user_id : " + dto.getUser_id());
              log.info("title : " + dto.getTitle());
              log.info("display_date : " + dto.getDisplay_date());
              log.info("---------------------");
          }

      위 코드의 경우 DTO를 작성하고 아무런 작업도 안한 상태로 dto를 매개변수로 넣어준 상황입니다!
      다만 이럴경우 단져주는 파라미터 명과 dto 필드가 일치해야 함.

  • spring에서의 PRG 패턴 적용

    • PRG(Post-Redirect-GET) : Post로 데이터 처리를 요청받아 데이터를 처리 후 브라우저에 Redirect주소를 던지고 브라우저는 해당 주소로 GET방식으로 호출하는 방식
    @PostMapping("/regist")
      public String registPost(BbsDTO dto,
                               Model model,
                               RedirectAttributes redirectAttributes) {
          log.info("---------------------");
          log.info("BbsController => registPost()");
          log.info("user_id : " + dto.getUser_id());
          log.info("title : " + dto.getTitle());
          log.info("content : " + dto.getContent());
          log.info("display_date : " + dto.getDisplay_date());
          log.info("dto.toString : " + dto.toString());
          log.info("---------------------");
          redirectAttributes.addAttribute("title", dto.getTitle()); //redirect 주소에 쿼리스트링으로 값을 넣어주는 방식
          redirectAttributes.addFlashAttribute("title2", dto.getTitle()); //안보이게 값 보내주는 방식
          return "redirect:/bbs/list";
      }
    • spring에서는 PRG 패턴에서 Redirect주소를 던질 때 redirect:경로 형식의 문자열을 그대로 리턴해주는 방식으로 사용할 수 있습니다. (아무런 작업없이도 spring이 알아서 처리 함)
    • redirect 주소를 넘길 때,
      1) redirectAttributes.addAttribute(키, 값)으로 쿼리스트링 형식으로 같이 넘겨주는 방식
      2) redirectAttributes.addFlashAttribute(키,값)으로 url 노출 없이 값을 넘겨주는 방식
    • 실행 결과
  • spring MVC의 예외처리
    spring MVC에서는 여러가지 예외를 처리하는 가장 일반적인 방식으로, @controllerAdvice 어노테이션을 이요하며, @controllerAdvice가 선언된 클래스를 Bean으로 처리하여 spring에서 해당 예외가 발생시 불러와 처리합니다.

    • 예시 : NumberFormatException
      int타입에 stirng을 넣었을 때 numberFormatException이 발생

      @GetMapping("/list2")
          public void list2(String s1, int i2) {
              log.info("--------------------");
              log.info("BbsController => list2()");
              log.info("s1 : " + s1);
              log.info("i2 : " + i2);
              log.info("--------------------");
          }


      => @controllerAdvice 이용해서 예외처리

      package org.fullstack4.springmvc.controller.exception;
      
      import lombok.extern.log4j.Log4j2;
      import org.springframework.web.bind.annotation.ControllerAdvice;
      import org.springframework.web.bind.annotation.ExceptionHandler;
      import org.springframework.web.bind.annotation.ResponseBody;
      
      @Log4j2
      @ControllerAdvice
      public class CommonException {
          @ResponseBody
          @ExceptionHandler(NumberFormatException.class)
          public String exceptionNumber(NumberFormatException numberFormatException) {
              log.info("-----------------------------");
              log.info("CommonException >> exceptionNumber");
              log.info(numberFormatException.getMessage());
              log.info("-----------------------------");
              return "Number format exception";
          }
      }

      결과!

      💡 와 그러면 예외마다 저렇게 해줘야하나요??

      @ResponseBody
          @ExceptionHandler(Exception.class)
          public String exception(Exception exception) {
              return exception.getMessage();
          }

      이렇게 냅다 Exception 채로 한번에 예외처리를 할 수도 있음.
      이 때도 일반적인 예외처리할 때 처럼 제일 예외 범위가 좁은것부터 넓은것 순으로 예외처리 클래스 내 예외메서드를 작성해야 옳습니다.

      💡 에러 공통페이지 작성해보기

      <!-- 에러 페이지 -->
      <%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
      <%@ page trimDirectiveWhitespaces="true" %>
      <html>
      <head>
          <title>페이지를 찾을 수 없습니다.</title>
      </head>
      <body>
          <h1>페이지를 찾을 수 없습니다.</h1>
      </body>
      </html>
      @ExceptionHandler(NoHandlerFoundException.class)
          @ResponseStatus(HttpStatus.NOT_FOUND)
          public String notFound(){
              return "custom404";
          }
      <!-- web.xml 중 일부 -->
      <servlet>
              <servlet-name>appServlet</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              <init-param>
                  <param-name>contextConfigLocation</param-name>
                  <param-value>/WEB-INF/servlet-context.xml</param-value>
              </init-param>
              <init-param>
                  <param-name>throwExceptionIfNoHandlerFound</param-name>
                  <param-value>true</param-value>
              </init-param>
              <load-on-startup>1</load-on-startup>
          </servlet>

Review

  • @RequestMapping과 파생 어노테이션을 이용해서 기존 Servlet만 사용 시에는 컨트롤러 하나 당 doGet, doPost로 나눠서 그 안에서 다 처리해주느라
    1) 서비스 수 만큼 Controller를 따로 만들거나
    2) url-patterns로 요청 url을 다 쪼개서 if문으로 나눠서 처리주거나
    했었는데, 컨트롤러 내 에서 method를 쪼개서 맞는 url에 매핑만 해주면 되니 아주 편리해보임
  • 내일부터 spring 사용해서 게시판 만들기 프로젝트 진행예정!!
profile
블로그 이전 했습니다. 아래 블로그 아이콘(🏠) 눌러서 놀러오세요

0개의 댓글