[1] DB 개념 정리
1. 데이터베이스와 DBMS + 관계형 데이터베이스
1) 데이터베이스
: 여러 응용 시스템이 공유할 수 있도록 통합, 저장된 공용 데이터의 집합
: 저장, 검색, 갱신을 효율적으로 수행할 수 있도록 데이터를 고도로 조직화해 저장
2) 관계형 데이터베이스(RDBMS) : 키(key)와 값(value)의 관계를 테이블 형태로 표현하는 모델

- 릴레이션 : 하나의 개체에 관한 데이터를 2차원 표(2차원 테이블 구조) 형태로 저장한 것
- 속성 (Attribute): 릴레이션의 '열', 파일관리시스템의 필드에 해당
- 투플 (Tuple): 릴레이션의 '행', 파일관리시스템의 레코드에 해당
- 도메인 (Domain) : 하나의 속성이 가질 수 있는 모든 값의 범위 또는 데이터타입 (ex. int, char, float 등)
2. SQL과 JDBC
- SQL : RDBMS에서 쓰는 쿼리용 언어로, DB 스키마 생성, 자료의 검색, 관리, 수정, DB 객체 접근 관리를 위해 고안됨.
- JDBC(Java DataBase Connectivity) : RDBMS에서 저장된 데이터를 접근/조작할 수 있게 하는 JAVA API
ㄴ 다양한 DBMS에 대해 일관된 API로 데이터베이스 연결, 검색, 수정, 관리 등이 가능
2. 기본 SQL 문법 (CRUD)
- CRUD : 데이터를 다루는 가장 기본적인 4가지 기능
- Create (생성): INSERT 문을 사용해 새 데이터를 테이블에 추가
ㄴ INSERT INTO student (hakbun, name, dept) VALUES ('2024001', '김이화', '컴퓨터공학과');
- Read (읽기): SELECT 문을 사용해 테이블의 데이터를 조회
ㄴ SELECT * FROM student WHERE dept = '컴퓨터공학과';
- Update (수정): UPDATE 문을 사용해 기존 데이터를 수정
ㄴ UPDATE student SET name = '박이화' WHERE hakbun = '2024001';
- Delete (삭제): DELETE 문을 사용해 기존 데이터를 삭제
ㄴ DELETE FROM student WHERE hakbun = '2024001';
[2] JDBC 흐름 정리
- JDBC(Java DataBase Connectivity) : 자바 애플리케이션이 DB에 연결하고 SQL 문을 실행할 수 있게 해주는 표준 API이므로, 덕분에 DB 종류가 바뀌더라도 자바 코드는 거의 변경할 필요 없다는 장점 존재

JDBC 프로그래밍 핵심 5단계>
1) JDBC 드라이버 로드
- 사용하려는 DB의 JDBC 드라이버 클래스를 JVM에 로드
- JDBC 4.0 이후에는 jar 파일이 classpath에 포함되어 있어면 생략 가능. (but 명시적으로 작성하는 경우도 있음.)
try { // MySQL 8.x 버전 기준 드라이버 클래스
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
CREATE TABLE user (
id BIGINT PRIMARY KEY, -- 숫자 형식의 고유 ID
name VARCHAR(50),
age INT
); -- 예제 데이터 삽입
INSERT INTO user (id, name, age) VALUES (1, '신짱구', 7);
INSERT INTO user (id, name, age) VALUES (2, '신짱아', 3);
(+ DB 테이블 준비)
2) 데이터베이스 연결 (Connection)
- DriverManager로 DB 서버 URL, 계정 정보로 실제 연결을 맺고 성공 시 Connection 객체를 반환받음
// DB 서버 주소
String url = "jdbc:mysql://localhost:3306/mydatabase"; //db
String user = "dbuser"; // 계정
String password = "dbpassword"; // 비밀번호
Connection conn = null;
try {
conn = DriverManager.getConnection(url, user, password);
System.out.println("DB 연결 성공!");
} catch (SQLException e) {
System.out.println("DB 연결 실패...");
e.printStackTrace();
}
3) SQL문 실행 (Statement / PreparedStatement)
- Connection 객체로부터 SQL을 실행할 PreparedStatement 객체를 생성
- PreparedStatement는 SQL 인젝션 공격을 방어할 수 있고 성능도 좋아 사용 강력 권장!
- .executeUpdate() 메서드 : INSERT, UPDATE, DELETE 쿼리
- .executeQuery() 메서드 : SELECT 쿼리 실행
// PreparedStatement 사용 예시 : user테이블에 id, name, age를 추가하는 예
String sql = "INSERT INTO user (id, name, age) VALUES (?, ?, ?)”;
PreparedStatement pstmt = conn.prepareStatement(sql);
// ?에 값 바인딩 (첫 번째 ?는 인덱스 1)
pstmt.setString(1, “user01”);
pstmt.setString(2, “신짱구");
pstmt.setString(3, 5);
int result = pstmt.executeUpdate(); // INSERT, UPDATE, DELETE 실행
4) 결과 처리 (ResultSet)
- SELECT 문의 실행 결과는 ResultSet 객체로 반환됨.
- next() 메서드를 while 문에서 호출하여 한 행(row)씩 데이터를 순회하며 읽어옴.
// user테이블을 PreparedStatement로 회원 조회
String sql = "SELECT id, name, age FROM user WHERE id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, “user01”);
ResultSet rs = pstmt.executeQuery(); // SELECT 실행
if (rs.next()) { // 조회된 데이터가 있다면
String name = rs.getString("name"); // "name" 컬럼의 값을 String으로 가져옴
String age = rs.getInt(“age");
System.out.println("이름: " + name + ", 나이: " + age);
}
5) 자원 해제 (Close)
- 사용한 ResultSet, PreparedStatement, Connection 객체는 시스템 자원 누수를 막기 위해 사용한 역순으로 닫아주기.
- try() 안의 리소스는 예외 발생하더라도 finally 없이 자동으로 close() 호출되어 편리함!
String sql = "SELECT id, name FROM user";
try ( Connection conn = DriverManager.getConnection(url, user, password);
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery()) {
// rs.next()가 false를 반환할 때까지 반복
while (rs.next()) {
System.out.println("아이디: " + rs.getString("id") + ", 이름: " + rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
} // try 블록을 벗어나는 순간 rs, pstmt, conn이 자동으로 close() 됨!
3. 실습 복기 (student 테이블)
- MariaDB 서버 + HeidiSQL을 이용

- test DB 안에 student 테이블 생성 -> '번호', '학과', '학번', '이름' 속성 추가 / '학번'에는 고유키(Unique Key) 속성 부여
- HeidiSQL의 '데이터' 탭에서 직접 or 이클립스에서 JDBC 코드를 작성하여 student 테이블에 INSERT와 SELECT 쿼리를 실행 등이 가능.
package student;
public class Student {
private int no;
private String dept;
private String sid;
private String name;
public Student() {};
public Student(String dept, String sid, String name) {
this.dept = dept;
this.sid = sid;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package student;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
// 학생 정보를 DB에 삽입, 조회
public class StudentDao {
private final DatabaseConnector connector;
public StudentDao(DatabaseConnector connector) {
this.connector = connector;
}
// 학생 정보를 INSERT 하는 메서드 : no, dept, stdID, name 필드이름과 같아야 함
public void insertStudent(Student student) {
String sql = "INSERT INTO student ( dept, stdID, name) VALUES (?, ?, ?)";
try (Connection conn = connector.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS)) {
pstmt.setString(1, student.getDept());
pstmt.setString(2, student.getSid());
pstmt.setString(3, student.getName());
pstmt.executeUpdate();
try (ResultSet generatedKeys = pstmt.getGeneratedKeys()) {
if (generatedKeys.next()) {
int no = generatedKeys.getInt(1); //첫번째 컬럼 자동증가
student.setNo(no);
System.out.println(no +" 학생 정보가 추가되었습니다.");
}
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 학생 정보 수정
public void updateStudent(Student student) {
String sql = "UPDATE student SET dept = ?, stdID = ?, name = ? WHERE no = ?";
try (Connection conn = connector.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, student.getDept());
pstmt.setString(2, student.getSid());
pstmt.setString(3, student.getName());
pstmt.setInt(4, student.getNo()); // 대상 학생의 no
int rows = pstmt.executeUpdate();
if (rows > 0) {
System.out.println("학생 정보가 수정되었습니다.");
} else {
System.out.println("해당 no의 학생이 존재하지 않습니다.");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 학생 정보 삭제
public void deleteStudentByNo(int no) {
String sql = "DELETE FROM student WHERE no = ?";
try (Connection conn = connector.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, no);
int rows = pstmt.executeUpdate();
if (rows > 0) {
System.out.println("학생 정보가 삭제되었습니다.");
} else {
System.out.println("해당 no의 학생이 존재하지 않습니다.");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// DB에서 모든 학생 정보를 가져오기
public List<Student> getAllStudents() {
List<Student> students = new ArrayList<>();
String sql = "SELECT * FROM student order by no ASC ";
try (Connection conn = connector.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
Student s = new Student();
s.setNo(rs.getInt("no"));
s.setDept(rs.getString("dept"));
s.setSid(rs.getString("stdID"));
s.setName(rs.getString("name"));
students.add(s);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return students; // 조회한 학생 리스트 반환
}
}
package user;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DatabaseConnector {
private static final String URL = "jdbc:mysql://localhost:3306/test";
private static final String USER = "stdUser";
private static final String PASS = "wkvmtlf2";
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASS);
}
public DatabaseConnector() {}
}
package student;
import java.util.List;
public class StudentTest {
public static void main(String[] args) {
DatabaseConnector connector = new DatabaseConnector();
StudentDao dao = new StudentDao(connector);
dao.insertStudent(new Student("떡잎마을학과","20240129","철수"));
List<Student> students = dao.getAllStudents();
Student student = new Student();
student.setNo(4);
student.setDept("컴퓨터공학과");
student.setSid("2466048");
student.setName("장은서");
dao.updateStudent(student);
dao.deleteStudentByNo(6);
for(Student s : students) {
System.out.printf("No: %d, 학과: %s , 학번: %s 이름: %s\n", s.getNo(), s.getDept(), s.getSid(), s.getName());
}
}
}
4. 애플리케이션 권장 구조 정리 : MVC + DAO + DTO + Service
- JDBC 코드는 설계를 어떻게 하느냐에 따라 프로젝트 품질이 크게 달라짐.
- SOLID 원칙과 디자인 패턴을 적용하여 역할을 분리하는 것 중요
| DTO (Model) |
Student | 학생 정보 객체 : DTO (Data Transter Object) |
택배상자처럼 데이터를 담아 다른계층으로 전달. 가볍고 단순해야 함. |
| DAO | StudentDAO | DB 접근 전담 CRUD 처리 클래스 (JDBC 사용해 SQL 실행) | DAO 인터페이스를 도입할 수도 있음 |
| Service | StudentService | 비즈니스 로직 처리, DAO 클래스의 CRUD처리 메소드 호출. | 예) 학생조회하기 메소드는 DAO의 read 처리용메소드를 호출하도록 구현. |
| View | StudentView | 사용자 입출력 처리(Scanner, System.out, JLabel 등) | 시각적인 UI를 만들지 않는 경우, Contoller나 Main가 View역할을 포함할 수 있으나 분리하는 게 좋음. |
| Controller | StudentController | 사용자 요청 처리 | |
| Main | DBManager | 프로그램 시작점 |
package user;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DatabaseConnector {
private static final String URL = "jdbc:mysql://localhost:3306/test";
private static final String USER = "stdUser";
private static final String PASS = "wkvmtlf2";
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASS);
}
public DatabaseConnector() {}
}
package user;
import java.util.Optional;
public class Main {
public static void main(String[] args) {
try {
UserRepository userRepository = new UserDao();
UserService service = new UserService(userRepository);
service.createUser("김철수", 7);
Optional<User> foundUser = service.getUser(1);
foundUser.ifPresentOrElse(user->System.out.println("사용자 찾음 : "+user.getName()), ()->System.out.println("사용자 없음"));
service.updateUser(5,"흰둥이", 4);
for (User u : service.getAllUsers()) {
System.out.println(u.getId() + " / " + u.getName() + " / " + u.getAge());
}
service.deleteUser(6);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package user;
public class User {
private int id;
private String name;
private int age;
// 전체 필드 생성자 (UPDATE, SELECT 시 사용)
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
// ID 없이 생성자 (INSERT 시 사용)
public User(String name, int age) {
this.name = name;
this.age = age;
}
// Getters and Setters
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 int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
package user;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class UserDao implements UserRepository{
public void create(User user) throws SQLException {
String sql = "INSERT INTO user (id, name, age) VALUES (?, ?, ?)";
try (Connection conn = DatabaseConnector.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, user.getId());
stmt.setString(2, user.getName());
stmt.setInt(3, user.getAge());
stmt.executeUpdate();
}
}
public User read(int id) throws SQLException {
String sql = "SELECT * FROM user WHERE id = ?";
try (Connection conn = DatabaseConnector.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, id);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return new User(rs.getInt("id"), rs.getString("name"), rs.getInt("age"));
}
}
return null;
}
public void update(User user) {
String sql = "UPDATE user SET name = ?, age = ? WHERE id = ?";
try (Connection conn = DatabaseConnector.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, user.getName());
stmt.setInt(2, user.getAge());
stmt.setInt(3, user.getId());
stmt.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void delete(int id) throws SQLException {
String sql = "DELETE FROM user WHERE id = ?";
try (Connection conn = DatabaseConnector.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, id);
stmt.executeUpdate();
}
}
@Override
public User save(User user) {
// TODO Auto-generated method stub
return null;
}
@Override
public Optional<User> findById(int id) {
// TODO Auto-generated method stub
return Optional.empty();
}
@Override
public List<User> findAll() {
// TODO Auto-generated method stub
return null;
}
@Override
public void deleteById(int id) {
// TODO Auto-generated method stub
}
}
package user;
import java.util.List;
import java.util.Optional;
public interface UserRepository {
User save(User user) ; //생성 (Create)
//void save(User user) ;
Optional<User> findById(int id) ; // ID로 조회 (Read)
List<User> findAll() ; // 전체 조회 (Read)
void update(User user) ;// 수정 (Update)
void deleteById(int id) ; // 삭제 (Delete)
}
package user;
import java.util.List;
import java.util.Optional;
public class UserService {
// 구체 클래스(UserDao)가 아닌 인터페이스(UserRepository)에 의존
private final UserRepository userRepository;
// 생성자를 통한 의존성 주입 (DI)
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User createUser(String name, int age) {
User newUser = new User(name, age);
return userRepository.save(newUser);
}
public Optional<User> getUser(int id) {
return userRepository.findById(id);
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
public void updateUser(int id, String newName, int newAge) {
User userToUpdate = new User(id, newName, newAge);
userRepository.update(userToUpdate);
}
public void deleteUser(int id) {
userRepository.deleteById(id);
}
}
5. 후기
- 초기 설정에 오류가 좀 났어서 해결하느라 시간을 좀 썼음. MariaDB 설치 시 root 비번을 잊지 않도록 하고 외부에서 접속하는 것도 막아두는 거 일단 기억하고 있음.
- try-catch-finally 구문으로 자원 해제 코드를 반복적으로 작성하는 것이 번거로웠는데, try-with-resources 쓰는 게 복잡해는 보여서 적응하는 데 좀 걸렸지만 익숙해지니 나름 편한 듯.
- DAO DTO 등의 구조가 어려웠음...
- 이번에 하는 자프실2 기말대체과제 프로젝트에 적용하면 좋을 개념들이 몇몇개 보여서 보람찼음.
'Develop > Java' 카테고리의 다른 글
| [11주차] SpringBoot - 2 (Spring Data JPA로 CRUD 구현) (0) | 2025.11.30 |
|---|---|
| [10주차] SpringBoot - 1 (0) | 2025.11.23 |
| [8주차] GUI + Event + Thread를 이용한 망나니 잡기 (0) | 2025.11.02 |
| [7주차-2] 디자인패턴 추가조사 - 데코레이터 패턴 (0) | 2025.10.18 |
| [7주차] 디자인패턴 기초실습 (0) | 2025.10.15 |