티스토리 뷰

JPA

SQL 직접 다루는 문제점

pyounani 2024. 4. 12. 17:01

자바로 개발하는 애플리케이션들은 대부분  관계형 데이터베이스를 이용해 데이터를 저장을 하게 된다.

데이터베이스에 있는 데이터를 관리하기 위해서는 SQL을 사용을 해야 하는데, 

이번 포스팅에서는 개발을 할 때 SQL를 직접 다룰 때의 문제점들을 이야기해보고자 합니다. 

 

1. 반복...반복....

자바 애플리케이션에서 JDBC API를 이용하여 데이터베이스와 상호 작용하려면  많은 반복적인 작업이 들어갑니다. 

간단하게 회원을 CRUD 하는 기능을 개발해 보며 살펴보도록 하겠습니다. 

 

우선, 회원 객체를 만들어줍니다.

public class Member {
    private Integer id;
    private String name;
}

 

회원 객체를 데이터베이스에 관리할 목적으로 회원용 DAO로 생성해 줍니다.

public class MemberDAO {
    public Member find(Integer id) {...}
}

 

자 이제 MemberDAO의 find() 메서드를 완성해서 회원을 조회하는 기능을 만들어보도록 하겠습니다. 

@Slf4j
public class MemberDAO {
    private final String url = "db 주소";
    private final String user = "db 계정 정보(아이디)";
    private final String password = "db 계정 정보(비밀번호)";
    
    public Member find(Integer id) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        final String sql = "SELECT ID, NAME FROM MEMBER M WHERE ID = ?";  // 회원 조회용 SQL
        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();  // JDBC API 이용해서 SQL 실행
            if (resultSet.next()) {  // 조회 결과를 Member 객체로 매핑
                Member member = new Member();
                member.setId(resultSet.getString("ID"));
                member.setName(resultSet.getString("NAME"));
                return member;
            }
        } catch (SQLException e) {
            log.error("failed to find member from database", e);
        } finally {
            try {
                if (resultSet != null) resultSet.close();
                if (preparedStatement != null) preparedStatement.close();
                if (connection != null) connection.close();
            } catch (SQLException e) {
                log.error("failed to close resources", e);
            }
        }
        return null;
    }
    
    private Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, user, password);
    }
}

 

여기까지가 조회 기능였습니다...!

이제 회원을 저장하는 기능도 만들어보도록 하겠습니다. 

@Slf4j
public class MemberDAO {
    private final String url = "db 주소";
    private final String user = "db 계정 정보(아이디)";
    private final String password = "db 계정 정보(비밀번호)";
    
    public Member find(String id) {
    	...
    }
    
    public void save(Member member) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        final String sql = "INSERT INTO MEMBER(ID, NAME) VALUES (?,?)"; // 회원 등록 SQL

        try {
           connection = getConnection();
           preparedStatement = connection.prepareStatement(sql);
           preparedStatement.setInt(1, member.getId());  // 회원 각체 값을 꺼내 등록 SQL 전달
           preparedStatement.setString(2, player.getName());
           preparedStatement.executeUpdate();  // JDBC API 사용해 SQL 실행
       } catch (SQLException e) {
           log.error("failed to find member from database", e);
       } finally {
           try {
                if (preparedStatement != null) preparedStatement.close();
                if (connection != null) connection.close();
            } catch (SQLException e) {
                log.error("failed to close resources", e);
            }
        }
    }
    
    private Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, user, password);
    }
}

 

이제 회원을 수정하고 삭제하는 API를 만들어줘야 하는데요, SQL을 작성하고 JDBC API를 사용하는 비슷한 작업을 반복해야 할 것입니다. 만약 데이터베이스 테이블이 100개라면 이러한 일을 100번은 더 반복해야 할 것입니다.

 

이렇듯 SQL를 직접 다뤄서 개발을 진행하게 되면 수많은 반복을 필요로 하는 것을 알 수 있습니다.

 

2. 의존성

위의 예제에서 회원 정보에 연락처도 추가하려고 합니다. 

먼저 회원 객체를 수정해 줍니다. 

public class Member {
    private Integer id;
    private String name;
    private String tel; // 추가
}

 

회원 조회하는 기능을 수정해 봅시다.

@Slf4j
public class MemberDAO {
    ...
    
    public Member find(Integer id) {
        ...
        final String sql = "SELECT ID, NAME, TEL FROM MEMBER M WHERE ID = ?";  // 수정
        try {
            connection = getConnection();
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();  
            if (resultSet.next()) {  
                Member member = new Member();
                member.setId(resultSet.getString("ID"));
                member.setName(resultSet.getString("NAME"));
                member.setTel(resultSet.getString("TEL"));  // 추가
                return member;
            }
        } ...
    }
}

 

 

고작 필드 하나가 추가가 되었을 뿐인데 DAO의 CRUD 코드와 SQL 대부분을 변경을 해야 합니다. 

이는 SQL에 의존적인 소스 코드가 되는 것이고, 수정이 이루어질 때마다 DAO, DTO를 일일이 확인해야 하는 불편함을 줍니다. 

 

3. JPA를 쓰면?

JPA를 사용하면 개발자가 직접 SQL을 작성하는 것이 아니라 JPA가 제공하는 API를 사용합니다. 

그러면 JPA가 개발자 대신해서 SQL을 생성해 데이터베이스에 전달을 합니다. 

 

위 예시를 JPA가 제공하는 API로 작성한 코드로 수정해 보자. 

// 저장
jpa.persist(member);

// 조회
Integer id = 1;
Member member = jpa.find(Member.class, id);

// 수정
Member member = jpa.find(Member.class, id);
member.setName("name");

 

지금까지 직접 SQL로 작성했을 때 문제들과 JPA가 제공하는 API를 간단하게 알아보았습니다. 

이러한 API는 이후 포스팅에서 알아보고 우선 객체와 관계형 데이터베이스의 패러다임 차이 때문에 발생하는 문제들을 살펴보도록 하겠습니다.

 

 

 

 

본 포스팅은 자바 ORM 표준 JPA 프로그래밍을 참고하여 작성했습니다. 

 

'JPA' 카테고리의 다른 글

기본 키 매핑(@Id, @GeneratedValue)  (1) 2024.04.23
JPA 데이터베이스 스키마 자동 생성  (0) 2024.04.22
JPA 매핑 어노테이션(@Entity, @Table)  (0) 2024.04.21
패러다임의 불일치  (0) 2024.04.16
JPA란?  (0) 2024.04.11