벡엔드 웹프로그래밍/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>&copy; 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>&copy; 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>&copy; 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">
장바구니 &raquo;
</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"> &laquo; 쇼핑 계속하기</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}')">
도서주문 &raquo;
</a>
<a href="<c:url value="/cart" />" class="btn btn-warning"> 장바구니 &raquo;</a>
<a href="<c:url value="/books"/>" class="btn btn-secondary">도서 목록 &raquo;</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);
}

}