어제부터 시작해서 기존에 만든 미니 프로젝트에 JDBC를 연결해보았다.
기존 프로젝트는 값을 입력하거나 삭제해도 프로그램이 종료되면 데이터가 초기값으로 돌아갔지만,
DB를 연결한 뒤에는 프로그램이 종료되어도 데이터가 DB에 저장되어 기존에 입력해둔 값을 그대로 사용할 수 있게 되었다.
기존 프로젝트를 다시 리팩토링하려고 보니 불필요한 클래스와 코드가 많은 것 같아서 새로 만들기로 했다.
기존과 같은 도서 조회 시스템을 만들기로 했고, 기존에 있던 User 클래스는 버리고 다음 기능 중심으로 구성했다.
테이블명은 book으로 만들고, 컬럼은 다음 정도로 설계했다.
id (PK, AUTO_INCREMENT)name (NOT NULL)author (NOT NULL)isbn도 고려했지만, 우선은 id로 조회가 가능하니 필수는 아니라고 판단해서 넣지 않았다.
DB 관련 패키지에는 다음이 필요하다고 생각했다.
DTO (BookDTO)DAO (BookDAO / BookDAOImpl)Util (DBUtil)자바 쪽(실행/화면)은 다음 정도면 충분하다고 봤다.
Main (메인 메뉴)Adminpage (관리자 메뉴)관리자 기능을 메인에 다 넣으면 코드가 길어질 것 같아서 별도 클래스로 분리해 메서드로 구현했다.
DBUtil은 DB 연결에 필요한 URL/계정/비밀번호를 getConnection()으로 묶어서 관리했다.
package Util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBUtil {
public static Connection getConnection() throws SQLException, ClassNotFoundException {
String url = "jdbc:mysql://localhost:3307/likedb";
String username = "root";
String password = "root1234";
return DriverManager.getConnection(url, username, password);
}
}
참고: 여기서는
ClassNotFoundException을 던지고 있는데, 최신 MySQL 드라이버는 대부분 자동 로딩이라 보통 생략 가능하다.
다만 수업 흐름/환경에 따라 유지해도 무방하다.
DTO는 생성자와 getter/setter를 만들고, 콘솔 출력이 편하도록 toString()을 오버라이딩했다.
여러 생성자를 둔 이유는 메서드마다 필요한 값이 달라서, 한 생성자만 두면 불필요한 값을 넣어야 하는 불편이 생길 수 있기 때문이다.
package Sql.DTO;
public class BookDTO {
private int id;
private String name;
private String author;
public BookDTO(int id, String name, String author) {
this.id = id;
this.name = name;
this.author = author;
}
public BookDTO(String name, String author) {
this.name = name;
this.author = author;
}
public BookDTO(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return " " +
"\nid = "+ id+", name = "+name+", author = "+author;
}
}
DAO 인터페이스에서는 CRUD와 조회 메서드를 정의했다.
package Repository;
import Sql.DTO.BookDTO;
import java.util.List;
public interface BookDAO {
// CRUD 메서드
void addBook(BookDTO bookDTO);
void updateBook(BookDTO bookDTO);
void deleteBook(BookDTO bookDTO);
List<BookDTO> getAllBooks(); // 전체 조회
List<BookDTO> getBooksByName(String name); // 일부 조회(이름 검색)
}
DAO 인터페이스를 implements하여 각 메서드를 구현했다.
try-with-resources를 사용해서 자원 반납까지 안정적으로 처리했다.
오타/불필요 import 수정 포인트
import java.awt.print.Book;는 사용하지 않으니 제거하는 게 좋다.
package Repository;
import Sql.DTO.BookDTO;
import Util.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
public class BookDAOImpl implements BookDAO {
// 추가
@Override
public void addBook(BookDTO bookDTO) {
boolean result = false;
String sql = "INSERT into book (name, author) values (?,?)";
try (
Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)
) {
ps.setString(1, bookDTO.getName());
ps.setString(2, bookDTO.getAuthor());
int count = ps.executeUpdate();
if (count > 0) {
result = true;
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
// 수정
@Override
public void updateBook(BookDTO bookDTO) {
boolean result = false;
String sql = "UPDATE book SET name = ?, author = ? where id = ?";
try (
Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)
) {
ps.setString(1, bookDTO.getName());
ps.setString(2, bookDTO.getAuthor());
ps.setInt(3, bookDTO.getId());
int count = ps.executeUpdate();
if (count > 0) {
result = true;
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
// 삭제
@Override
public void deleteBook(BookDTO bookDTO) {
boolean result = false;
String sql = "DELETE FROM book WHERE id = ?";
try (
Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)
) {
ps.setInt(1, bookDTO.getId());
int count = ps.executeUpdate();
if (count > 0) {
result = true;
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
// 전체 조회
@Override
public List<BookDTO> getAllBooks() {
List<BookDTO> books = new ArrayList<>();
String sql = "SELECT * FROM book";
try (
Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()
) {
while (rs.next()) {
BookDTO book = new BookDTO(
rs.getInt("id"),
rs.getString("name"),
rs.getString("author")
);
books.add(book);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
return books;
}
// 이름으로 검색(부분 조회)
@Override
public List<BookDTO> getBooksByName(String name) {
String sql = "SELECT * FROM book WHERE name LIKE ?";
List<BookDTO> books = new ArrayList<>();
try (
Connection conn = DBUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)
) {
ps.setString(1, "%" + name + "%"); // 포함된 값을 찾을 수 있게
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
int id = rs.getInt("id");
String bookTitle = rs.getString("name");
String author = rs.getString("author");
books.add(new BookDTO(id, bookTitle, author));
}
}
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
return books;
}
}
참고:
result변수는 현재 출력/반환에 사용되지 않아서, 필요 없다면 제거해도 된다.
기존 코드를 가져와 수정했다. switch-case로 메뉴를 구성했고, 입력값에 따라 기능이 작동하도록 했다.
또한 메뉴 선택이나 입력 시 발생할 수 있는 예외도 처리했다.
오타 수정:
swich-case→switch-case
package BookJava.main.Mainpage;
import BookJava.main.adminpage.Adminpage;
import Repository.BookDAO;
import Repository.BookDAOImpl;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
BookDAO bookDAO = new BookDAOImpl();
Adminpage adminpage = new Adminpage();
while (true) {
System.out.println("========================\n");
System.out.println("=====원하시는 기능을 선택해 주세요=====\n");
System.out.println("========================\n");
System.out.println("1. 도서 검색");
System.out.println("2. 도서 전체보기");
System.out.println("3. 관리자 페이지");
System.out.println("0. 종료");
int intinput = -1;
try { // 메뉴 선택 예외처리
intinput = scanner.nextInt();
scanner.nextLine();
} catch (Exception e) {
System.out.println("메뉴에 있는 숫자를 선택해주세요");
scanner.nextLine();
}
if (intinput > 3) {
System.out.println();
System.out.println("잘못된 입력입니다.");
}
if (intinput == 0) {
System.out.println("프로그램을 종료합니다");
break;
}
switch (intinput) {
case 1:
System.out.println("========================\n");
System.out.println("찾으시는 도서 제목을 입력해주세요\n");
System.out.println("========================\n");
String name = scanner.nextLine();
if (name.isEmpty()) {
System.out.println("공백값을 입력하셨습니다. 다시 입력해주세요");
break;
}
System.out.println(bookDAO.getBooksByName(name));
break;
case 2:
System.out.println("========================\n");
System.out.println("도서 전체보기\n");
System.out.println("========================\n");
System.out.println("===결과를 출력합니다===\n");
System.out.println(bookDAO.getAllBooks());
break;
case 3:
adminpage.Adminpage();
break;
default:
System.out.println("잘못된 입력입니다. 다시 입력해주세요");
break;
}
}
}
}
관리자 페이지는 입력값에 따라 추가/삭제/수정 기능이 실행된다.
프로그램이 종료되지 않도록 “뒤로가기” 기능과 입력 예외 처리도 적용했다.
package BookJava.main.adminpage;
import Repository.BookDAO;
import Repository.BookDAOImpl;
import Sql.DTO.BookDTO;
import java.util.Scanner;
public class Adminpage {
BookDAO bookDAO = new BookDAOImpl();
Scanner scanner = new Scanner(System.in);
public void Adminpage(){
while (true) {
System.out.println("========================\n");
System.out.println("관리자 페이지로 이동합니다\n");
System.out.println("========================\n");
System.out.println("원하시는 작업을 선택해 주세요\n");
System.out.println("1.도서 추가 | 2. 도서 삭제 | 3. 도서 수정 | 4. 뒤로가기\n");
int admininput = 4;
try { // 메뉴선택 예외처리
admininput = scanner.nextInt();
scanner.nextLine();
} catch (Exception e) {
System.out.println("메뉴에 있는 숫자를 선택해주세요");
scanner.nextLine();
}
switch (admininput) {
case 1:
System.out.println("추가하실 책을 입력해 주세요 : ");
System.out.println("책이름 : ");
String addtitle = scanner.nextLine();
System.out.println("지은이 : ");
String addauthor = scanner.nextLine();
if (addtitle.isEmpty() || addauthor.isEmpty()) {
System.out.println("값이 입력되지 않았습니다. 다시 시도해주세요 \n");
break;
}
bookDAO.addBook(new BookDTO(addtitle, addauthor));
System.out.println("도서가 추가되었습니다.");
break;
case 2:
System.out.println("삭제하실 책의 ID를 입력해 주세요 : ");
String subid = scanner.nextLine();
if (subid.isEmpty()) {
System.out.println("잘못된 입력입니다. 다시 시도해주세요");
break;
}
try {
int delid = Integer.parseInt(subid);
bookDAO.deleteBook(new BookDTO(delid));
System.out.println("도서가 삭제되었습니다.");
} catch (Exception e) {
System.out.println(e.getMessage() + " 잘못된 입력입니다.");
}
break;
case 3:
try {
System.out.println("수정할 도서를 입력해 주세요 :");
System.out.println("새 책이름 : ");
String updatetitle = scanner.nextLine();
System.out.println("지은이 : ");
String updateauthor = scanner.nextLine();
System.out.println("기존 책의 ID : ");
String subupdateid = scanner.nextLine();
if (subupdateid.isEmpty() || updatetitle.isEmpty() || updateauthor.isEmpty()) {
System.out.println("잘못된 입력입니다. 다시 시도해주세요");
break;
}
int updateid = Integer.parseInt(subupdateid);
bookDAO.updateBook(new BookDTO(updateid, updatetitle, updateauthor));
System.out.println("도서가 수정되었습니다.");
} catch (Exception e) {
System.out.println(e.getMessage() + " 잘못된 입력입니다.");
}
break;
case 4:
System.out.println("\n이전 페이지로 이동합니다.");
break;
default:
System.out.println("잘못된 입력입니다. 다시 입력해주세요");
continue;
}
if (admininput == 4) {
break;
}
}
}
}
프로그램 시작 시 메뉴가 출력되고, 알맞은 값을 입력하면
전부 정상 작동한다.




기존에 했던 프로젝트를 새로 만들려고 하니까 걱정이 좀 되었지만, 한 번 작성해봤던 거라 그런지 예상보다 빨리 작성할 수 있었고, 내가 생각했던 대로 바로바로 구현이 되어 재미있게 프로젝트를 진행했던 것 같다.