
- Description 추가 기능 구현하기
。기존static List에 입력된 내용을 바탕으로 새로운TodoClass의 instance를 생성하여 추가하기.
- Description을 추가하는 JSP page 생성
。<a>로 특정 url로 연결하는 버튼 생성해보기
▶class=""속성을 이용해 bootstrap 정의.<a href="add-todo" class="btn btn-secondary">Add Todo</a>
- 다음처럼 클릭 시 add-todo의 url로 연결되는 버튼이 생성됨.
bootstrap 버튼 색상 종류
。btn-primary: 파랑
。btn-secondary: 회색
。btn-success: 초록
。btn-danger: 빨강
。btn-warning: 노랑
。btn-info: 하늘
。btn-light: 흰색
。btn-dark: 검정
- 다음 구문으로 연결되는
"addTodo"의 JSP Page(addTodo.jsp) 생성하기
。Textfield에 값을 입력 후 버튼을 누를경우 name=description변수로<form>의 data로서POST로 전달됨.<!-- addTodo.jsp --> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!doctype html> <html lang="ko"> <head> <link href="webjars/bootstrap/5.1.3/css/bootstrap.min.css" rel="stylesheet"> <title>Document</title> </head> <body> <div class="container"> <h1>Todo Details</h1> <form method="POST"> <label for="textId">Description : </label> <input type="text" id="textId" name="description"> <input type="submit" class="btn btn-secondary"> </form> </div> <script src="webjars/bootstrap/5.1.3/js/bootstrap.min.js"></script> <script src="webjars/jquery/3.6.0/jquery.min.js"></script> </body> </html>
- 다음 같은 결과 도출.
- 최초 진입 시 JSP 페이지를 지시하는 HTTP 메소드(GET)과 값 입력 후 제출 시 다른 페이지로 값을 전달하면서 지시하는 HTTP 메소드(POST)로 구분하기.
。"redirect:url"처리 기능 사용
▶ 동일한 jsp page를 다시 구현 하려면 List의 자료를 표현해야하는 등 많은 logic이 중복되서 구현해야하는 단점이 존재하므로, 해당 JSP Page가 return되는Controller Method의 Mapping된url을 호출하는 형식.@RequestMapping(value = "add-todo", method = RequestMethod.POST) public String addTodo(){ return "redirect:list-todo"; }
ListAllTodos()Controller Method에 Mapping된 URL을 호출하여 해당Controller Method를 통해listTodos.jsp를 호출.Spring에서의
redirect:처리 방법
。Spring Framework 사용 시Controller Method에서 return type을String으로 설정 후 return되는 문자열을 기존처럼 VIEW ( =jsp) 파일명 이름이 아닌,"redirect:해당 JSP의 Controller Method로 Mapping된 url"로 문자열 반환 시 해당 url를 통해 Mapping된Controller Method의 jsp를 return.
- 사용자가 입력한 Description을 새로운
TodoClass의 instance를 생성하여 Static list에 추가하는 기능 구현하기.
- 생성한
Todoinstance를 Static list에 추가하는addTodo()Method를TodoService.java에 따로 구현하기.
。todosCount: primary key 역할.
。static변수는static block을 통해 초기화 수행.// TodoService.java import org.springframework.stereotype.Service; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; @Service public class TodoService { private static int todosCount = 0; private static List<Todo> todos = new ArrayList<>(); static { // static List 초기값 todos.add(new Todo(todosCount++,"wjdtn747","Learn AWS", LocalDate.now().plusYears(1),false)); todos.add(new Todo(todosCount++,"wjdtn0619","Learn DevOps", LocalDate.now().plusYears(2),true)); todos.add(new Todo(todosCount++,"wjdtn3902","Learn FullStackDevelopment", LocalDate.now().plusYears(3),false)); } public List<Todo> findByUsername(String username) { return todos; } public static void addTodo( String username, String description, LocalDate targetDate, boolean completed){ todos.add(new Todo(todosCount++,username,description,targetDate,completed)); } }▶
TodoController에서TodoServiceclass의addTodo()method를 통해todos에 추가하기위한TodoClass의 instance를 생성하기위해 매개변수로String username, String description, LocalDate targetDate, boolean completed요구.
Controller Method에description과username변수 가져오기
。description은addTodo.jsp에서POST로 전달된 값(name=description)을@RequestParam String description으로 가져오기.
ModelMap객체.get("JSP변수명")
。로그인하여 JSP에 표현된username을Model과Session을 통해 가져오기.。// TodoController.java @RequestMapping(value = "add-todo", method = RequestMethod.GET) public String addTodo(){ return "addTodo"; } @RequestMapping(value = "add-todo", method = RequestMethod.POST) public String addTodo(@RequestParam String description, ModelMap model){ TodoService.addTodo((String)model.get("name"),description, LocalDate.now(),false); return "redirect:list-todo"; }add-todourl은GET,POST를 사용하므로@RequestMapping의method=""속성으로 각각의 Controller Method를 구분하여 구현.
(String)model.get("name")
。@SessionAttributes("name")을 통해JSP로 전달된 변수(name)의 값을Model을 통해 java로 가져온object를String으로 casting하여username로 활용.
。이후 입력한 요소를 기반으로Todo객체를 static list에 추가하는 기능이 구현됨.
Static List특징 :
Static List는 서버가 재시작 될때마다 초기화됨.
▶MySQL등과 같은 DB를 활용.
- JSP에 Validation을 구현하여 Description을 Todo list로 추가 시 입력값에
Null을 입력하지 못하게 하기
。다음 구문을 JSP의 text를 담당하는 input에 required 속성 추가하여 검증기능을 구현.<label for="textId">Description : </label> <input type="text" id="textId" name="description" required="required">。
addTodo.jsp에<input>요소에 다음required="required"속성을 추가 시null값을 제출하지 못하도록 설정됨.
Spring Boot Starter Validation을 이용한 검증 기능(Validation) 구현
。 프론트엔드(Client-side)에서 HTML이나 JS로 구현한Authentication의 경우 쉽게 해킹이 가능.
▶ 최선의 방식은 Server-side Validation.
。Java 코드를 이용한 검증 구현이 가능하지만, Spring Boot를 활용하여 더욱 용이하게 Validation 구현이 가능.
- Spring Validation
。Client로부터POSTRequest로 전송된<form>객체에 대한 유효성 검증을 수행.
validation: 어떤 데이터( 주로 사용자 또는 다른서버의 request )의 값이 유효한지, 잘못된 내용이 있는지 확인하는 단계.
Form Backing Object:
。Web Application에서<form>을 통한 사용자의 입력을 담는 개체로서,<form>의 데이터를 검증 및Controller에서 처리하는데 활용.
。<form>과 관련된 Validation logic을 포함 가능.
。Spring MVC에서 Client-side에서 HTML의<form>과 Server-side에서Model객체( = Spring Bean )간 Data Binding을 수행
ex ) JSP의<form>의 요소에 상관 없이TodoClass를Model객체로서 Data Binding 하여 사용.
Form Backing Object의 역할
<form>의 data 저장
。사용자가 입력한 Form data를 저장.
- Data Binding
。HTTP Request를 Java 객체로 변환하는 기능
- Validation
。사용자가 입력한 Data의 유효성 검증
- Data Transfer
。Controller에서 View로 Data를 전달하는 역할
- Spring Boot를 활용한 검증기능 구현
- Validation과 관련된 Spring Boot Starter의 dependency를 정의
。spring-boot-starter-validation를pom.xml에 추가.<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>.
Form Backing Object를 활용해서<form>의<input>과Controller Method를 통해 Spring Bean을 Binding.
。todo.jsp와TodoController.java간의 양방향(2-way) Binding을 제공.
。<form>을 Spring Bean으로 Binding 할 경우,<form>의 요소(<input>)를 Model 객체( = Spring Bean) 의 field로 쉽게 mapping 가능.
<form>의<input>에 값이 입력될 경우, 이를 Mapping하는Controller method와 Binding을 수행하기
。<input>이 10개 존재할 경우,Controller method에서 각각의<input>요소의name속성에 대해 매개변수로@RequestParams를 각각 10개로 지정해야하므로 코드의 복잡성 증가.
▶Form Backing Object를 통해Todo class( = Spring Bean )에 직접 Binding하기.
。Form Backing Object활용 시Controller method의 첫번째 매개변수는Model로 설정 및 두번째 매개변수는 직접 Binding될 Spring Bean을Model객체로서 설정.// TodoController.java @RequestMapping(value = "add-todo", method = RequestMethod.POST) public String addTodo( ModelMap model, Todo todo ){ TodoService.addTodo((String)model.get("name"),todo.getDescription(), LocalDate.now(),false); return "redirect:list-todo"; }。
addTodo()의 첫번째 매개변수는model, 두번째 매개변수에 Binding될 Spring Bean으로 설정.
▶Spring MVC에 의해<form>과 Controller Method의 매개변수todoSpring Bean instance를Model객체로서 Data Binding.
。<form>의<input name="description">과 Data Binding된todo의descriptionfield를todo.getDescription()을 통해 가져온다.
。해당Todoclass의todoinstance는 Spring Bean instance로서Form Backing Object의Model객체 역할을 수행
▶Form Backing Object으로서 Binding이 완료 된 경우 나중에 추가적으로 다른 변수(targetDate, done... etc)의<input>를 추가하더라도 자동으로 Binding이 수행됨.
GET의Controller Method구현
。 최초add-todo로 url 연결 시addTodo.jsp로 연결하는GET의Controller method에도JSP가spring form taglib의modelAttribute="Model객체명"속성을 통해 binding할Model객체( =todo)이 필요하므로, 초기화된Model객체를JSP에 전달.
。초기화된Model객체는 하드코딩으로 정의.@RequestMapping(value = "add-todo", method= RequestMethod.GET) public String ShowNewTodoPage(ModelMap model){ // 하드코딩으로 초기화된 todo를 생성하여 model 객체로 사용. Todo todo = new Todo(0,(String)model.get("name"),"",LocalDate.now(),false); model.put("todo",todo); return "Todo"; }
JSP에서Form Backing Object기능 활용하기.
form tag library를 사용하여 Data Binding을 수행.
참조
。JSP 문서 상단에 다음 taglib 추가하여Spring form tag library를 import.<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>。이를 추가할 경우, 접두사로
form을 사용하면서<form:>이라는 접두사를 사용 가능.
。JSP의<form>태그를<form:form>로 수정 후<form>을TodoClass와 mapping하기 위해modelAttribute="model객체이름"속성을 추가하여 정의.<form:form method="post" modelAttribute="todo">▶ 이때 반드시
modelAttribute의 값은 Mapping할Controller method의 매개변수Model객체의 이름과 동일해야됨!
。기존<input>을<form:input>으로 수정 후 해당 tag를 Mapping된Model객체todo의descriptionfield와 mapping하기 위해path="Model객체의Field명"속성을 선언.<form:input type="text" path="description" required="required"/>▶
path=""속성은TodoClass의 Mapping할 field명과 동일해야한다.
。이후 다음처럼 작성한 후 Mapping URLlocalhost:8080/add-todo로 접속.<div class="container"> <h1>Todo Details</h1> <form:form method="post" modelAttribute="todo"> <label for="textId">Description : </label> <form:input type="text" path="description" id="textId" required="required"/> <form:input type="text" path="id"/> <form:input type="text" path="completed"/> <input type="submit" class="btn btn-secondary"> </form:form> </div>。
<form:form>:modelAttribute="todo"를 통해 model객체todo와 Data Binding.
。<form:input>:TodoClass에 구현된 field와path="Model객체field명"으로 Data binding
。Form Binding Object에 의해GET의Controller Method인addTodo()에서 생성된 초기 Model 객체todo가<form:form>에 mapping 되고,todo의 각 Field가 각 JSP의<form:input>에 Mapping되어 표현됨.
▶Model객체todo의 각 Field의 초기값이<form:input>에 표현되면서<form:form>과 Model 객체(todo)가 자동으로 Binding되었음을 확인 가능.
。Application 실행 후<form:form>을 제출 시,Spring Validation을 통해 Model 객체todo의 각 field가null값을 인 것을 검출가능.
▶<form:input type="text" path="description" id="textId" required="required"/>의 내용을 비운채로 제출이 불가능.
<form:input>을 "type=hidden"으로 설정하여 태그요소를 숨김처리가능.<form:form method="post" modelAttribute="todo"> <label for="textId">Description : </label> <form:input type="text" path="description" id="textId" required="required"/> <form:input type="hidden" path="id"/> <form:input type="hidden" path="completed"/> <input type="submit" class="btn btn-secondary"> </form:form>
- 2-way Binding :
。Controller method의Model객체 데이터로 JSP의<form>에서 표시되는 단방향 Binding.
。<form>의<input>에 문자열을 입력 후POST요청 request를 통해 문자열이todoinstance의 해당되는 field로 단방향 Binding.
<%@ taglib %>JSP 태그 종류
。taglib: 사용자가 만든 tag library를 사용하기위해 사용하는JSP지시자
ex) spring form taglib :
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
uri="tag library 위치"
prefix="tag를 지칭하는 이름"
- Spring form taglib :
。Spring Framework의Spring MVCModule에서 제공하는 library.
。modelAttibute속성을 통해 JSP의<form:form>에Spring Bean( =Model객체)을 Binding 수행 가능.
▶ Error message 처리가 간단해진다.
。해당taglib을 정의할 경우<form:태그>이라는 접두사를 활용하여 사용 가능.
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
。form taglib을 정의할 JSP 문서 상단에 다음 구문을 정의.
<form:errors>
。JSP에서Spring form taglib의<form:form>의 하위 특정<form:태그>요소에서 발생하는Validation Error에 대하여 Validation Annotation에서 정의한 Error Message를 표현하는 태그요소.
。path="Model객체Field명"을 통해Validation Error이 발생할 Binding된 Model 객체의 Field 이름을 지정.
<form:errors path="description" cssClass="text-warning"/>
<form:label>:
。spring form taglib의 form의 내부 요소(tag)의 label을 표기.
path="model객체field명":Form Backing Object에 의해 Data binding될 Model객체의 field명과 동일해야한다.
<form:label path="description">Description : </form:label>
modelAttribute="model객체이름":
<form:form method="post" modelAttribute="todo">
。<form:form>tag의 속성.
。JSP의<form>과Controller Method의 Model 객체와 Binding 시 사용.
▶ form data를 Java 객체로 변환.
。Spring MVC에서View와Controller간 Data Binding을 수행.
。Mapping할Controller Method의 매개변수Model객체의 이름과 동일해야한다.
▶<form>제출 시 해당 model 객체와 mapping.
path="model객체의field명":
<form:input type="text" path="description" required="required"/>
。<form:form>의 내부 태그요소( ex.<form:errors>)를Controller Method에 Binding된Model객체의 특정 field( ex.description)와 Data Binding을 수행.
form taglibCSS 관련
。기존 HTML의 tag에서 CSS 활용 시<form class="">속성을 사용하지만,Spring taglib태그의 경우cssClass=""속성 사용!
▶Spring taglib에서 사용되는cssClass도Bootstrap에서 사용되는 class를 사용가능.
<form:errors path="description" cssClass="text-warning"/>
- Spring Bean에 Validation을 추가하기
。Spring Bean( =TodoClass )의 field에 Annotation을 선언하여 Validation을 정의.
▶ 이후Form Backing Object에 의해 Mapping된<form:input>의 태그요소에 Validation 기능이 적용됨.
。spring-boot-starter-validation의 dependency를 통해 설치되는jakarta.validation.constraints.jar에서 최소 길이, 과거, 미래의 날짜 등에 대한 확인을 수행하는 Class가 존재.
▶ 구현할 수 있는 다량의 Validation이 해당 starter에 포함됨.Spring Bean의 Field 최소길이 10 이하인 검증기능 구현하기
<form:form>과 Mapping된 Model 객체의 원본 Spring Bean에서 검증을 수행할 Field앞에@Size선언@Size(min = 10, message="Enter at least 10 characters") private String description;
Controller Method의 매개변수에서 Binding되는Model객체 앞에@Valid선언.public String addTodoPage(ModelMap model, @Valid Todo todo)。
@Valid선언 시 Form과 Bean간의 Binding이 이뤄지기 전 Bean에 대해 Validation을 먼저 수행하게됨.
▶ 10개 미만으로 문자열을 입력하여 제출 시 Validation error page 도출.
Validation Annotation
。jakarta.validation.constraintspackage
ID 혹은 PW에 작성 제한(글자수, 한글, 특수문자 등)을 둘때 사용하는 유효성검사 Annotation Class를 포함하는 pacakge.
@Past:
。선언된 field의 Data Binding된<input>값이 현재시점을 기준으로 과거시점이어야 한다.
message="에러메세지": 검증 실패 시 표현될 메시지.
@Size:
。선언된 field의 Data Binding된<input>으로 입력된 값의 크기가 min과 max 사이인 경우에만 값을 저장하도록 유효성 검증.
。JPA혹은Hibernate로부터 독립적인 Spring Bean 생성.
。위반 시Validation Error이 발생하면 이는BindingResult의 객체에 의해 검출될 수 있음.
@Size의 속성
。min="최솟값": 입력값의 최솟값 설정
。max="최솟값": 입력값의 최댓값 설정
。message="에러메세지": 검증 실패 시 표현될 메시지.
@Size(min=2, message = "Enter at least 2 characters")
@Valid:
。JSP Form또는RequestBody와 Model객체( = Spring Bean )간의 Binding이 되기 전 Spring Bean에 들어오는 값에 대하여 Validation을 먼저 수행 후 Binding.
。주로RequestBody를 검증 시 사용됨.
- Validaition Error를 View에 표시.
。검증 이후 발생한 Validation Error를JSP에 표시.
Controller method의 매개변수에서Validation Error를 검출 시JSP로 return하는 기능 구현.
。BindingResult의 객체를 생성해서Validation Error를 검출.@RequestMapping(value = "add-todo", method= RequestMethod.POST) public String addTodoPage(ModelMap model, @Valid Todo todo, BindingResult result){ if(result.hasErrors()){ // Validation Error 등장 시. // Todo.jsp를 계속 표현. return "Todo"; } TodoService.addTodo((String)model.get("name"),todo.getDescription(), LocalDate.now().plusYears(1),false); return "redirect:list-todo"; }。문자열을 10자 이하로 적을 경우,
@Size를 통한 Validation Error이 발생 및BindingResult에서 검출하여Todo.jsp를 계속 반환.
spring form taglib을 활용해서 error message도 같이 도출하게 하기.
<form:errors>
。JSP에서Spring form taglib의<form:form>의 하위<form:>요소에서 발생하는Validation Error의 Message를 표현.
。path="Model객체Field명"을 통해Validation Error이 발생할 Binding된 Model 객체의 Field 이름을 지정.<form:errors path="description" cssClass="text-warning"/>
▶ Spring BeanTodoClass의@Size가 선언된descriptionfield에서Validation Error발생 시Controller method의BindingResult에 의해 검출되어addTodo.jsp를 return하면서@Size에 정의된 Error Message를path속성으로 Binding된<form:errors path="description">태그요소로 표현.
。cssClass=""속성으로spring form taglib의 style을 지정가능.
▶ 일반 HTML 태그의class=""속성과 동일.
BindingResult:BindingResult br
。 Spring MVC에서<form>data를 Data Binding 하는 중 발생하는 Validation Error를 수집 및 처리하는 객체.
▶ Spring이 제공하는 Validation Error을 보관.
BindingResult객체.hasError():
。Spring에서 Validation Error 발생 시true반환하는 method
- 기존
Static List에서Todoinstance를 삭제하는 기능 넣기.삭제 기능
- 웹 페이지에서 각각의 Todo list 옆에 삭제 버튼을 생성
。listTodos.jsp파일의<table>에서foreach를 이용해<tr>내부에서 열 역할을 수행하는<td>에<a>를 이용해서 DELETE 버튼을 추가.
。 각각의 DELETE 버튼의 href 링크에@RequestMapping으로 Mapping하는 url("delete-todo")을 지정 및 해당 버튼의 todo의 id를 Query parameter("?id=${todo.id}")로@RequestParam를 통해Controller method에 전달.<td><a href="delete-todo?id=${todo.id}" class="btn btn-danger">DELETE</a></td>
。id=1의 delete button을 inspection하면href를 통해 Query Parameter가id=1인것을 확인 가능.
- Todo instance를
Static List에서 삭제하기 위한 method를TodoService.java로 따로 생성하기public static void deleteTodo(int id){ Predicate<? super Todo> predicate = todo -> todo.getId() == id ; todos.removeIf(predicate); }
predicate:Predicate를 통해 입력값(todoinstance )를 받으면todo.getId()의 getter method로 얻은todo의 id와 매개변수로 전달된 id를 비교하여 true 또는 false 반환.
▶List객체.removeIf(predicate객체)에 predicate를 전달하여Static List내부의Todoinstance 가 각각Predicate에 전달되어 람다식에서 true를 반환 시 해당Todoinstance를 Static List에서 삭제.
@RequestParam를 통해 Query Parameter인id를 받아Todoinstance를Static List에서 삭제 후list-todo로 redirect 해주는Controller Method생성하기@RequestMapping("delete-todo") public String DeleteTodo(@RequestParam int id){ TodoService.deleteTodo(id); return "redirect:list-todo"; }
predicate<T>:
Predicate<Type> predicate = 람다식
。입력 값을 받아서boolean값을 반환하는 함수형 Interface
。전달식을 요구하는 특정 List객체의 Method에 predicate를 사용 시 값을 input 받아 람다식 구문을 수행 후 Boolean을 반환.
▶ 조건이 참이면 true, 거짓이면 false를 return.
。주로Stream.filter(Predicate객체)로 조건으로서 활용된다.
Predicate<? super 클래스타입>:
。해당 Class type뿐만 아닌 부모 Class Type까지 적용할 수 있는 predicate를 의미.
활용례 )private static List<Todo> todos = new ArrayList<>(); static { todos.add(new Todo(todosCount++,"wjdtn747","Learn AWS", LocalDate.now().plusYears(1),false)); todos.add(new Todo(todosCount++,"wjdtn0619","Learn DevOps", LocalDate.now().plusYears(2),true)); todos.add(new Todo(todosCount++,"wjdtn3902","Learn FullStackDevelopment", LocalDate.now().plusYears(3),false)); } Predicate<? super Todo> predicate = todo -> todo.getId() == 3; todos.removeIf(predicate);▶
List객체.removeIf(predicate객체)에 predicate를 전달하여Static List내부의Todoinstance 가 각각Predicate에 전달되어 람다식에서 true를 반환 시 해당Todoinstance를 Static List에서 삭제.
list객체.removeif(조건):
。해당 List객체의 모든 각 요소에대해 predicate를 실시한 후 해당 조건에 대해 true이면 remove.list객체.stream().filter(predicate).findFirst().get();
- 기존
Static List에서Todoinstance를 UPDATE 하는 기능 넣기.
- UPDATE button 구현하기
<td><a href="update-todo?id=${todo.id}" class="btn btn-secondary">UPDATE</a></td>
▶ DELETE 버튼과 동일하게 query parameter를 통해id를 전달.
- UPDATE
Controller Method구현하기.
Static List에서 Update할Todoinstance를 찾아서 return 하기 위한 method를TodoService.java에서 생성하기. Stream 관련public static Todo findById(int id){ Predicate<? super Todo> predicate = todo -> todo.getId() == id; return todos.stream().filter(predicate).findFirst().get(); }▶
Predicate를 만족하는stream.filter를 통해 생성된Stream의 첫번째todoinstance를 추출
id와description을 입력받아Static List의 todo를 수정하는 method를TodoService.java에서 생성하기.public static void updateById(int id, String description){ Predicate<? super Todo> predicate = todo -> todo.getId() == id; todos.stream().filter(predicate).findFirst().get().setDescription(description); }▶
Predicate를 만족하는stream.filter를 통해 생성된Stream의 첫번째todoinstance를 추출하여setter를 통해 description을 수정.
- 특정
Spring Bean을 불러와서 Update를 수행할JSPpage를 보여주기 위한 Controller Method@RequestMapping(value="update-todo",method=RequestMethod.GET) public String UpdateTodo(@RequestParam int id, ModelMap model){ model.put("todo",TodoService.findById(id)); return "addTodo"; }。Update를 수행할 page를 보여주기 위한 method이므로,
GET으로 설정.
。Todo.jsp는<form:form method="post" modelAttribute="todo">을 통해 binding될Todoinstance를 요구하므로, 특정 Id에 해당하는Todoinstance를 찾아서Model로 put하여 자동으로 Binding 되도록 설정.
- 선택된 Id와 새로 입력된 description으로 URL를 전송해서 변경사항을 저장하기 위한 Controller method 생성하기
@RequestMapping(value="update-todo",method=RequestMethod.POST) public String UpdateTodo(@RequestParam int id,@RequestParam String description,ModelMap model, @Valid Todo todo, BindingResult result){ if (result.hasErrors()) { return "addTodo"; }else{ TodoService.updateById(id,description); return "redirect:list-todo"; } }。
addTodo.jsp에서 Id와 새로 입력된 description를POST로<form>data로서 해당Controller Method로 전달되어 Static List의 기존 todo를 수정하는 method를 실행.
。@Valid를 통해Spring Validation검증을 수행.
。이후 수정작업이 마친 경우list-todo로 redirect.Java Stream Stream
- 함수형 프로그래밍
- 데이터의 흐름으로서
Array,Collection instance에 대해 함수 여러개를 조합해서 원하는 결과를 필터링해서 얻음.
JSP에 공통된 field의 tag에 대해fieldset생성하기.
<fieldset>:
。공통된 path를 가진 관련된 모든 field의 tag를 한개의FieldSet으로 그룹화하기.
。fieldset간 여백을 만드는법
class를 이용해서 mb-1 , mb-2 , mb-3 , mb-4의 값을 선언.
▶ 설정 시 fieldset 하단에 여백이 생성됨.<!--Descripition FieldSet--> <fieldset class="mb-3"> <!--class 속성의 mb-3은 fieldset간 여백 생성.--> <form:label path="description">Description</form:label> <form:input type="text" path="description" required="required"/> <form:errors path="description" cssClass="text-warning"/> </fieldset>
- Todo를 Delete 또는 Update 할 경우 설정할 Target Date 정의하기.
- Target Date의
<form:input>을JSP에서 정의하기.<!--TargetDate FieldSet--> <fieldset class="mb-3"> <form:label path="targetDate">Target Date</form:label> <form:input type="text" path="targetDate" required="required"/> <form:errors path="targetmDate" cssClass="text-warning"/> </fieldset>
Controller methodaddTodo()에서 Target Date 입력 시Static List에 반영되도록 설정@RequestMapping(value = "add-todo", method= RequestMethod.POST) public String addTodoPage(ModelMap model ,@Valid Todo todo, BindingResult result){ if(result.hasErrors()){ return "Todo"; } TodoService.addTodo((String)model.get("name"),todo.getDescription(), todo.getTargetDate(),false); return "redirect:list-todo"; }。
Form Backing Object활용 시Controller method의 첫번째 매개변수는Model로 설정 및 두번째 매개변수는 직접 Binding될Spring Bean을Model객체로서 설정.
。Validation 수행 시Controller Method의 매개변수에서 Binding되는Model객체 앞에@Valid선언.
Controller methodupdateTodo()에서 Target Date 입력 시Static List에 반영되도록 설정.
。TodoService.java의 Update methodpublic static void updateById(int id, String description, LocalDate targetDate){ Predicate<? super Todo> predicate = todo -> todo.getId() == id; todos.stream().filter(predicate).findFirst().get().setDescription(description); todos.stream().filter(predicate).findFirst().get().setTargetDate(targetDate); }。Controller method
@RequestMapping(value="update-todo",method=RequestMethod.POST) public String UpdateTodo(@RequestParam int id,@RequestParam String description,@RequestParam LocalDate targetDate,ModelMap model, @Valid Todo todo, BindingResult result){ if (result.hasErrors()) { return "addTodo"; }else{ TodoService.updateById(id,description,targetDate); return "redirect:list-todo"; } }▶ 이후 Update시 form의 data가 자동으로 Bean의 field에 Auto binding.
- 입력 field에 특정 양식의 data(=
LocalDate) 입력 시 표현되는 form의 양식을 통일하기.
。application.properties에서 다음 구문을 입력.spring.mvc.format.date=yyyy-MM-dd。spring에서
"m"은 minute을 의미하므로, Month를 표시하려면"M"을 사용.
▶ 아래 사진처럼 변경!
- 날짜선택팝업이 도출되어
Target Date를 설정할 수 있게끔하기.
。Bootstrap Datepicker:
날짜 관련 팝업을 추가하는 Bootstrap framework.
- Pom.xml에 Bootstrap Datepicker을 추가.
<dependency> <groupId>org.webjars</groupId> <artifactId>bootstrap-datepicker</artifactId> <version>1.9.0</version> </dependency>
- 다음 경로의 다음 항목의 경로를 복사 후 jsp 파일에 정의하기.
。bootstrap-datepicker.standalone.min.css
。bootstrap-datepicker.min.jswebjars/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.standalone.min.css webjars/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js。webjars를 이용해 css 파일 사용 시
/META-INF/resources/를 지정할 필요가 없음.
。html 문서에 css 활용 시<head>부분에 를 이용해 css 파일 연결.<link href="webjars/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.standalone.min.css" rel="stylesheet">。html 문서에서 javascript 활용 시 body의 종료태그
</body>앞에<script>를 통해 파일 연결.<script src="webjars/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js"></script>.
Datepicker framework를 targetDate field에 연결하기.
。https://bootstrap-datepicker.readthedocs.io/en/latest/
다음 주소의 다음의 javascript 구문을 사용
▶ 다음 구문은jQuery형식임.$('.datepicker').datepicker({ format: 'mm/dd/yyyy', startDate: '-3d' });。작성된 datepicker를 jsp에
<script type="text/javascript">구문으로 추가하기.
▶ 해당 구문은 jquery<script>의 하단에 위치해야한다.<script src="webjars/jquery/3.6.0/jquery.min.js"></script> <script type="text/javascript"> $('#targetDate').datepicker({ format: 'yyyy-mm-dd' }); </script>。소괄호 안
$(#tagID명)는 jsp의 정의할<form:input path="targetDate">의 id(=#targetDate)를 입력.
。$('#elementID'): JSP Page 내 특정 ID의 element를 객체로서 참조.
。#: id 앞에 선언.
。startDate는 필요없으므로 제외 가능.
。format은 위에서 정의했던 양식"yyyy-mm-dd"으로 설정.
▶ spring과 다르게datepicker framework의"m"은 월을 의미하므로, 소문자로 작성.
<!-- addTodo.jsp --> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <!doctype html> <html lang="ko"> <head> <link href="webjars/bootstrap/5.1.3/css/bootstrap.min.css" rel="stylesheet"> <link href="webjars/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.standalone.min.css" rel="stylesheet"> <title>Document</title> </head> <body> <div class="container"> <h1>Todo Details</h1> <form:form method="post" modelAttribute="todo"> <fieldset class="mb-3"> <form:label path="description">Description : </form:label> <form:input type="text" path="description" required="required"/> <form:errors path="description" cssClass="text-warning"/> </fieldset> <fieldset class="mb-3"> <form:label path="targetDate">Target Date</form:label> <form:input type="text" id="targetDate" path="targetDate" required="required"/> <form:errors path="targetDate" cssClass="text-warning"/> </fieldset> <input type="submit" class="btn btn-secondary"> </form:form> </div> <script src="webjars/bootstrap/5.1.3/js/bootstrap.min.js"></script> <script src="webjars/jquery/3.6.0/jquery.min.js"></script> <script src="webjars/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js"></script> <script type="text/javascript"> $('#targetDate').datepicker({ format : 'yyyy-mm-dd' }); </script> </body> </html>
- Bootstrap의 Navigation bar 활용
HTML-nav 관련
Bootstrap
깃허브 코드참조
。Spring-Boot-Web-Application-V2 항목<!-- navigation bar --> <nav class="navbar navbar-expand-md navbar-light bg-light mb-3 p-1"> <a class="navbar-brand m-1" href="https://velog.io/@wjdtn747/posts">wjdtn747</a> <div class="collapse navbar-collapse"> <ul class="navbar-nav"> <li class="nav-item"><a class="nav-link" href="/login">Home</a></li> <li class="nav-item"><a class="nav-link" href="/list-todo">Todos</a></li> </ul> </div> <ul class="navbar-nav"> <li class="nav-item"><a class="nav-link" href="/logout">Logout</a></li> </ul> </nav>。
<a>로 정의된 각각의 버튼은 href를 통해 연결되는 url로 설정.
。Home, Todos는<div>로collapse되는 구획이며, wjdtn747과 Logout 버튼과 독립적인 list로 구분되어있음.
。해당 html코드를nav-bar을 생성할 jsp page의<body>태그에 생성.
▶ 다음 사진처럼 상단에 navigation bar가 생성됨!
이때 , 모든 JSP Page에 대해서 navigation bar를 정의할 경우, 중복된 코드를 계속 반복해서 활용(WET)하게된다.
▶JSPF사용!
JSPF(Java Server Page Fragment) :
。JSP 코드의 일부분으로서.jspf확장자명으로 정의.
。중복되는 코드를 분리하여JSPF생성 후JSP마다 코드를 붙여주는 역할을 수행
▶JSP의 코드의 중복 해소 (WET방지 )
。 각각의JSPPage에서 중복정의되는 부분에 대해서JSPF정의.
JSP의 공통적으로 중복되는 부분의 코드를JSPF로 정리하여JSP에 적용하기.
。JSP의header,navigation,footer의 코드 공통부분에 대해 각각JSPF를 생성하기.
<%@ Include %>: include 지시자 JSP 태그 종류
。별도의 페이지를 현재 페이지에 삽입.
。특정JSP의 코드내용을 다른JSP파일 내에서 원하는 공간에 포함시키는 기능.
。file="JSP상대경로"속성을 지정하여JSP기준JSPF의 상대경로를 설정.
▶<%@ include file="JSP상대경로" %>
- Navigation bar를 구현 할
JSP파일에 위치한 디렉토리경로 기준common폴더 생성 후navigation.jspf를 생성하여 다음 코드를 입력<!-- navigation.jspf --> <nav class="navbar navbar-expand-md navbar-light bg-light mb-3 p-1"> <a class="navbar-brand m-1" href="https://velog.io/@wjdtn747/posts">wjdtn747</a> <div class="collapse navbar-collapse"> <ul class="navbar-nav"> <li class="nav-item"><a class="nav-link" href="/login">Home</a></li> <li class="nav-item"><a class="nav-link" href="/list-todo">Todos</a></li> </ul> </div> <ul class="navbar-nav"> <li class="nav-item"><a class="nav-link" href="/logout">Logout</a></li> </ul> </nav>
JSP에서<%@ include file="JSP상대경로" %>로JSPF파일을 포함시키기.
。file=""속성으로JSP파일 기준 상대경로로JSPF파일의 경로를 설정하기.
。해당 구문은JSP의<body>태그 상단에서 선언.<!-- JSPF 불러오기.--> <%@ include file="common/navigation.jspf" %>▶ 모든 JSP Page에 대하여 동일한 JSPF를 사용 가능!
JSP의<head>부분에 대해서도 공통된 중복되는 부분을 묶어서header.jspf를 생성하여 입력 후 jsp 파일에서 불러와서 사용<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> <html lang="kor"> <head> <link href="webjars/bootstrap/5.1.3/css/bootstrap.min.css" rel="stylesheet"> <link href="webjars/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.standalone.min.css" rel="stylesheet"> <!-- 웹페이지 태그 제목 --> <title>Manage Your Todo</title> </head><%@ include file="common/header.jspf" %>。
JSP의footer부분도 동일하게footer.jspf구현 후 적용하기!<!-- script 파일 설정 --> <script src="webjars/bootstrap/5.1.3/js/bootstrap.min.js"></script> <script src="webjars/jquery/3.6.0/jquery.min.js"></script> <script src="webjars/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.min.js"></script> <script type="text/javascript"> $('#targetDate').datepicker({ format: 'yyyy-mm-dd' }); </script> </body> </html><%@ include file="common/footer.jspf" %>
- 사용 례
。shift + tab할 경우 선택된 행의 tab이 한 칸 삭제됨.<%@ include file="common/header.jspf" %> <body> <%@ include file="common/navigation.jspf" %> <div class="container"> <h1>Your name : ${name}</h1> <a href="list-todo">Manage</a> your todos </div> <%@ include file="common/footer.jspf" %>