묻고 답해요
141만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
hello.html 404 에러가 나옵니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]개인적인 사정으로 인해 인텔리제이가 아닌 codespace ide를 사용해서 수강중입니다.controller 파일과 hello.html 파일 둘 다 pdf 에서 그대로 복사하여 실행했는데 404에러가 나옵니다.문제가 무엇일까요
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
스프링mcv2 - 로그인처리2 - 스프링 인터셉터 요청 로그
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]스프링mvc2 강의에서 로그인처리2 - 스프링 인터셉터 요청 로그 강의에서 LogInterceptor 가 싱글톤처럼 사용된다고 했는데 왜 그런건가요??빈으로 등록이 안됐는데 어떻게 싱글톤으로 관리되는건가요??
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
File, Resource 차이점이 궁금해요
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. 업로드 할땐 new File() 을다운로드와 조회할땐 new URLResouce()를 사용하셨는데둘의 차이점이 뭔지 설명은 없어서요.검색해봐도 명확한 답변이 안 나와 올려봅니다.
-
해결됨스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
수업자료 오타 문의
========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]교재 '6장 스프링 MVC - 기본기능' 의 20페이지에서 @Conroller로 표기되어 있는데 혹시 @Controller 아닌가요?
-
해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
게시글 만들기 구현
안녕하세요. 질문이 있어서 글을 올립니다. 게시글을 타임리프로 구현할 때,게시글 상세화면에서 댓글들을 가져와야 하는 상황입니다.댓글은 페이징이 필요합니다. 댓글 첫 페이지 부터 무조건 ajax로 만드는 게 맞는 건가요?처음에는 최신 댓글 10개를 보여줄건데,게시글상세 요청 -> 게시글 랜더링 -> ajax로 댓글 첫페이지 요청게시글상세 요청 -> 게시글과 댓글첫페이지 랜더링 -> 댓글 두번째페이지부터 ajax로 요청 어느 방법이 맞는 건가요?
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
view에 대한 질문
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요. public FrontControllerServletV1() { // 해쉬맵은 키와 값으로 저장됨 => URI는 키, 객체는 값임 controllerV1Map.put("/front-controller/v1/members/new-form", new MemberFormControllerV1()); controllerV1Map.put("/front-controller/v1/members/save", new MemberSaveControllerV1()); controllerV1Map.put("/front-controller/v1/members", new MemberListControllerV1()); } WEB-INF는 외부에서 들어오지 못하니 webapp 폴더 밑에 jsp 폴더 안에 있는 파일로 먼저 호출한 다음 컨트롤러로 WEB-INF 하위 파일들을 RequestDispatcher 객체을 통해 서버 단에서 이동하는 게 맞나요?
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
responseEntity
return new ResponseEntity<>("OK",HttpStatus.OK); // response.setStatus(200);위 두개가 무슨 차이 인가요??
-
미해결스프링 프레임워크는 내 손에 [스프2탄]
섹션 01 8강 질문입니다
8강 스프2탄_Junit리스트보기 Controller테스트 강의 중 질문입니다.테스트코드 BoardControllerTest.java에서@Testpublic void testList() throws Exception{log.info(mockMvc.perform(MockMvcRequestBuilders.get("/board/list")).andReturn().getModelAndView().getModelMap());} 굵은 글씨 get이라고 하셨는데BoardController.java에서@GetMapping("/list")public String getList(Model model) {List<Board> list=boardService.getList();// 객체바인딩model.addAttribute("list",list);return "board/boardList";}굵은 글씨 @GetMapping이라고 해줘서 테스트코드에서 get인 것은 알겠는데왜 get이라고 해 줬는데 이해가 잘 안되서요..특별한 거 없으면 그냥 get이라고 해주면 되는 건가요? 두번째 질문은다음 강의 언제 나오나요
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
MemberRepository test에서 객체생성
[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예[질문 내용]스프링 MVC 1편 - 회원 관리 웹 애플리케이션 요구사항 강의를 듣다 질문이 생깁니다. 이 때 test를 하면서 MemberRepository를 싱글톤으로 유지한다 하셨는데 테스트 코드에서 바로 MemberRepository.getInstance를 사용할 수 있는 이유가 MemberRepository 클래스에서 static으로 new를 해서 맞나요?? 제 이해로는 코드가 실행이 되면 모든 static 부분 코드를 읽어 실행하는데 이 때 new MemberRepository가 실행되어 객체가 생성되었다 이해했습니다. 제 이해가 맞을까요?
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
Item -> ItemSaveForm 교체하는 과정에서 질문 있습니다!
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? 네2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 네3. 질문 잘하기 메뉴얼을 읽어보셨나요? 네[질문 내용]여기에 질문 내용을 남겨주세요.Item 도메인 객체 대신 폼 데이터 전달을 위해서 ItemSaveForm으로 교체해서 사용을 했는데Post요청으로 폼 데이터 불러오는 부분 말고 Get요청으로 상품 추가 폼 불러오는 부분에서model.addAttribute("item", new Item()); 대신model.addAttribute("item", new ItemSaveForm()); 으로 변경해야 하는 것이 아닌지 궁금합니다!이 부분에 대한 언급이 없으셔서 혹시 바꾸지 않은 특별한 이유가 있으실까요?
-
미해결스프링 프레임워크는 내 손에 [스프1탄]
github repository 어떤 것일까요?
현재 스프1탄 mvc01버전 듣고 있는데 hikari관련 오류가 나서 깃허브가서 소스코드를 가져오려고 합니다.깃 들어가니 레포지토리가 정리가 잘 안되어있어서 찾지를 못하겠는데어떤 레포지토리를 봐야하나요???
-
해결됨스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
데이터 전송 시 보안 방법
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]위 그림과 같이 전송한 데이터를 다 볼 수 있는데 만약 민감한 데이터를 전송하는 경우에는 어떤 방식으로 전송을 하게 되나요?
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
스프링 웹 요청왔을때
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]스프링 mvc 강의듣다 의문이 들어서 그러는데요 서버 시작하고 인터넷띄어서 예를 들어 localhost:8080/index.html 이런 식으로 접근할때 서버는 resources/static 아래에 있는 index.html 을 보여주는 건가요 아님 resources/template 아래에 있는 index.html 을 보여주는 건가요??만약에 static 이나 template 아래에 같은 이름의 파일이 있다면 static 아래에 있는 파일을 보여주나요 아님 template 아래에 있는 파일을 보여주나요?
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
memberRepository관련
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]해당 강의 52초 경에 MemberService.java클래스에서 private final MemberRepository memberRepository; public MemberService(MemberRepository memberRepository) { this.memberRepository = memberRepository; }//어노테이션은 지웠습니다.이부분에서 public MemberService(MemberRepository memberRepository) 이 괄호안에 매개변수가 MemoryMemberRepository가 되어야 하는게 아닌가요? 제가 저기를 MemoryMemberRepository로 진행을 하고 있더라구요. 그래서 에러가 떳었는데 왜 MemberRepository인지 궁금합니다.
-
미해결스프링 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, DB 접근 기술
hello-mvc 오류
# hello-template.html <!DOCTYPE HTML> <html xmlns:th="http://www.thymeleaf.org"> <body> <p th:text="'hello '+ ${name}">hello. empty</p> </body> </html>package sebdev.prac1.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @Controller public class HelloController { @GetMapping("hello") public String hello(Model model){ model.addAttribute("data","spring"); return "hello"; } @GetMapping("hello-mvc") public String helloMvc(@RequestParam("name") String name, Model model){ model.addAttribute("name", name); return "hello-template"; } } 리턴값 hello-template에 맞춰 hello-template.html으로 맞춰주었는데이런식으로 오류가 납니다.
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
자바 강의
안녕하세요 이 강의를 본격적으로 듣기 전에 자바강의를 충분히 이해하고 싶습니다 그래서 이것이 자바다 와 자바의 정석중에 고민이 됩니다 자바의 정석은 서점에 가도 밀봉되어있어 책 내용을 알수가없어서 고민이됩니다. 각자의 장단점이 뚜렷하겠지만 혹시 선생님이 추천해주실만한 자바 강의가 있을까요? 그리고 자바를 공부하는데 어느 부분이 제일 필요한지도 궁금합니다
-
미해결스프링 부트 웹 개발 입문 - 따라하며 배우기
수업내용과 어느정도 일치하지만 좀 다른게 궁금해서 문의드립니다
컨트롤러에서int i = menuSvc.doUpdate(coffeeMenu);앞에 int i 변수선언을 하는 이유가 있을까요??강의 내용중 Mybatis 가 숫자를 반환해준다고 하신거같은데 살짝 이해가 안가서요 혹시 제가 국비 다니고있습니다.프로젝트 진행중인데 프론트 쪽이 리엑트 쓰는데JSON형식으로 넘겨줘야한다고 하더라고요RestComtroller 또는 Controller 클래스 레벨에 선언후메소드에서 Responsbody 선언하고 똑같이 리턴은String 으로 해서 View 페이지에 보내면될까요? 제가 다니는 학원에서는 리엑트를 안배우고 vue2 를 배워서 이부분이 너무 무지해서 좀 막막합니다