📚 강의 출처
강사님께 항상 감사합니다. 🧑🏻💻
해당 글은 김영한님의 강의와 개인적 지식을 바탕으로 정리한 내용입니다.
모든 자료의 출처는 김영한 강사님임을 미리 밝힙니다.
섹션 1. JDBC 이해
- 커넥션 연결 : TCP/IP를 사용해 커넥션 연결
- SQL 전달 : DB가 이해할 수 있는 SQL을 연결된 커넥션을 통해 DB에 전달
- 결과 응답 : DB는 전달된 SQL을 수행하고 그 결과 반환
만약, DB 종류를 변경하면 애플리케이션 서버에 개발된 사용 코드를 변경해야 한다. 또한, 각 DB의 지식을 새로 습득해야 한다. 이러한 문제점을 해결하기 위해 JDBC 자바 표준이 등장한다.
대표적인 기능
- Connection - 연결
- Statement - SQL 담을 내용
- ResultSet - SQL 요청 응답
표준 인터페이스를 사용해 설계하면 된다. 각 DB 회사는 자신의 DB에 맞도록 JDBC 드라이버 라이브러리를 제공한다. 라이브러리는 표준 인터페이스를 토대로 설계되어 있기에, 표준 인터페이스를 의존해 사용한다.
SQL Mapper - JDBC를 편리하게 사용하도록 도움
- SQL 응답 결과를 객체로 편리하게 변환
- JDBC의 반복 코드 제거
- ex) 스프링 JdbcTemplate, MyBatis
ORM 기술 - 객체를 관계형 데이터베이스 테이블과 매핑
- SQL 동적으로 만들어 실행
- 다른 SQL을 사용하는 문제도 중간에서 해결(JDBC에서 최대한 해결했지만, 개선이 필요한 부분)
- JPA는 자바 ORM 표준 인터페이스이며, 하이버네이트와 이클립스 링크 대표적인 기술이 존재
데이터 베이스 연결
- JDBC 제공 DriverManager.getConnection(...) 사용
회원등록 예제
public Member save(Member member) throws SQLException {
String sql = "insert into member(member_id, money) values(?, ?)"; // sql 문
Connection con = null;
PreparedStatement pstmt = null;
try {
con = getConnection(); // 연결
pstmt = con.prepareStatement(sql); // sql문 적용
pstmt.setString(1, member.getMemberId()); // sql 파라미터 삽입
pstmt.setInt(2, member.getMoney());
pstmt.executeUpdate(); // 데이터베이스 전달
return member;
} catch (SQLException e) {
log.error("db error", e);
throw e;
} finally {
close(con, pstmt, null); // 리소스 정리
}
}
private void close(Connection con, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close(); // 종료
} catch (SQLException e) {
log.info("error", e);
}
}
if (stmt != null) {
try {
stmt.close(); // 종료
} catch (SQLException e) {
log.info("error", e);
}
}
if (con != null) {
try {
con.close(); // 종료
} catch (SQLException e) {
log.info("error", e);
}
}
}
private Connection getConnection() {
return DBConnectionUtil.getConnection();
}
쿼리를 실행하고, 오류가 발생하더라도 리소스 정리를 해야 한다. 정리할 때는 역순으로 해야 한다.
ResultSet - 데이터 반환
ResultSet rs = null;
rs = pstmt.executeQuery();
if (rs.next()) {
Member member = new Member();
member.setMemberId(rs.getString("member_id"));
member.setMoney(rs.getInt("money"));
return member;
} else {
throw new NoSuchElementException("member not found memberId=" +memberId);
}
🧑🏻💻 강의 실습 기록
섹션 2. 커넥션풀과 데이터소스 이해
사용자 요청마다 커넥션을 새로 만드는 것을 매우 비효율적이다. 요청마다 TCP/IP 커넥션의 새로 생성에 의해 리소스와 시간이 추가적으로 발생해 응답 속도에 영향을 준다. 이를 해결하기 위해 '커넥션 풀' 방법을 사용한다.
애플리케이션 시작 시점에 미리 커넥션을 확보해 풀에 보관한다.
커넥션 풀 사용 로직
- 커넥션 조회 : 커넥션 풀에 있는 남은 커넥션을 참조
- 커넥션 획득 : 남아있는 커넥션이 있으면 획득
- 커넥션 사용 : 획득한 커넥션으로 DB연결 후 사용
- 커넥션 풀에 반환 : 사용한 커넥션은 다시 풀에 반환
커넥션 풀
- 실무에서 항상 기본 사용
- 커넥션 풀 숫자 정의 가능(기본 값 10)
- 오픈 소스 커넥션 풀 중에서 HikariCP 대부분 사용
- DataSource는 커넥션을 획득하는 방법을 추상화한 인터페이스
설정과 사용의 분리
- 설정 : 설정과 관련된 속성들은 한 곳에 있는 것이 향후 변경에 더 유연하게 대처
- 사용 : 설정은 신경 쓰지 않고, 호출해서 사용
HikariCP
void dataSourceConnectionPool() throws SQLException, InterruptedException {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(URL); // DB 설정
dataSource.setUsername(USERNAME);
dataSource.setPassword(PASSWORD);
dataSource.setMaximumPoolSize(10); // 사이즈 설정
dataSource.setPoolName("MyPool"); // 풀 이름 설정
useDataSource(dataSource);
Thread.sleep(1000);
}
- 애플리케이션 실행 시 별도의 스레드를 사용해 커넥션 풀에 커넥션을 생성(애플리케이션 실행 시간 영향 X)
JdbcUtils 편의 메서드
private void close(Connection con, Statement stmt, ResultSet rs) {
JdbcUtils.closeResultSet(rs); // 연결 해제
JdbcUtils.closeStatement(stmt);
JdbcUtils.closeConnection(con);
}
🧑🏻💻 강의 실습 기록
섹션 3. 트랜잭션 이해
애플리케이션이 동작할 때, 저장하고 싶은 데이터는 분명 파일로도 저장할 수 있다. 그러나, 현대 개발은 DB를 사용한다. 사용 이유 중 하나가 트랜잭션 때문이다. 만약에 A 데이터와 B 데이터를 저장하는 로직이 있다고 하자. A 데이터가 정상적으로 저장되고, B 데이터를 저장하려는 순간 에러가 발생했다. 그럼 A 데이터만 저장이 완료되고 B 데이터는 저장이 안 된다. 이게 어떤 큰 문제를 야기하는지는 A, B 데이터를 계좌이체 실제 사례를 들어보면 확연하게 와닿을 거다. 한 사용자는 돈이 빠져나가고, 한 사용자는 돈이 입금이 안 되는 상황이 발생한다. 이러한 문제를 방지하기 위해 트랜잭션이라는 개념을 통해 Rollback도 할 수 있고, 정상적으로 로직이 마무리되면 Commit이 될 수 있다.
트랜잭션 ACID
- 원자성 : 트랜잭션 내 작성은 모두 성공하거나 실패해야 한다.
- 일관성 : 트랜잭션은 일관성 있는 데이터베이스 상태를 유지해야 한다.
- 격리성 : 동시에 실행되는 트랜잭션들 사이에 서로 영향을 미치지 않도록 격리해야 한다.
- 지속성 : 트랜잭션의 결과는 항상 기록되어야 한다.
트랜잭션은 위와 같은 제약을 보장한다. 하지만, 모두 준수한다면 동시 처리 성능이 매우 나빠진다. 이를 해결하기 위해 격리 수준을 4단계로 정의했다.
트랜잭션 격리 수준 - Isolation level
- READ uncommitted(커밋되지 않은 읽기)
- READ COMMITTED(커밋된 읽기)
- REPEATABLE READ(반복 가능한 읽기)
- SERIALIZABLE(직렬화 가능)
- 애플리케이션의 요청으로 데이터베이스 서버는 내부에 세션을 생성, 연결 종료 시 세션도 종료
트랜잭션
- 커밋 호출 전까지는 임시로 데이터를 저장
- 자동 커밋, 수동 커밋
- 자동 커밋 : 쿼리 실행 직후 자동으로 커밋 호출
- 수동 커밋 : 쿼리 실행 후 커밋 전까지는 반영 X
- 보통 수동 커밋 모드로 전환하는 것을 "트랜잭션을 시작한다" 표현
DB락
- 트랜잭션 시작 후 커밋이나 롤백 전까지 다른 세션에서 접근할 수 없게 방지
- 조회는 락을 획득하지 않아도 가능 (데이터베이스마다 차이)
- 락이 없는 상태에서 쿼리 요청할 시 락이 반환될 때까지 대기
- 타임 아웃이 지나면 에러 발생
- 테스트 사용한 데이터는 트랜잭션을 활용해 제거
- 트랜잭션을 활용하려면 커넥션 유지 필요
이후 내용은 트랜잭션을 직접 코드로 구현한 내용이다. 구조를 파악하려면 PDF를 따로 참고하면 좋다. 이렇게 살펴본 복잡한 코드를 어떻게 스프링에서 사용하는지는 다음 섹션에 나온다.
🧑🏻💻 강의 실습 기록
'✍️ 정리 > Spring' 카테고리의 다른 글
[Inflearn] 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 정리 (B) (0) | 2024.08.06 |
---|---|
[Inflearn] 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 정리 (A) (0) | 2024.07.31 |
[Inflearn] 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 정리 (B) (3) | 2024.07.24 |
[Inflearn] 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 정리 (A) (0) | 2024.07.18 |
[Inflearn] 모든 개발자를 위한 HTTP 웹 기본 지식 정리 (2) | 2024.07.03 |
Backend 개발자를 꿈꾸는 꿈나무💭 기술 블로그
꾸준함을 목표로 하는 꿈나무 개발자 택이✌️입니다. 궁금하신 점이나 잘못된 정보가 있으면 언제든지 연락주세요. 📩 함께 프로젝트 및 스터디도 언제든지 희망합니다! 📖