2023. 12. 31. 16:25ㆍ학부 강의/웹프로그래밍 (Spring)
0. 출처
아직 배우고 있는 중이라 부정확한 정보가 포함되어 있을 수 있습니다!
주의하세요!
올인원 스프링 프레임워크 참고.
1. 관리자 관련 기능
관리자 계정 정보 수정새 비밀번호 생성 및 메일 발송- 신규 도서 등록 (+파일 업로드)
- 도서 검색 및 상세 정보 출력
- 도서 정보 수정과 삭제
2. 신규 도서 등록
가. 환경 설정
필요한 패키지와 클래스를 추가한다.
package com.office.library.book;
public class BookVo {
int b_no;
String b_thumbnail;
String b_name;
String b_author;
String b_publisher;
String b_publish_year;
String b_isbn;
String b_call_number;
int b_rental_able;
String b_reg_date;
String b_mod_date;
public int getB_no() {
return b_no;
}
public void setB_no(int b_no) {
this.b_no = b_no;
}
... //getter, setter 생략
}
BookVo
를 구현한다.
file
은 없다.
CREATE TABLE tbl_book(
b_no INT AUTO_INCREMENT,
b_thumbnail VARCHAR(100),
b_name VARCHAR(30) NOT NULL,
b_author VARCHAR(20) NOT NULL,
b_publisher VARCHAR(20) NOT NULL,
b_publish_year CHAR(4) NOT NULL,
b_isbn VARCHAR(30) NOT NULL,
b_call_number VARCHAR(30) NOT NULL,
b_rental_able TINYINT NOT NULL DEFAULT 1,
b_reg_date DATETIME,
b_mod_date DATETIME,
PRIMARY KEY(b_no)
);
데이터베이스에 테이블을 생성한다.
나. 도서 등록 화면
신규 도서를 등록하는 페이지로 이동하는 기능 구현하기.
@Controller
@RequestMapping("/book/admin")
public class BookController {
@GetMapping("/registerBookForm")
public String registerBookForm() {
System.out.println("[BookController] registerBookForm()");
String nextPage = "admin/book/register_book_form";
return nextPage;
}
}
BookController.java
를 만들고 registerBookForm()
선언한다.
...
<form action="<c:url value='/book/admin/registerBookConfirm' />" name="register_book_form" method="post" enctype="multipart/form-data">
<input type="text" name="b_name" placeholder="INPUT BOOK NAME."> <br>
<input type="text" name="b_author" placeholder="INPUT BOOK AUTHOR."> <br>
<input type="text" name="b_publisher" placeholder="INPUT BOOK PUBLISHER."> <br>
<input type="text" name="b_publish_year" placeholder="INPUT BOOK PUBLISH YEAR."> <br>
<input type="text" name="b_isbn" placeholder="INPUT BOOK ISBN."> <br>
<input type="text" name="b_call_number" placeholder="INPUT BOOK CALL NUMBER."> <br>
<select name="b_rental_able">
<option value="">SELECT BOOK RANTAL ABLE.</option>
<option value="0">UNABLE.</option>
<option value="1">ABLE.</option>
</select><br>
<input type="file" name="file"><br>
<input type="button" value="register book" onclick="registerBookForm();">
<input type="reset" value="reset">
</form>
도서의 정보를 입력하는 admin/book/register_book_form
이다.
<form ... enctype="multipart/form-data">
: HTML<form>
태그에서 사용되는 인코딩 유형을 지정하는 속성.
:<input type="file">
을 사용하여 파일을 업로드할 때 필수.
: 비텍스트 데이터(파일, 이미지, 비디오 등)를 서버로 전송하기 위해 필요. 일반 텍스트 데이터와 달리 이진 형태로 처리하기 때문."multipart/form-data"
: 다양한 유형과 크기의 데이터를 하나의 요청으로 분할하여 전송하는 인코딩 방식<input type="file" name="file"><br>
: 파일을 선택해서 업로드할 수 있다.<input type="button" value="register book" onclick="registerBookForm();">
: 도서 정보 입력을 마치고 서버로 보낸다.
:registerBookForm()
실행하여 입력하지 않은 정보가 있는지 확인한다.<input type="reset" value="reset">
: 도서의 정보를 리셋하는 버튼.
다. 파일 업로드
스프링에서 파일을 업로드하려면 commons-fileupload
모듈을 사용한다.
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.5</version>
</dependency>
pom.xml
에 추가.
앞서 사용된 enctype="multipart/form-data"
의 multipart를 사용하기 위해서CommonMultipartResolver
를 빈으로 등록한다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10240000" />
<property name="defaultEncoding" value="utf-8" />
</bean>
</beans>
CommonMultipartResolver
를 등록하기 위해서 file-context.xml
생성한다.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/root-context.xml
/WEB-INF/spring/jdbc-context.xml
/WEB-INF/spring/security-context.xml
/WEB-INF/spring/mail-context.xml
/WEB-INF/spring/file-context.xml
</param-value>
</context-param>
web.xml
에 추가.
package com.office.library.book.admin.util;
import java.io.File;
import java.util.UUID;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@Service
public class UploadFileService {
final static public String BOOK_THUMBNAIL_UPLOAD_DIR = makeUploadDir();
static public String makeUploadDir() {
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("win")) {
return "C:\\library\\upload\\";
} else if (osName.contains("mac")) {
return System.getProperty("user.home") + "/Desktop/Study_JavaSpring_2/download/upload";
} else if (osName.contains("nix") || osName.contains("nux") || osName.contains("aix")) {
return System.getProperty("user.home") + "/Desktop/Study_JavaSpring_2/download/upload";
} else {
return null;
}
}
public String upload(MultipartFile file) {
boolean result = false;
String fileOriName = file.getOriginalFilename();
String fileExtension = fileOriName.substring(fileOriName.lastIndexOf("."), fileOriName.length());
if(BOOK_THUMBNAIL_UPLOAD_DIR == null) return null;
UUID uuid = UUID.randomUUID();
String uniqueName = uuid.toString().replaceAll("-", "");
File saveFile = new File(BOOK_THUMBNAIL_UPLOAD_DIR + File.separator + uniqueName + fileExtension);
System.out.println("[UploadFileService]" + " UPLOAD_DIR : " + BOOK_THUMBNAIL_UPLOAD_DIR + " saveFile : " + saveFile.getAbsolutePath());
if(!saveFile.exists()) saveFile.mkdirs();
try {
file.transferTo(saveFile);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
if(result) {
System.out.println("[UploadFileService] FILE UPLOAD SUCCESS!!");
return uniqueName + fileExtension;
} else {
System.out.println("[UploadFileService] FILE UPLOAD FAIL!!");
return null;
}
}
}
UploadFileService.upload()
구현.
final static public String BOOK_THUMBNAIL_UPLOAD_DIR = makeUploadDir();
: 운영체제에 따라서 파일의 저장 위치를 달리함.
makeUploadDir()
등 운영체제에 따라서 저장 위치를 달리하는 기능은 필요에 의해서 없던 기능을 추가한 것.
라. 도서 등록 처리
@Autowired
UploadFileService uploadFileService;
@Autowired
BookService bookService;
...
@PostMapping("/registerBookConfirm")
public String registerBookConfirm(BookVo bookVo, @RequestParam("file") MultipartFile file) {
System.out.println("[BookController] registerBookConfirm()");
String nextPage = "admin/book/register_book_ok";
String savedFileName = uploadFileService.upload(file);
if(savedFileName != null) {
bookVo.setB_thumbnail(savedFileName);
int result = bookService.registerBookConfirm(bookVo);
if(result <= 0)
nextPage = "admin/book/register_book_ng";
}else {
nextPage = "admin/book/register_book_ng";
}
return nextPage;
}
BookController
에 /book/admin/registerBookConfirm
요청을 처리하는 registerBookConfirm()
을 선언.
public String registerBookConfirm(BookVo bookVo, @RequestParam("file") MultipartFile file) {
:BookVo
에 없는file
은 따로 전달받는다.String savedFileName = uploadFileService.upload(file);
: 파일을 업로드한다. 성공하면 파일의 이름을savedFileName
에 반환한다. 실패하면null
.int result = bookService.registerBookConfirm(bookVo);
:BookService
에 구체적인 처리를 위임.
@Service
public class BookService {
final static public int BOOK_ISBN_ALREADY_EXIST = 0;
final static public int BOOK_REGISTER_SUCCESS = 1;
final static public int BOOK_REGISTER_FAIL = -1;
@Autowired
BookDao bookDao;
...
public int registerBookConfirm(BookVo bookVo) {
System.out.println("[BookService] registerBookConfirm()");
boolean isISBN = bookDao.isISBN(bookVo.getB_isbn());
if(!isISBN) {
int result = bookDao.insertBook(bookVo);
if(result > 0) return BOOK_REGISTER_SUCCESS;
else return BOOK_REGISTER_FAIL;
} else {
return BOOK_ISBN_ALREADY_EXIST;
}
}
}
boolean isISBN = bookDao.isISBN(bookVo.getB_isbn());
: 이미 등록된 도서인지 확인한다.int result = bookDao.insertBook(bookVo);
: 도서를 등록한다.
@Component
public class BookDao {
@Autowired
JdbcTemplate jdbcTemplate;
public boolean isISBN(String b_isbn) {
System.out.println("[BookDao] isISBN()");
String sql = "SELECT COUNT(*) FROM tbl_book " + "WHERE b_isbn = ?";
int result = jdbcTemplate.queryForObject(sql, Integer.class, b_isbn);
return result > 0 ? true : false;
}
public int insertBook(BookVo bookVo) {
System.out.println("[BookDao] insertBook()");
String sql = "INSERT INTO tbl_book(b_thumbnail, "
+ "b_name, " + "b_author, " + "b_publisher, "
+ "b_publish_year, " + "b_isbn, " + "b_call_number, "
+ "b_rental_able, " + "b_reg_date, " + "b_mod_date) "
+ "VALUES(?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";
int result = -1;
try {
result = jdbcTemplate.update(sql,
bookVo.getB_thumbnail(), bookVo.getB_name(),
bookVo.getB_author(), bookVo.getB_publisher(),
bookVo.getB_publish_year(), bookVo.getB_isbn(),
bookVo.getB_call_number(), bookVo.getB_rantal_able());
} catch(Exception e) {
e.printStackTrace();
}
return result;
}
}
isISBN()
: 동일한 도서가 존재하는지 확인한다.int result = jdbcTemplate.queryForObject(sql, Integer.class, b_isbn);
: 결과가 단일 객체인 SQL 쿼리를 실행.RowMapper
가 필요 없다.insertBook()
: 새로운 도서 정보를 데이터베이스에 삽입.
3. 도서 검색
등록된 도서를 검색하여 찾는 기능을 구현한다.
가. 도서 검색
<div class="search">
<form action="<c:url value='/book/admin/searchBookConfirm' />" name="search_book_form" method="get">
<input type="text" name="b_name" placeholder="Enter the name of the book you are looking for.">
<input type="button" value="search" onclick="searchBookForm();">
</form>
</div>
BookRentalPjt/src/main/webapp/WEB-INF/views/admin/include/nav.jsp
에서 검색창에 해당하는 부분이다.
버튼을 누르면 /book/admin/searchBookConfirm?b_name=[입력한 키워드]
요청을 보낸다.
@Controller
@RequestMapping("/book/admin")
public class BookController {
...
@GetMapping("/searchBookConfirm")
public String searchBookConfirm(BookVo bookVo, Model model) {
System.out.println("[BookController] searchBookConfirm()");
String nextPage = "admin/book/search_book";
List<BookVo> bookVos = bookService.searchBookConfirm(bookVo);
model.addAttribute("bookVos", bookVos);
return nextPage;
}
}
BookController
에 searchBookConfirm()
을 추가한다.
List<BookVo> bookVos = bookService.searchBookConfirm(bookVo);
: 서비스에 처리를 위임model.addAttribute("bookVos", bookVos);
: 검색 결과를 뷰에 전달하기 위해서 저장
@Service
public class BookService {
...
public List<BookVo> searchBookConfirm(BookVo bookVo) {
System.out.println("[BookService] searchBookConfirm()");
return bookDao.selectBooksBySearch(bookVo);
}
}
BookService
다.
@Component
public class BookDao {
...
public List<BookVo> selectBooksBySearch(BookVo bookVo) {
System.out.println("[BookDao] selectBooksBySearch()");
String sql = "SELECT * FROM tbl_book "
+ "WHERE b_name LIKE ? "
+ "ORDER BY b_no DESC";
List<BookVo> bookVos = null;
try {
bookVos = jdbcTemplate.query(sql, new RowMapper<BookVo>() {
@Override
public BookVo mapRow(ResultSet rs, int rowNum) throws SQLException {
BookVo bookVo = new BookVo();
bookVo.setB_no(rs.getInt("b_no"));
bookVo.setB_thumbnail(rs.getString("b_thumbnail"));
bookVo.setB_name(rs.getString("b_name"));
bookVo.setB_author(rs.getString("b_author"));
bookVo.setB_publisher(rs.getString("b_publisher"));
bookVo.setB_publish_year(rs.getString("b_publish_year"));
bookVo.setB_isbn(rs.getString("b_isbn"));
bookVo.setB_call_number(rs.getString("b_isbn"));
bookVo.setB_rantal_able(rs.getInt("b_rental_able"));
bookVo.setB_reg_date(rs.getString("b_reg_date"));
bookVo.setB_mod_date(rs.getString("b_mod_date"));
return bookVo;
}
}, "%" + bookVo.getB_name() + "%");
}catch(Exception e) {
e.printStackTrace();
}
return bookVos.size() > 0 ? bookVos : null;
}
}
BookDao
는 데이터베이스에 SELECT * FROM tbl_book WHERE b_name LIKE %입력한키워드% ORDER BY b_no DESC
질의문을 전달한다.
%입력한키워드%
의 %
는 와일드카드로 0개 이상의 문자열을 뜻한다.
이름에 키워드가 포함된 모든 도서를 조회한다.
나. 도서 상세 보기
<tbody>
<c:forEach var="item" items="${bookVos}">
<tr>
<td>
<c:url value='/book/admin/bookDetail' var='detail_url'>
<c:param name='b_no' value='${item.b_no}'/>
</c:url>
<a href="${detail_url}">${item.b_name}</a>
</td>
...
</tr>
</c:forEach>
</tbody>
BookRentalPjt/src/main/webapp/WEB-INF/views/admin/book/search_book.jsp
일부다.
도서명 클릭 시 /book/admin/bookDetail?b_no=[클릭한 도서의 b_no]”
요청이 발생한다.
@Controller
@RequestMapping("/book/admin")
public class BookController {
...
@GetMapping("/bookDetail")
public String bookDetail(@RequestParam("b_no") int b_no, Model model) {
System.out.println("[BookController] bookDetail()");
String nextPage = "admin/book/book_detail";
BookVo bookVo = bookService.bookDetail(b_no);
model.addAttribute("bookVo", bookVo);
return nextPage;
}
}
BookController
에 bookDetail()
추가.
조회한 도서의 상세 정보를 뷰에 전달한다.
@Service
public class BookService {
...
public BookVo bookDetail(int b_no) {
System.out.println("[BookService] bookDetail()");
return bookDao.selectBook(b_no);
}
}
BookService
에 bookDetail()
추가.
@Component
public class BookDao {
...
public BookVo selectBook(int b_no) {
System.out.println("[BookDao] selectBook()");
String sql = "SELECT * FROM tbl_book WHERE b_no = ?";
List<BookVo> bookVos = null;
try {
bookVos = jdbcTemplate.query(sql, new RowMapper<BookVo>() {
@Override
public BookVo mapRow(ResultSet rs, int rowNum) throws SQLException {
BookVo bookVo = new BookVo();
bookVo.setB_no(rs.getInt("b_no"));
bookVo.setB_thumbnail(rs.getString("b_thumbnail"));
bookVo.setB_name(rs.getString("b_name"));
bookVo.setB_author(rs.getString("b_author"));
bookVo.setB_publisher(rs.getString("b_publisher"));
bookVo.setB_publish_year(rs.getString("b_publish_year"));
bookVo.setB_isbn(rs.getString("b_isbn"));
bookVo.setB_call_number(rs.getString("b_isbn"));
bookVo.setB_rental_able(rs.getInt("b_rental_able"));
bookVo.setB_reg_date(rs.getString("b_reg_date"));
bookVo.setB_mod_date(rs.getString("b_mod_date"));
return bookVo;
}
}, b_no);
}catch(Exception e) {
e.printStackTrace();
}
return bookVos.size() > 0 ? bookVos.get(0) : null;
}
}
BookDao.selectBook()
구현.
b_no
바탕으로 도서의 정보를 조회.
아직 이미지를 가져오지 못한다.
1) 외부 url 요청을 물리적 경로에 매핑하기
정적인 파일을 서비스하기 위해서 경로를 등록해야 한다.
<img src="<c:url value="/libraryUploadImg/${bookVo.b_thumbnail}"/>">
/libraryUploadImg/${bookVo.b_thumbnail}
요청을 실제 물리적 경로에 매핑해야 한다.
윈도우의 경우 C:\\library\\upload\\
에 파일이 저장되어 있다. (macOS, linux, unix → ~/Desktop/Study_JavaSpring_2/download/upload
)
servlet-context.xml
에 <resource>
를 추가한다.
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<resources mapping="/libraryUploadImg/**" location="file:///C:/library/upload/" />
이미 CSS와 일부 이미지를 반환하기 위해서 존재하는 resources를 볼 수 있다.
/libraryUploadImg/
요청을 C:/library/upload/
로 매핑했다.
'학부 강의 > 웹프로그래밍 (Spring)' 카테고리의 다른 글
[Spring] 사용자 관련 기능 구현 (0) | 2024.01.04 |
---|---|
[Spring] 도서 수정, 삭제 (0) | 2024.01.01 |
[Spring] Mail 보내기 (2) | 2023.12.21 |
[Spring] 관리자 로그인 (2) | 2023.12.20 |
[Spring] 데이터베이스 (0) | 2023.11.09 |