🎯 F-lab Java 6주차 학습 커리큘럼

6주차 자료의 모든 토픽을 두 개의 큰 흐름으로 정리한 학습 경로.
1) 학습 도구와 환경 — JUnit 테스트, 웹 인프라(서버/WAS/JAR/WAR)
2) DB 접근의 진화 — JDBC → Connection Pool → DataSource → 트랜잭션 → JdbcTemplate

5주차에서 IoC/DI를 배웠다면, 6주차는 그것을 테스트로 검증 하고 웹·DB 환경으로 확장한다.


📊 학습 경로 한눈에 보기

[Part A — 학습 도구와 환경]
  [Phase 1] JUnit 테스트
     ↓
  [Phase 2] 웹 인프라 기초 (웹서버/WAS/서블릿/JAR/WAR)

[Part B — DB 접근의 진화] ← 6주차의 정점
  [Phase 3] JDBC 표준화의 등장
     ↓
  [Phase 4] Connection Pool과 DB 세션
     ↓
  [Phase 5] DataSource 인터페이스 (추상화의 진화)
     ↓
  [Phase 6] 트랜잭션과 ACID
     ↓
  [Phase 7] JdbcTemplate (반복 제거)

총 7 Phase × 28 Unit

🔗 1~6주차 흐름 정리

주차주제핵심 변화
1주차OOP·JVM·GC·컬렉션·I/O 개론자바 큰 그림
2주차JVM 내부·바이트코드·G1 GC"어떻게 돌아가나"
3주차컬렉션·제네릭·함수형자바 표현력
4주차멀티스레딩·동시성·Executor동시성 정복
5주차Atomic + Spring IoC/DI 입문자바 → Spring 다리
6주차 (지금)테스트 + 웹 인프라 + DB 접근의 진화Spring 실전 환경

🗓️ 권장 학습 일정 (압축 7일)

DayPhase학습 목표
1일차Phase 1JUnit 테스트 마스터
2일차Phase 2웹 인프라 기초 + JAR/WAR
3일차Phase 3JDBC의 본질 이해
4일차Phase 4Connection Pool + DB 세션
5일차Phase 5DataSource 추상화
6일차Phase 6트랜잭션 ACID 완전 정복
7일차Phase 7JdbcTemplate 실전

🧪 Part A — 학습 도구와 환경

📚 Phase 1 — JUnit 테스트

목표: 5주차에서 만든 IoC/DI 코드를 테스트 코드로 검증하는 방법을 익힌다. 테스트 가능한 코드 = 좋은 코드 라는 명제를 체화한다.

Unit 1.1 — 단위 테스트의 필요성

선수 지식: 5주차 Phase 8 (DI)

핵심 개념

왜 테스트인가:

  • main()으로 검증 = 매번 직접 보고 판단해야 함 → 비효율
  • 코드 변경 시 모든 기능을 다시 검증해야 함 → 사람이 못 한다
  • 자동화된 단위 테스트 = 코드가 코드를 검증

좋은 단위 테스트의 조건:

  • 자동화: 사람 개입 없이 실행
  • 격리: 다른 테스트의 영향 없음
  • 빠름: 자주 돌릴 수 있는 속도
  • 반복 가능: 같은 결과 보장

자기 점검

  • main()으로 동작 확인하는 것과 단위 테스트의 본질적 차이는?
  • 5주차에서 만든 UserDao 코드를 테스트하기 어려운 부분이 있다면?

Unit 1.2 — assertThat과 매처(Matcher)

선수 지식: Unit 1.1

핵심 개념

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

assertThat(user2.getName(), is(user.getName()));

구성:

  • assertThat(actual, matcher): 첫 인자(실제 값)를 매처와 비교
  • is(expected): equals 비교 매처 (Hamcrest 라이브러리)

전통 방식 vs Hamcrest:

// 전통 (JUnit 4)
assertEquals(user.getName(), user2.getName());

// Hamcrest 매처
assertThat(user2.getName(), is(user.getName()));

장점: 자연어처럼 읽힘 ("user2의 이름이 user의 이름이다")

자기 점검

  • is() 외에 자주 쓰는 매처는? (힌트: nullValue, containsString, greaterThan)
  • 최신 JUnit 5의 assertEquals와 Hamcrest 중 무엇을 쓸까?

원본 자료: 토비의 스프링 vol.1, p.159


Unit 1.3 — JUnit이 테스트를 실행하는 방식

선수 지식: Unit 1.2

핵심 개념

JUnit이 한 테스트 클래스를 처리하는 7단계:
1. @Test가 붙은 public void 파라미터 없는 메서드를 모두 찾음
2. 테스트 클래스의 오브젝트를 하나 만듦
3. @BeforeEach(또는 @Before) 메서드 실행
4. @Test 메서드 하나 호출, 결과 저장
5. @AfterEach 메서드 실행
6. 나머지 테스트 메서드에 대해 2~5 반복
7. 모든 결과 종합 반환

핵심 포인트: 각 테스트마다 새 인스턴스 생성

자기 점검

  • 테스트 클래스의 인스턴스 변수에 데이터를 누적시키는 코드가 가능한가? (힌트: NO)
  • @BeforeAll@BeforeEach 의 차이는?

원본 자료: 토비의 스프링 vol.1, p.181


Unit 1.4 — 매번 새 오브젝트를 만드는 이유

선수 지식: Unit 1.3

핵심 개념

"각 테스트가 서로 영향을 주지 않고 독립적으로 실행됨을 확실히 보장하기 위해"

얻는 이점:

  • 인스턴스 변수를 부담 없이 사용 가능 (어차피 다음 테스트에서 초기화됨)
  • 테스트 간 의존성 제거
  • 실행 순서 무관

자기 점검

  • 인스턴스 변수에 비싼 자원(예: DB Connection)을 만들면 어떻게 되는가? (힌트: 매번 생성)
  • 테스트 간 공유가 정말 필요한 자원은 어떻게 처리할까? (힌트: @BeforeAll)

원본 자료: 토비의 스프링 vol.1, p.182


Unit 1.5 — 픽스처(Fixture)와 @BeforeEach

선수 지식: Unit 1.4

핵심 개념

픽스처(Fixture): 테스트 수행에 필요한 정보·오브젝트

  • 여러 테스트가 공통으로 쓰는 준비물
  • @BeforeEach로 매 테스트 전에 새로 생성
public class UserDaoTest {
    private UserDao dao;  // 픽스처

    @BeforeEach
    public void setUp() {
        ApplicationContext context = ...;
        this.dao = context.getBean("userDao", UserDao.class);
    }

    @Test
    public void test1() {
        dao.add(...);  // 픽스처 사용
    }
}

테스트 결과의 일관성:

  • 코드 변경이 없으면 항상 같은 결과
  • DB 데이터 잔여로 인한 실패 = 잘못된 테스트
  • dao.deleteAll() 같이 시작 상태 보장

자기 점검

  • 통합 테스트에서 DB 잔여 데이터 문제는 어떻게 해결하는가? (힌트: @Transactional, @Sql)
  • "테스트는 외부 환경에 의존하면 안 된다"는 원칙의 의미는?

📚 Phase 2 — 웹 인프라 기초

목표: Spring MVC 학습 전, 웹 백엔드의 기본 인프라 용어를 정리한다. 모르면 면접·실무 모두에서 헤맨다.

Unit 2.1 — 웹서버(Web Server) vs WAS(Web Application Server)

선수 지식: 1주차 Phase 7 (I/O)

핵심 개념

구분웹서버WAS
역할정적 자원 (HTML, CSS, 이미지) 제공동적 자원 (서블릿, JSP) 처리
예시Apache, NginxTomcat, Jetty, Undertow
처리파일 그대로 응답코드 실행 → 결과 응답

실무 구성:

[클라이언트] ↔ [Nginx (웹서버)] ↔ [Tomcat (WAS)] ↔ [DB]
                정적 파일 처리      동적 요청만 위임

자기 점검

  • WAS만 있어도 정적 파일 서빙이 되는데, 왜 웹서버를 따로 두는가?
  • Spring Boot의 내장 톰캣은 어느 영역에 해당하는가?

Unit 2.2 — 서블릿(Servlet)과 JSP

선수 지식: Unit 2.1

핵심 개념

서블릿(Servlet):

  • 자바 코드로 HTTP 요청을 처리하는 클래스
  • WAS 위에서 실행됨

JSP (Java Server Pages):

  • HTML 안에 자바 코드를 섞을 수 있는 템플릿
  • 결국 컴파일 시 서블릿으로 변환되어 실행

역할 분담의 진화:

  • 초기: 서블릿으로 HTML까지 다 출력 → 가독성 최악
  • 한때: JSP로만 모든 로직 → 유지보수 지옥
  • 현재: JSP는 View, 서블릿은 비즈니스 로직 (MVC 패턴)

자기 점검

  • JSP가 서블릿으로 변환된다는 사실이 어떤 의미를 갖는가?
  • 현대 Spring 환경에서 JSP는 여전히 쓰이는가? (힌트: Thymeleaf)

Unit 2.3 — SSR vs CSR

선수 지식: Unit 2.2

핵심 개념

구분SSR (Server Side Rendering)CSR (Client Side Rendering)
화면 생성서버에서 HTML 완성브라우저(JS)가 화면 그림
초기 로딩빠름느림 (JS 다운로드 필요)
SEO좋음까다로움
서버 부하작음 (JSON만)
예시JSP, Thymeleaf, Next.js (SSR)React SPA, Vue SPA

자기 점검

  • SEO가 중요한 서비스(예: 쇼핑몰, 블로그)에 적합한 방식은?
  • SSR에서도 React를 쓸 수 있는가? (힌트: Next.js)

Unit 2.4 — JAR vs WAR

선수 지식: Unit 2.2

핵심 개념

JARWAR
정식 명칭Java ArchiveWeb Application Archive
포함Class, 라이브러리+ JSP, Servlet, WEB-INF, META-INF
실행 환경JRE만 있으면 OK외부 WAS 필요
실행java -jar app.jarTomcat 등에 배포
구조자유사전 정의된 디렉터리 구조

Spring Boot의 선택: JAR

  • 이유: WAS(Tomcat) 내장 → 배포·실행 간단
  • 별도 WAS 운영 불필요 → DevOps 부담 ↓

WAR가 필요한 경우:

  • JSP를 반드시 써야 하는 레거시
  • 외장 WAS 사용 정책이 있는 환경

자기 점검

  • Spring Boot의 JAR가 내장 톰캣을 어떻게 가지고 있는가? (힌트: BOOT-INF/lib)
  • 마이크로서비스에서 JAR가 선호되는 이유는?

💾 Part B — DB 접근의 진화 (6주차의 정점)

이 파트의 큰 그림:
"DB 코드 한 줄이 어떻게 JdbcTemplate까지 진화하는지" — 5주차의 DAO 진화에 이어, 이번엔 DB 접근 자체의 추상화 여정 을 따라간다.

📚 Phase 3 — JDBC 표준화의 등장

목표: JDBC가 왜 등장했는지, 어떤 문제를 해결했고 어떤 문제는 안 해결했는지 명확히 한다.

Unit 3.1 — JDBC 없던 시절의 고통

선수 지식: Phase 2

핵심 개념

JDBC 등장 전:

  • 각 DB마다 고유 API (Oracle용, MySQL용, ...)
  • 데이터베이스 변경 시 모든 DB 코드를 다시 작성
  • 각 DB의 사용법을 새로 학습

예시 (JDBC 없을 때):

// 직접 소켓으로 MySQL 프로토콜과 통신
Socket socket = new Socket("localhost", 3306);
DataOutputStream out = new DataOutputStream(...);
out.writeUTF("Handshake request to MySQL server");
// MySQL의 바이너리 프로토콜을 직접 알아야 함

개발자가 DB 프로토콜까지 알아야 하는 지옥

자기 점검

  • 같은 코드를 Oracle로 마이그레이션하려면 어떻게 해야 했을까?
  • 이 문제와 5주차의 OCP 위반은 어떻게 닮았는가?

Unit 3.2 — JDBC의 표준화 효과

선수 지식: Unit 3.1

핵심 개념

JDBC = 자바의 DB 접근 표준 API

같은 코드, 다른 DB:

// MySQL
String url = "jdbc:mysql://localhost:3306/mydb";
// Oracle
String url = "jdbc:oracle:thin:@localhost:1521:xe";
// PostgreSQL  
String url = "jdbc:postgresql://localhost:5432/mydb";

// 이후 모든 코드는 동일!
Connection conn = DriverManager.getConnection(url, user, pwd);
PreparedStatement ps = conn.prepareStatement("SELECT ...");
ResultSet rs = ps.executeQuery();

JDBC가 해결한 것:

  • DB 연결 방식 통일
  • SQL 실행 방식 통일
  • 결과 처리 방식 통일
  • DB 교체 시 URL과 드라이버만 변경

자기 점검

  • JDBC는 5주차의 어떤 디자인 패턴과 닮았는가? (힌트: 전략 패턴, 인터페이스)
  • DriverManager가 하는 일은?

Unit 3.3 — JDBC가 해결하지 않는 것

선수 지식: Unit 3.2

핵심 한계

JDBC는 API는 통일 했지만, SQL 문법 차이는 그대로:

  • LIMIT vs ROWNUM (MySQL vs Oracle)
  • 자동 증가 컬럼 문법 (AUTO_INCREMENT vs SEQUENCE)
  • 페이징 처리 방식

해결: 상위 프레임워크 사용

  • JdbcTemplate (Spring) — JDBC 코드 반복 제거 (Phase 7에서)
  • JPA / Hibernate — ORM, SQL 자체를 거의 안 씀
  • MyBatis — SQL을 XML로 분리, DB별 분기 가능

자기 점검

  • "JDBC는 표준화가 됐는데 왜 ORM이 등장했나?"의 답은?
  • ORM이 SQL 문법 차이를 어떻게 해결하는가? (힌트: Dialect)

📚 Phase 4 — Connection Pool과 DB 세션

목표: "왜 매번 새로 연결하면 안 되는가" 라는 근본 질문에 TCP/IP 레벨로 답할 수 있게 된다.

Unit 4.1 — 매 요청마다 연결의 비효율

선수 지식: Phase 3

핵심 개념

DB 연결의 비용:
1. TCP/IP 3-way handshake (네트워크 왕복)
2. DB 인증 (ID/PW 검증)
3. DB가 세션 생성

매 요청마다 이걸 다 하면?

  • 100만 요청 = 100만 번의 handshake
  • DB 응답시간보다 연결 설정 시간이 더 길 수 있음

자기 점검

  • TCP 3-way handshake가 정확히 뭔가? (힌트: SYN, SYN-ACK, ACK)
  • DB 연결 1회 설정에 걸리는 시간은 대략 얼마? (힌트: 수~수백 ms)

Unit 4.2 — Connection Pool 개념

선수 지식: Unit 4.1

핵심 개념

"Connection의 수영장"

동작 방식:
1. 애플리케이션 시작 시 N개의 Connection 미리 생성
2. 요청 시: 풀에서 빌려옴
3. 사용 후: 풀에 반환
4. 풀의 Connection은 재사용

[요청1] ──> [Pool에서 Conn1 빌림] ──> [DB 작업] ──> [Conn1 반환]
[요청2] ──> [Pool에서 Conn2 빌림] ──> [DB 작업] ──> [Conn2 반환]
                ...

대표 구현체:

  • HikariCP (Spring Boot 기본)
  • DBCP2
  • Tomcat JDBC Pool

자기 점검

  • Connection 풀 크기는 어떻게 결정하는가? (힌트: CPU 코어, 작업 시간)
  • 풀 크기보다 동시 요청이 많으면?

Unit 4.3 — DB 세션과 연결 구조

선수 지식: Unit 4.2

핵심 개념

연결 절차:
1. 클라이언트 → DB 서버에 연결 요청 (TCP 연결)
2. DB가 세션 생성 (트랜잭션 단위)
3. 클라이언트가 SQL 전달 → 세션이 실행
4. 세션이 트랜잭션 시작 → commit/rollback으로 종료
5. 사용자가 커넥션 닫거나 DB 관리자가 세션 종료

중요한 매핑:

  • 커넥션 풀의 Connection N개 = DB 세션 N개

자기 점검

  • 한 Connection에서 동시에 두 트랜잭션이 가능한가? (힌트: NO)
  • 풀에 Connection 100개면 DB 입장에서 무엇이 만들어지는가?

Unit 4.4 — DB Lock 개념

선수 지식: Unit 4.3

핵심 개념

왜 락이 필요한가:

  • 세션1: 데이터 수정 중 (아직 commit 안 함)
  • 세션2: 같은 데이터 동시 수정 시도
  • 트랜잭션의 원자성 깨짐
  • → 세션1이 롤백하면 세션2는 잘못된 데이터 수정

해결: 트랜잭션 진행 중에는 다른 세션이 해당 데이터를 수정 못 하도록 락

락의 종류 (간략):

  • 공유 락(S): 읽기용, 여러 세션이 동시 보유 가능
  • 배타 락(X): 쓰기용, 한 세션만 보유

자기 점검

  • DB 락과 4주차의 자바 synchronized 락은 어떤 점이 닮았는가?
  • 데드락이 DB에서도 발생할 수 있는가?

📚 Phase 5 — DataSource 인터페이스 (추상화의 진화)

목표: 5주차의 인터페이스 추상화 정신이 자바 표준 라이브러리에 어떻게 녹아있는지 본다.

Unit 5.1 — 다양한 커넥션 획득 방식

선수 지식: Phase 4

핵심 개념

커넥션을 얻는 방법은 다양:

  • DriverManager.getConnection() — 매번 새로 만듦 (풀 X)
  • HikariCP — 커넥션 풀
  • DBCP2 — 커넥션 풀
  • Tomcat JDBC Pool — 커넥션 풀

문제:

// DriverManager 사용 코드
Connection c = DriverManager.getConnection(url, user, pwd);

// HikariCP로 변경 시
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(url); ...
Connection c = ds.getConnection();  // ← 사용법이 다름!

커넥션 획득 방법이 바뀌면 애플리케이션 코드가 다 바뀐다

자기 점검

  • 이 문제와 5주차의 ConnectionMaker 인터페이스 도입은 어떻게 닮았는가?

Unit 5.2 — 추상화의 필요성

선수 지식: Unit 5.1

핵심 통찰

5주차에서 본 OCP 정신과 같은 문제:

  • 변경의 압력이 애플리케이션 코드까지 전파
  • 인터페이스로 추상화
  • → 애플리케이션은 인터페이스에만 의존

해결의 방향:

"커넥션을 얻는 방법" 자체를 인터페이스로 분리

자기 점검

  • 5주차의 ConnectionMaker와 이번에 나올 DataSource는 같은 사상의 다른 이름인가?

Unit 5.3 — DataSource 인터페이스

선수 지식: Unit 5.2

핵심 개념

javax.sql.DataSource — 자바 표준 인터페이스

public interface DataSource {
    Connection getConnection() throws SQLException;
    // ... 기타
}

핵심 메서드는 getConnection() 하나.

구현체들:

  • HikariDataSource (HikariCP)
  • BasicDataSource (DBCP2)
  • DriverManagerDataSource (Spring 제공, 풀 없음)

애플리케이션 코드:

@Autowired
private DataSource dataSource;  // 인터페이스에 의존

public void something() {
    Connection c = dataSource.getConnection();  // 어떤 구현체든 OK
    // ...
}

HikariCP → DBCP2 변경 시: 설정 파일만 변경, 애플리케이션 코드는 그대로.

자기 점검

  • DataSource는 1주차의 어떤 SOLID 원칙의 구현체인가? (힌트: DIP)
  • Spring Boot가 기본 DataSource로 무엇을 쓰는가?

Unit 5.4 — DriverManagerDataSource (특수 케이스)

선수 지식: Unit 5.3

핵심 개념

문제: DriverManager는 DataSource를 구현하지 않음

Spring의 해결: DriverManagerDataSource

  • 내부적으로 DriverManager 호출
  • 외부적으로는 DataSource 인터페이스 제공
  • → 애플리케이션은 DataSource에만 의존

용도:

  • 테스트 환경 (풀 불필요)
  • 학습용

프로덕션에서는 절대 쓰지 말 것 (풀이 없어서 성능 폭망)

자기 점검

  • DriverManagerDataSource와 HikariDataSource를 같은 코드로 사용 가능한 이유는?
  • 이게 5주차의 어댑터 패턴과 닮은가?

📚 Phase 6 — 트랜잭션과 ACID

목표: DB 트랜잭션의 4가지 핵심 성질을 실생활 예제로 체화하고, 멀티 세션 환경에서의 동작을 본다.

Unit 6.1 — 트랜잭션이란

선수 지식: Phase 4

핵심 개념

"한 단위의 작업 으로 취급되는 모든 작업"

계좌 이체 예시:

  • 작업1: A 계좌에서 출금
  • 작업2: B 계좌에 입금
  • 두 작업을 하나의 단위 로 묶음 = 트랜잭션

트랜잭션의 끝:

  • Commit: 모든 변경 영구 적용
  • Rollback: 모든 변경 취소

자기 점검

  • 게시글 작성 시 트랜잭션 단위는?
  • "단위"의 크기는 어떻게 정하는가?

Unit 6.2 — Atomicity (원자성)

선수 지식: Unit 6.1

핵심 개념

"트랜잭션의 모든 연산이 모두 성공 하거나 모두 실패 해야 한다"

계좌 이체 시나리오:

  • A에서 출금 ✅
  • B에 입금 ❌ (장애)
  • → A의 돈은 사라짐? NO!
  • → 전체 롤백, A의 출금도 취소

4주차의 자바 Atomic과 다른 점:

  • 자바 Atomic: 하나의 변수에 대한 원자성
  • DB Atomicity: 여러 작업의 묶음 원자성

자기 점검

  • 원자성이 깨지면 어떤 사고가 발생하는가?
  • 부분 성공을 허용하는 시나리오가 있을 수 있는가? (힌트: Saga 패턴)

Unit 6.3 — Consistency (일관성)

선수 지식: Unit 6.2

핵심 개념

"트랜잭션 완료 후 DB 제약조건이 항상 지켜져야 한다"

예시:

  • "계좌 잔액은 음수가 될 수 없다"는 제약
  • 이체 후에도 이 제약이 깨지면 안 됨
  • 깨지는 트랜잭션은 자동 롤백

제약조건의 종류:

  • NOT NULL, UNIQUE, CHECK
  • FOREIGN KEY
  • 비즈니스 규칙 (애플리케이션에서 강제)

자기 점검

  • 일관성과 무결성(Integrity)은 같은 말인가?
  • 일관성을 깨는 시도를 DB가 어떻게 감지하는가?

Unit 6.4 — Isolation (격리성)

선수 지식: Unit 6.3

핵심 개념

"각 트랜잭션은 다른 트랜잭션과 독립적 으로 실행되어야 한다"

예시:

  • A 계좌에 1만원
  • 트랜잭션1: A → B로 6천원 이체
  • 트랜잭션2: A → C로 6천원 이체 (동시)
  • → 둘 다 성공하면 잔액 -2천원? NO!
  • → 격리성이 보장되면 한쪽이 실패해야 함

격리 수준 (간략):

  • READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE
  • 높을수록 안전, 낮을수록 빠름

자기 점검

  • 격리 수준이 너무 높으면 어떤 문제가? (힌트: 동시성 ↓)
  • "Dirty Read" 가 무엇인가?

Unit 6.5 — Durability (지속성)

선수 지식: Unit 6.4

핵심 개념

"Commit된 트랜잭션 은 DB에 영구 보존 되어야 한다"

보장 수단:

  • 트랜잭션 로그 (WAL: Write-Ahead Logging)
  • 비휘발성 저장소 (HDD, SSD)
  • 시스템 장애 후 복구

시스템이 다운되어도 커밋된 데이터는 살아있어야 한다.

자기 점검

  • 지속성이 단순히 "디스크 저장"보다 복잡한 이유는? (힌트: 캐시, fsync)
  • ACID 중 가장 비싼 성질은?

Unit 6.6 — Commit 이전의 트랜잭션 동시성

선수 지식: Unit 6.5

핵심 시나리오

세션1: 데이터 변경 (commit X)
세션2: 같은 데이터 조회

결과:

  • 세션2는 변경 전 데이터를 봄 (격리성)
  • 세션1의 변경은 자기 세션에서만 보임 (임시 저장)
  • 세션1이 commit하면 → 그제서야 세션2도 변경 데이터 확인

비유:

  • 세션 = 각자의 노트북
  • 변경 = 노트북에 임시 기록
  • Commit = 공유 클라우드에 저장

자기 점검

  • 이 격리 동작은 격리 수준에 따라 달라지는가?
  • 공유 캐시(Redis)의 일관성과 DB 격리의 차이는?

📚 Phase 7 — JdbcTemplate (반복 제거)

목표: JDBC 코드의 끔찍한 반복을 Spring이 어떻게 해결했는지 본다.

Unit 7.1 — JDBC만 쓸 때의 반복 코드

선수 지식: Phase 3

핵심 문제

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;

try {
    conn = ...;
    stmt = conn.prepareStatement(sql);
    stmt.setInt(1, age);
    rs = stmt.executeQuery();
    while (rs.next()) {
        // 결과 처리
    }
} catch (SQLException e) {
    e.printStackTrace();
} finally {
    if (rs != null) try { rs.close(); } catch (...) { ... }
    if (stmt != null) try { stmt.close(); } catch (...) { ... }
    if (conn != null) try { conn.close(); } catch (...) { ... }
}

문제:

  • 매 쿼리마다 똑같은 try/catch/finally
  • 자원 해제 코드가 본 로직보다 길음
  • 실수로 close 누락 → Connection 누수

5주차에서 본 패턴:

  • "변하지 않는 흐름 + 변하는 부분" → 템플릿 메소드 패턴 / 전략 패턴

자기 점검

  • 위 코드에서 변하는 부분과 변하지 않는 부분은?
  • try-with-resources로는 충분히 못 줄이는 이유는?

Unit 7.2 — JdbcTemplate의 등장

선수 지식: Unit 7.1

핵심 개념

JdbcTemplate = 5주차에서 본 템플릿 메소드 + 전략 패턴의 실제 구현

@Autowired
private JdbcTemplate jdbcTemplate;

public List<Customer> getCustomersByAge(int age) {
    String sql = "SELECT * FROM customers WHERE age > ?";
    return jdbcTemplate.query(sql, new Object[]{age},
        new BeanPropertyRowMapper<>(Customer.class));
}

숨겨진 것:

  • Connection 획득/반환 (DataSource에서 자동)
  • PreparedStatement 생성/해제
  • ResultSet 처리
  • 예외 처리

개발자는 변하는 부분만:

  • SQL
  • 파라미터
  • 결과 매핑

자기 점검

  • "Template"이 이름에 들어간 이유는?
  • 5주차의 어떤 디자인 패턴이 여기 적용됐는가?

Unit 7.3 — update() / queryForObject() / query()

선수 지식: Unit 7.2

핵심 메서드

메서드용도반환
update(sql, params)INSERT / UPDATE / DELETE영향받은 행 수
queryForObject(sql, mapper, params)단일 행 조회객체 1개
query(sql, mapper, params)여러 행 조회List<객체>
execute(sql)임의 SQL (DDL 등)void

예시:

// UPDATE
jdbcTemplate.update(
    "UPDATE item SET price=? WHERE id=?",
    newPrice, itemId
);

// 단일 행
Item item = jdbcTemplate.queryForObject(
    "SELECT * FROM item WHERE id=?",
    itemRowMapper, id
);

// 여러 행
List<Item> items = jdbcTemplate.query(
    "SELECT * FROM item",
    itemRowMapper
);

// DDL
jdbcTemplate.execute(
    "CREATE TABLE mytable (id INT, name VARCHAR(100))"
);

자기 점검

  • queryForObject가 결과 0건이면 무슨 예외가? (힌트: EmptyResultDataAccessException)
  • queryForObject가 결과 2건 이상이면? (힌트: IncorrectResultSizeDataAccessException)

Unit 7.4 — RowMapper

선수 지식: Unit 7.3

핵심 개념

RowMapper: ResultSet의 한 행 → 자바 객체로 매핑

전통 JDBC:

while (rs.next()) {
    User user = new User();
    user.setId(rs.getInt(1));
    user.setName(rs.getString(2));
    userList.add(user);
}

RowMapper 사용:

RowMapper<Item> itemRowMapper = (rs, rowNum) -> {
    Item item = new Item();
    item.setId(rs.getLong("id"));
    item.setItemName(rs.getString("item_name"));
    return item;
};

// 사용
Item item = jdbcTemplate.queryForObject(sql, itemRowMapper, id);
List<Item> items = jdbcTemplate.query(sql, itemRowMapper);

람다와 잘 맞음 (함수형 인터페이스):

RowMapper<Item> mapper = (rs, n) -> new Item(...);

BeanPropertyRowMapper — 자동 매핑:

new BeanPropertyRowMapper<>(Item.class)
// SQL 컬럼명과 자바 필드명이 같으면 자동 매핑
// item_name → itemName 처럼 snake → camel 변환도 됨

자기 점검

  • RowMapper가 함수형 인터페이스인 게 어떤 이점을 주는가? (힌트: 3주차 람다)
  • BeanPropertyRowMapper가 안 통하는 케이스는?

Unit 7.5 — JdbcTemplate의 구조적 의미

선수 지식: Unit 7.4

핵심 통찰

JdbcTemplate은 5주차에서 배운 모든 원칙의 집약체:

원칙/패턴JdbcTemplate에서의 적용
관심사 분리SQL/매핑 ↔ 자원 관리 분리
템플릿 메소드 패턴변하지 않는 흐름이 템플릿 안
전략 패턴RowMapper, PreparedStatementSetter
OCP새 매핑 추가 시 기존 코드 변경 X
DIDataSource를 외부에서 주입받음
람다 활용 (3주차)RowMapper를 람다로

→ "Spring을 배우면 OOP의 좋은 사례가 보인다"

자기 점검

  • JPA/Hibernate가 등장한 이후 JdbcTemplate은 왜 여전히 유효한가? (힌트: 단순성, 성능)
  • MyBatis와 JdbcTemplate의 차이는?

🎓 종합 자기 점검 (6주차 졸업 시험)

Part A: 테스트와 웹 인프라

  1. JUnit이 매 테스트마다 새 인스턴스를 만드는 이유는?
  2. @BeforeEach@BeforeAll 의 차이는?
  3. 픽스처(Fixture)의 정의는?
  4. assertThat + 매처 방식이 assertEquals보다 갖는 장점은?
  5. 웹서버와 WAS의 역할 차이는?
  6. JSP가 결국 무엇으로 변환되는가?
  7. SSR과 CSR 중 SEO에 유리한 것은?
  8. JAR과 WAR의 차이 3가지는?
  9. Spring Boot가 JAR를 권장하는 이유는?

Part B: JDBC

  1. JDBC가 등장하기 전 어떤 고통이 있었는가?
  2. JDBC가 해결한 것과 해결하지 않은 것은?
  3. Connection Pool이 필요한 이유를 TCP 관점에서?
  4. Connection 풀의 Connection 100개 = DB의 무엇이 100개?

Part B: DataSource

  1. DataSource 인터페이스의 핵심 메서드는?
  2. DriverManagerDataSource가 존재하는 이유는?
  3. DataSource 도입이 5주차의 어떤 원칙(SOLID)의 구현인가?

Part B: 트랜잭션 ACID

  1. 트랜잭션의 정의는?
  2. ACID 4가지를 한 문장씩 설명하라
  3. 격리성이 깨지면 어떤 사고가 가능한가?
  4. 4주차의 자바 Atomic과 DB Atomicity의 차이는?
  5. Commit 이전 변경은 다른 세션에서 보이는가?

Part B: JdbcTemplate

  1. JdbcTemplate이 숨겨주는 4가지는?
  2. queryForObject가 결과 0건일 때의 예외는?
  3. RowMapper가 함수형 인터페이스인 게 주는 이점은?
  4. JdbcTemplate에 적용된 디자인 패턴 2가지는?

📌 학습 운영 팁

9-섹션 마스터 프롬프트로 깊이 파야 할 Unit

반드시 깊이 파기:

  • Unit 4.2 — Connection Pool (실무 직결)
  • Unit 5.3 — DataSource 인터페이스 (Spring DB의 출발점)
  • Unit 6.2 ~ 6.5 — ACID 4가지 (면접 단골)
  • Unit 6.6 — Commit 이전의 격리성 (멀티 세션 이해)
  • Unit 7.2 — JdbcTemplate의 구조 (디자인 패턴의 집약)

Phase별 진도 체크리스트

[ ] Phase 1 — JUnit 테스트 (Unit 1.1~1.5)
[ ] Phase 2 — 웹 인프라 기초 (Unit 2.1~2.4)
[ ] Phase 3 — JDBC 표준화 (Unit 3.1~3.3)
[ ] Phase 4 — Connection Pool과 DB 세션 (Unit 4.1~4.4)
[ ] Phase 5 — DataSource 인터페이스 (Unit 5.1~5.4)
[ ] Phase 6 — 트랜잭션 ACID (Unit 6.1~6.6)
[ ] Phase 7 — JdbcTemplate (Unit 7.1~7.5)
[ ] 종합 자기 점검 25문항 통과

5주차 → 6주차 연결 포인트

5주차에서 배운 디자인 원칙이 이번 주에 실제 라이브러리에서 어떻게 구현되었는지 추적해보세요:

5주차 개념6주차에서의 구현
관심사의 분리DataSource (커넥션 획득과 사용 분리)
템플릿 메소드 / 전략 패턴JdbcTemplate
OCPDataSource로 풀 구현 교체 가능
DI@Autowired DataSource

"좋은 라이브러리는 좋은 OOP 원칙의 집약체다" 가 6주차의 메시지.


1~6주차 통합 흐름

  • 1~3주차: 자바 언어 자체 (OOP·JVM·컬렉션·함수형)
  • 4주차: 멀티스레드와 동시성
  • 5주차: Spring IoC/DI 입문
  • 6주차 (지금): 테스트 + 웹 인프라 + DB 접근의 진화
profile
Software Developer

0개의 댓글