[C-2-3] 도서평 수정 기능

2020. 1. 21. 12:19Project C (SBMS)/Project C 파트2

반응형

도서 페이지에서 오른쪽 영역의 도서평 목록중 수정하고자하는 항목의 수정 버튼을 클릭하면 

왼쪽 영역인 도서평 등록 폼이 수정 폼으로 바뀌며, 내용을 수정할 수 있게 된다.

 

views/book

list.jsp

더보기
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>Simple Book Manager System</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="/sbms/resources/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript" src="/sbms/resources/js/book-info.js"></script>
</head>
<body>
		<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
			<a class="navbar-brand" href="/sbms/book/list">SBMS</a>
			<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarColor01" aria-controls="navbarColor01" aria-expanded="false" aria-label="Toggle navigation">
				<span class="navbar-toggler-icon"></span>
			</button>
	
			<div class="collapse navbar-collapse" id="navbarColor01">
				<ul class="navbar-nav mr-auto">
					<li class="nav-item active"><a class="nav-link" href="#">Home
							<span class="sr-only">(current)</span>
					</a></li>
					<li class="nav-item"><a class="nav-link" href="/sbms/book/list">BookManagement</a></li>
					
                        <li class="nav-item">
                        	<a class="nav-link" href="/sbms/customLogout">Logout</a>
                        </li>
                        
                        <li class="nav-item">
                        	<a class="nav-link" href="/sbms/customLogin">Login</a>
                        </li>
					
					<li class="nav-item"><a class="nav-link" href="#">About</a></li>
				</ul>
				<form class="form-inline my-2 my-lg-0">
					<input class="form-control mr-sm-2" type="text" placeholder="Search">
					<button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button>
				</form>
			</div>
		</nav>
		
	<div style="width: 200px;border: 1px solid black; background:#D9E5FF; text-align:center; position: fixed;">
	<form>
		<input type='hidden' id='bookNum' name='bookNum' />
		<input type='hidden' id='userId' name='userId' />
		<h4>개인독서노트</h4>
		
	    <div class="form-group" >
	      <label  class="col-form-label col-form-label-sm" for="exampleSelect1">책장 종류</label>
	      <select class="form-control form-control-sm" id="shelfName" required="required">
					<option value="">책장선택</option>
					<option value="IT">IT</option>
					<option value="외국어">외국어</option>
					<option value="소설">소설</option>
					<option value="교육">교육</option>
	      </select>
	    </div>
	
		<div class="form-group">
		  <label class="col-form-label col-form-label-sm" for="inputSmall">책 이름</label>
		  <input class="form-control form-control-sm" type="text"  placeholder="책 이름을 입력해주세요." id="bookTitle" required="required">
		</div>
		<div class="form-group">
		  <label class="col-form-label col-form-label-sm" for="inputSmall">책 저자</label>
		  <input class="form-control form-control-sm" type="text" placeholder="책 저자 입력해주세요." id="writer" required="required">
		</div>
		<div class="form-group">
		  <label class="col-form-label col-form-label-sm" for="inputSmall">출판사</label>
		  <input class="form-control form-control-sm" type="text" placeholder="출판사를 입력해주세요." id="publisher" required="required">
		</div>
		<div class="form-group">
			<label class="col-form-label col-form-label-sm" for="inputDefault">구매날짜</label> 
			<input type="date" class="form-control form-control-sm" placeholder="구매날짜를 선택해주세요." id="purchaseDate" required="required">
		</div>
		<div class="form-group">
		  <label class="col-form-label col-form-label-sm" for="inputSmall">책 가격</label>
		  <input class="form-control form-control-sm" type="number" placeholder="책 가격을 입력해주세요." id="price" required="required">
		</div>
		<div class="form-group">
			<label class="col-form-label col-form-label-sm" for="inputSmall">독서 평</label>
			<textarea class="form-control form-control-sm" id="bookReview" rows="3" required="required"></textarea>
		</div>
		<div class="form-group">
			<input type="submit" class="btn btn-primary" id="btnReg"  value="독서평 등록"/>
			<button type="button" class="btn btn-danger" id="btnCan" onClick="cancelBookInfo();">취소</button>
		</div>
		
		<br/>
		</form>
	</div>
	<div style="left:201px;  width:1318px; position: absolute; " > 

		<h4>내 독서 노트</h4>
		
		<div align="right">
			<select id="search-shelf" name="type" style="width: 100px;">
				<option value="" selected="selected">전체 책장</option>
				<option value="IT">IT</option>
				<option value="외국어">외국어</option>
				<option value="소설">소설</option>
				<option value="교육">교육</option>
			</select>
			<button type="button" class="btn btn-primary" id="btn-search">검색</button>
		</div>
		
		<table class="table table-hover myBookList" >
			<!-- 			<thead>
			<tr>
		      <th scope="row">책장명</th>
		      <th scope="row">책이름</th>
		      <th scope="row">저자</th>
		      <th scope="row">출판사</th>
		      <th scope="row">구매날짜</th>
		      <th scope="row">책가격</th>
		      <th scope="row">독서평</th>
		      <th scope="row">삭제</th>
		    </tr> 
			</thead>
		  <tbody>
		   <tr>
		      <th scope="row">IT</th>
		      <td>자바 웹 프로그래밍</td>
		      <td>미노쿠마</td>
		      <td>미노북스</td>
		      <td>2020-01-11</td>
		      <td>35000</td>
		      <td>실습할 수 있어서 아주 좋다.</td>
		      <td>
		      	<button type="button" class="btn btn-outline-success">수정</button>
				<button type="button" class="btn btn-outline-info">삭제</button>
			  </td>
		    </tr> 
		    
 		    <tr class="table-primary">
		      <th scope="row">Primary</th>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>
		      	<button type="button" class="btn btn-outline-success">수정</button>
				<button type="button" class="btn btn-outline-info">삭제</button>
			  </td>
		    </tr>
		    <tr class="table-secondary">
		      <th scope="row">Secondary</th>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>
		      	<button type="button" class="btn btn-outline-success">수정</button>
				<button type="button" class="btn btn-outline-info">삭제</button>
			  </td>
		    </tr>
		    <tr class="table-success">
		      <th scope="row">Success</th>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>
		      	<button type="button" class="btn btn-outline-success">수정</button>
				<button type="button" class="btn btn-outline-info">삭제</button>
			  </td>
		    </tr>
		    <tr class="table-danger">
		      <th scope="row">Danger</th>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>
		      	<button type="button" class="btn btn-outline-success">수정</button>
				<button type="button" class="btn btn-outline-info">삭제</button>
			  </td>
		    </tr>
		    <tr class="table-warning">
		      <th scope="row">Warning</th>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>
		      	<button type="button" class="btn btn-outline-success">수정</button>
				<button type="button" class="btn btn-outline-info">삭제</button>
			  </td>
		    </tr>
		    <tr class="table-info">
		      <th scope="row">Info</th>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>
		      	<button type="button" class="btn btn-outline-success">수정</button>
				<button type="button" class="btn btn-outline-info">삭제</button>
			  </td>
		    </tr>
		    <tr class="table-light">
		      <th scope="row">Light</th>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>
		      	<button type="button" class="btn btn-outline-success">수정</button>
				<button type="button" class="btn btn-outline-info">삭제</button>
			  </td>
		    </tr>
		    <tr class="table-dark">
		      <th scope="row">Dark</th>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>Column content</td>
		      <td>
		      	<button type="button" class="btn btn-outline-success">수정</button>
				<button type="button" class="btn btn-outline-info">삭제</button>
			  </td>
		    </tr> 
		     
		  </tbody>
		  -->
		</table>
		
		<script type="text/javascript">
		/* 도서 목록 JSON 데이터 추출 */
		
			var thead = "";
			thead += "<thead>";
			thead += "<tr>";
			thead += "<th scope=\"col\" width=\"10px;\">책장명</th>";
			thead += "<th scope=\"col\" width=\"10px;\">책 이름</th>";
			thead += "<th scope=\"col\" width=\"10px;\">저자</th>";
			thead += "<th scope=\"col\" width=\"10px;\">출판사</th>";
			thead += "<th scope=\"col\" width=\"10px;\">구매날짜</th>";
			thead += "<th scope=\"col\" width=\"20px\">책 가격</th>";
			thead += "<th scope=\"col\" width=\"100px;\">독서평</th>";
			thead += "<th scope=\"col\" width=\"50px;\">수정 및 삭제</th>";
			thead += "</tr>";
			thead += "</thead>";
		
		
		var bookinfoUL = $(".myBookList");
		bookInfoService.getList(function(list) {
			
			var innerTable = "";
			innerTable += thead;
			for (var i = 0, len = list.length || 0; i < len; i++) {
	    		console.log(list[i]);
	    		innerTable += "<tr>";
	    		innerTable += "<th scope=\"row\">" + list[i].shelfName + "</th>";
	    		innerTable += "<td>" + list[i].bookTitle + "</td>";
	    		innerTable += "<td>" + list[i].writer + "</td>";
	    		innerTable += "<td>" + list[i].publisher + "</td>";
	    		innerTable += "<td>" + list[i].purchaseDate.substr(0,10) + "</td>";
	    		innerTable += "<td>" + list[i].price + "</td>";
	    		innerTable += "<td>" + list[i].bookReview + "</td>";
	    		innerTable += "<td><button class=\"btn btn-warning\" type='button' onClick='removeBookInfo(" + list[i].bookNum + ")'>삭제</button><br/><button class=\"btn btn-success\" type='button' onClick='editBookInfo(" + list[i].bookNum + ")'>수정</button></td>";
	    		innerTable += "</tr>";
	    	}
				bookinfoUL.html(innerTable);
			});
		
		/* 도서 등록 또는 수정 JSON 데이터 전송 */
		$("#btnReg").click(function() {
			/* 폼 유효성 검사 */
			if($("#shelfName").val() === "" || $("#bookTitle").val() === "" || $("#writer").val() === "" || $("#publisher").val() === "" || $("#purchaseDate").val() === "" || $("#price").val() === "" || $("#bookReview").val() === ""){
				alert("개인독서노트 폼을 빠짐없이 모두 작성해주세요.");
				return;
			}
			
			if($("#btnReg").val() === "독서평 등록"){
				bookInfoService.add(
						{ 
						  shelfName: $("#shelfName").val(),
						  userId: $("#userId").val(),
						  bookTitle:$("#bookTitle").val(),
						  writer:$("#writer").val(),
						  publisher: $("#publisher").val(),
						  purchaseDate:$("#purchaseDate").val(),
						  price:$("#price").val(),
						  bookReview:$("#bookReview").val()
						},
						function(result){
							showList();
							alert("새 독서평이 등록되었습니다.");
							$("#btnReg").val("독서평 등록");
							$("#btnReg").attr('class','btn btn-primary');
							$("#bookNum").val(""),
							$("#userId").val(""),
							$("#bookTitle").val(""),
							$("#writer").val(""),
							$("#publisher").val(""),
							$("#purchaseDate").val(""),
							$("#price").val(""),
							$("#bookReview").val("")
							
						}
				);
			}else if($("#btnReg").val() === "독서평 수정"){
				bookInfoService.update(
						{ 
						  bookNum: $("#bookNum").val(),
						  shelfName: $("#shelfName").val(),
						  userId: $("#userId").val(),
						  bookTitle:$("#bookTitle").val(),
						  writer:$("#writer").val(),
						  publisher: $("#publisher").val(),
						  purchaseDate:$("#purchaseDate").val(),
						  price:$("#price").val(),
						  bookReview:$("#bookReview").val()
						  
						},
						function(result){
							showList();
							alert($("#bookNum").val() + "번의 독서평이 수정되었습니다.");
							$("#bookNum").val(""),
							$("#userId").val(""),
							$("#bookTitle").val(""),
							$("#writer").val(""),
							$("#publisher").val(""),
							$("#purchaseDate").val(""),
							$("#price").val(""),
							$("#bookReview").val("")
							$("#btnReg").val("독서평 등록");
							$("#btnReg").attr('class','btn btn-primary');
							
						}
				);
			}
		});
		
		/* 도서 항목 JSON 데이터 출력 갱신 */
		function showList() {
	
			var searchShelf = $("#search-shelf").val();
			var replyUL = $(".myBookList");
			var innerTable = "";
			
			bookInfoService.getList(function(list) {
			innerTable += thead;
			
			for (var i = 0, len = list.length || 0; i < len; i++) {
	    		console.log(list[i]);
	    		
	    		innerTable += "<tr>";	
	    		innerTable += "<th scope=\"row\">" + list[i].shelfName + "</th>";
	    		innerTable += "<td>" + list[i].bookTitle + "</td>";
	    		innerTable += "<td>" + list[i].writer + "</td>";
	    		innerTable += "<td>" + list[i].publisher + "</td>";
	    		innerTable += "<td>" + list[i].purchaseDate.substr(0,10) + "</td>";
	    		innerTable += "<td>" + list[i].price + "</td>";
	    		innerTable += "<td>" + list[i].bookReview + "</td>";
	    		innerTable += "<td><button class=\"btn btn-warning\" type='button' onClick='removeBookInfo(" + list[i].bookNum + ")'>삭제</button><br/><button class=\"btn btn-success\" type='button' onClick='editBookInfo(" + list[i].bookNum + ")'>수정</button></td>";
	      		innerTable += "</tr>";
	    	}
	    	replyUL.html(innerTable);
			});
		}
		
		/* 도서 등록 폼 초기화 */
		function cancelBookInfo(){
			$("#btnReg").val("독서평 등록");
			$("#btnReg").attr('class','btn btn-primary');
			$("#bookNum").val(""),
			$("#userId").val(""),
			$("#bookTitle").val(""),
			$("#writer").val(""),
			$("#publisher").val(""),
			$("#purchaseDate").val(""),
			$("#price").val(""),
			$("#bookReview").val("")
		}
		
		/* 도서 수정 폼 (도서 등록 폼 불러오기) */
		function editBookInfo(bookNum) {
			bookInfoService.get(bookNum, function(result){
				$("#shelfName").val(result.shelfName);
				$("#userId").val(result.userId);
				$("#bookTitle").val(result.bookTitle);
				$("#writer").val(result.writer);
				$("#publisher").val(result.publisher);
				$("#purchaseDate").val(result.purchaseDate.substr(0,10));
				$("#price").val(result.price);
				$("#bookReview").val(result.bookReview);
				$("#bookNum").val(result.bookNum);
				$("#btnReg").val("독서평 수정");
				$("#btnReg").attr('class','btn btn-success');
			});
		}
		
		</script>
	</div>
</html>



resources/js

book-info.js

 

더보기
console.log("bookInfo Module........");
 
var bookInfoService = (function() {

	function getList(callback, error) {
	     $.getJSON("/sbms/bookinfo/getList.json",
	        function(data) {
	          if (callback) {
	            callback(data);
	          }
	        }).fail(function(xhr, status, err) {
		      if (error) {
		        error();
		      }
	        });
	}
	
	function add(bookinfo, callback, error) {
		console.log("도서 정보를 추가합니다.");
		$.ajax({
			type : 'post',
			url : '/sbms/bookinfo/new',
			data : JSON.stringify(bookinfo),
			contentType : "application/json; charset=utf-8",
			success : function(result, status, xhr) {
				if (callback) {
					callback(result);
				}
			},
			error : function(xhr, status, er) {
				if (error) {
					error(er);
				}
			}
		})
	}
	
	function get(bookNum, callback, error) {
		$.get("/sbms/bookinfo/" + bookNum + ".json", function(result) {
			if (callback) {
				callback(result);
			}

		}).fail(function(xhr, status, err) {
			if (error) {
				error();
			}
		});
	}
	
	function update(bookinfo, callback, error) {
		$.ajax({
			type : 'put',
			url : '/sbms/bookinfo/' + bookinfo.bookNum,
			data : JSON.stringify(bookinfo),
			contentType : "application/json; charset=utf-8",
			success : function(result, status, xhr) {
				//alert('수정 reply!!!! 일단 성공!!');
				if (callback) {
					callback(result);
				}
			},
			error : function(xhr, status, er) {
				//alert('수정 reply!!!! 에러남!! ㅠㅠㅠㅠ');
				if (error) {
					error(er);
				}
			}
		});
	}
	
	return {
		getList : getList,
		add : add,
		get : get,
		update : update
	};
})();

 


 

src/main/java

com.sbms.controller

BookInfoController.java

 

더보기
package com.sbms.controller;

import java.util.List;

import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.sbms.domain.BookVO;
import com.sbms.service.BookInfoService;

import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j;

@RequestMapping("/bookinfo/")
@RestController
@Log4j
@AllArgsConstructor
public class BookInfoController {
	
	private BookInfoService bookInfoService;
	
	@GetMapping(value = "/getList", produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE })
	public ResponseEntity<List<BookVO>> getList() {
		log.info("[http://localhost/sbms/bookinfo/getList.json]");
		return new ResponseEntity<>(bookInfoService.getList(), HttpStatus.OK);
	}
	
	@PostMapping(value = "/new", consumes = "application/json", produces = { MediaType.TEXT_PLAIN_VALUE })
	public ResponseEntity<String> create(@RequestBody BookVO book) {
		log.info("책 정보 : " + book);
		int insertCount = bookInfoService.register(book);
		log.info("Reply INSERT COUNT: " + insertCount);

		return new ResponseEntity<>("success", HttpStatus.OK);
	}
	
	@GetMapping(value = "/{bookNum}", produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE })
	public ResponseEntity<BookVO> get(@PathVariable("bookNum") Long bookNum) {

		log.info("bookNum : " + bookNum);

		return new ResponseEntity<>(bookInfoService.get(bookNum), HttpStatus.OK);
	}
	
	@RequestMapping(method = { RequestMethod.PUT, RequestMethod.PATCH }, value = "/{bookNum}", consumes = "application/json", produces = {
					MediaType.TEXT_PLAIN_VALUE })
	public ResponseEntity<String> modify(@RequestBody BookVO book, @PathVariable("bookNum") Long bookNum) {

		book.setBookNum(bookNum);

		return bookInfoService.modify(book) == 1 ? new ResponseEntity<>("success", HttpStatus.OK)
				: new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);

	}
	
}

 


src/main/java

com.sbms.service

BookInfoService.java 인터페이스

 

더보기
package com.sbms.service;

import java.util.List;

import com.sbms.domain.BookVO;

public interface BookInfoService {
	public List<BookVO> getList();
	public int register(BookVO book);
	public BookVO get(Long bookNum);
	public int modify(BookVO book);
}

 


 

src/main/java

com.sbms.service

BookInfoServiceImpl.java

 

더보기
package com.sbms.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.sbms.domain.BookVO;
import com.sbms.mapper.BookInfoMapper;

import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j;

@Service
@Log4j
@AllArgsConstructor
public class BookInfoServiceImpl implements BookInfoService {

	private BookInfoMapper bookInfoMapper;
	
	@Override
	public List<BookVO> getList() {
		return bookInfoMapper.getList();
	}

	@Override
	public int register(BookVO book) {
		return bookInfoMapper.insertSelectKey(book);
	}

	@Override
	public BookVO get(Long bookNum) {
		return bookInfoMapper.read(bookNum);
	}

	@Override
	public int modify(BookVO book) {
		return bookInfoMapper.update(book);
	}
}

 


src/main/java

com.sbms.mapper

BookInfoMapper.java

 

더보기
package com.sbms.mapper;

import java.util.List;

import com.sbms.domain.BookVO;

public interface BookInfoMapper {
	public List<BookVO> getList();
	public int insertSelectKey(BookVO book);
	public BookVO read(Long bookNum);
	public int update(BookVO reply);
}

 


src/main/resources

com

sbms

mapper

BookInfoMapper.xml

 

더보기
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sbms.mapper.BookInfoMapper">

	<select id="getList" resultType="com.sbms.domain.BookVO">
	<![CDATA[
		select * 
		from TBL_BOOK
		where BOOKNUM > 0
	]]>
	</select>

	<insert id="insertSelectKey">
		<selectKey keyProperty="bookNum" order="BEFORE" resultType="long">
			select SEQ_BOOK_BOOKNUM.nextval
			from DUAL
		</selectKey>
		
		insert into TBL_BOOK (
			BOOKNUM, 
			SHELFNAME, 
			USERID,
			BOOKTITLE, 
			WRITER, 
			PUBLISHER, 
			PURCHASEDATE, 
			PRICE,
			BOOKREVIEW
		)
		values (
			#{bookNum}, 
			#{shelfName},
			'tester',  
			#{bookTitle}, 
			#{writer}, 
			#{publisher}, 
			#{purchaseDate},
			#{price}, 
			#{bookReview}
		)
	</insert>

	<select id="read" resultType="com.sbms.domain.BookVO">
		select *
		from TBL_BOOK
		where BOOKNUM = #{bookNum}
	</select>

	<update id="update">
		update TBL_BOOK set 
				SHELFNAME = #{shelfName},
				USERID = #{userId},
				BOOKTITLE = #{bookTitle},
				WRITER = #{writer},
				PUBLISHER = #{publisher},
				PURCHASEDATE = #{purchaseDate},
				PRICE = #{price},
				BOOKREVIEW = #{bookReview}
		where BOOKNUM = #{bookNum}
	</update>


</mapper>

 


도서평 목록에서 한개의 도서평의 수정버튼을 클릭하면, 왼쪽의 수정 폼으로 변경되고,

수정 폼에서 내용을 수정한 후, 수정 테스트를 하면, 데이터베이스에 저장이 잘 되는지 확인한다.

 

반응형

'Project C (SBMS) > Project C 파트2' 카테고리의 다른 글

[C-2-5] 도서 책장 검색  (0) 2020.01.21
[C-2-4] 도서평 삭제 기능  (0) 2020.01.21
[C-2-2] 도서평 작성 기능  (0) 2020.01.21
[C-2-1] 도서평 목록 기능  (0) 2020.01.21