Spring DB : JdbcTemplate을 이용한 반복 문제 해결

    이 글은 인프런 김영한님의 강의를 복습하며 작성한 글입니다. 

    JDBC 기술은 반복을 부른다. 

    앞서 여러 추상화 과정들을 통해 코드를 개선해나가는 작업을 해왔다. 그렇지만 아직까지 개선의 여지가 있는 부분이 남아있다. 아래 코드를 한번 살펴보자.

    @Override
    public Member findById(String memberId){
        String sql = "select * from member where member_id = ?";
    
        PreparedStatement pstmt = null;
        ResultSet rs = null;
    
        try {
    
            Connection conn = getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, memberId);
    
            rs = pstmt.executeQuery();
            log.info("Connection = {}, class = {}", conn,conn.getClass());
    
            if (rs.next()) {
                Member member = new Member();
                member.setMemberId(rs.getString("member_id"));
                member.setMoney(rs.getInt("money"));
                return member;
            }else{
                throw new NoSuchElementException("member not found memberId = " + memberId);
            }
        } catch (SQLException e) {
            log.error("error", e);
            throw exTranslator.translate("find", sql, e);
        }finally {
            JdbcUtils.closeResultSet(rs);
            JdbcUtils.closeStatement(pstmt);
        }
    }

    위 코드에서 살펴보면, 계속 반복되는 부분이 어떤 것인지 대충 감을 잡을 수 있다. 

    • 커넥션 조회, 커넥션 동기화
    • PreparedStatement 생성 및 파라미터 바인딩
    • 쿼리 실행
    • ResultSet 바인딩 
    • 예외 발생 시, 스플이 예외 변환기 실행
    • 리소스 종료

    위에서 발생하는 반복들은 대부분 JDBC 기술을 사용하기 때문에 발생하는 것이다. 이런 반복적인 부분들을 해결할 수 있다면, 코드가 아주 아름답게 리팩토링 될 것이다. 그렇다면 어떤 방식으로 접근할 수 있을까? 

    먼저 위 구문들은 실제 비즈니스 로직 사이에 적용되어있다. 따라서 이런 것들은 따로 메서드로 빼서 처리를 할 수 없다. 이런 경우에는 템플릿 콜백 패턴을 이용해서 처리할 수 있다. 다행히 이런 문제를 해결하기 위한 템플릿 콜백 패턴이 구현되어있다. 바로 JdbcTemplate이다. 위에서 발생한 문제들은 JdbcTemplate을 이용하면 깔끔하게 처리할 수 있다. 

     

    JdbcTemplate을 이용한 코드 리팩토링

    Jdbc이를 이용하면 다음과 같이 불필요한 코드의 반복을 제거해준다. 

    @Slf4j
    public class MemberRepositoryV5 implements MemberRepository{
    
        private final JdbcTemplate jdbcTemplate;
    
        public MemberRepositoryV5(DataSource dataSource) {
            this.jdbcTemplate = new JdbcTemplate(dataSource);
        }
    
        @Override
        public Member save(Member member) {
            String sql = "insert into member(member_id, money) values(?,?)";
            jdbcTemplate.update(sql,member.getMemberId(), member.getMoney());
            return member;
        }
    
        @Override
        public Member findById(String memberId){
            String sql = "select * from member where member_id = ?";
            return jdbcTemplate.queryForObject(sql, memberRowMapper(), memberId);
        }
    
        @Override
        public void update(String memberId, int money){
            String sql = "update member set money = ? where member_id = ?";
            jdbcTemplate.update(sql, memberId, money);
        }
    
        @Override
        public void delete(String memberId){
            String sql = "delete from member where member_id=?";
            jdbcTemplate.update(sql, memberId);
        }
    
        private RowMapper<Member> memberRowMapper() {
            return (rs, rowNum) -> {
                Member member = new Member();
                member.setMemberId(rs.getString("member_id"));
                member.setMoney(rs.getInt("money"));
                return member;
            };
        }
    
    }

     

    정리

    • JdbcTemplate은 트랜잭션을 위한 커넥션 동기화 / 예외 발생 시 스프링 예외 변환기도 자동으로 실행해준다. 
    • 서비스 계층의 순수성
      • 트랜잭션 추상화 + 트랜잭션 AOP 덕분에 서비스 계층의 순수성을 최대한 유지하면서 서비스 계층에서 트랜잭션을 사용할 수 있다.
      • 스프링에 제공하는 예외 추상화 + 예외 변환기 덕분에, DB 접근 기술이 변경되어도 서비스 계층의 순수성을 유지하면서 예외도 사용할 수 있다.
      • 서비스 계층이 리포지토리 인터페이스에 의존한 덕분에 향후 리포지토리가 다른 구현 기술로 변경되어도 서비스 계층을 순수하게 유지할 수 있다.
    • 리포지토리에서 JDBC를 사용하는 반복 코드가 JdbcTemplate에 의해 제거되었다.

    댓글

    Designed by JB FACTORY