데이터 베이스 작성
create database bookmarketdb;
use bookmarketdb;
create table if not exists book(
b_id varchar(10) not null primary key,
b_name varchar(20),
b_unitPrice integer,
b_author varchar(20),
b_description text,
b_publisher varchar(20),
b_category varchar(20),
b_unitsInStock long,
b_releaseDate varchar(20),
b_condition varchar(20),
b_filename varchar(20)
) default charset=utf8;
BookMarket에 등록될 책들의 정보를 저장할 데이터 베이스를 만들고 book 테이블을 추가했다. 원래 데이터베이스와 DTO객체의 변수명은 통일시키는 것이 좋은데, 교재를 무심코 따라하다 보니 다른 변수명을 가지게 됐다. 나중에 연결할 때는 확인해서 설계단계를 꼼꼼히 거치도록 해야겠다.
데이터베이스 연결하기
private static Connection DBconn() {
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/bookmarketdb";
String user = "root";
String pw = "1234";
conn = DriverManager.getConnection(url,user,pw);
System.out.println("데이터 베이스 연결 성공");
} catch(Exception e) {System.out.println("데이터 베이스 연결 실패");}
return conn;
}
forName() 함수를 통해 드라이버를 로딩한 후, Connection 객체를 사용하여 데이터 베이스에 연결한다. CRUD를 돌리기 위해서 제일 먼저 되어야 하는 기능이므로 항상 코드를 작성하는 것 보다 함수로 만들어 필요할 때 호출하도록 만들었다.
모델(BookRepository)
private BookRepository() {} //기본 생성자
private static BookRepository repository = new BookRepository();
//생성자 호출을 미리 해둠 (싱글톤)
//private - 외부에서 접근을 막기 때문에, 함수도 같이 생성되나 접근할 수 없음
public static BookRepository getRepository() {
return repository;
//생성된 객체를 반환
}
CRUD를 제대로 배우기 전, MVC 수업만 들을 때 까지만 해도 나는 데이터베이스는 모델이랑 같은 의미인 줄 알았다. 하지만 데이터베이스와 모델은 엄연히 다른 것으로, 데이터 베이스는 말 그대로 데이터를 보관하는 저장소지만 모델은 데이터 베이스로 가기 위한 임시 저장소에 가깝다고 보면 될 것 같다. 따라서 Java에서 보내줄 데이터를 임시로 저장하기 위한 공간을 매번 새로 생성할 이유는 없으니 싱글톤 패턴을 사용하여 모델을 하나로 유지하는 작업을 진행했다.
기본 생성자 또한 다른 곳에서 생성될 이유가 없으므로 접근 제어자로 외부의 사용을 막고, 대신 모두가 접근 가능한 함수를 생성하여(public static) 고정된 저장소를 new 할 수 있도록 만들어준다.
Read 컨트롤러
@WebServlet("/products")
public class Read_Controller extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.out.println("전체상품 컨트롤 페이지 (Read)");
BookRepository dao = BookRepository.getRepository();
ArrayList<Book> arr = dao.getAllBooks();
req.setAttribute("list", arr);
RequestDispatcher rd = req.getRequestDispatcher("books.jsp");
rd.forward(req, resp);
}
이제 HTML에서 <a>태그나 <form>태그를 통해 요청이 발생하면 @webServlet() 으로 매핑된 컨트롤러 클래스에 도착하게 된다. 맨 처음 모든 상품을 보여주기 위한 컨트롤러를 위와 같이 작성했다. 먼저 전체상품을 사용자의 화면에 보여주려면 도서들의 정보가 필요하다. 이 정보는 모두 데이터베이스에 저장되어 있으므로 모델을 통해 데이터베이스에 접근하여 도서들의 객체 정보를 DTO로 담은 ArrayList 배열 형태로 받아온다. 이를 req(request)에 set() 함수를 통해 저장하고, forward함수로 해당 jsp 페이지로 이동하면 데이터베이스에서 그대로 받은 정보를 HTML에 전송할 수 있게 된다.
Read :: getAllBooks
public ArrayList<Book> getAllBooks() {
//해당 메서드는 도서 목록을 반환
PreparedStatement ps = null;
ResultSet rs = null;
ArrayList<Book> list = new ArrayList<Book>();
try {
Connection conn = DBconn();
String sql = "select * from book";
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
System.out.println("전체 도서 목록을 가져옵니다.");
while(rs.next()) {
Book book = new Book();
book.setBookId(rs.getString("b_id"));
book.setName(rs.getString("b_name"));
book.setUnitPrice(rs.getInt("b_unitPrice"));
book.setAuthor(rs.getString("b_author"));
book.setDescription(rs.getString("b_description"));
book.setPublisher(rs.getString("b_publisher"));
book.setCategory(rs.getString("b_category"));
book.setUnitsInStock(rs.getLong("b_unitsInStock"));
book.setReleaseDate(rs.getString("b_releaseDate"));
book.setCondition(rs.getString("b_condition"));
book.setFilename(rs.getString("b_filename"));
System.out.println("확인용 아이디"+rs.getString("b_id"));
list.add(book);
}
rs.close();
ps.close();
conn.close();
}
catch(Exception e) { System.out.println("전체 도서 불러오기 실패"); }
System.out.println("반환 완료");
return list;
}
전체 도서 목록을 반환하는 함수이다. 모든 정보를 반환하기 때문에 따로 파라미터를 받을 건 없으며, 목록을 ArrayList로 반환해야 하기 때문에 반환타입이 존재한다. 이제 데이터베이스에 연결하는 함수는 이미 작성해두었으니 Connection 객체를 가져온 다음 연결되어 있는 데이터베이스에 SQL로 명령문을 내려야 한다.
이때 명령문을 전달하기 위해서는 Statement객체를 사용해야 하는데, Statement객체보다는 PreparedStatement를 사용하는 것을 권장한다(객체생성의 문제가 있다고 한다). 전체 도서목록을 가져오기 위해서는 테이블에 저장되어 있는 모든 데이터를 가져와야하기 때문에 "SELECT * FROM (테이블 명)" 을 통해 모든 내용을 불러온 후, Connection 객체의 도움을 받아 Java에서 데이터베이스로 명령어를 전송해준다. 이 때 Statement객체와 PreparedsStatement객체의 사용법이 아주 살짝 차이가 있으니 알아두도록 하자. Statement객체는 명령어를 전송할 커넥션 객체를 생성한 후, execute문을 통해 명령어를 전송하지만 PreparedStatement는 커넥션 객체를 생성할 때 명령어를 전송하고 이후 execute문으로 실행을 한다.
이렇게 객체를 통해 명령어가 전송되면 SQL에서 반환해주는 데이터를 ResultSet 객체를 통해 다시 받아온다. ResultSet객체의 내용은 데이터베이스에서 보여주는 엑셀 화면과 비슷하다 생각하면 쉽다. 그래서 모든 데이터를 한 줄씩(행) 읽어와야 하는데, 이 때 사용하는 함수가 next() 함수이다. 1행, 2행, 3행... 하나씩 넘기며 데이터가 있는지 확인하고 데이터가 존재하면 TRUE, null데이터를 만나면 FALSE를 반환하여 while문을 빠져나오게 된다.
ResultSet에서 size()나 length() 함수를 사용하지 못하는 이유는 크기를 반환해주지 않기 때문이다. 따라서 데이터가 나오지 않을 때까지 다음줄로 넘기는 next()함수를 사용하는 것이다.
이렇게 반복문을 통해 정보를 받아올 때마다 컨트롤러에 반환해야하는 ArrayList객체에 add()함수를 사용하여 불러온 객체를 저장한다. 이 때, 반복문은 다시 처음부터 실행하기 때문에 DTO객체는 계속해서 새로운 주소를 생성하여 데이터를 저장하기 위해서 반복문 안에서 객체 생성을 했고, ArrayList는 이 내용물이 그대로 유지되어야 하기 때문에 반복문 밖에서 생성해야 한다.
Read JSP
<% ArrayList<Book> listOfBooks = (ArrayList<Book>)request.getAttribute("list"); %>
<div class="row align-items-md-stretch text-center">
<%
for(int i=0; i<listOfBooks.size(); i++){
Book book = listOfBooks.get(i);
%>
<div class="col-md-4">
<div class="h-100 p-2">
<img src="resources/img/<%= book.getFilename() %>">
<h5><b><%= book.getName() %></b></h5>
<p><%= book.getAuthor() %></p>
<p><%= book.getPublisher() %> | <%= book.getReleaseDate() %></p>
<p><%= book.getDescription() %>...</p>
<p><%= book.getUnitPrice() %>원</p>
<p>
<% if(edit.equals("update")) { %>
<a href="./update?id=<%=book.getBookId()%>"> 수정하기 »
<% } else if(edit.equals("delete")) { %>
<a href="#" onclick="deleteConfirm('<%=book.getBookId()%>')">도서삭제 »
<% } else { %>
<a href="./book?id=<%=book.getBookId()%>"> 상세 정보 » <% } %>
</a></p>
</div>
</div>
<%
}
%>
</div>
<script type="text/javascript">
function deleteConfirm(id) {
if(confirm("!!해당 도서를 삭제합니다!!")==true){
location.href = "deleteBook?id=" + id;
} else { return; }
}
</script>
이제 받아온 정보를 HTML로 바꾸어 클라이언트 화면에 내보내는 작업을 진행했다. 컨트롤러에서 받아온 리스트를 다시 get()함수를 통해 받아주고(Object로 저장됐기 때문에 형변환 필요), ArrayList는 크기를 알 수 있으므로 for문 사용으로 꺼내는 것이 가능하다. 필요한 정보를 양식에 맞게 HTML 형식으로 작성하면 전체 도서 목록을 반환하는 페이지가 완성된다.
'아이티에듀넷' 카테고리의 다른 글
2024-10-31 :: 066 로그인 (0) | 2024.10.31 |
---|---|
2024-10-30 :: 065 BookMarket CRUD 완성본 ② (0) | 2024.10.30 |
2024-10-28 :: 063 CRUD 정리하기 (0) | 2024.10.28 |
2024-10-25 :: 062 CRUD 이해하기 (0) | 2024.10.25 |
2024-10-24 :: 061 데이터베이스 맛보기 (0) | 2024.10.24 |