[신세계I&C KDT][Spring Boot] #39 스프링부트 2 (0611)

박현아·2024년 7월 1일
0

신세계아이앤씨 KDT

목록 보기
43/56

cf) 아키텍쳐

              요청 처리 ( @RequestParam, @RequestHeader, @CookieValue )           
  웹브라우저 ---------------> Controller

                            	|
			    				|
	 		<------------   hello ( /WEB-INF/views/hello.jsp )

7. Model 생성 방법 (jsp에서 보여줄 데이터 의미)

  • 이전 서블릿/jsp 는 scope에 setAttribute(key,value) 로 저장하고 jsp에서 보여줌

scope 종류에 따라서 저장하고

request.setAttribute(key,value);
session.setAttribute(key,value);
application.setAttribute(key,value)

나중에 jsp에 보여줌
EL: ${key}

cf) Spring에서 JSTL 사용하는 방법

1- 의존성 설정
-- pom.xml

<dependency>
		<groupId>org.apache.tomcat.embed</groupId>
		<artifactId>tomcat-embed-jasper</artifactId>
		<scope>provided</scope>
</dependency>
<dependency>
		<groupId>javax.servlet</groupId>
		<artifactId>jstl</artifactId>
</dependency>

2- jsp에 taglib 등록
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

방법 1) HttpServletRequest 이용

@GetMapping("/m1")
public String m1(HttpServletRequest request) {
	request.setAttribute("m1", "홍길동");
}

방법 2) Model 이용

@GetMapping("/m2")
public String m2(Model m) { // 자동으로 new Model()해서 주입됨
	m.addAttribute("m1", "홍길동");
	m.addAttribute("m1_dto", new LoginDTO("홍길동", "1234"));
}

방법 3) ModelMap 이용

@GetMapping("/m3")
	public String m3(ModelMap m) { // 자동으로 new ModelMap()해서 주입됨.
	
		m.addAttribute("m1", "홍길동");
		m.addAttribute("m1_dto", new LoginDTO("홍길동", "1234"));

방법 4) Map<String,Object> 이용

     @GetMapping("/m4")
	public String m4(Map<String, Object> m) { // 자동으로 new ModelMap()해서 주입됨.
	
		m.put("m1", "홍길동");
		m.put("m1_dto", new LoginDTO("홍길동", "1234"));

방법 5) 자동으로 key값 사용해서 저장

@GetMapping("/m5")
public String m5(LoginDTO dto) { 
		// LoginDTO가 Model이 됨. 즉 내부적으로 request.setAttribute(key, dto);가 됨
// 자동으로 빈 이름으로 key 설정됨
		dto.setUserid("이순신");
		dto.setPasswd("9876");
		
		return "m5";
}

방법 6) 명시적으로 key값 사용해서 저장 ⭐

@GetMapping("/m6")
public String m6(@ModelAttribute("dto_model") LoginDTO dto) { 
// LoginDTO가 Model이 됨. 즉 내부적으로 request.setAttribute(명시적 지정, dto);가 됨
// key값 설정은 @ModelAttribute("key값") 사용
		dto.setUserid("이순신");
		dto.setPasswd("9876");
		
		return "m6";
}

방법 7)

@GetMapping("/m7")
public String m7(@ModelAttribute("list_model") ArrayList<LoginDTO> list) { 
		// LoginDTO가 Model이 됨. 즉 내부적으로 request.setAttribute(명시적지정, dto);가 됨
		// 자동으로 빈 이름으로 key 설정됨
		
		list.add(new LoginDTO("홍길동1", "1234"));
		list.add(new LoginDTO("이순신1", "1234"));
		
		return "m7";
}

===> 메서드의 파라미터로 지정해서 Model을 생성함

===> 메서드의 리턴 타입으로 Model을 생성함
View 정보는 요청맵핑값을 view 정보로 사용함

방법 8) 자동으로 key 사용해서 저장

    @GetMapping("/m8") // /WEB-INF/views/m8.jsp 찾음
public LoginDTO m8() {   // 리턴타입인 LoginDTO가 Model임.
	
	return new LoginDTO("유관순", "1234");
}

방법 9) 명시적으로 key 사용해서 저장

   @GetMapping("/m9") // /WEB-INF/views/m9.jsp
   @ModelAttribute("dto_model")
public  LoginDTO m9() {   // 리턴타입인 LoginDTO가 Model임.
	
	return new LoginDTO("유관순", "1234");
}
   

방법 10)

       @GetMapping("/m10") // /WEB-INF/views/m10.jsp
	@ModelAttribute("list_model")
	public ArrayList<LoginDTO>  m10() {   // 리턴타입인 LoginDTO가 Model임.
		
		ArrayList<LoginDTO> list = new ArrayList<>();
		list.add(new LoginDTO("홍길동", "1234"));
		list.add(new LoginDTO("이순신", "9876"));
		
		return list;
	}

=== > 이전까지는 Model과 View 정보가 분리되서 설정했음

=== > 지금부터는 Model과 View 정보를 한번에 설정 가능

  • ModelAndView 빈 이용
    @GetMapping("/m11") 
	public ModelAndView  m11() {   
		
		ModelAndView mav = new ModelAndView();
		
		//뷰정보
		mav.setViewName("m11");  // /WEB-INF/views/m11.jsp
		
		//모델정보
		mav.addObject("m11", "홍길동");
		mav.addObject("m11_dto", new LoginDTO("홍길동", "1234"));
		
		List<LoginDTO> list =
				 Arrays.asList(new LoginDTO("홍길동1", "1234"),
						       new LoginDTO("홍길동2", "9999"));
		mav.addObject("m11_list", list);
		
		return mav;
	}

8. Model Scope

1) session scope

  • @SessionAttribute(names={"key값","key값"})
    Model을 저장할 때 위에서 설정한 key값을 사용하면 session scope에 Model이 저장된다

  • 세션을 사용하고자하는 Controller 마다 매번 @SessionAttribute 설정해야 된다 ⭐

2) application scope

@Autowired
ServletContext ctx;

ctx.setAttribute(key,value);

-- JSP
${requestScope.key}
${sessionScope.key}
${applicationScope.key}
  • ${key} <=== request에서 먼저찾고 없으면 session 찾고 또 없으면 application에서 찾음

9. Ajax + JSON 통신

Ajax (jQuery)	   JSON
웹 브라우저 ----------------------------> Controller (전달된 JSON을 DTO에 자동 저장)
									    @RequestBody
                                        
         <----------------------------   
전달된 값/DTO         값, DTO
JSON 받을 수 있음
(@ResponseBody)
  • 최종 정리
     웹브라우저          @RequestBody              서버
       JSON     ------------------------->   자바(DTO, List) 
	            <-------------------------
		             @ResponseBody

10. redirect와 forward

1) 문법

  • return "redirect:요청맵핑값";
    이전 서블릿의 response.sendRedirect("") 동일
  • return "forward:요청맵핑값";
    이전 서블릿의 request.getRequestDispatcher("").forward(request, response)와 동일

11. redirect-flashScope-forward

  • redirect 했음에도 불구하고 request scope에 저장된 Model을 사용할 수 있음. 단 한번만
  • RedirectAttributes 모델 + m.addFlashAttribute("model", "홍길동");

12. 파일 업로드

1) 개요

  • 서블릿/JSP 및 Spring Framework 에서는 의존성 설정이 필요함
  • SpringBoot는 의존성 필요없음. ( -starter-web 에 자동으로 포함되어 있음 )

2) 파일 업로드 화면

      <form method="POST" enctype="multipart/form-data" action="fup.cgi">
	  File to upload: <input type="file" name="upfile"><br/>
	  Notes about the file: <input type="text" name="note"><br/>
	  <br/>
	  <input type="submit" value="Press"> to upload the file!
	</form>
  • 반드시 method와 enctype를 다음값으로 설정해야 된다
    method="POST"
    enctype="multipart/form-data"

  • 멀티 업로드 가능

<input type="file" name="upfile1">
<input type="file" name="upfile2">

3) UploadDTO 작성

public class UploadDTO {

	String theText;
 	MultipartFile  theFile;

4) Controller 업로드 작업

 @PostMapping("/upload")
	public String upload(UploadDTO dto) {
		
		String theText = dto.getTheText();
		MultipartFile  theFile = dto.getTheFile();
		
		long size = theFile.getSize();
		String name = theFile.getName();
		String fileName = theFile.getOriginalFilename();
		String contentType = theFile.getContentType();
		
		
		logger.info("logger:theText:{}", theText);
		logger.info("logger:size:{}", size);
		logger.info("logger:name:{}", name);
		logger.info("logger:fileName:{}", fileName);
		logger.info("logger:contentType:{}", contentType);
		
		// 서버의 물리적인 디렉터리에 파일 저장 예> c:\\upload
		
		// 파일이 저장할 경로만 알려줌
		File f = new File("c:\\upload", fileName);
		
		try {
			theFile.transferTo(f);
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return "uploadInfo";
	}

5) Controller 다운로드 작업

    // uploadInfo 작업
	<h2>uploadInfo</h2>
	내용:${uploadDTO.theText}<br>
	파일명:${uploadDTO.theFile.originalFilename}<br>
	파일명:<a href="down?fileName=${uploadDTO.theFile.originalFilename}">
	     ${uploadDTO.theFile.originalFilename}</a><br>
	</body>
	//파일다운로드
		@Autowired
		ServletContext ctx;
		
		@GetMapping("/down")
		public void fileDown(HttpServletRequest request, HttpServletResponse response)
		throws Exception{

		 String fileName = request.getParameter( "fileName" );
		        File fNew = new File("c:\\upload", fileName);
			String sFilePath = fNew.getPath();
		//	String sFilePath = sDownloadPath + fileName;

			byte b[] = new byte[4096];
			FileInputStream in = new FileInputStream(sFilePath);

			String sMimeType = ctx.getMimeType(sFilePath);
	System.out.println("sMimeType>>>"+sMimeType );

			if(sMimeType == null) sMimeType = "application/octet-stream";

			response.setContentType(sMimeType);

			String sEncoding = new String(fileName.getBytes("UTF-8"),"8859_1");

			response.setHeader("Content-Disposition", "attachment; filename= " + sEncoding);
			
			ServletOutputStream out = response.getOutputStream();
			int numRead;

			while((numRead = in.read(b, 0, b.length)) != -1) {
				out.write(b, 0, numRead);
			}
			out.flush(); 
			out.close();
			in.close();
		}

13. HandlerInterceptor

  • 기본적인 Spring MVC에서 가로채기 가능
  • Spring MVC 아키텍쳐 세 군데 위치에서 사용 가능
  • Spring MVC 아키텍쳐
    웹브라우저 ----필터---> DispatcherServlet -v--> Controller --> Service --> Repository
    | <-v--
    |
    <--------v------- jsp

1) 구현 방법

(1) implements HandlerInterceptor

(2) 3개의 메서드 재정의 (default 메서드)

# Controller 호출전에 실행 ( 주요기능 예> 로그인 여부 확인작업등 )
      public boolean preHandle(HttpServletRequest request, 
			                 HttpServletResponse response, 
			                 Object handler)
# Controller 호출후에 실행 ( 주요기능 예> View 변경 및 Model 추가/수정 )
      public void postHandle(HttpServletRequest request, 
			               HttpServletResponse response, 
			               Object handler,
			               ModelAndView mav) throws Exception
# JSP가 웹브라우저에 랜더링 된후 실행
      public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception 

(3) web.xml 역할의 @Configuration 으로 지정된 빈을 만들어서 HandlerInterceptor 등록

@Configuration
	public class WebConfig implements WebMvcConfigurer {

		@Autowired
		MyHandlerInterceptor myHandlerInterceptor;
		
		@Override
		public void addInterceptors(InterceptorRegistry registry) {
			registry.addInterceptor(myHandlerInterceptor)
				.addPathPatterns("/login","/logout");
		}

==================================
cf) 필터(filter)

  • 서블릿, Spring framework에서 가로채기
    (서블릿 요청전)
    웹브라우저 ------필터1--필터2------> 서블릿
    <----- 필터1--필터2------
    (웹브라우저 응답전)

  • 기능: 서블릿 요청전 또는 웹브라우저 응답전에 특정 기능을 추가/변경/삭제할 수 있다.

예>
post요청
웹브라우저 -------------------> 서블릿1 post한글처리. request.setCharacterEncoding("utf-8");
post요청
-------------------> 서블릿.. post한글처리. request.setCharacterEncoding("utf-8");
post요청
-------------------> 서블릿n post한글처리. request.setCharacterEncoding("utf-8");

웹브라우저 -------------------> 서블릿1
post요청
---------필터-------> 서블릿..
post요청
-------------------> 서블릿n

  • 구현
    1) implments Filter
    2) 3개의 메서드 재정의
    3) web.xml에 등록

14. 예외 처리

  • Controller에서 예외발생시 예외처리하는 방법임.

1) 기본

@GetMapping("/errorPage")
	public String errorPage() {
		logger.info("logger:MainController:{}", "/errorPage 요청");	
		throw new IllegalArgumentException("IllegalArgumentException 발생됨");
	}
  • SpringBoot는 자동으로 Whitelabel Error Page를 제공한다.
    이것이 boot의 특징인 auto configuration 이다
  • spring-boot-autoconfigure-2.7.18.jar 담당함.

2) 발생된 예외를 auto configuration 처리하지 말고 커스터마이징하자

(1) 지역 예외 처리

  • 예외가 발생된 특정 Controller 내에서 예외처리
@ExceptionHandler
         # Controller 안에서 
        @ExceptionHandler(value = {NullPointerException.class,
			                   IllegalArgumentException.class})
	public String errorPage(Exception e, Model m) {
		logger.info("logger:errorPage:{}", e.getMessage());
		m.addAttribute("error_message",  e.getMessage());
		return "errorPage";  // errorPage.jsp
	}

(2) 글로벌 예외처리

  • 모든 Controller 예외처리
@ControlAdvice 
         # 새로운 빈에서
          @ControllerAdvice
       public class GlobalExceptionHandler {
	Logger logger = LoggerFactory.getLogger(getClass());
	
	@ExceptionHandler(value = {ClassCastException.class,
			                  ArithmeticException.class})
	public String errorPage(Exception e, Model m) {
		logger.info("logger:errorPage:{}", e.getMessage());
		m.addAttribute("error_message",  e.getMessage());
		return "errorPage";  // errorPage.jsp
	  }
        }

15. 국제화 (I18N : InternationalizatioN)

https://docs.spring.io/spring-boot/docs/2.7.18/reference/htmlsingle/#features.internationalization

  • locale에 따라 화면의 언어를 다르게 랜더링이 가능하다
    다국어 처리 가능
  • Spring이 특정 언어를 선택하는 방법
    ==> 기본적으로 LocaleResolver를 이용해서 Locale을 인식하여 원하는 특정 언어를 선택

가. AcceptHeaderLocaleResolver
- 요청 헤더값을 이용해서 locale 정보를 얻음
- Accept-Language 헤더값 이용
- 기본적으로 적용되는 방법임.

나. CookieLocaleResolver
- 쿠키를 이용해서 locale 정보를 저장및얻음
- setLocale("ko|en|");

다. SessionLocaleResolver
- 세션을 이용해서 locale 정보를 저장및얻음
- setLocale("ko|en|");

  • 구현 1
    1) 리소스 번들 파일 작성 (resouece bundle 파일)
    - 문법:
    src/main/resources 폴더
    파일명-언어코드.properties
    - 키=값
    - 기본 번들 파일: message.properties
    greeting=안녕하세요
    한국어 번들 파일: message_ko.properties
    greeting=안녕하세요
    영어 번들 파일 : message_en.propertis
    greeting=hello

     - 번들 파일의 인코딩은 모두 utf-8로 변경.

2) application.properties에 리소스 번들 등록

      # 리소스 번들 파일 등록
	spring.messages.basename=bundle/message
	spring.messages.encoding=utf-8

3) spring.messages.fallback-to-system-locale=false
기본: true. message.properties 가 아닌 system의 locale(제어판의 국가별 설정)ㅑ 적용함.
false. message.properties 적용.

4) SessionLocaleResolver 생성
@ Configuration
public class WebMvc {

// 기본인 AcceptHeaderLocaleResolver 인데
// 기본을 SessionLocaleResolver로 변경하는 작업이다.
@Bean
public SessionLocaleResolver localeResolver() {
	SessionLocaleResolver locale =
			new SessionLocaleResolver();
	locale.setDefaultLocale(new Locale("ko"));
	return locale;
}

}

5) Controller 에서 개발자가 직접 locale 변경
@GetMapping("/bundle")
public String bundle( HttpServletRequest request,
HttpServletResponse response,
@RequestParam String lang) {

	//SessionLocaleResolver의 로케일을 변경.
	locale.setLocale(request, response,
			new Locale(lang));
  • 구현 2
  • LocaleChangeInterceptor 이용해서 로케일 변경해줌.
    따라서 Controller에서 명시적으로 setLocale(new Locale("값")) 할 필요가 없음.
@Configuration
public class WebMvc implements WebMvcConfigurer{

	// 기본인 AcceptHeaderLocaleResolver 인데
	// 기본을 SessionLocaleResolver로 변경하는 작업이다.
	@Bean
	public SessionLocaleResolver localeResolver() {
		SessionLocaleResolver locale =
				new SessionLocaleResolver();
		locale.setDefaultLocale(new Locale("ko"));
		return locale;
	}
	//LocalChangeInterceptor 생성
	@Bean
	public LocaleChangeInterceptor changeInterceptor() {
		LocaleChangeInterceptor xxx = new LocaleChangeInterceptor();
		// ?lang=en|ko|jp
		xxx.setParamName("lang");
		return xxx;
	}

	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(changeInterceptor());
	}
}

16. webjar

1) 현재 사용 방식

2) 적용 방식

  • jquery와 bootstrap을 의존성 설정해서 사용하자

(1) webjar의 jQuery 경로

/META-INF/resources/webjars/jquery/3.7.1/jquery.min.js

(2) 사용할 경로

< script src="webjars/jquery/3.7.1/jquery.min.js"></ script>

(3) webjar의 bootstrap 경로

/META-INF/resources/webjars/bootstrap/5.3.3/css/bootstrap.min.css
/META-INF/resources/webjars/bootstrap/5.3.3/js/bootstrap.min.js

(4) 사용할 경로

< script src="webjars/bootstrap/5.3.3/js/bootstrap.min.js"></ script>
< link rel="stylesheet" href="webjars/bootstrap/5.3.3/css/bootstrap.min.css">

  • pom.xml
<!-- https://mvnrepository.com/artifact/org.webjars/jquery -->
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.7.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.webjars/bootstrap -->
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>bootstrap</artifactId>
    <version>5.3.3</version>
</dependency>

17. 폼 유효성 체크

1) 개요

  • 사용자 입력 폼의 데이터 유효성 검사 의미
  • 예> 필수 사항, 과거 날짜 선택 불가능, 값이 길이 n개 이상,...

2) 구현 방식

  • html 속성 이용 (클라이언트에서 처리하는 방법)
  • JS 이용 (클라이언트에서 처리하는 방법)
    ==> 클라이언트에서 JS를 비활성화 시키거나 보안 이슈 발생 가능성이 높음
  • Spring Boot 이용
    ==> 서버에서 유효성 체크하는 방법이다

3) 적용

(1) 의존성 설정

<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

(2) DTO 생성

: 유효성 체크하기 위한 DTO
(Command Bean / form backing object : 양식보조객체 라고도 함)
==> 변수에 어노테이션으로 유효성 조건을 지정한다

public class memberDTO {

	@NotNull
	String userid;
	
	@Size(min=3, message="세 글자 이상입니다!!")
	String username;
	
	// 010-1234-5678 전화번호 정규표현식 검색해서 쓰세용
	//@Pattern()
	//String phone;
	
    @NotNull
	@FutureOrPresent(message = "현재 또는 미래 날짜만 가능합니다")
	LocalDate targetDate;

(3) Controller 작업

  • 양방향 적용 (two-way binding)
				화면 요청 (GET)
Controller -----------------------> jsp
		   <-----------------------
           		실제 요청 (POST)
  • GET 방식과 POST 방식 요청에 전부 Command Bean(DTO)을 모델로 사용해야된다
				화면요청(GET)
Controller ---------------------> jsp
		   <---------------------
				실제요청(POST)

(1) Get 요청 구현

@GetMapping("/member")  // 화면 요청
public String memberForm(ModelMap m) {
			
		MemberDTO dto = new MemberDTO();
//		MemberDTO dto = new MemberDTO("aaa","홍길동", LocalDate.now());
//		m.addAttribute("memX", dto);  // 임의의 값 지정하면 에러 발생
		m.addAttribute("memberDTO", dto);  // command bean으로 반드시 설정한다
			
		return "memberForm"; 
}

(2) Post 요청 구현

@PostMapping("/member") // 로직 요청
		public String member(@Valid  MemberDTO dto, BindingResult result) {
		System.out.println("MemberDTO" + dto);
		if(result.hasErrors()) {
				return "memberForm"; 
		}
			
		return "memberInfo"; 
}
  • memberForm.jsp
<%@ taglib uri="http://www.springframework.org/tags/form" 
    prefix="form"%>
<form:form  method="post" 
			   modelAttribute="memberDTO">
아이디:<form:input type="text" path="userid" />
		      <form:errors path="userid"></form:errors>
<br>
이름:<form:input type="text" path="username" />
<form:errors path="username"></form:errors>
<br>
날짜:<form:input type="date" path="targetDate" />
<form:errors path="targetDate"></form:errors>
<br>
<input type="submit" value="가입">
</form:form>

0개의 댓글