월 16,940원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
[질문글아님!] 재수정! 공부한 내용 올려봅니다!
제가 공부하면서 주석으로 정리한 것 있는데하찮은 실력이지만 다른 공부하시는 분들께 도움이 될 수 있을까 해서 공부한 내용을 올려봅니다!범위는 "셀렉트박스"까지 내용입니다!addForm.html<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <link th:href="@{/css/bootstrap.min.css}" href="../css/bootstrap.min.css" rel="stylesheet"> <style> .container { max-width: 560px; } </style> </head> <body> <div class="container"> <div class="py-5 text-center"> <h2>상품 등록 폼</h2> </div> <form action="item.html" th:action method="post" th:object="${item}"> <!-- items.html 에서 상품 등록 버튼을 누름 : th:onclick="|location.href='@{/form/items/add}'|" 즉, 사용자가 /form/items/add 로 GET 요청이 보내면 public String addForm(Model model) {...} 호출 addForm() 로직은 1. model.addAttirbute("item", new Item()) : 빈 item 객체를 만들고 모델에 담아 2. addForm.html 에게 전달하는 것이었음 이제, addForm.html 에서 사용자가 입력값을 넣어 /form/items/add 로 POST 요청을 보낸다면 @ModelAttribute Item item 이므로 value(입력값)가 object(item객체)의 각 field(item객체의 필드)들에 바인딩되고 바인딩된 객체는 수정된 객체를 의미하고 이 객체를 item.html로 전달함 (action="item.html") addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes) {...} 호출될 것 addItem() 로직은 1. save() 1-1. sequence(=PK) 증가시키고 sequence를 item 객체의 id로 설정하고(setter) 1-2. store(=Map)에 id(key)와 item객체(value)를 저장하고 2. redirectAttributes.addAttribute("itemId", savedItem.getId()) : 해당 객체를 리다이렉트 url 의 경로변수로 사용하고 2. redirectAttributes.addAttribute("status", true) : 불린값을 리다이렉트 url 의 쿼리 파라미터로 사용하고 3. "redirect:/form/items/{itemId}" 리다이렉트하는(GET요청) 것이었음 localhost:8080/form/items/{itemId} 으로 GET 요청이 온다면 item(@PathVariable long itemId, Model model) {...} 호출할 것 item() 로직은 1. findById() : {itemId} 를 사용하여 특정 item 객체를 조회하고 2. 그 item 객체를 모델에 담아 item.html 에 전달하는 것이었음 --> <div> <label for="itemName">상품명</label> <input type="text" id="itemName" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요"> <!-- th 가 name, value, id 를 만들어준다. *{itemName}: ${item.itemName}의 생략버전. 초기화되지 않은 item 객체이므로 빈 객체. 사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어오고 item 객체에 각 필드들이 바인딩됨 --> </div> <div> <label for="price">가격</label> <input type="text" id="price" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요"> <!-- th 가 name, value, id 를 만들어준다. *{itemName}: ${item.itemName}의 생략버전. 초기화되지 않은 item 객체이므로 빈 객체. 사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어오고 item 객체에 각 필드들이 바인딩됨 --> </div> <div> <label for="quantity">수량</label> <input type="text" id="quantity" th:field="*{quantity}" class="form-control" placeholder="수량을 입력하세요"> <!-- th 가 name, value, id 를 만들어준다. *{itemName}: ${item.itemName}의 생략버전. 초기화되지 않은 item 객체이므로 빈 객체. 사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어오고 item 객체에 각 필드들이 바인딩됨 --> </div> <hr class="my-4"> <!-- single checkbox --> <div>판매 여부</div> <div> <div class="form-check"> <!-- spring 은 히든필드를 운용한다. HTML checkbox 는 체크되지 않으면 서버로 히든필드만 전송한다. 그럼 spring 은 필드가 false 라고 판단한다. HTML 메시지: itemName=22&price=22&quantity=22&_open=on spring: item.open = false HTML checkbox 가 체크되면 필드와 히든필드를 서버로 전송된다. HTML 메시지: itemName=33&price=33&quantity=33&open=on&_open=on. 그럼 spring 은 필드가 true 라고 판단한다. spring: item.open = true --> <input type="checkbox" id="open" th:field="*{open}" class="form-check-input" /> <!-- th 를 사용하면 알아서 히든필드를 만들어내기 때문에 HTML checkbox 를 편하게 사용할 수 있다. --> <label for="open" class="form-check-label">판매 오픈</label> </div> </div> <!-- multi checkbox --> <div> <div>등록 지역</div> <div th:each="region : ${regions}" class="form-check form-check-inline"> <!-- ${regions} : 컨트롤러가 호출될 때 @ModelAttribute regions() 가 호출된 결과로 넘어온 완전체 Map<String, String> region : Map<String, String> 의 요소. String 객체(key값) --> <input type="checkbox" th:field="*{regions}" th:value="${region.key}" class="form-check-input"> <!-- th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다. *{regions} : ${item.regions}의 생략 버전. item 객체은 어떠한 필드도 초기화하지 않는 Item() 생성자로 초기화되어 넘어왔으므로 List<String> regions 필드는 null ${region.key} : 완전체 Map의 key 중 하나. "SEOUL", "BUSAN", "JEJU" 중 하나 (반복문이니깐). 사용자가 서버로 보낼 값 사용자가 0개 이상 3개 이하의 체크박스를 체크해서 POST 요청 보내면 폼데이터가 서버로 넘어오고 item 객체에 각 필드들이 바인딩됨 --> <label th:for="${#ids.prev('regions')}" th:text="${region.value}" class="form-check-label">서울</label> <!-- ${#ids.prev('필드명')} : 바로 이전 필드의 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다. ${region.value} : 완전체 Map(통짜)의 value 중 하나. "서울", "부산", "제주" 중 하나 (반복문이니깐). label 태그 기본값 대체할 값 --> </div> </div> <!-- radio button --> <div> <div>상품 종류</div> <div th:each="type : ${itemTypes}" class="form-check form-check-inline"> <!-- ${itemTypes} : 컨트롤러가 호출될 때 @ModelAttribute itemTypes() 가 호출된 결과로 넘어온 완전체 ItemType[] (Enum 배열) type : ItemType[] 의 요소. 열거 객체 (반복문이니깐) --> <input type="radio" th:field="*{itemType}" th:value="${type.name()}" class="form-check-input"> <!-- th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다. *{itemType} : ${item.itemType}의 생략 버전. item 객체은 어떠한 필드도 초기화하지 않는 Item() 생성자로 초기화되어 넘어왔으므로 ItemType itemType 필드는 null ${type.name()} : 완전체 ItemType[]의 상수 중 하나의 문자열 형태. "BOOK", "FOOD", "ETC" 중 하나 (반복문이니깐). 사용자가 3개 중 1개의 라디오박스를 체크해서 POST 요청 보내면 폼데이터가 서버로 넘어오고 item 객체에 각 필드들이 바인딩됨 --> <label th:for="${#ids.prev('itemType')}" th:text="${type.description}" class="form-check-label"> BOOK</label> <!-- ${#ids.prev('itemType')} : 바로 이전 필드의 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다. 사용자가 서버로 보낼 값 ${type.description} : 완전체 ItemType[]의 상수 중 하나의 멤버변수(description). "도서", "음식", "기타" 중 하나 (반복문이니깐). label 태그 기본값 대체할 값 --> </div> </div> <!-- SELECT --> <div> <div>배송 방식</div> <select th:field="*{deliveryCode}" class="form-select"> <!-- *{deliveryCode} : ${item.deliveryCode}의 생략 버전. item 객체은 어떠한 필드도 초기화하지 않는 Item() 생성자로 초기화되어 넘어왔으므로 DeliveryCode deliveryCode 필드는 null 사용자가 3개 중 1개의 셀렉트를 체크해서 POST 요청 보내면 폼데이터가 서버로 넘어오고 item 객체에 각 필드들이 바인딩됨 --> <option value="">==배송 방식 선택==</option> <!-- 맨 위에 보여줄 옵션 설정 --> <option th:each="deliveryCode : ${deliveryCodes}" th:value="${deliveryCode.code}" th:text="${deliveryCode.displayName}"> FAST </option> <!-- 그 다음으로 보여줄 옵션들 설정 ${deliveryCodes} : 컨트롤러가 호출될 때 @ModelAttribute deliveryCodes() 가 호출된 결과로 넘어온 완전체 List<DeliveryCode> deliveryCode : List<DeliveryCode> 의 요소. DeliveryCode 객체 (반복문이니깐) ${deliveryCode.code} : Delivery 객체의 멤버변수(String code). "FAST", "NORMAL", "SLOW" 중 하나 (반복문이니깐). 사용자가 서버로 보낼 값 ${deliveryCode.displayName} : Delivery 객체의 멤버변수(String displayName). "빠른배송", "일반배송", "느린배송" 중 하나 (반복문이니깐). option 태그 기본값을 대체할 값 --> </select> </div> <hr class="my-4"> <div class="row"> <div class="col"> <button class="w-100 btn btn-primary btn-lg" type="submit">상품 등록</button> </div> <div class="col"> <button class="w-100 btn btn-secondary btn-lg" onclick="location.href='items.html'" th:onclick="|location.href='@{/form/items}'|" type="button">취소</button> </div> </div> </form> </div> <!-- /container --> </body> </html>editForm.html<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <link th:href="@{/css/bootstrap.min.css}" href="../css/bootstrap.min.css" rel="stylesheet"> <style> .container { max-width: 560px; } </style> </head> <body> <div class="container"> <div class="py-5 text-center"> <h2>상품 수정 폼</h2> </div> <form action="item.html" th:action method="post" th:object="${item}"> <!-- item.html 에서 상품 수정 버튼을 누름 : th:onclick="|location.href='@{/form/items/{itemId}/edit(itemId=${item.id})}'|" 즉, 사용자가 /form/items/{itemId}/edit 로 GET 요청이 보내면 editForm(@PathVariable Long itemId, Model model) {...} 호출 editForm() 로직은 1. findByid() : {itemId} 를 사용하여 특정 item 객체를 조회하고 2. 그 item 객체를 모델에 담아 editForm.html 에 전달하는 것이었음 이제, editForm.html 에서 사용자가 입력값을 넣어 /form/items/{itemId}/edit 로 POST 요청을 보낸다면 @ModelAttribute Item item 이므로 value(입력값)가 object(item객체)의 각 field(item객체의 필드)들에 바인딩되고 바인딩된 객체는 수정된 객체를 의미하고 이 객체를 item.html로 전달함 (action="item.html") editItem(@PathVariable Long itemId, @ModelAttribute Item item) {...} 호출될 것 editItem() 로직은 1. update() 1-1. findById() : {itemId} 를 사용하여 수정할 item 객체를 조회하고 1-2. getter() : 바인딩된 객체(=수정된 객체)의 각 필드들을 조회하고 1-3. setter() : 수정할 item 객체의 각 필드들을 수정된 객체의 각 필드들로 수정하고 2. "redirect:/form/items/{itemId}" 리다이렉트하는(GET요청) 것이었음 localhost:8080/form/items/{itemId} 으로 GET 요청이 온다면 item(@PathVariable long itemId, Model model) {...} 호출할 것 item() 로직은 1. findById() : {itemId} 를 사용하여 특정 item 객체를 조회하고 그 item객체를 모델에 담아 2. item.html 에 전달하는 것이었음 --> <div> <label for="id">상품 ID</label> <input type="text" id="id" class="form-control" th:field="*{id}" readonly> <!-- th 가 name, value, id 를 만들어준다. *{id}: ${item.id}의 생략버전. editForm() 에서 model 에 담겨서 넘어온 특정 Item 객체 (null 아님) 사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어오고 item 객체에 각 필드들이 바인딩됨 --> </div> <div> <label for="itemName">상품명</label> <input type="text" id="itemName" class="form-control" th:field="*{itemName}"> <!-- th 가 name, value, id 를 만들어준다. *{itemName}: ${item.itemName}의 생략버전. editForm() 에서 model 에 담겨서 넘어온 특정 Item 객체 (null 아님) 사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어옴 --> </div> <div> <label for="price">가격</label> <input type="text" id="price" class="form-control" th:field="*{price}"> <!-- th 가 name, value, id 를 만들어준다. *{itemName}: ${item.itemName}의 생략버전. editForm() 에서 model 에 담겨서 넘어온 특정 Item 객체 (null 아님) 사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어옴 --> </div> <div> <label for="quantity">수량</label> <input type="text" id="quantity" class="form-control" th:field="*{quantity}"> <!-- th 가 name, value, id 를 만들어준다. *{quantity}: ${item.quantity}의 생략버전. editForm() 에서 model 에 담겨서 넘어온 특정 Item 객체 (null 아님) 사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어옴 --> </div> <hr class="my-4"> <!-- single checkbox --> <div>판매 여부</div> <div> <div class="form-check"> <!-- spring 은 히든필드를 운용한다. HTML checkbox 는 체크되지 않으면 서버로 히든필드만 전송한다. 그럼 spring 은 필드가 false 라고 판단한다. HTML 메시지: itemName=22&price=22&quantity=22&_open=on spring: item.open = false HTML checkbox 가 체크되면 필드와 히든필드를 서버로 전송된다. HTML 메시지: itemName=33&price=33&quantity=33&open=on&_open=on. 그럼 spring 은 필드가 true 라고 판단한다. spring: item.open = true --> <input type="checkbox" id="open" th:field="*{open}" class="form-check-input"> <!-- th 가 name, value, id 를 만들어준다. *{open} : ${item.open}의 생략 버전. editForm() 에서 model 에 담겨져 넘어온 특정 item 객체의 open(Boolean객체) 사용자가 입력값을 넣어 POST 요청을 보내면 폼데이터가 서버로 넘어옴 --> <label for="open" class="form-check-label">판매 오픈</label> </div> </div> <!-- multi checkbox --> <div> <div>등록 지역</div> <div th:each="region : ${regions}" class="form-check form-check-inline"> <!-- ${regions} : 컨트롤러가 호출될 때 @ModelAttribute regions() 가 호출된 결과로 넘어온 완전체 Map<String, String> region : Map<String, String> 의 요소. String 객체(key값) --> <input type="checkbox" th:field="*{regions}" th:value="${region.key}" class="form-check-input"> <!-- th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다. *{regions} : ${item.regions}의 생략 버전. item 객체은 어떠한 필드도 초기화하지 않는 Item() 생성자로 초기화되어 넘어왔으므로 List<String> regions 필드는 null ${region.key} : 완전체 Map의 key 중 하나. "SEOUL", "BUSAN", "JEJU" 중 하나 (반복문이니깐). 사용자가 서버로 보낼 값 사용자가 0개 이상 3개 이하의 체크박스를 체크해서 POST 요청 보내면 폼데이터가 서버로 넘어옴 --> <label th:for="${#ids.prev('regions')}" th:text="${region.value}" class="form-check-label">서울</label> <!-- ${#ids.prev('필드명')} : 바로 이전 필드의 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다. ${region.value} : 완전체 Map(통짜)의 value 중 하나. "서울", "부산", "제주" 중 하나 (반복문이니깐). label 태그 기본값 대체할 값 --> </div> </div> <!-- radio button --> <div> <div>상품 종류</div> <div th:each="type : ${itemTypes}" class="form-check form-check-inline"> <!-- ${itemTypes} : 컨트롤러가 호출될 때 @ModelAttribute itemTypes() 가 호출된 결과로 넘어온 완전체 ItemType[] (Enum 배열) type : ItemType[] 의 요소. 열거 객체 (반복문이니깐) --> <input type="radio" th:field="*{itemType}" th:value="${type.name()}" class="form-check-input"> <!-- th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다. *{itemType} : ${item.itemType}의 생략 버전. item 객체은 어떠한 필드도 초기화하지 않는 Item() 생성자로 초기화되어 넘어왔으므로 ItemType itemType 필드는 null ${type.name()} : 완전체 ItemType[]의 상수 중 하나의 문자열 형태. "BOOK", "FOOD", "ETC" 중 하나 (반복문이니깐). 사용자가 3개 중 1개의 라디오박스를 체크해서 POST 요청 보내면 폼데이터가 서버로 넘어옴 --> <label th:for="${#ids.prev('itemType')}" th:text="${type.description}" class="form-check-label"> BOOK</label> <!-- ${#ids.prev('itemType')} : 바로 이전 필드의 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다. 사용자가 서버로 보낼 값 ${type.description} : 완전체 ItemType[]의 상수 중 하나의 멤버변수(description). "도서", "음식", "기타" 중 하나 (반복문이니깐). label 태그 기본값 대체할 값 --> </div> </div> <!-- SELECT --> <div> <div>배송 방식</div> <select th:field="*{deliveryCode}" class="form-select"> <!-- *{deliveryCode} : ${item.deliveryCode}의 생략 버전. item 객체은 어떠한 필드도 초기화하지 않는 Item() 생성자로 초기화되어 넘어왔으므로 DeliveryCode deliveryCode 필드는 null 사용자가 3개 중 1개의 셀렉트를 체크해서 POST 요청 보내면 폼데이터가 서버로 넘어옴 --> <option value="">==배송 방식 선택==</option> <!-- 맨 위에 보여줄 옵션 설정 --> <option th:each="deliveryCode : ${deliveryCodes}" th:value="${deliveryCode.code}" th:text="${deliveryCode.displayName}"> FAST </option> <!-- 그 다음으로 보여줄 옵션들 설정 ${deliveryCodes} : 컨트롤러가 호출될 때 @ModelAttribute deliveryCodes() 가 호출된 결과로 넘어온 완전체 List<DeliveryCode> deliveryCode : List<DeliveryCode> 의 요소. DeliveryCode 객체 (반복문이니깐) ${deliveryCode.code} : Delivery 객체의 멤버변수(String code). "FAST", "NORMAL", "SLOW" 중 하나 (반복문이니깐). 사용자가 서버로 보낼 값 ${deliveryCode.displayName} : Delivery 객체의 멤버변수(String displayName). "빠른배송", "일반배송", "느린배송" 중 하나 (반복문이니깐). option 태그 기본값을 대체할 값 --> </select> </div> <div class="row"> <div class="col"> <button class="w-100 btn btn-primary btn-lg" type="submit">저장</button> </div> <div class="col"> <button class="w-100 btn btn-secondary btn-lg" onclick="location.href='item.html'" th:onclick="|location.href='@{/form/items/{itemId}(itemId=${item.id})}'|" type="button">취소</button> </div> </div> </form> </div> <!-- /container --> </body> </html>item.html<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <link th:href="@{/css/bootstrap.min.css}" href="../css/bootstrap.min.css" rel="stylesheet"> <style> .container { max-width: 560px; } </style> </head> <body> <!-- ${param.status} : addItem() 또는 editItem() 에서 "redirect:/form/items/{itemId}" 로 리다이렉트해왔음 "redirect:/form/items/{itemId}" url 로 요청이 들어왔기 때문에 item() 로직 수행됨 item() 로직에서 경로변수 인자를 사용하여 특정 item 객체를 조회하고 item 객체를 model에 담고 item.html 로 보냄 --> <div class="container"> <div class="py-5 text-center"> <h2>상품 상세</h2> </div> <!-- 추가 --> <h2 th:if="${param.status}" th:text="'저장 완료'"></h2> <!-- ${param.status} : addItem()에서 "redirect:/form/items/{itemId}" 로 리다이렉트해왔음 즉, http://localhost:8080/form/items/3?status=true 이런 식으로 리다이렉트 (addItem()에서 redirectAttributes 로 itemId, status 를 설정했음. itemId, status는 한번 설정하면 불변해야 하므로 editItem()에선 rdirectAttributes 설정하지 않음) itemId 는 이미 리다이렉트 url로 사용되었고 status 는 param 으로 url 경로변수에 접근하여 속성을 추출하여 사용하면 된다. --> <div> <label for="itemId">상품 ID</label> <input type="text" id="itemId" name="itemId" class="form-control" value="1" th:value="${item.id}" readonly> <!-- th 가 name, value, id 를 만들어준다. ${item.id} : 사용자가 addForm.html 또는 editForm.html 에서 입력값을 넣어 POST 요청을 보내면 value가 서버로 넘어갔었다. 서버는 쿼리 문자열을 받아(getter) Item 객체의 각 필드에 매핑(setter)했었다. 그 Item 객체가 FormItemController 의 addItem() 또는 editItem()에 의해 item.html 로 리다이렉트되었다. 그 Item 객체를 item 라는 이름으로 다시 각각 add() 또는 editItem()이 호출되어 리다이렉트할 때 넘겨준 Item 객체 사용자가 입력값을 넣어 POST 요청을 보내면 value가 서버로 넘어가고, 서버는 쿼리 문자열을 받아(getter) Item 객체의 각 필드(Long id)에 매핑한다(setter) 이후 form 태그의 action 과 method 에 따라 행동하면 된다 --> </div> <div> <label for="itemName">상품명</label> <input type="text" id="itemName" name="itemName" class="form-control" value="상품A" th:value="${item.itemName}" readonly> <!-- *{itemName}: ${item.itemName}의 생략버전. 초기화되지 않은 item 객체이므로 빈 객체. 사용자가 입력값을 넣어 POST 요청을 보내면 서버는 쿼리 문자열을 받아(getter) Item 객체의 각 필드(String itemName)에 매핑한다(setter) 이후 form 태그의 action 과 method 에 따라 행동하면 된다 <input type="text" id="itemName" name="itemName" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요"> --> </div> <div> <label for="price">가격</label> <input type="text" id="price" name="price" class="form-control" value="10000" th:value="${item.price}" readonly> </div> <div> <label for="quantity">수량</label> <input type="text" id="quantity" name="quantity" class="form-control" value="10" th:value="${item.quantity}" readonly> </div> <hr class="my-4"> <!-- single checkbox --> <div>판매 여부</div> <div> <div class="form-check"> <input type="checkbox" id="open" th:field="${item.open}" class="form-check-input" disabled> <!-- th 가 name, value, id 을 만들어줌 체크박스가 체크되어 있다면 checked="checked" 속성까지 만들어줌. 체크되지 않았다면 속성 만들어지지 않음. --> <label for="open" class="form-check-label">판매 오픈</label> </div> </div> <!-- multi checkbox --> <div> <div>등록 지역</div> <div th:each="region : ${regions}" class="form-check form-check-inline"> <!-- @ModelAttribute 에 담았던 것 반복문 돌리기 --> <!-- @ModelAttribute 에 담겨서 넘어온 것(regions()) 반복문 돌리기 ${regions} : Map<String, String>. 사용자가 선택해서 넘어온 것이 아니라 FormItemController 에서 @ModelAttribute 자체가 그대로 넘어왔기 때문. --> <input type="checkbox" th:field="${item.regions}" th:value="${region.key}" class="form-check-input" disabled> <!-- th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다. ${item.regions} : List. 사용자가 선택했던 요소들이 담긴 리스트이므로 ["SEOUL", "BUSAN"] 등의 리스트. ${region.key} : 위 div 태그의 Map의 key 하나. "SEOUL", "BUSAN", "JEJU" 중 하나 (반복문이니깐) value("SEOUL", "BUSAN", "JEJU" 중 하나)가 field(Map<String, String>) 의 요소에 존재한다면 th가 checked="checked" 속성을 만들어줌. --> <label th:for="${#ids.prev('regions')}" th:text="${region.value}" class="form-check-label">서울</label> <!-- ${#ids.prev('필드명')} : 바로 이전 input 태그에서 사용되었던 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다. ${region.value} : 위 div 태그의 Map의 value 하나. "서울", "부산", "제주" 중 하나 (반복문이니깐) --> </div> </div> <!-- radio button --> <div> <div>상품 종류</div> <div th:each="type : ${itemTypes}" class="form-check form-check-inline"> <!-- @ModelAttribute 에 담겨서 넘어온 것(itemTypes()) 반복문 돌리기 ${itemTypes} : ItemType[](ENUM배열). 사용자가 선택해서 넘어온 것이 아니라 FormItemController 에서 @ModelAttribute 자체가 그대로 넘어왔기 때문. --> <input type="radio" th:field="${item.itemType}" th:value="${type.name()}" class="form-check-input" disabled> <!-- th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다. ${item.itemType} : ENUM. 사용자가 선택했던 ENUM. ${type.name()} : 위 div 태그의 ENUM배열의 ENUM 중 하나의 문자열 형태. "BOOK", "FOOD", "ETC" 중 하나 (반복문이니깐). 사용자가 각 라디오박스를 체크해서 POST 요청 보낼 때 item.itemType 에 문자열이 바인딩되어 날아감. 이후 서버에서 다시 상수로 변환하여 itemType에 저장함. value("BOOK", "FOOD", "ETC" 중 하나)가 field(사용자가 선택했던 ENUM)와 일치한다면 th가 checked="checked" 속성을 만들어줌. --> <label th:for="${#ids.prev('itemType')}" th:text="${type.description}" class="form-check-label"> BOOK</label> <!-- ${#ids.prev('필드명')} : 바로 이전 태그에서 사용되었던 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다. ${type.description} : 위 div 태그의 ENUM배열의 ENUM 중 하나의 값. "도서", "음식", "기타" 중 하나 (반복문이니깐). --> </div> </div> <div class="row"> <div class="col"> <button class="w-100 btn btn-primary btn-lg" onclick="location.href='editForm.html'" th:onclick="|location.href='@{/form/items/{itemId}/edit(itemId=${item.id})}'|" type="button">상품 수정</button> </div> <div class="col"> <button class="w-100 btn btn-secondary btn-lg" onclick="location.href='items.html'" th:onclick="|location.href='@{/form/items}'|" type="button">목록으로</button> </div> </div> </div> <!-- /container --> </body> </html>
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
[질문글아님!] addForm. editForm, item html 파일 참고하세요!
제가 공부하면서 주석으로 정리한 것 있는데하찮은 실력이지만 다른 공부하시는 분들께 도움이 될 수 있을까 해서 공부한 내용을 올려봅니다!범위는 "체크박스-멀티"까지 내용입니다! addForm.html<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <link th:href="@{/css/bootstrap.min.css}" href="../css/bootstrap.min.css" rel="stylesheet"> <style> .container { max-width: 560px; } </style> </head> <body> <div class="container"> <div class="py-5 text-center"> <h2>상품 등록 폼</h2> </div> <form action="item.html" th:action method="post" th:object="${item}"> <!-- ${item} : addForm() 에서 model 에 담겨서 넘어온 새로운 객체 (아래 코드 주석) model.addAttribute("item", new Item()); --> <div> <label for="itemName">상품명</label> <input type="text" id="itemName" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요"> <!-- <input type="text" id="itemName" name="itemName" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요"> --> </div> <div> <label for="price">가격</label> <input type="text" id="price" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요"> <!-- <input type="text" id="price" name="price" class="form-control" placeholder="가격을 입력하세요"> --> </div> <div> <label for="quantity">수량</label> <input type="text" id="quantity" th:field="*{quantity}" class="form-control" placeholder="수량을 입력하세요"> <!-- <input type="text" id="quantity" name="quantity" class="form-control" placeholder="수량을 입력하세요"> --> </div> <hr class="my-4"> <!-- single checkbox --> <div>판매 여부</div> <div> <div class="form-check"> <!-- 히든필드 추가 <input type="checkbox" id="open" name="open" class="form-check-input" /> <input type="hidden" id="_open" name="_open" value="on" /> --> <!-- HTML checkbox 는 체크하지 않으면 서버로 값 자체를 보내지 않는다. (히든필드 사용하지 않을 경우) HTML 메시지: itemName=11&price=11&quantity=11 spring: item.open = null 즉. HTML checkbox 는 사용하기 불편할 수 있다. 그래서 spring 은 히든필드를 운용한다. HTML checkbox 는 체크되지 않으면 서버로 히든필드만 전송한다. 그럼 spring 은 필드가 false 라고 판단한다. HTML 메시지: itemName=22&price=22&quantity=22&_open=on spring: item.open = false HTML checkbox 가 체크되면 필드와 히든필드를 서버로 전송된다. HTML 메시지: itemName=33&price=33&quantity=33&open=on&_open=on. 그럼 spring 은 필드가 true 라고 판단한다. spring: item.open = true --> <input type="checkbox" id="open" th:field="*{open}" class="form-check-input" /> <!-- th 를 사용하면 알아서 히든필드를 만들어내기 때문에 HTML checkbox 를 편하게 사용할 수 있다. --> <label for="open" class="form-check-label">판매 오픈</label> </div> </div> <!-- multi checkbox --> <div> <div>등록 지역</div> <div th:each="region : ${regions}" class="form-check form-check-inline"> <!-- @ModelAttribute 에 담겨서 넘어온 것(regions()) 반복문 돌리기 ${regions} : Map<String, String>. 사용자가 선택해서 넘어온 것이 아니라 FormItemController 에서 @ModelAttribute 자체가 그대로 넘어왔기 때문. --> <input type="checkbox" th:field="*{regions}" th:value="${region.key}" class="form-check-input"> <!-- th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다. *{regions} : ${item.regions}의 생략 버전. Map<String, String>. 사용자가 선택해서 넘어온 것이 아니라 FormItemController 에서 @ModelAttribute 자체가 그대로 넘어왔기 때문. ${region.key} : 위 div 태그의 Map의 key 중 하나. "SEOUL", "BUSAN", "JEJU" 중 하나 (반복문이니깐). 사용자가 각 체크박스를 체크해서 POST 요청 보낼 때 regions.key가 날아감. 이후 item.regions 에 값이 바인딩될 것. --> <label th:for="${#ids.prev('regions')}" th:text="${region.value}" class="form-check-label">서울</label> <!-- ${#ids.prev('필드명')} : 바로 이전 태그에서 사용되었던 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다. ${region.value} : 위 div 태그의 Map의 value 중 하나. "서울", "부산", "제주" 중 하나 (반복문이니깐) --> </div> </div> <div> <div>상품 종류</div> <div th:each="type : ${itemTypes}" class="form-check form-check-inline"> <!-- @ModelAttribute 에 담겨서 넘어온 것(itemTypes()) 반복문 돌리기 ${regions} : ItemTypes[]. 사용자가 선택해서 넘어온 것이 아니라 FormItemController 에서 @ModelAttribute 자체가 그대로 넘어왔기 때문. --> <input type="radio" th:field="*{itemType}" th:value="${type.name()}" class="form-check-input"> <!-- *{itemType} : --> <label th:for="${#ids.prev('itemType')}" th:text="${type.description}" class="form-check-label"> BOOK </label> <!-- --> </div> </div> <div class="row"> <div class="col"> <button class="w-100 btn btn-primary btn-lg" type="submit">상품 등록</button> </div> <div class="col"> <button class="w-100 btn btn-secondary btn-lg" onclick="location.href='items.html'" th:onclick="|location.href='@{/form/items}'|" type="button">취소</button> </div> </div> </form> </div> <!-- /container --> </body> </html> editForm.html<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <link th:href="@{/css/bootstrap.min.css}" href="../css/bootstrap.min.css" rel="stylesheet"> <style> .container { max-width: 560px; } </style> </head> <body> <div class="container"> <div class="py-5 text-center"> <h2>상품 수정 폼</h2> </div> <form action="item.html" th:action method="post" th:object="${item}"> <!-- ${item} : editForm() 에서 model 에 담겨서 넘어온 특정 객체 (아래 코드 주석) Item item = itemRepository.findById(itemId); model.addAttribute("item", item); --> <div> <label for="id">상품 ID</label> <input type="text" id="id" class="form-control" th:field="*{id}" readonly> <!-- th 가 name, value, id 를 만들어준다. <input type="text" id="id" name="id" class="form-control" value="1" th:value="${item.id}" readonly> --> </div> <div> <label for="itemName">상품명</label> <input type="text" id="itemName" class="form-control" th:field="*{itemName}"> <!-- th 가 name, value, id 를 만들어준다. <input type="text" id="itemName" name="itemName" class="form-control" value="상품A" th:value="${item.itemName}"> --> </div> <div> <label for="price">가격</label> <input type="text" id="price" class="form-control" th:field="*{price}"> <!-- th 가 name, value, id 를 만들어준다. <input type="text" id="price" name="price" class="form-control" value="10000" th:value="${item.price}"> --> </div> <div> <label for="quantity">수량</label> <input type="text" id="quantity" class="form-control" th:field="*{quantity}"> <!-- th 가 name, value, id 를 만들어준다. <input type="text" id="quantity" name="quantity" class="form-control" value="10" th:value="${item.quantity}"> --> </div> <hr class="my-4"> <!-- single checkbox --> <div>판매 여부</div> <div> <div class="form-check"> <input type="checkbox" id="open" th:field="*{open}" class="form-check-input"> <label for="open" class="form-check-label">판매 오픈</label> </div> </div> <!-- multi checkbox --> <div> <div>등록 지역</div> <div th:each="region : ${regions}" class="form-check form-check-inline"> <!-- @ModelAttribute 에 담겨서 넘어온 것(regions()) 반복문 돌리기 ${regions} : Map<String, String>. 사용자가 선택해서 넘어온 것이 아니라 FormItemController 에서 @ModelAttribute 자체가 그대로 넘어왔기 때문. --> <input type="checkbox" th:field="*{regions}" th:value="${region.key}" class="form-check-input"> <!-- th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다. *{regions} : ${item.regions}의 생략 버전. 빈 리스트. 사용자가 각 체크박스를 체크해서 POST 요청 보낼 때 regions.key가 날아감. item.regions 에 값이 바인딩될 것. ${region.key} : 위 div 태그의 Map의 key 중 하나. "SEOUL", "BUSAN", "JEJU" 중 하나 (반복문이니깐). 사용자가 각 체크박스를 체크해서 POST 요청 보낼 때 regions.key가 날아감. 이후 item.regions 에 값이 바인딩될 것. --> <label th:for="${#ids.prev('regions')}" th:text="${region.value}" class="form-check-label">서울</label> <!-- ${#ids.prev('필드명')} : 바로 이전 태그에서 사용되었던 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다. ${region.value} : 위 div 태그의 Map의 value 하나. "서울", "부산", "제주" 중 하나 (반복문이니깐) --> </div> </div> <div class="row"> <div class="col"> <button class="w-100 btn btn-primary btn-lg" type="submit">저장</button> </div> <div class="col"> <button class="w-100 btn btn-secondary btn-lg" onclick="location.href='item.html'" th:onclick="|location.href='@{/form/items/{itemId}(itemId=${item.id})}'|" type="button">취소</button> </div> </div> </form> </div> <!-- /container --> </body> </html> item.html<!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <link th:href="@{/css/bootstrap.min.css}" href="../css/bootstrap.min.css" rel="stylesheet"> <style> .container { max-width: 560px; } </style> </head> <body> <div class="container"> <div class="py-5 text-center"> <h2>상품 상세</h2> </div> <!-- 추가 --> <h2 th:if="${param.status}" th:text="'저장 완료'"></h2> <div> <label for="itemId">상품 ID</label> <input type="text" id="itemId" name="itemId" class="form-control" value="1" th:value="${item.id}" readonly> </div> <div> <label for="itemName">상품명</label> <input type="text" id="itemName" name="itemName" class="form-control" value="상품A" th:value="${item.itemName}" readonly> </div> <div> <label for="price">가격</label> <input type="text" id="price" name="price" class="form-control" value="10000" th:value="${item.price}" readonly> </div> <div> <label for="quantity">수량</label> <input type="text" id="quantity" name="quantity" class="form-control" value="10" th:value="${item.quantity}" readonly> </div> <hr class="my-4"> <!-- single checkbox --> <div>판매 여부</div> <div> <div class="form-check"> <input type="checkbox" id="open" th:field="${item.open}" class="form-check-input" disabled> <!-- th 가 name, value, id 을 만들어줌 체크박스가 체크되어 있다면 checked="checked" 속성까지 만들어줌. 체크되지 않았다면 속성 만들어지지 않음. --> <label for="open" class="form-check-label">판매 오픈</label> </div> </div> <!-- multi checkbox --> <div> <div>등록 지역</div> <div th:each="region : ${regions}" class="form-check form-check-inline"> <!-- @ModelAttribute 에 담았던 것 반복문 돌리기 --> <!-- @ModelAttribute 에 담겨서 넘어온 것(regions()) 반복문 돌리기 ${regions} : Map<String, String>. 사용자가 선택해서 넘어온 것이 아니라 FormItemController 에서 @ModelAttribute 자체가 그대로 넘어왔기 때문. --> <input type="checkbox" th:field="${item.regions}" th:value="${region.key}" class="form-check-input" disabled> <!-- th 가 name, value, id 을 만들어줌. 이 때, id는 field 에 숫자가 붙여져 고유하게 만들어진다. ${item.regions} : List. 사용자가 선택했던 요소들이 담긴 리스트이므로 ["SEOUL", "BUSAN"] 등의 리스트. ${region.key} : 위 div 태그의 Map의 key 하나. "SEOUL", "BUSAN", "JEJU" 중 하나 (반복문이니깐) value("SEOUL", "BUSAN", "JEJU" 중 하나)가 field(Map<String, String>) 의 요소에 존재한다면 th가 checked="checked" 속성을 만들어줌. --> <label th:for="${#ids.prev('regions')}" th:text="${region.value}" class="form-check-label">서울</label> <!-- ${#ids.prev('필드명')} : 바로 이전 input 태그에서 사용되었던 id를 그대로 가져와서 사용. 물론 위 태그에서 th가 id 자동생성해줬다. ${region.value} : 위 div 태그의 Map의 value 하나. "서울", "부산", "제주" 중 하나 (반복문이니깐) --> </div> </div> <div class="row"> <div class="col"> <button class="w-100 btn btn-primary btn-lg" onclick="location.href='editForm.html'" th:onclick="|location.href='@{/form/items/{itemId}/edit(itemId=${item.id})}'|" type="button">상품 수정</button> </div> <div class="col"> <button class="w-100 btn btn-secondary btn-lg" onclick="location.href='items.html'" th:onclick="|location.href='@{/form/items}'|" type="button">목록으로</button> </div> </div> </div> <!-- /container --> </body> </html>
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
검증1-validation 강의 관련 질문
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]검증1 - validation 강의에서 validator 분리2 섹션에서 설명하실때 파라미터 바인딩한다고 하셨는데 파라미터 바인딩이란게 정확히 뭔가요??넘어오는 파라미터를 매개변수로 매핑하는 건가요??
- 해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
다국언어를 지원하는 웹사이트에서 LocaleResolver의 사용
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]안녕하세요 선생님강의 복습 도중 질문이 생겨서 글 작성하게 되었습니다.웹사이트들을 보다보면 다국적 기업들의 웹사이트의 경우 사용자가 선택하면 다른 언어의 웹사이트가 표시되도록 국가 리스트를 선택할 수 있게 해둔 경우가 많은데요이때 보면 url의 뒤에 /ko, /en, /us, /cn 등URL Pattern을 붙여서 구분하는 경우가 많은데 이 부분은 스프링 국제화와는 다소 거리가 먼 것 같습니다/ko /en은 urlPattern의 차이를 두는 방식이고 스프링 메세지 국제화는 브라우저의 request accept-header를 기준으로 하는 것으로 이해했는데요혹시 저렇게 /ko /en /us /cn등을 사용하지 않고 국제화를 사용하기 위해서 국가 리스트에서 특정 국가를 선택하면 요청헤더의 accept-lang을 바꾸는 개발방법도 많이 이용되기는 하는지 궁금합니다.
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
기본 언어가 영어인 경우 messages.properties
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]현재 MAC OS X Sonoma Beta 상에서 실습 진행중이며기본 언어는 영어로 설정되어있습니다.기본 언어가 영어로 설정되어있어 그런지 default가 messages_en.properties로 잡히는 것 같은데요 Locale 파라미터를 설정하려고 보니 Locale.KOREALocale.KOREAN 2가지가 존재하고 두가지를 모두 입력해봐도 한국어는 테스트가 성공하지 않네요 그렇다면 1) 현재 기본 언어가 영어이기 때문에 TEST 코드에서도 null로 가는 경우 accepted-language가 영어로 가기 때문에 default가 무시되고 messages_en으로 처리됨 2) LOCALE을 KOREAN으로 하더라도 messages_en.properties처럼 ko properties를 작성하지 않았기 때문에 결국에는 messages.properties는 기본 언어가 한국어가 아닌 경우 꼭 _ko를 뒤에 붙여주지 않는 이상 기본값으로 동작하지 않는것으로 이해했는데 이부분이 맞을까요?
- 해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
공통 Dto validation 방법 질문입니다!!
1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)질문 잘하기 메뉴얼을 읽어보셨나요? (예)==================================================================안녕하세요. 선생님 덕분에 많은 것을 배우고 있는 취준생입니다.현재 상황회원 정보에 대한 공통 DTO 생성 (회원 엔티티에 노출 가능한 모든 필드를 담았습니다.) 목표공통 DTO에 validation 적용 문제 상황공통 DTO를 사용하니 컨트롤러, 서비스 로직에 따라 조건이 달라 Bean validation 적용에 어려움을 겪고 있습니다.ex) 서비스 메소드 마다 member_id가 필요한 경우가 있고, 없는 경우가 있습니다. 생각해본 해결 방법Bean validation(어노테이션)을 통해 최소한에 검증만 한 후 개별 로직안에서 추가 validation을 진행한다.가장 간단하나 로직이 지저분해질 것 같습니다. 공통 DTO를 validation을 따라 분리한다.DTO 분리 기준이 validation이라는 점이 타당하지 않은 것 같습니다. DTO 생성을 별도의 팩토리로 분리하여 팩토리 내에서 validation을 진행한다.DTO 생성과 검증에 대한 로직을 숨길 수 있는 장점이 있지만 DTO 수가 늘어날수록 팩토리 클래스도 많아져서 복잡도가 올라 갈 것 같습니다. DTO 내에 별도의 검증 메소드를 만들어 생성될 때 validation을 진행한다. Bean validation group을 사용한다. 질문JPA 강의에서 공통 DTO를 사용 후 예외 케이스만 분리하신다는 답변을 봤습니다. 공통 DTO에 경우 validation을 주로 어떻게 해결하시는지 궁금합니다!!공통 DTO에 회원 엔티티에 대부분 필드를 담았는데, 위에 말씀하신 공통 DTO를 잘못 이해하여 범위를 너무 크게 잡아 생긴 문제인지 혹은 Bean validation으로 풀어내려는 강박인지(?) 궁금합니다!!읽어주셔서 감사합니다!!
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
MyHandlerExceptionResolver 테스트 관련 문의
안녕하세요 강사님 예전에 들었던 강의 다시 복습하면서 테스트해보고 있는데 http://localhost:8080/api/members/bad 를 포스트맨으로 실행하면 강의에서처럼 json 형태로 응답을 받지만 크롬에서 해당 url을 치면 BasicErrorController가 실행되는지 error/4xx.html 화면이 렌더링이 됩니다.빈 ModelAndView를 리턴하는데 이게 어떻게 가능한 건지 잘 이해가 되지 않습니다.sendError에 400을 설정해줘서 BasicErrorController가 알아서 error/4xx.html 화면을 렌더링해주는건가요?답변 주시면 정말 감사드리겠습니다 !
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
Argument Resolver를 이용한 세션객체 활용성
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]실무에서 Argument Resolver를 이용해서 Session관련 객체를 컨트롤러의 파라미터로 받는 방법을 많이 이용하는 편인가요? 대부분 들어가는 기능인지, 있으면 좋고 없으면 말고 정도의 기능인지 궁금합니다.
- 해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
multibox
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]th:field="*{regions}" 의 역할이 무엇인가요??
- 해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
Session 사용 시 RESTful api users/{userid}에서 userid 가져오기에서
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]spring에서 지원하는 서블렛 세션을 사용하면 클라이언트에 JSESSIONID가 쿠키로 전달된다고 알고 있습니다.질문 1. RESTful API 설계 시 유저의 마이페이지 관련 정보를 가져오는 백엔드 api path는 '/users/{userid}'가 올바른가요, '/profile'이 올바른가요? 혹은 각기 다른 api path를 통해 뷰에 해당하는 프론트엔드에서 조립해야 하나요? 질문 2. 위의 질문 대답이 전자라면, 클라이언트가 마이페이지 버튼을 눌렀을 때 클라이언트에게 userid가 없고 JSESSIONID만 있는 상태인데, userid를 가져오는 과정을 거친 후에 '/users/{userid}'를 실행해야 하나요? 혹은 로그인 시 userid를 쿠키 등으로 클라이언트에게 전달하여 클라이언트가 갖고 있도록 해야 하나요? 실무에서는 어떤 방식을 사용하는지 궁금합니다. 질문 3. 같은 맥락으로 회원탈퇴 기능의 api를 '/users/{userid}'라고 할 때, JSESSIONID만 가지고 있는 클라이언트가 해당 api를 사용하기 위해서는 userid를 가져오는 작업을 수행해야 하나요, 아니면 다른 방법이나 접근방식이 있을까요?
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
??page.addItem_ko_KR??
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]메시지 부분 실습하는데 한글이 계속 깨집니다..커뮤니티에서 같은 오류 나신 분 해결방안보고 따라해봤는데도 계속 이 상태더라고요.. 혹시 다른 문제 있는 부분 알려주실 수 있으실까요
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
Form 전송 객체 분리에서 상속을 사용하는 것은 어떤가요?
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예)[질문 내용]ItemSaveForm과 ItemUpdateForm으로 Form 전송 객체를 분리하였는데 이 두 Form 객체를 Item을 상속받아서 만드는 것은 어떤가요?이렇게 만들 경우 add, edit할 때 item 객체를 따로 만들지 않고 form을 바로 itemRepository에 전달 할 수 있습니다.이 방법이 괜찮은 방법인지 아니면 너무 특수한 경우인지 궁금합니다.
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
스프링 MVC2 입력폼처리
안녕하세요 MVC-2 강의중 2. 타임리프 - 스프링 통합과 폼강의자료 3~4페이지 중에서 질문이 있습니다. 첨부한 사진을 코드를 살펴보시면, 클라이언트가 /add 페이지를 요청할 때마다 new Item()을 생성하게 됩니다. 물론 new를 하는 이유는 타임리프가 제공하는 편리한 기능인 커맨드 객체를 사용하기 위함이라고 말씀하셨습니다.제가 궁금한 부분은, 실제 서비스에서 클라이언트는 단지 get을 할 뿐인데 이처럼 매번 객체를 생성하면 서버의 메모리가 낭비되지 않을까라는 생각이 들어서 질문을 남깁니다.실무에서 이같은 코드를 실제로 사용하는지 궁금합니다.아니라면, 실무에서 적용가능한 개선점도 말씀해주시면 감사하겠습니다.
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
static final 질문
public static final String ERROR_EXCEPTION = "javax.servlet.error.exception"; public static final String ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type"; public static final String ERROR_MESSAGE = "javax.servlet.error.message"; public static final String ERROR_REQUEST_URI = "javax.servlet.error.request_uri"; public static final String ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name"; public static final String ERROR_STATUS_CODE = "javax.servlet.error.status_code"; static final로 선언돼있는데 어떻게 다른 예외가 발생할 때 마다 로그 찍어보면 다른 값이 들어가 있나요??
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
messages_en.properties 가 기본 설정값
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]설정 아무것도 안 건들고 소스 코드도 아무것도 안 건드린 상태인데도 서버 열고 localhost:8080 접속하면 messages_en.properties가 인식돼서 나옵니다.물론 기본 파일인 messages.properties 도 다 존재하는 상태구요. 기본 브라우저도 1순위 한국어로 다 설정해놓은 상태이며 HTTP request를 봐도 ko로 잘 넘어옵니다. 근데 파일 자체는 messages_en이 읽히는 것 같습니다. 어떻게 해결해야 할까요?
- 해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
Test코드에 response와 request 순서가 반대가 아닌가요 잘 이해가 안되는 것 같습니다
클라이언트가 request하니까 request가 먼저 아닌가요?서버가 response받으니까 response인가요?개념 이해가 잘 안되는 것 같습니다.. 어떻게 이해하면 좋을까요?
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
WAS 에 오류가 전달되는 방식의 차이에 대해 질문있습니다.
ServletExController 에서 sendError() 로 직접 오류를 WAS 에 전달해주면 Exception 이 터지지않은 것으로 간주되어 ErrorPageController 에서 사용중인httpServletRequest.getAttribute(ERROR_EXCEPTION) 의 결과가 로그에 찍히지않은 것이고, Exception 을 throw 해주어 간접적으로 WAS 에게 오류를 전달해주면 Exception 이 터진것으로 간주되어 httpServletRequest.getAttribute(ERROR_EXCEPTION) 의 결과가 로그에 찍힌것으로 이해하면 될까요? 답변부탁드리겠습니다!!
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
ExceptionResolver ModelAndView 반환
안녕하세요스프링 MVC 2편 강의 중 'HandlerExceptionResolver 시작' 강의를 수강 중에 있는데요.강의시간 13:19분쯤에'빈 ModelAndView를 반환하면 뷰를 렌더링하지 않고, 정상흐름으로 서블릿이 리턴된다.''ModelAndView에 View, Model 등의 정보를 지정해서 반환하면 뷰를 렌더링한다.'라고 되어 있습니다. Api는 json데이터를 주고 받는 것인데 ModelAndView를 넘겨준다는게 이해가 가지 않습니다.jsonView로 담아서 모델앤뷰로 리턴하는 것도 아니고 화면으로 넘기는 모델앤뷰를 어떤의미로 사용하는 건가요? 그리고 만약 페이지 화면을 리턴하는 것이라면 여기서 빈 ModelAndView를 반환할 때는 따로 다른 뷰를 렌더링하지 않고 Exception이 발생했던 페이지 그대로 유지하나요?그리고 ModelAndView에 View, Model 등의 정보를 지정해서 반환해 뷰를 렌더링할 때에도 마찬가지로 정상흐름으로 서블릿이 리턴되는 것인가요?답변 부탁드립니다.
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
컨트롤러(핸들러) 밖으로 던져진 예외
안녕하세요스프링MVC 2편 백엔드 웹개발 활용기술 중 'HandlerExceptionResolver의 시작' 강의를 수강 중에 있는데 강의시간 3:26분쯤에 "컨트롤러(핸들러) 밖으로 던져진 예외를 제외하고~" 이런 표현이 나왔습니다. 컨트롤러(핸들러) 밖으로 던져진 예외에는 어떤 것이 있고, 컨트롤러에서 발생하는 예외와는 어떤 차이가 있고 어떻게 분류가 되나요?답변 부탁드립니다.
- 미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
Validator 분리 2 - 4:25를 듣던 중 궁금한 점이 생겼습니다!
@InitBinder 에 여러 개의 검증이 등록되고 @Validated에 의해 실행될 때 각 검증을 구분하는 것이 Validator의 supports라고 설명하셨는데그래서 임의로 UserValidator를 추가하고 실행해본 결과 IllegalStateException이 발생하는 것을 확인했습니다. 유사한 질문으로 https://www.inflearn.com/questions/811214/initbinder-%EC%A7%88%EB%AC%B8%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4또한 확인했습니다. @InitBinder에 모델명을 등록하는 방식, 한 컨트롤러에서 하나의 모델 객체만 사용하는 방식 등 을 사용하게 된다면 결국 Validator의 supports는 다른 타입을 받게되는 경우가 없는 것 같은데 어떤 경우에 사용되는 것 일까요?