SQL을 직접 다룰 때 발생하는 문제점
관계형 데이터베이스는 가장 대중적이고 신뢰할 만한 안전한 데이터 저장소이다. 자바로 개발하는 애플리케이션은 대부분 관계형 데이터베이스를 데이터 저장소로 사용한다.
데이터베이스에 데이터를 관리하려면 SQL을 사용해야 한다. 자바로 작성한 애플리케이션은 JDBC API를 사용해서 SQL을 데이터베이스에 전달하는데, 자바 서버 개발자들에게는 이것은 너무나 당연한 이야기이다.
반복, 반복 그리고 반복
SQL을 직접 다룰 때의 문제점을 알아보기 위해 자바와 관계형 데이터베이스를 사용해서 회원 관리 기능을 개발해본다. 회원 테이블은 이미 만들어져 있다고 가정하고 회원을 CRUD(등록, 수정, 삭제, 조회)하는 기능을 개발한다.
public class Memeber {
private String memberId;
private String name;
}
Java
복사
다음에는 회원 객체를 데이터베이스에 관리할 목적으로 회원용 DAO(데이터 접근 객체를) 만든다.
public class MemberDAO {
public Member find(String memberId) {...}
}
Java
복사
이제 MemberDAO의 find() 메소드를 완성해서 회원을 조회하는 기능을 개발한다.
보통 다음 순서로 개발을 진행할 것이다.
1.
회원 조회용 SQL을 작성한다.
SELECT MEMBER_ID, NAME FROM MEMBER M WHERE MEMBER_ID = ?
Java
복사
2.
JDBC API를 사용해서 SQL을 실행한다.
ResultSet rs = stmt.executeQuery(sql);
Java
복사
3.
조회 결과를 Member 객체로 매핑한다.
String memberID = rs.getString("MEMBER_ID");
String name = rs.getString("NAME");
Member member = new Member();
member.setMemberId(memberId);
member.setName(name);
...
Java
복사
회원 조회 기능을 완성했다면 회원 등록 기능을 만든다.
public class MemberDAO {
public Member find(String memberId){...}
public void save(Member member){...} // 추가
}
Java
복사
1.
회원 등록용 SQL을 작성한다.
String sql = "INSERT INTO MEMBER(MEMBER_ID, NAME) VALUES(?, ?)";
Java
복사
2.
회원 객체의 값을꺼내서 등록 SQL에 전달한다.
pstmt.setString(1, member.getMemberId());
pstmt.setString(2, member.getName());
Java
복사
3.
JDBC API를 사용해서 SQL을 실행한다.
pstmt.executeUpdate(sql);
Java
복사
회원을 조회하는 기능과 등록하는 기능을 만들었다. 다음으로 회원을 수정하고 삭제하는 기능도 추가해본다. 아마도 SQL을 작성하고 JDBC API를 사용하는 비슷한 일을 반복해야 할 것이다.
회원 객체를 데이터베이스가 아닌 자바 컬렉션에 보관하면 어떨까? 컬렉션은 다음 한 줄로 객체를 저장할 수 있다.
list.add(member);
Java
복사
하지만 데이터베이스는 객체 구조와는 다른 데이터 중심의 구조를 가지므로 객체를 데이터베이스에 직접 저장하거나 조회할 수는 없다. 따라서 개발자가 객체지향 애플리케이션과 데이터베이스 중간에서 SQL과 JDBC API를 사용해서 변환 작업을 직접 해주어야 한다.
문제는 객체를 데이터베이스에 CRUD 하려면 너무 많은 SQL과 JDBC API를 코드로 작성해야 한다는 점이다. 그리고 테이블마다 이런 비슷한 일을 반복해야 하는데, 개발하려는 애플리케이션에서 사용하는 데이터베이스 테이블이 100개라면 무수히 많은 SQL을 작성해야 하고 이런 비슷한 일은 100번은 더 반복해야 한다. 데이터 접근 계층(DAO)을 개발하는 일은 이처럼 지루함과 반복의 연속이다.
SQL에 의존적인 개발
앞에서 만든 회원 객체를 관리하는 MemberDAO를 완성하고 애플리케이션의 나머지 기능도 개발을 완료했다. 그런데 갑자기 회원의 연락처도 함께 저장해달라는 요구사항이 추가되었다.
등록 코드 변경
조회 코드 변경
수정 코드 변경
연관된 객체
SQL과 문제점을 정리해보면 다음과 같다. Member 객체가 연관된 Team 객체를 사용할 수 있는지의 여부는 전적으로 사용하는 SQL에 달려 있다. 이런 방식의 가장 큰 문제는 데이터 접근 계층(DAO)를 사용해서 SQL을 숨겨도 어쩔 수 없이 DAO를 열어서 어떤 SQL이 실행되는지 확인해야 한다는 점이다.
Member나 Team처럼 비즈니스 요구사항을 모델링한 객체를 엔티티라 하는데, 지금처럼 SQL에 모든 것을 의존하는 상황에서는 개발자들이 엔티티를 신뢰하고 사용할 수 없다. 대신 DAO를 열어서 어떤 SQL이 실행되고 어떤 객체들이 함께 조회되는지 일일이 확인해야 한다. 이것은 진정한 의미의 계층 분할이 아니다. 물리적으로 SQL과 JDBC API를 데이터 접근 계층에 숨기는 데 성공했을지는 모르지만 논리적으로는 엔티티와 아주 강한 의존관계를 가지고 있다. 이런 강한 의존관계 때문에 회원을 조회할 때는 물론이고 회원 객체에 필드를 하나 추가할 때도 DAO의 CRUD 코드와 SQL 대부분을 변경해야 하는 문제가 발생한다.
애플리케이션에서 SQL을 직접 다룰 때 발생하는 요약점을 요약하면 다음과 같다.
•
진정한 의미의 계층 분할이 어렵다.
•
엔티티를 신뢰할 수 없다.
•
SQL에 의존적인 개발을 피하기 어렵다.
JPA와 문제 해결
JPA를 사용하면 객체를 데이터베이스에 저장하고 관리할 때, 개발자가 직접 SQL을 작성하는 것이 아니라 JPA가 제공하는 API를 사용하면 된다. 그러면 JPA가 개발자 대신에 적절한 SQL을 생성해서 데이터베이스에 전달한다.
저장 기능
조회 기능
수정 기능
연관된 객체 조회