벡엔드 웹프로그래밍/Spring
Spring 웹 프로그래밍 89일차 (25/3/25)
wkun
2025. 3. 25. 17:38
contents
스프링 프로젝트
----------------------------------------------------------------
스프링 프로젝트
분류
|
라이브러리의존성 | 객체생성 | 요청명 | 도메인 | Controller | Repository | View | |||
소분류 | pom.xml | web.xml | servlet-context | 세부객체설정 | 세분류 | 세분류 | 세분류 | |||
MVC패턴 | MVC | <properties> <java-version>17</java-version> <org.springframework-version>5.3.19</org.springframework-version> </properties> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${org.springframework-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework-version}</version> </dependency> |
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/root-context.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>appServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>appServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
<annotation-driven /> <resources mapping="/resources/**" location="/resources/" /> <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <beans:property name="prefix" value="/WEB-INF/views/" /> <beans:property name="suffix" value=".jsp" /> </beans:bean> <context:component-scan base-package="com.springmvc.*" /> |
public class Book { private String bookId; // 도서ID private String name; // 도서명 private int unitPrice; // 가격 private String author; // 저자 private String description; // 설명 private String publisher; // 출판사 private String category; // 분류 private long unitsInStock; // 재고수 private String releaseDate; // 출판일(월/년) private String condition; // 신규 도서 or 중고 도서 or 전자책 // getter, setter 생략 // toString() 생략 } |
@Controller public class BookController { @Autowired private BookService bookService; @RequestMapping(value = "/books", method = RequestMethod.GET) public String requestBookList(Model model) { List<Book> list = bookService.getAllBookList(); model.addAttribute("bookList", list); return "books"; } } |
@Repository public class BookRepositoryImpl implements BookRepository{ private List<Book> listOfBooks = new ArrayList<Book>(); public BookRepositoryImpl() { Book book1 = new Book("ISBN1234", "C# 교과서", 30000); // book1 세부set은 생략됨 Book book2 = new Book("ISBN1235", "Node.js 교과서", 36000); // book2 세부set은 생략됨 Book book3 = new Book("ISBN1236", "어도비 XD CC 2020", 25000); // book3 세부set은 생략됨 listOfBooks.add(book1); listOfBooks.add(book2); listOfBooks.add(book3); } @Override public List<Book> getAllBookList() { // TODO Auto-generated method stub return listOfBooks; } } |
<body> <nav class="navbar navbar-expand navbar-dark bg-dark"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="./home">Home</a> </div> </div> </nav> <div class="jumbotron"> <div class="container"> <h1 class="display-3">도서 목록</h1> </div> </div> <div class="container"> <div class="row" align="center"> <c:forEach items="${bookList}" var="book"> <div class="col-md-4"> <h3>${book.name}</h3> <p>${book.author} <br> ${book.publisher} | ${book.releaseDate} <p align=left>${fn:substring(book.description, 0, 100)}... <p>${book.unitPrice}원 </div> </c:forEach> </div> <hr> <footer> <p>© BookMarket</p> </footer> </div> </body> |
||
@Controller로 컨트롤러를 정의하여 도서 목록 출력하기 |
컨트롤러 | http://localhost:8080/BookMarket/books | @Controller //해당클래스를 컨트롤러로 지정 public class BookController { @Autowired private BookService bookService; @RequestMapping("/books") //요청명 books랑 매핑됨 public String requestBookList(Model model) { List<Book> list = bookService.getAllBookList(); model.addAttribute("bookList", list); return "books"; } } |
|||||||
컨트롤러에 @RequestMapping 작성하기 | 컨트롤러 | http://localhost:8080/BookMarket/books | @Controller @RequestMapping("/books") //books요청이 클래스 매핑됨 public class BookController { @Autowired private BookService bookService; @RequestMapping // 디폴트 매핑 public String requestBookList(Model model) { List<Book> list = bookService.getAllBookList(); model.addAttribute("bookList", list); return "books"; } } |
|||||||
메서드에 @RequestMapping 작성하기 | 컨트롤러 | http://localhost:8080/BookMarket/books/all | @Controller @RequestMapping("/books") //books요청이 클래스 매핑됨 public class BookController { @Autowired private BookService bookService; @RequestMapping("/all") // books/all 이 매핑됨 public String requestAllBooks(Model model) { List<Book> list = bookService.getAllBookList(); model.addAttribute("bookList", list); return "books"; } } |
|||||||
단순화한 @RequestMapping 사용하기 | 컨트롤러 | http://localhost:8080/BookMarket/books/all | @Controller @RequestMapping("/books") //books요청이 클래스 매핑됨 public class BookController { @Autowired private BookService bookService; @GetMapping // 디폴트 매핑 public String requestBookList(Model model) { List<Book> list = bookService.getAllBookList(); model.addAttribute("bookList", list); return "books"; } @GetMapping("/all") // books/all 이 매핑됨 public String requestAllBooks(Model model) { List<Book> list = bookService.getAllBookList(); model.addAttribute("bookList", list); return "books"; } } |
|||||||
ModelAndView클래스를 사용하여 결과 반환하기 |
컨트롤러 | http://localhost:8080/BookMarket/books/all | @Controller @RequestMapping("/books") //books요청이 클래스 매핑됨 public class BookController { @Autowired private BookService bookService; @GetMapping // 디폴트 매핑 public String requestBookList(Model model) { List<Book> list = bookService.getAllBookList(); model.addAttribute("bookList", list); return "books"; } @GetMapping("/all") // books/all 이 매핑됨 public ModelAndView requestAllBooks( ) { ModelAndView modelAndView = new ModelAndView(); List<Book> list = bookService.getAllBookList(); modelAndView.addObject("bookList",list); modelAndView.setViewName("books"); return modelAndView; } } |
|||||||
@PathVariable을 이용하여 도서 분야와 일치하는 도서 목록 출력하기 |
컨트롤러 | http://localhost:8080/BookMarket/books/IT전문서 | @Controller @RequestMapping("/books") //books요청이 클래스 매핑됨 public class BookController { @Autowired private BookService bookService; @GetMapping("/{category}") public String requestBooksByCategory( @PathVariable("category") String BookCategory, Model model) { List<Book> list = bookService.getBookListByCategory(bookCategory); model.addAttribute("bookList", booksByCategory); return "books"; } } |
@Repository public class BookRepositoryImpl implements BookRepository { private List<Book> listOfBooks = new ArrayList<Book>(); public List<Book> getBookListByCategory(String category) { for(int i = 0; i< listOfBooks.size( ) ; i++) { Book book = listOfBooks.get(i); if (category.equalsIgnoreCase(book.getCategory())) { booksByCategrory.add(book) } } return booksByCategory; } } |
||||||
@MatrixVarialbe을 이용하여 매트릭스 변수 값과 일치하는 도서 목록 출력하기 |
컨트롤러 | <annotation-driven enable-matrix-variables="true" /> | http://localhost:8080/BookMarket/books/filter/bookFilter ;publisher=길벗;category=IT전문서 |
@Controller @RequestMapping("/books") //books요청이 클래스 매핑됨 public class BookController { @Autowired private BookService bookService; @GetMapping("/filter/{bookFilter}") public String requestBooksByFilter( @MatrixVariable(pathVar="bookFilter") Map<String,List<String>> bookFilter, Model model) { Set<Book> booksByFilter = bookService.getBookListByFilter(bookFilter); model.addAttribute("bookList", booksByFilter); return "books"; } } |
@Repository public class BookRepositoryImpl implements BookRepository { public Set<Book> getBookListByFilter(Map<String, List<String>> filter) { Set<Book> booksByPublisher = new HashSet<Book>(); Set<Book> booksByCategory = new HashSet<Book>(); Set<String> booksByFilter = filter.keySet(); if (booksByFilter.contains("publisher")) { for (int j = 0; j < filter.get("publisher").size(); j++) { String publisherName = filter.get("publisher").get(j); for (int i = 0; i < listOfBooks.size(); i++) { Book book = listOfBooks.get(i); if (publisherName.equalsIgnoreCase(book.getPublisher())) booksByPublisher.add(book); } } } if (booksByFilter.contains("category")) { for (int i = 0; i < filter.get("category").size(); i++) { String category = filter.get("category").get(i); List<Book> list = getBookListByCategory(category); booksByCategory.addAll(list); } } booksByCategory.retainAll(booksByPublisher); return booksByCategory; } } |
|||||
@RequestParam을 이용하여 도서 ID와 일치하는 도서의 상세정보 출력하기 |
컨트롤러 | http://localhost:8080/BookMarket/books/book?id=ISBN1234 | @Controller @RequestMapping("/books") //books요청이 클래스 매핑됨 public class BookController { @Autowired private BookService bookService; @GetMapping("/book") public String requestBookById(@RequestParam("id") String bookId, Model model) { Book bookById = bookService.getBookById(bookId); model.addAttribute("book", bookById ); return "book"; } } |
@Repository
public class BookRepositoryImpl implements BookRepository { public Book getBookById(String bookId) { Book bookInfo = null; for(int i =0 ;i<listOfBooks.size(); i++) { Book book = listOfBooks.get(i); if (book!=null && book.getBookId()!=null && book.getBookId().equals(bookId)){ bookInfo = book; break; } } if(bookInfo == null) throw new IllegalArgumentException("도서 ID가 "+bookId + "인 해당 도서를 찾을 수 없습니다."); return bookInfo; } } |
||||||
스프링 폼 태그로 도서 등록 페이지 만들기 | 뷰 | http://localhost:8080/BookMarket/books/add | @GetMapping("/add") public String requestAddBookForm(Book book) { return "addBook"; } |
<div class="container"> <form:form modelAttribute = "book" class="form-horizontal" > <fieldset> <div class="form-group row"> <label class="col-sm-2 control-label">도서 ID</label> <div class="col-sm-3"> <form:input path="bookId" class="form-control"/> </div> </div> <div class="form-group row"> <label class="col-sm-2 control-label" >도서명</label> <div class="col-sm-3"> <form:input path="name" class="form-control"/> </div> </div> ... 중간 생략 </fieldset> </form:form> <hr> <footer> <p>© BookMarket</p> </footer> </div> |
||||||
@ModelAttribute를 이용하여 새로운 도서 등록하기 |
컨트롤러 | http://localhost:8080/BookMarket/books/add | @GetMapping("/add") public String requestAddBookForm(@ModelAttribute("NewBook") Book book) { return "addBook"; } @PostMapping("/add") public String submitAddNewBook(@ModelAttribute("NewBook") Book book) { bookService.setNewBook(book); return "redirect:/books"; } @ModelAttribute public void addAttributes(Model model) { model.addAttribute("addTitle", "신규 도서 등록"); } |
public void setNewBook(Book book) { listOfBooks.add(book); } |
<div class="container"> <form:form modelAttribute = "NewBook" class="form-horizontal" > <fieldset> <legend>${addTitle}</legend> <div class="form-group row"> <label class="col-sm-2 control-label">도서 ID</label> <div class="col-sm-3"> <form:input path="bookId" class="form-control"/> </div> </div> <div class="form-group row"> <label class="col-sm-2 control-label" >도서명</label> <div class="col-sm-3"> <form:input path="name" class="form-control"/> </div> </div> ... 중간 생략 </fieldset> </form:form> <hr> <footer> <p>© BookMarket</p> </footer> </div> |
|||||
@InitBinder를 이용하여 커스텀 데이터 바인딩하기 |
컨트롤러 | http://localhost:8080/BookMarket/books/add | @InitBinder public void initBinder(WebDataBinder binder) { binder.setAllowedFields("bookId","name","unitPrice","author", "description", "publisher","category","unitsInStock","totalPages", "releaseDate", "condition"); } |
|||||||
도서 이미지 파일 업로드하기 | 컨트롤러 | <properties> <commons-fileupload-version>1.4</commons-fileupload-version> <commons-io-version>2.11.0</commons-io-version> </properties> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>${commons-fileupload-version}</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>${commons-io-version}</version> </dependency> |
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <beans:property name="maxUploadSize" value="10240000"/> </beans:bean> |
http://localhost:8080/BookMarket/books/add | public class Book { private MultipartFile bookImage; // getter, setter 생략 } |
@PostMapping("/add") public String submitAddNewBook(@ModelAttribute("NewBook") Book book) { MultipartFile bookImage = book.getBookImage(); String saveName = bookImage.getOriginalFilename(); // 실제 저장되는 resources/images폴더의 경로를 request에서 구해서 처리가능 File saveFile = new File("C:\\upload", saveName); if (bookImage != null && !bookImage.isEmpty()) { try { bookImage.transferTo(saveFile); } catch (Exception e) { throw new RuntimeException("도서 이미지 업로드가 실패하였습니다", e); } } bookService.setNewBook(book); return "redirect:/books"; } |
<form:form modelAttribute = "NewBook" action="./add?${_csrf.parameterName}=${_csrf.token}" class="form-horizontal" enctype="multipart/form-data"> ... 내용생략 <div class="form-group row"> <label class="col-sm-2 control-label" >도서이미지</label> <div class="col-sm-7"> <form:input path="bookImage" type="file" class="form-control" /> </div> </div> </form:form> |
|||
RESTful 방식의 장바구니
기본 구조 만들기 |
MVC
|
|
|
|
|
<a href="cart" class="btn btn-warning">
장바구니 » </a> |
public class CartItem { private Book book; // 도서 private int quantity; // 도서 개수 private int totalPrice; // 도서 가격 public void updateTotalPrice() { totalPrice = this.book.getUnitPrice()*this.quantity; } //getter, setter 는 생략 } |
@Controller
@RequestMapping(value = "/cart") public class CartController { @Autowired private CartService cartService; @GetMapping public String requestCartId(HttpServletRequest request) { String sessionid = request.getSession(true).getId(); return "redirect:/cart/"+ sessionid; } @PostMapping public @ResponseBody Cart create(@RequestBody Cart cart ) { return cartService.create(cart); } } |
@Repository
public class CartRepositoryImpl implements CartRepository{ private Map<String, Cart> listOfCarts; public CartRepositoryImpl() { listOfCarts = new HashMap<String,Cart>(); } public Cart create(Cart cart) { if(listOfCarts.keySet().contains(cart.getCartId())) { throw new IllegalArgumentException( String.format("장바구니를 생성할 수 없습니다. 장바구니 id(%)가 존재합니다", cart.getCartId())); } listOfCarts.put(cart.getCartId(), cart); return cart; } public Cart read(String cartId) { return listOfCarts.get(cartId); } } |
<div class="container" >
<div> <form:form name="clearForm" method="delete"> <a href="javascript:clearCart()" class="btn btn-danger pull-left">삭제하기</a> </form:form > <a href="#" class="btn btn-success float-right"> 주문하기</a> </div> <div style="padding-top: 50px"> <table class="table table-hover"> <tr><th>도서</th><th>가격</th><th>수량</th><th>소계</th><th>비고</th></tr> <form:form name="removeForm" method="put"> <c:forEach items="${cart.cartItems}" var="item"> <tr> <td>${item.value.book.bookId}-${item.value.book.name}</td> <td>${item.value.book.unitPrice}</td> <td>${item.value.quantity}</td> <td>${item.value.totalPrice}</td> <td> <a href="javascript:removeFromCart('../cart/remove/${item.value.book.bookId}')" class="badge badge-danger">삭제</a></td> </tr> </c:forEach> </form:form> <tr><th></th><th></th><th>총액</th><th>${cart.grandTotal}</th><th></th></tr> </table> <a href="<c:url value="/books" />" class="btn btn-secondary"> « 쇼핑 계속하기</a> </div> |
public class Cart{ private String cartId; // 장바구니 ID private Map<String,CartItem> cartItems; // 장바구니 항목 private int grandTotal; // 총액 public Cart() { cartItems = new HashMap<String, CartItem>(); grandTotal = 0; } public Cart(String cartId) { this(); this.cartId = cartId; } public void updateGrandTotal() { grandTotal= 0; for(CartItem item : cartItems.values()){ grandTotal = grandTotal + item.getTotalPrice(); } } } |
||||||||||
RESTful 웹 서비스를 위한 장바구니 CRUD만들기 |
MVC
|
<filter> <filter-name>httpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>httpMethodFilter</filter-name> <servlet-name>appServlet</servlet-name> </filter-mapping> |
<form:form name="addForm" method="put"> <a href="#" onclick="addToCart('../cart/add/${book.bookId}')"> 도서주문 » </a> <a href="<c:url value="/cart" />" class="btn btn-warning"> 장바구니 »</a> <a href="<c:url value="/books"/>" class="btn btn-secondary">도서 목록 »</a> </form:form> |
public class Cart{ private String cartId; // 장바구니 ID private Map<String,CartItem> cartItems; // 장바구니 항목 private int grandTotal; // 총액 public void addCartItem(CartItem item) { String bookId = item.getBook().getBookId(); if(cartItems.containsKey(bookId)) { CartItem cartItem = cartItems.get(bookId); cartItem.setQuantity(cartItem.getQuantity()+item.getQuantity()); cartItems.put(bookId, cartItem); } else { cartItems.put(bookId, item); } updateGrandTotal(); } public void removeCartItem(CartItem item) { String bookId = item.getBook().getBookId(); cartItems.remove(bookId); updateGrandTotal(); } } |
@Controller @RequestMapping(value = "/cart") public class CartController { @Autowired private BookService bookService; @PutMapping("/add/{bookId}") @ResponseStatus(value = HttpStatus.NO_CONTENT) public void addCartByNewItem(@PathVariable String bookId, HttpServletRequest request) { String sessionId = request.getSession(true).getId(); Cart cart = cartService.read(sessionId); if(cart== null) cart = cartService.create(new Cart(sessionId)); Book book = bookService.getBookById(bookId); if(book == null) throw new IllegalArgumentException(new BookIdException(bookId)); cart.addCartItem(new CartItem(book)); cartService.update(sessionId, cart); } @PutMapping("/remove/{bookId}") @ResponseStatus(value = HttpStatus.NO_CONTENT) public void removeCartByItem(@PathVariable String bookId, HttpServletRequest request) { String sessionId = request.getSession(true).getId(); Cart cart = cartService.read(sessionId); if(cart== null) cart = cartService.create(new Cart(sessionId)); Book book = bookService.getBookById(bookId); if(book == null) throw new IllegalArgumentException(new BookIdException(bookId)); cart.removeCartItem(new CartItem(book)); cartService.update(sessionId, cart); } @DeleteMapping("/{cartId}") @ResponseStatus(value = HttpStatus.NO_CONTENT) public void deleteCartList(@PathVariable(value = "cartId") String cartId) { cartService.delete(cartId); } } |
@Repository
public class CartRepositoryImpl implements CartRepository{ private Map<String, Cart> listOfCarts; public void update(String cartId, Cart cart) { if(!listOfCarts.keySet().contains(cartId)) { // 장바구니 ID가 존재하지 않은 경우 예외 처리 throw new IllegalArgumentException( String.format("장바구니 목록을 갱신할 수 없습니다. 장바구니 id(%)가 존재하지 않습니다",cartId)); } listOfCarts.put(cartId, cart); } public void delete(String cartId) { if(!listOfCarts.keySet().contains(cartId)) { // 장바구니 ID가 존재하지 않으면 예외 처리 throw new IllegalArgumentException( String.format("장바구니 목록을 삭제할 수 없습니다. 장바구니 id(%)가 존재하지 않습니다",cartId)); } listOfCarts.remove(cartId); } } |
||||