단순히 SQL을 실행하는 단계에서 더 나아가 객체를 데이터베이스에 저장하고 관리하는 Repository 패턴을 구현해 보았다. 이전까지는 SELECT 문을 실행하여 데이터를 조회하는 것에 집중했다면, 이번에는 Create, Read, Update, Delete(CRUD)를 하나의 Repository 클래스에 모아 관리하면서 객체지향적인 데이터 접근 방식을 경험하였다.
또한 record를 이용하여 데이터를 표현하고, DBUtil 클래스로 데이터베이스 연결을 공통화하면서 유지보수성과 재사용성을 높이는 방법도 함께 학습하였다.
| 학습 내용 | 상세 내용 |
|---|---|
| Record | Java에서 데이터를 저장하기 위한 불변(Immutable) 객체를 간단하게 생성할 수 있는 문법이다. |
| Immutable Object | 생성 이후 내부 데이터를 변경할 수 없는 객체이다. |
| DTO(Data Transfer Object) | 계층 간 데이터를 전달하기 위한 객체로 많이 사용된다. |
| 자동 생성 기능 | Getter, equals(), hashCode(), toString() 등이 자동으로 생성된다. |
| 코드 간결화 | 기존 클래스보다 훨씬 적은 코드로 동일한 기능을 구현할 수 있다. |
public record SideDish(
long sideDishId,
String sideDishName
) {
}
이번 실습에서는 Java의 Record를 이용하여 반찬 정보를 저장하는 객체를 생성하였다.
기존 클래스를 작성하려면 다음과 같은 코드가 필요하다.
하지만 Record는
public record SideDish(
long sideDishId,
String sideDishName
){}
한 줄만 작성하면 위 기능들이 자동으로 생성된다.
또한 Record는 생성 이후 값을 변경할 수 없기 때문에 데이터가 의도치 않게 수정되는 것을 방지할 수 있다.
특히 데이터베이스에서 조회한 데이터를 표현하는 객체는 대부분 값을 변경할 필요가 없기 때문에 Record가 매우 적합하다는 점을 알게 되었다.
| 기존 클래스 | Record |
|---|---|
| Getter 작성 필요 | 자동 생성 |
| 생성자 작성 | 자동 생성 |
| equals 작성 | 자동 생성 |
| hashCode 작성 | 자동 생성 |
| toString 작성 | 자동 생성 |
| 코드 김 | 매우 짧음 |
데이터를 저장하기 위한 객체는
따라서
"데이터만 표현하는 객체"
에는 Record가 가장 적합하다는 것을 배웠다.
| 느낀 점 | 내용 |
|---|---|
| 코드 간결성 | Record 하나만으로 수십 줄의 코드를 줄일 수 있다는 점이 인상적이었다. |
| 불변 객체 | 데이터를 안전하게 관리하는 방법을 이해하였다. |
| DTO 활용 | 앞으로 API 응답 객체를 만들 때 Record를 적극 활용할 수 있을 것 같다. |
| 학습 내용 | 상세 내용 |
|---|---|
| Utility Class | 공통 기능을 제공하는 클래스이다. |
| static 메서드 | 객체 생성 없이 사용할 수 있는 메서드이다. |
| 환경 변수 | URL, USER, PASSWORD를 안전하게 관리한다. |
| DriverManager | Connection 객체를 생성한다. |
| 코드 재사용 | 여러 클래스에서 동일한 Connection 생성 코드를 공유한다. |
public class DBConnectionUtil {
private DBConnectionUtil(){}
public static Connection openConnection()
throws SQLException{
Class.forName("org.postgresql.Driver");
return DriverManager.getConnection(
URL,
USER,
PASSWORD
);
}
}
DB 연결은 모든 Repository에서 공통적으로 수행된다.
만약 DB 연결 코드를
마다 작성한다면
동일한 코드가 수십 번 반복된다.
그래서
DBUtil
클래스를 만들어
Connection 생성 기능을 하나로 모아두었다.
이후에는
Connection conn = DBUtil.getConnection();
한 줄만 작성하면 된다.
이러한 구조는
유지보수성과 재사용성을 크게 높여준다.
| 장점 | 설명 |
|---|---|
| 코드 중복 제거 | Connection 생성 코드를 한 번만 작성 |
| 유지보수 | URL 변경 시 한 곳만 수정 |
| 보안 | 환경 변수 사용 |
| 재사용 | 모든 Repository에서 사용 가능 |
실습에서는
System.getenv()
를 사용하였다.
그 이유는
DB 비밀번호를
String PASSWORD="1234";
처럼 코드에 작성하면
GitHub에 업로드될 위험이 있기 때문이다.
환경 변수는 운영체제에 저장되므로
소스 코드가 공개되어도 비밀번호는 노출되지 않는다.
실무에서는
를 이용하여 Connection을 관리하지만
결국 내부적으로는
DriverManager를 이용한 Connection 생성 과정을 거친다.
이번 실습은 그 원리를 직접 구현해본 경험이었다.
| 느낀 점 | 내용 |
|---|---|
| 코드 재사용 | 공통 기능을 Utility 클래스로 만드는 이유를 이해하였다. |
| 보안 | 환경 변수 관리의 중요성을 알게 되었다. |
| Spring 이해 | Spring이 내부적으로 하는 일을 직접 구현해보니 프레임워크의 역할이 더욱 명확하게 느껴졌다. |
| 학습 내용 | 상세 내용 |
|---|---|
| Repository Pattern | 데이터 접근을 담당하는 클래스를 별도로 분리하는 설계 방식이다. |
| CRUD | Create, Read, Update, Delete 기능을 하나의 Repository에서 관리한다. |
| PreparedStatement | 모든 SQL 실행에 사용하여 보안성을 높였다. |
| Transaction | 여러 작업을 하나의 작업 단위로 처리하였다. |
| ResultSet | 조회 결과를 Record 객체로 변환하였다. |
Repository는
데이터베이스와 직접 통신하는 역할만 담당하는 클래스
이다.
즉,
Controller가 SQL을 직접 작성하지 않는다.
Service도 SQL을 작성하지 않는다.
오직 Repository만 SQL을 작성한다.
Controller
│
▼
Service
│
▼
Repository
│
▼
Database
이 구조를 사용하면
각 클래스의 역할이 명확하게 분리된다.
이번 Repository에서는
| 메서드 | 기능 |
|---|---|
| save() | 데이터 저장(Create) |
| findById() | 단건 조회(Read) |
| findAll() | 전체 조회(Read) |
| update() | 수정(Update) |
| deleteById() | 삭제(Delete) |
를 모두 구현하였다.
객체 생성
↓
Connection 생성
↓
PreparedStatement 생성
↓
executeUpdate()
↓
COMMIT
데이터 저장은
SELECT가 아니라
INSERT 문을 실행하므로
executeUpdate()를 사용하였다.
수정은
ID를 기준으로 수행하였다.
UPDATE banchan
SET name=?
WHERE id=?
ID가 동일한 데이터를 찾아
새로운 이름으로 변경하였다.
이 과정에서
Primary Key의 중요성을 이해할 수 있었다.
삭제 역시
ID를 이용하였다.
삭제 성공 여부는
executeUpdate()
가 반환하는
영향 받은 행(Row)의 개수로 확인하였다.
0이면
삭제 실패
1이면
삭제 성공이다.
이번 실습에서는
START TRANSACTION
↓
SQL 실행
↓
COMMIT
순서로 작성하였다.
트랜잭션은
여러 작업을 하나의 작업처럼 처리하기 위한 기능이다.
만약 중간에 오류가 발생하면
ROLLBACK을 수행하여
원래 상태로 되돌릴 수 있다.
사용자 요청
↓
Repository 호출
↓
Connection 생성
↓
PreparedStatement 생성
↓
SQL 실행
↓
ResultSet 또는 실행 결과 반환
↓
Connection 종료
이 흐름이
JDBC CRUD의 핵심이라는 것을 이해하였다.
| 느낀 점 | 내용 |
|---|---|
| 객체지향 설계 | SQL을 Repository로 분리하는 이유를 이해하였다. |
| CRUD 구현 | 데이터 저장부터 삭제까지 하나의 클래스로 관리하는 구조를 익혔다. |
| 트랜잭션 | 여러 작업을 하나로 묶는 이유를 이해하였다. |
| 실무 연결 | Spring Data JPA의 JpaRepository가 이번에 구현한 Repository와 같은 역할이라는 점을 알게 되었다. |