4차산업혁명의 일꾼/Java&Spring웹개발과 서버 컴퓨터

스프링의 정석 : 남궁성과 끝까지 간다 1 : 스프링MVC , SpringMVC

르무엘 2023. 3. 13. 01:14

스프링의 정석 : 남궁성과 끝까지 간다  스프링MVC

 스프링MVC

Spring MVC

1. 들어가며

[Servlet -> JSP -> Spring] -> SpringBoot

Spring :  STS3 , 인텔리제이(유료에서 지원 -> 웹,db지원)

SpringBoot : STS4, 인텔리제이(무료지원)

 

2. Http의 요청과 응답

http1.1 : Connection 하나당 하나의 요청

http2 : Connection 하나당 여러개 요청 가능, 응답은 Stream으로 주고 받음

 

3. web.xml 과 server.xml

web.xml => 애너테이션

server.xml => 포트, DB, 웹리소스참조경로, 이벤트리스너 설정

 

4. GET/ POST 요청 : GET은 공유에 유리(소용량) , POST는 보안에 유리(대용량)

 

5. MVC 패턴 => 관심사의 분리

M(데이터) View(출력)  Controller(처리)

스프링MVC 구조                                      DispatcherServlet <=> InternalResoureView

                                                            JstlView

 

6. 서블릿과 JSP

7. @RequestParam과 @ModelAttribute

8. @RequestMapping

 

@Controller
public class YoilTellerMVC6 {
   @ExceptionHandler(Exception.class)
   public String catcher(Exception ex, BindingResult result) {
         System.out.println("result="+result);
         FieldError error = result.getFieldError();
         
         System.out.println("code="+error.getCode());
         System.out.println("field="+error.getField());
         System.out.println("msg="+error.getDefaultMessage());
      ex.printStackTrace();
      return "yoilError";
   }
   
    @RequestMapping("/getYoilMVC6") // http://localhost/ch2/getYoilMVC6
//    public String main(@ModelAttribute("myDate") MyDate date, Model model) { // 아래와 동일
        public String main(MyDate date, BindingResult result) {
   System.out.println("result="+result);
 
        // 1. 유효성 검사
       if(!isValid(date)) 
          return "yoilError";  // 유효하지 않으면, /WEB-INF/views/yoilError.jsp로 이동
       
        // 2. 처리
//     char yoil = getYoil(date);

        // 3. Model에 작업 결과 저장
//        model.addAttribute("myDate", date);
//        model.addAttribute("yoil", yoil);
        
        // 4. 작업 결과를 보여줄 View의 이름을 반환
        return "yoil"; // /WEB-INF/views/yoil.jsp
    }
    
    private boolean isValid(MyDate date) {
      return isValid(date.getYear(), date.getMonth(), date.getDay());
   }

   private @ModelAttribute("yoil") char getYoil(MyDate date) {
      return getYoil(date.getYear(), date.getMonth(), date.getDay());
   }

   private char getYoil(int year, int month, int day) {
        Calendar cal = Calendar.getInstance();
        cal.set(year, month - 1, day);

        int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
        return " 일월화수목금토".charAt(dayOfWeek);
    }
    
    private boolean isValid(int year, int month, int day) {    
       if(year==-1 || month==-1 || day==-1) 
          return false;
       
       return (1<=month && month<=12) && (1<=day && day<=31); // 간단히 체크 
    }
} 
. 회원가입 화면 작성하기

10. @GetMapping, @PostMapping

@RequestMapping(value="/add", method={RequestMethod.GET, RequestMethod.POST})
// @GetMapping("/add")
   public String register() {
      return "registerForm"; // WEB-INF/views/registerForm.jsp
   }
@PostMapping("/save")  // 4.3부터
   public String save(User user, Model m) throws Exception {
      // 1. 유효성 검사
      if(!isValid(user)) {
         String msg = URLEncoder.encode("id를 잘못입력하셨습니다.", "utf-8");

         m.addAttribute("msg", msg);
         return "forward:/register/add";
//       return "redirect:/register/add?msg="+msg; // URL재작성(rewriting)
      }

      // 2. DB에 신규회원 정보를 저장
      return "registerInfo";
   }

   private boolean isValid(User user) {
      return false;
   }

11. redirect 와 forward

@PostMapping("/save")  // 4.3부터
   public String save(User user, Model m) throws Exception {
      // 1. 유효성 검사
      if(!isValid(user)) {
         String msg = URLEncoder.encode("id를 잘못입력하셨습니다.", "utf-8");

         m.addAttribute("msg", msg);
         return "forward:/register/add";
//       return "redirect:/register/add?msg="+msg; // URL재작성(rewriting)
      }

      // 2. DB에 신규회원 정보를 저장
      return "registerInfo";
   }

12. 쿠키(Cookie)란?

 @PostMapping("/login")
    public String login(String id, String pwd, String toURL, boolean rememberId, HttpServletRequest request, HttpServletResponse response) throws Exception {

        // 1. id와 pwd를 확인
        if (!loginCheck(id, pwd)) {
            // 2-1   일치하지 않으면, loginForm으로 이동
            String msg = URLEncoder.encode("id 또는 pwd가 일치하지 않습니다.", StandardCharsets.UTF_8);

            return "redirect:/login/login?msg=" + msg;
        }
        // 2-2. id와 pwd가 일치하면,
        //  세션 객체를 얻어오기
        HttpSession session = request.getSession();
        //  세션 객체에 id를 저장
        session.setAttribute("id", id);

        if (rememberId) {
            //     1. 쿠키를 생성
            Cookie cookie = new Cookie("id", id); // ctrl+shift+o 자동 import
//           2. 응답에 저장
            response.addCookie(cookie);
        } else {
            // 1. 쿠키를 삭제
            Cookie cookie = new Cookie("id", id); // ctrl+shift+o 자동 import
            cookie.setMaxAge(0); // 쿠키를 삭제
//           2. 응답에 저장
            response.addCookie(cookie);
        }
//           3. 홈으로 이동
        toURL = toURL == null || toURL.equals("") ? "/" : toURL;

        return "redirect:" + toURL;
    }

13. 세션 이론

// 1. 원격 호출 가능한 프로그램으로 등록
@Controller
@RequestMapping("/login")
public class LoginController {
    @GetMapping("/login")
    public String loginForm() {
        return "loginForm";
    }

    @GetMapping("/logout")
    public String logout(HttpSession session) {
        // 1. 세션을 종료
        session.invalidate();
        // 2. 홈으로 이동
        return "redirect:/";
    }

    @PostMapping("/login")
    public String login(String id, String pwd, String toURL, boolean rememberId, HttpServletRequest request, HttpServletResponse response) throws Exception {

        // 1. id와 pwd를 확인
        if (!loginCheck(id, pwd)) {
            // 2-1   일치하지 않으면, loginForm으로 이동
            String msg = URLEncoder.encode("id 또는 pwd가 일치하지 않습니다.", StandardCharsets.UTF_8);

            return "redirect:/login/login?msg=" + msg;
        }
        // 2-2. id와 pwd가 일치하면,
        //  세션 객체를 얻어오기
        HttpSession session = request.getSession();
        //  세션 객체에 id를 저장
        session.setAttribute("id", id);

        if (rememberId) {
            //     1. 쿠키를 생성
            Cookie cookie = new Cookie("id", id); // ctrl+shift+o 자동 import
//           2. 응답에 저장
            response.addCookie(cookie);
        } else {
            // 1. 쿠키를 삭제
            Cookie cookie = new Cookie("id", id); // ctrl+shift+o 자동 import
            cookie.setMaxAge(0); // 쿠키를 삭제
//           2. 응답에 저장
            response.addCookie(cookie);
        }
//           3. 홈으로 이동
        toURL = toURL == null || toURL.equals("") ? "/" : toURL;

        return "redirect:" + toURL;
    }

    private boolean loginCheck(String id, String pwd) {
        return "asdf".equals(id) && "1234".equals(pwd);
    }
}

14. 예외처리


// 예외 처리
@Controller
public class ExceptionController {

   // catcher2 ( 널 , 파일 낫파운드)
   @ExceptionHandler({NullPointerException.class, FileNotFoundException.class})
   public String catcher2(Exception ex, Model m) {
      m.addAttribute("ex", ex);
      return "error";
   }

   // catcher (서버 에러러
   @ExceptionHandler(Exception.class)
   @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // 200 -> 500
   public String catcher(Exception ex, Model m) {
      System.out.println("catcher() in ExceptionController");
      System.out.println("m="+m);
//    m.addAttribute("ex", ex);

      return "error";
   }
   
   @RequestMapping("/ex")
   public String main(Model m) throws Exception {
      m.addAttribute("msg", "message from ExceptionController.main()");
      throw new Exception("예외가 발생했습니다.");
   }

   @RequestMapping("/ex2")
   public String main2() throws Exception {
      throw new NullPointerException("예외가 발생했습니다.");
   }
}

// 예외 처리
   @ControllerAdvice("com.fastcampus.ch3") // 지정된 패키지에서 발생한 예외만 처리
// @ControllerAdvice // 모든 패키지에 적용
   public class GlobalCatcher {

      // catcher2 ( 널 , 파일 낫파운드)
      @ExceptionHandler({NullPointerException.class, FileNotFoundException.class})
      public String catcher2(Exception ex, Model m) {
         m.addAttribute("ex", ex);
         return "error";
      }

      // catcher (서버 에러러
      @ExceptionHandler(Exception.class)
      public String catcher(Exception ex, Model m) {
         m.addAttribute("ex", ex);
   
         return "error";
      }
   }

15. DispatcherServlet 파헤치기

16. 데이터의 변환과 검증

 

 


public class GlobalValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        return User.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        System.out.println("GlobalValidator.validate() called");
        User user = (User) target;
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "pwd", "required");

        if(user.getId() == null || user.getId().trim().isEmpty() || user.getId().length() < 4 || user.getId().length() > 10 ) {
            errors.rejectValue("id", "invalidLength", new String[]{"","4","12"}, null );
        }
    }
}

 

<beans:bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
   <beans:property name="basenames">
      <beans:list>
         <beans:value>error_message</beans:value> <!-- /src/main/resources/error_message.properties -->
      </beans:list>
   </beans:property>
   <beans:property name="defaultEncoding" value="UTF-8"/>
</beans:bean>

 

LIST