풀스택 개발 (Full-Stack Development)/자바프로그래밍
JDBC SQL CRUD 실행객체 Statement PreparedStatement executeQuery executeUpdate DAO 패턴 Scanner
윤슬새벽
2025. 6. 24. 15:11
반응형
✅ 1. 실습 주제
JDBC 프로그래밍의 기본 구조와 SQL 연동을 학습하기 위한 콘솔 기반 CRUD 실습
✅ 2. 학습 목표 및 성과
항목 | 상세 내용 |
📌 학습 주제 | JDBC를 활용한 자바 콘솔 CRUD 애플리케이션 개발 |
🎯 학습 성과 | DB 연동, SQL 작성, PreparedStatement 적용, 콘솔 입출력 구현 |
🔐 보안 고려 | SQL Injection 방지를 위한 PreparedStatement 사용 |
💡 고급 기능 | ResultSetMetaData 활용 동적 쿼리 실행기(nativeQuery) 구현 |
🛠 예외 처리 | try-catch-finally를 통한 안정적인 DB 자원 관리 |
✅ 3. 구현 기능 요약
기능 | 설명 |
insertPhonebook() | 연락처 정보 신규 입력 |
updatePhonebook() | ID 기반 연락처 정보 수정 |
deletePhonebook() | ID 기반 연락처 삭제 |
selectAllPhonebook() | 전체 연락처 조회 (정렬된 테이블 출력) |
nativeQuery() | 사용자가 직접 SQL을 입력하고 결과 확인 |
✅ 4. Java 코드 (PhoneBookDao.java)
package edu.pnu;
import java.sql.*;
import java.util.Scanner;
public class PhoneBookDao {
private static Scanner sc = new Scanner(System.in);
private static String url = "jdbc:mysql://localhost:3306/db";
public static void main(String[] args) throws Exception {
Connection con = DriverManager.getConnection(url, "user", "*****");
boolean flag = true;
while (flag) {
System.out.print("[I]nsert/[U]pdate/[D]elete/[S]elect/[N]ative/[Q]uit:");
char c = sc.next().toUpperCase().charAt(0);
switch (c) {
case 'I': insertPhonebook(con); break;
case 'U': updatePhonebook(con); break;
case 'D': deletePhonebook(con); break;
case 'S': selectAllPhonebook(con); break;
case 'N': nativeQuery(con); break;
case 'Q': flag = false; break;
}
}
System.out.println("Bye~");
}
private static void insertPhonebook(Connection con) throws SQLException {
String name = "홍길동프5";
String mobile = "010-1234-5678";
String home = "010-4567-1234";
String company = "hong com";
String email = "hong@hong.com";
String sql = "INSERT INTO phonebook(name, mobile, home, company, email) VALUES (?, ?, ?, ?, ?)";
PreparedStatement psmt = con.prepareStatement(sql);
psmt.setString(1, name);
psmt.setString(2, mobile);
psmt.setString(3, home);
psmt.setString(4, company);
psmt.setString(5, email);
int cnt = psmt.executeUpdate();
System.out.println(cnt + "건이 입력되었습니다.");
}
private static void updatePhonebook(Connection con) throws SQLException {
int id = 18;
String home = "010-4567-1234";
String company = "hong2 com";
String email = "hong2@hong.com";
String sql = "UPDATE phonebook SET home=?, company=?, email=? WHERE id=?";
PreparedStatement psmt = con.prepareStatement(sql);
psmt.setString(1, home);
psmt.setString(2, company);
psmt.setString(3, email);
psmt.setInt(4, id);
int cnt = psmt.executeUpdate();
System.out.println(cnt + "건이 수정되었습니다.");
}
private static void deletePhonebook(Connection con) throws SQLException {
int id = 15;
String sql = "DELETE FROM phonebook WHERE id=?";
PreparedStatement psmt = con.prepareStatement(sql);
psmt.setInt(1, id);
int cnt = psmt.executeUpdate();
System.out.println(cnt + "건이 삭제되었습니다.");
}
private static void selectAllPhonebook(Connection con) throws SQLException {
Statement st = null;
ResultSet rs = null;
try {
st = con.createStatement();
String sql = "SELECT * FROM phonebook";
rs = st.executeQuery(sql);
System.out.println("\n[결과]");
System.out.printf("%-4s %-8s %-15s %-15s %-15s %-20s\n",
"ID", "이름", "모바일", "집", "회사", "이메일");
while (rs.next()) {
System.out.printf("%-4s %-8s %-15s %-15s %-15s %-20s\n",
rs.getString("id"),
rs.getString("name"),
rs.getString("mobile"),
rs.getString("home"),
rs.getString("company"),
rs.getString("email"));
}
} catch (Exception e) {
System.out.println("SELECT 오류: " + e.getMessage());
} finally {
try {
if (rs != null) rs.close();
if (st != null) st.close();
} catch (SQLException e) {
System.out.println("자원 정리 오류: " + e.getMessage());
}
}
}
private static void nativeQuery(Connection con) {
Statement st = null;
ResultSet rs = null;
try {
Scanner sc = new Scanner(System.in);
System.out.print("실행할 SQL문 입력 (예: SELECT * FROM phonebook): ");
String sql = sc.nextLine();
st = con.createStatement();
if (sql.trim().toUpperCase().startsWith("SELECT")) {
rs = st.executeQuery(sql);
ResultSetMetaData meta = rs.getMetaData();
int colCount = meta.getColumnCount();
System.out.println("\n[결과]");
while (rs.next()) {
for (int i = 1; i <= colCount; i++) {
System.out.print(rs.getString(i));
if (i < colCount) System.out.print(" | ");
}
System.out.println();
}
} else {
int cnt = st.executeUpdate(sql);
System.out.println(cnt + "건이 영향을 받았습니다.");
}
} catch (Exception e) {
System.out.println("SQL 실행 오류: " + e.getMessage());
} finally {
try {
if (rs != null) rs.close();
if (st != null) st.close();
} catch (SQLException e) {
System.out.println("자원 정리 오류: " + e.getMessage());
}
}
}
}
✅ 테이블 구조 예시 (MySQL 기준)
CREATE TABLE phonebook (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
mobile VARCHAR(20),
home VARCHAR(20),
company VARCHAR(50),
email VARCHAR(50)
);
✅ 다음 단계 추천
학습 방향 | 실습 아이디어 |
MVC 구조 리팩토링 | DAO, DTO 클래스로 분리 |
사용자 입력 받기 | Scanner로 사용자 입력 기반 insert/update |
GUI 확장 | JavaFX로 화면 기반 전화번호부 만들기 |
웹 확장 | Spring Boot + Thymeleaf 또는 REST API |
🧠 핵심 학습 목표 요약
항목 | 학습 내용 |
🔌 JDBC 연결 | DriverManager.getConnection()으로 MySQL과 Java 연동 |
🛠 SQL 실행 방법 | Statement vs PreparedStatement 차이 학습 |
📄 CRUD 구현 | Insert / Update / Delete / Select 기능 각각 구현 |
🔐 보안 | PreparedStatement로 SQL Injection 방지 |
🔁 반복 처리 | ResultSet을 활용한 다중 행 조회 처리 |
🧪 동적 실행기 | 사용자가 입력한 SQL을 실행하는 nativeQuery() 작성 (SQL Console 시뮬레이션) |
💡 콘솔 인터페이스 | 메뉴 선택을 통한 반복 구조 설계 (while + switch) |
🔷 키워드 요약:
- JDBC 설정 및 연결
- PreparedStatement와 Statement의 차이 이해
- 기본적인 CRUD 구현
- 콘솔 기반 SQL 입력 기능
- ResultSetMetaData로 동적 결과 출력
- 예외 처리 및 자원 해제 패턴
🧱 확장 가능성
영역 | 확장 예시 |
DAO 분리 | JDBC 코드 → DAO 클래스로 분리 |
사용자 입력 | 하드코딩 값 → Scanner로 동적 입력 |
MVC 구조 | DAO, DTO, Service 분리하여 관리 |
웹 개발 연결 | 이 구조를 Spring Boot 기반 REST API로 전환 가능 |
데이터 조작 도구 | SQL 실습용 콘솔 툴로 재활용 가능 (교육 목적) |
✅ DAO란?
**DAO(Data Access Object)**는
**데이터베이스와의 모든 상호작용을 담당하는 객체(또는 클래스)**입니다.
즉, INSERT, SELECT, UPDATE, DELETE 같은 SQL 쿼리 실행 코드를 한 곳에 모아서 관리하는 역할을 합니다.
🧱 왜 DAO가 필요할까?
1. 관심사 분리 (Separation of Concerns)
- 비즈니스 로직과 데이터 접근 로직을 분리
- 유지보수성과 재사용성을 높임
2. 중복 제거
- 여러 클래스에서 DB에 접근하는 코드가 반복되면 → 한 곳에 모아 관리
3. 단위 테스트가 쉬움
- DB 연동을 모킹(mock)하거나 테스트 더블로 대체 가능
💡 예시: DAO 없이 만든 구조
public class MainApp {
public static void main(String[] args) throws SQLException {
Connection con = DriverManager.getConnection(...);
PreparedStatement ps = con.prepareStatement("SELECT * FROM phonebook");
ResultSet rs = ps.executeQuery();
...
}
}
→ DB 접근 코드가 로직에 섞여 혼란스럽고 확장성 낮음
✅ DAO 도입한 구조 예시
// PhonebookDao.java
public class PhonebookDao {
private Connection con;
public PhonebookDao(Connection con) {
this.con = con;
}
public List<Phonebook> selectAll() throws SQLException {
List<Phonebook> list = new ArrayList<>();
String sql = "SELECT * FROM phonebook";
PreparedStatement ps = con.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
Phonebook p = new Phonebook(
rs.getInt("id"),
rs.getString("name"),
...
);
list.add(p);
}
return list;
}
}
// MainApp.java
public class MainApp {
public static void main(String[] args) throws SQLException {
Connection con = DriverManager.getConnection(...);
PhonebookDao dao = new PhonebookDao(con);
List<Phonebook> list = dao.selectAll();
list.forEach(System.out::println);
}
}
👉 DB 처리 로직은 PhonebookDao에만 집중
👉 MainApp은 화면 흐름 또는 입력 처리에만 집중
🧩 비유로 이해하는 DAO (Data Access Object)
비유 요소실세계 대응의미
📞 사용자 | 웹 애플리케이션, 서비스 로직 | DB에 직접 접근하지 않음 |
💼 DAO (사무보조, 비서) | 중간 관리자, DB와 대화하는 책임자 | SQL 실행, 결과 가공 |
🏛 DB | 전화번호부 보관소 | 데이터가 저장된 실제 장소 |
☎️ 사용자가 전화번호 하나를 찾고 싶다고 하면,
👉 직접 DB 창고에 들어가 검색하는 것이 아니라,
💼 DAO에게 "홍길동 전화번호 알려줘" 요청을 하면,
DAO가 DB에서 검색해서 값을 가져와 전달하는 중간 대행자 역할을 합니다.
✅ 정리
항목 | 내용 |
정의 | DB 접근 로직을 담당하는 객체 (혹은 클래스) |
역할 | SQL 실행 (CRUD), 결과 반환 |
장점 | 구조 분리, 유지보수 쉬움, 테스트 용이 |
관련 개념 | DTO, Service Layer, MVC 패턴 등과 함께 자주 사용됨 |

반응형