6주차 자료의 모든 토픽을 두 개의 큰 흐름으로 정리한 학습 경로.
1) 학습 도구와 환경 — JUnit 테스트, 웹 인프라(서버/WAS/JAR/WAR)
2) DB 접근의 진화 — JDBC → Connection Pool → DataSource → 트랜잭션 → JdbcTemplate5주차에서 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주차 | OOP·JVM·GC·컬렉션·I/O 개론 | 자바 큰 그림 |
| 2주차 | JVM 내부·바이트코드·G1 GC | "어떻게 돌아가나" |
| 3주차 | 컬렉션·제네릭·함수형 | 자바 표현력 |
| 4주차 | 멀티스레딩·동시성·Executor | 동시성 정복 |
| 5주차 | Atomic + Spring IoC/DI 입문 | 자바 → Spring 다리 |
| 6주차 (지금) | 테스트 + 웹 인프라 + DB 접근의 진화 | Spring 실전 환경 |
| Day | Phase | 학습 목표 |
|---|---|---|
| 1일차 | Phase 1 | JUnit 테스트 마스터 |
| 2일차 | Phase 2 | 웹 인프라 기초 + JAR/WAR |
| 3일차 | Phase 3 | JDBC의 본질 이해 |
| 4일차 | Phase 4 | Connection Pool + DB 세션 |
| 5일차 | Phase 5 | DataSource 추상화 |
| 6일차 | Phase 6 | 트랜잭션 ACID 완전 정복 |
| 7일차 | Phase 7 | JdbcTemplate 실전 |
목표: 5주차에서 만든 IoC/DI 코드를 테스트 코드로 검증하는 방법을 익힌다. 테스트 가능한 코드 = 좋은 코드 라는 명제를 체화한다.
선수 지식: 5주차 Phase 8 (DI)
핵심 개념
왜 테스트인가:
좋은 단위 테스트의 조건:
자기 점검
선수 지식: 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)assertEquals와 Hamcrest 중 무엇을 쓸까?원본 자료: 토비의 스프링 vol.1, p.159
선수 지식: Unit 1.2
핵심 개념
JUnit이 한 테스트 클래스를 처리하는 7단계:
1. @Test가 붙은 public void 파라미터 없는 메서드를 모두 찾음
2. 테스트 클래스의 오브젝트를 하나 만듦
3. @BeforeEach(또는 @Before) 메서드 실행
4. @Test 메서드 하나 호출, 결과 저장
5. @AfterEach 메서드 실행
6. 나머지 테스트 메서드에 대해 2~5 반복
7. 모든 결과 종합 반환
핵심 포인트: 각 테스트마다 새 인스턴스 생성
자기 점검
@BeforeAll 과 @BeforeEach 의 차이는?원본 자료: 토비의 스프링 vol.1, p.181
선수 지식: Unit 1.3
핵심 개념
"각 테스트가 서로 영향을 주지 않고 독립적으로 실행됨을 확실히 보장하기 위해"
얻는 이점:
자기 점검
@BeforeAll)원본 자료: 토비의 스프링 vol.1, p.182
선수 지식: 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(...); // 픽스처 사용
}
}
테스트 결과의 일관성:
dao.deleteAll() 같이 시작 상태 보장자기 점검
@Transactional, @Sql)목표: Spring MVC 학습 전, 웹 백엔드의 기본 인프라 용어를 정리한다. 모르면 면접·실무 모두에서 헤맨다.
선수 지식: 1주차 Phase 7 (I/O)
핵심 개념
| 구분 | 웹서버 | WAS |
|---|---|---|
| 역할 | 정적 자원 (HTML, CSS, 이미지) 제공 | 동적 자원 (서블릿, JSP) 처리 |
| 예시 | Apache, Nginx | Tomcat, Jetty, Undertow |
| 처리 | 파일 그대로 응답 | 코드 실행 → 결과 응답 |
실무 구성:
[클라이언트] ↔ [Nginx (웹서버)] ↔ [Tomcat (WAS)] ↔ [DB]
정적 파일 처리 동적 요청만 위임
자기 점검
선수 지식: Unit 2.1
핵심 개념
서블릿(Servlet):
JSP (Java Server Pages):
역할 분담의 진화:
자기 점검
선수 지식: 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 |
자기 점검
선수 지식: Unit 2.2
핵심 개념
| JAR | WAR | |
|---|---|---|
| 정식 명칭 | Java Archive | Web Application Archive |
| 포함 | Class, 라이브러리 | + JSP, Servlet, WEB-INF, META-INF |
| 실행 환경 | JRE만 있으면 OK | 외부 WAS 필요 |
| 실행 | java -jar app.jar | Tomcat 등에 배포 |
| 구조 | 자유 | 사전 정의된 디렉터리 구조 |
Spring Boot의 선택: JAR
WAR가 필요한 경우:
자기 점검
BOOT-INF/lib)이 파트의 큰 그림:
"DB 코드 한 줄이 어떻게 JdbcTemplate까지 진화하는지" — 5주차의 DAO 진화에 이어, 이번엔 DB 접근 자체의 추상화 여정 을 따라간다.
목표: JDBC가 왜 등장했는지, 어떤 문제를 해결했고 어떤 문제는 안 해결했는지 명확히 한다.
선수 지식: Phase 2
핵심 개념
JDBC 등장 전:
예시 (JDBC 없을 때):
// 직접 소켓으로 MySQL 프로토콜과 통신
Socket socket = new Socket("localhost", 3306);
DataOutputStream out = new DataOutputStream(...);
out.writeUTF("Handshake request to MySQL server");
// MySQL의 바이너리 프로토콜을 직접 알아야 함
→ 개발자가 DB 프로토콜까지 알아야 하는 지옥
자기 점검
선수 지식: 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가 해결한 것:
자기 점검
선수 지식: Unit 3.2
핵심 한계
JDBC는 API는 통일 했지만, SQL 문법 차이는 그대로:
해결: 상위 프레임워크 사용
자기 점검
목표: "왜 매번 새로 연결하면 안 되는가" 라는 근본 질문에 TCP/IP 레벨로 답할 수 있게 된다.
선수 지식: Phase 3
핵심 개념
DB 연결의 비용:
1. TCP/IP 3-way handshake (네트워크 왕복)
2. DB 인증 (ID/PW 검증)
3. DB가 세션 생성
→ 매 요청마다 이걸 다 하면?
자기 점검
선수 지식: Unit 4.1
핵심 개념
"Connection의 수영장"
동작 방식:
1. 애플리케이션 시작 시 N개의 Connection 미리 생성
2. 요청 시: 풀에서 빌려옴
3. 사용 후: 풀에 반환
4. 풀의 Connection은 재사용
[요청1] ──> [Pool에서 Conn1 빌림] ──> [DB 작업] ──> [Conn1 반환]
[요청2] ──> [Pool에서 Conn2 빌림] ──> [DB 작업] ──> [Conn2 반환]
...
대표 구현체:
자기 점검
선수 지식: Unit 4.2
핵심 개념
연결 절차:
1. 클라이언트 → DB 서버에 연결 요청 (TCP 연결)
2. DB가 세션 생성 (트랜잭션 단위)
3. 클라이언트가 SQL 전달 → 세션이 실행
4. 세션이 트랜잭션 시작 → commit/rollback으로 종료
5. 사용자가 커넥션 닫거나 DB 관리자가 세션 종료
중요한 매핑:
자기 점검
선수 지식: Unit 4.3
핵심 개념
왜 락이 필요한가:
해결: 트랜잭션 진행 중에는 다른 세션이 해당 데이터를 수정 못 하도록 락
락의 종류 (간략):
자기 점검
목표: 5주차의 인터페이스 추상화 정신이 자바 표준 라이브러리에 어떻게 녹아있는지 본다.
선수 지식: 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(); // ← 사용법이 다름!
→ 커넥션 획득 방법이 바뀌면 애플리케이션 코드가 다 바뀐다
자기 점검
선수 지식: Unit 5.1
핵심 통찰
5주차에서 본 OCP 정신과 같은 문제:
해결의 방향:
"커넥션을 얻는 방법" 자체를 인터페이스로 분리
자기 점검
선수 지식: 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 변경 시: 설정 파일만 변경, 애플리케이션 코드는 그대로.
자기 점검
선수 지식: Unit 5.3
핵심 개념
문제: DriverManager는 DataSource를 구현하지 않음
Spring의 해결: DriverManagerDataSource
용도:
프로덕션에서는 절대 쓰지 말 것 (풀이 없어서 성능 폭망)
자기 점검
목표: DB 트랜잭션의 4가지 핵심 성질을 실생활 예제로 체화하고, 멀티 세션 환경에서의 동작을 본다.
선수 지식: Phase 4
핵심 개념
"한 단위의 작업 으로 취급되는 모든 작업"
계좌 이체 예시:
트랜잭션의 끝:
자기 점검
선수 지식: Unit 6.1
핵심 개념
"트랜잭션의 모든 연산이 모두 성공 하거나 모두 실패 해야 한다"
계좌 이체 시나리오:
4주차의 자바 Atomic과 다른 점:
자기 점검
선수 지식: Unit 6.2
핵심 개념
"트랜잭션 완료 후 DB 제약조건이 항상 지켜져야 한다"
예시:
제약조건의 종류:
자기 점검
선수 지식: Unit 6.3
핵심 개념
"각 트랜잭션은 다른 트랜잭션과 독립적 으로 실행되어야 한다"
예시:
격리 수준 (간략):
자기 점검
선수 지식: Unit 6.4
핵심 개념
"Commit된 트랜잭션 은 DB에 영구 보존 되어야 한다"
보장 수단:
시스템이 다운되어도 커밋된 데이터는 살아있어야 한다.
자기 점검
선수 지식: Unit 6.5
핵심 시나리오
세션1: 데이터 변경 (commit X)
세션2: 같은 데이터 조회
결과:
비유:
자기 점검
목표: JDBC 코드의 끔찍한 반복을 Spring이 어떻게 해결했는지 본다.
선수 지식: 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 (...) { ... }
}
문제:
5주차에서 본 패턴:
자기 점검
선수 지식: 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));
}
숨겨진 것:
개발자는 변하는 부분만:
자기 점검
선수 지식: 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))"
);
자기 점검
선수 지식: 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주차 람다)선수 지식: Unit 7.4
핵심 통찰
JdbcTemplate은 5주차에서 배운 모든 원칙의 집약체:
| 원칙/패턴 | JdbcTemplate에서의 적용 |
|---|---|
| 관심사 분리 | SQL/매핑 ↔ 자원 관리 분리 |
| 템플릿 메소드 패턴 | 변하지 않는 흐름이 템플릿 안 |
| 전략 패턴 | RowMapper, PreparedStatementSetter |
| OCP | 새 매핑 추가 시 기존 코드 변경 X |
| DI | DataSource를 외부에서 주입받음 |
| 람다 활용 (3주차) | RowMapper를 람다로 |
→ "Spring을 배우면 OOP의 좋은 사례가 보인다"
자기 점검
@BeforeEach 와 @BeforeAll 의 차이는?반드시 깊이 파기:
[ ] 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주차에서 배운 디자인 원칙이 이번 주에 실제 라이브러리에서 어떻게 구현되었는지 추적해보세요:
| 5주차 개념 | 6주차에서의 구현 |
|---|---|
| 관심사의 분리 | DataSource (커넥션 획득과 사용 분리) |
| 템플릿 메소드 / 전략 패턴 | JdbcTemplate |
| OCP | DataSource로 풀 구현 교체 가능 |
| DI | @Autowired DataSource |
→ "좋은 라이브러리는 좋은 OOP 원칙의 집약체다" 가 6주차의 메시지.