INSERT 쿼리, 이제 그만 귀찮게! SimpleJdbcInsert로 깔끔하게 해결하기

Jayson·2025년 6월 16일
0
post-thumbnail

INSERT 쿼리, 매번 똑같이 작성하기 번거롭지 않으신가요? 🥹 SimpleJdbcInsert가 도와드립니다!

개발자 여러분, 데이터베이스 작업 중 INSERT 쿼리를 작성하는 일이 반복되어 지루하게 느껴지실 때가 많으실 겁니다. 특히 자바 애플리케이션에서 JDBC나 JdbcTemplate을 사용하여 데이터를 삽입할 때마다, "조금 더 편리한 방법은 없을까?"라는 고민을 해보셨을 텐데요. 오늘은 이러한 번거로움을 크게 줄여줄 Spring Framework의 SimpleJdbcInsert에 대해 소개해 드리고자 합니다!


😫 왜 INSERT 쿼리는 늘 번거롭게 느껴질까요? (Plain JDBC & JdbcTemplate의 고충)

일반적인 JDBC나 JdbcTemplate을 활용한 INSERT 작업에는 다음과 같은 불편함이 존재합니다.

  1. 반복적인 보일러플레이트 코드: Connection을 얻고, PreparedStatement를 생성하며, 파라미터를 수동으로 바인딩하고, 쿼리를 실행한 뒤, ResultSet을 처리하고, 마지막으로 Connection, Statement, ResultSet과 같은 자원을 안전하게 닫는 일련의 과정이 모든 INSERT 작업마다 반복됩니다. JdbcTemplate이 이러한 반복을 상당 부분 추상화해주지만, 여전히 파라미터 바인딩이나 자동 생성 키(Auto-generated keys) 처리에는 직접적인 코드 작성이 필요합니다.
  2. 수동적인 파라미터 바인딩: INSERT INTO users (name, email) VALUES (?, ?)와 같이 SQL 쿼리 내의 ? 플레이스홀더에 대해 preparedStatement.setString(1, name); preparedStatement.setString(2, email);처럼 각 파라미터를 인덱스에 맞춰 수동으로 바인딩해야 합니다. 컬럼의 수가 많아질수록 실수할 확률이 높아지고 코드의 가독성이 떨어지는 경향이 있습니다.
  3. 자동 생성 키 처리의 복잡성: 대부분의 테이블에서 기본 키(Primary Key)는 자동으로 생성됩니다. 이 생성된 키 값을 다시 가져와야 할 때, 순수 JDBC에서는 Statement.RETURN_GENERATED_KEYS 옵션을 사용하고 getGeneratedKeys()를 통해 ResultSet을 처리하는 등 추가적인 코드가 필요합니다. JdbcTemplate 역시 KeyHolder를 사용해야 하므로 다소 번거로울 수 있습니다.

이러한 문제점들은 개발 생산성을 저하시키고 잠재적인 오류를 유발할 수 있습니다. 😥


SimpleJdbcInsert, 무엇이 특별할까요?

SimpleJdbcInsert는 Spring Framework의 org.springframework.jdbc.core.simple 패키지에 속하는 유틸리티 클래스입니다. 이름에서 알 수 있듯이 JDBC INSERT 작업단순화하는 데 중점을 둡니다.

주요 특징은 다음과 같습니다:

  • 데이터베이스 메타데이터 활용: SimpleJdbcInsert는 테이블의 컬럼 정보(이름, 타입 등)를 데이터베이스 메타데이터에서 자동으로 읽어와 SQL INSERT 문을 동적으로 생성합니다. 덕분에 개발자가 직접 SQL 쿼리를 작성해야 하는 부담이 줄어듭니다.
  • 간편한 파라미터 바인딩: Map 객체나 SqlParameterSource 인터페이스(BeanPropertySqlParameterSource, MapSqlParameterSource 등)를 사용하여 컬럼 이름과 값을 쉽게 매핑할 수 있어, 파라미터 바인딩이 매우 직관적입니다.
  • 자동 생성 키 처리: 데이터 삽입 후 자동으로 생성된 기본 키 값을 매우 편리하게 가져올 수 있는 기능을 제공합니다.

결론적으로, SimpleJdbcInsert는 내부적으로 JdbcTemplate을 사용하면서도, INSERT 작업에 특화된 기능을 제공하여 코드를 훨씬 간결하고 안전하게 만들어주는 유용한 도구입니다. 👍


SimpleJdbcInsert 사용 방법 (자바 코드 예제)

SimpleJdbcInsert를 사용하시려면 먼저 JdbcTemplate이 필요합니다. 일반적으로 Spring 설정에서 DataSource를 주입받아 JdbcTemplate 빈(Bean)을 생성하고, 이를 다시 SimpleJdbcInsert에 주입하는 방식으로 사용합니다.

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

public class UserRepository {

    private final JdbcTemplate jdbcTemplate;
    private final SimpleJdbcInsert simpleJdbcInsert;

    // 생성자를 통해 DataSource를 주입받아 SimpleJdbcInsert를 초기화하는 것이 일반적입니다.
    public UserRepository(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        this.simpleJdbcInsert = new SimpleJdbcInsert(dataSource)
                                    .withTableName("users"); // ✨ 대상 테이블을 반드시 지정해야 합니다.
    }

    // User 엔티티 정의 (예시)
    public static class User {
        private Long id;
        private String name;
        private String email;

        public User() {}
        public User(String name, String email) {
            this.name = name;
            this.email = email;
        }

        // Getter와 Setter는 생략했습니다.
        public Long getId() { return id; }
        public void setId(Long id) { this.id = id; }
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public String getEmail() { return email; }
        public void setEmail(String email) { this.email = email; }
    }

    ---

1. 기본 INSERT (Map 사용)

가장 기본적인 사용법으로, Map을 사용하여 삽입할 데이터를 전달합니다. SimpleJdbcInsert는 테이블 메타데이터를 기반으로 INSERT 쿼리를 자동으로 생성합니다.

    public int insertUser(String name, String email) {
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("name", name);
        parameters.put("email", email); // 💡 Map의 키는 데이터베이스 컬럼명과 일치해야 합니다.

        // execute() 메서드는 영향받은 행의 수를 반환합니다.
        return simpleJdbcInsert.execute(parameters);
    }

2. 자동 생성 키(Auto-Generated Key) 가져오기

테이블의 기본 키가 자동으로 생성되는 경우, withTableName() 다음에 usingGeneratedKeyColumns()를 추가하여 자동 생성 키 컬럼을 지정할 수 있습니다. 이후 executeAndReturnKey() 또는 executeAndReturnKeyHolder() 메서드를 사용합니다.

    public Long insertUserAndGetKey(String name, String email) {
        // ✨ SimpleJdbcInsert를 초기화할 때 자동 생성 키 컬럼을 지정합니다.
        // 예를 들어, 'users' 테이블의 'id' 컬럼이 자동 생성 키라면:
        // this.simpleJdbcInsert = new SimpleJdbcInsert(dataSource)
        //                             .withTableName("users")
        //                             .usingGeneratedKeyColumns("id"); // 'id' 컬럼 지정

        Map<String, Object> parameters = new HashMap<>();
        parameters.put("name", name);
        parameters.put("email", email);

        // executeAndReturnKey()는 주로 단일 숫자형 키를 반환합니다.
        Number newId = simpleJdbcInsert.executeAndReturnKey(parameters);
        return newId.longValue();
    }

    // 또는 KeyHolder를 사용하여 다양한 타입의 키나 여러 개의 키를 처리할 수도 있습니다.
    // 만약 키가 여러 개이거나 UUID와 같이 숫자가 아닌 경우에 유용합니다.
    public User insertUserWithKeyHolder(User user) {
        // simpleJdbcInsert 초기화 시 "id" 컬럼 지정이 필수입니다.
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("name", user.getName());
        parameters.put("email", user.getEmail());

        KeyHolder keyHolder = new GeneratedKeyHolder(); // 키 값을 담아둘 객체입니다.
        simpleJdbcInsert.execute(parameters, keyHolder); // execute(Map, KeyHolder) 메서드를 사용합니다.

        user.setId(keyHolder.getKey().longValue()); // 생성된 키를 User 객체에 설정합니다.
        return user;
    }

3. 특정 컬럼만 지정하여 INSERT

usingColumns()를 사용하여 명시적으로 INSERT할 컬럼들을 지정할 수 있습니다. 이는 테이블에 기본값이 있는 컬럼이 많거나, 특정 컬럼만 삽입하려는 경우에 유용합니다.

    public int insertUserWithSpecificColumns(String name) {
        // ✨ SimpleJdbcInsert를 초기화할 때 사용될 컬럼들을 지정합니다.
        // this.simpleJdbcInsert = new SimpleJdbcInsert(dataSource)
        //                             .withTableName("users")
        //                             .usingColumns("name"); // 'name' 컬럼만 사용하도록 선언합니다.

        Map<String, Object> parameters = new HashMap<>();
        parameters.put("name", name); // 'name' 컬럼에 해당하는 값만 제공합니다.
        // 이 메서드에서는 'email' 컬럼은 삽입되지 않습니다.

        return simpleJdbcInsert.execute(parameters);
    }

4. SqlParameterSource 사용 (더 객체 지향적인 방법)

MapSqlParameterSourceBeanPropertySqlParameterSource와 같은 SqlParameterSource 구현체를 사용하시면 데이터를 좀 더 객체 지향적으로 바인딩할 수 있습니다.

MapSqlParameterSource 사용

Map과 유사하지만, 조금 더 강력하고 유연합니다.

    public int insertUserWithMapSqlParameterSource(String name, String email) {
        MapSqlParameterSource parameters = new MapSqlParameterSource()
                                                .addValue("name", name)
                                                .addValue("email", email); // 메서드 체이닝으로 편리하게 값을 추가합니다.

        return simpleJdbcInsert.execute(parameters);
    }

BeanPropertySqlParameterSource 사용 (강력 추천 드립니다! 👍)

Java Bean(getter/setter를 가진 POJO)의 프로퍼티를 자동으로 파라미터로 매핑해줍니다. 데이터베이스 컬럼명과 POJO의 필드명이 동일한 경우 매우 편리합니다.

    public Long insertUserWithBeanPropertySqlParameterSource(User user) {
        // SimpleJdbcInsert 초기화 시 "id" 컬럼 지정이 필수입니다.
        BeanPropertySqlParameterSource parameters = new BeanPropertySqlParameterSource(user);

        Number newId = simpleJdbcInsert.executeAndReturnKey(parameters);
        user.setId(newId.longValue()); // 생성된 키를 User 객체에 다시 설정해줄 수 있습니다.
        return user.getId();
    }

🆚 SimpleJdbcInsert vs. JdbcTemplate.update() - 어떤 점이 다를까요?

SimpleJdbcInsert도 결국 내부적으로는 JdbcTemplate을 사용합니다. 그렇다면 언제 SimpleJdbcInsert를 사용하고 언제 JdbcTemplate.update()를 사용해야 할까요?

  • JdbcTemplate.update(): 개발자가 직접 SQL INSERT 쿼리 문자열을 작성하고, PreparedStatementSetter 등을 통해 파라미터를 수동으로 바인딩해야 합니다. 이는 복잡하거나 동적인 SQL 쿼리, 또는 INSERT 외의 UPDATE, DELETE 등 모든 DML 작업에 유연하게 사용할 수 있습니다.
  • SimpleJdbcInsert: 테이블 이름과 파라미터 Map 또는 SqlParameterSource만 제공하면, 나머지는 SimpleJdbcInsert가 데이터베이스 메타데이터를 활용하여 자동으로 처리합니다. 특히 자동 생성 키를 가져오는 과정이 매우 간편해집니다.

결론적으로 말씀드리면:
단순하고 정형화된 INSERT 작업, 특히 데이터를 삽입한 후 자동으로 생성된 키 값을 받아와야 하는 경우에는 SimpleJdbcInsert가 코드의 양을 획기적으로 줄이고 가독성을 크게 높여줍니다. 반면, SQL 쿼리 자체에 대한 세밀한 제어나 복잡한 INSERT 로직이 필요한 경우에는 JdbcTemplate.update()가 더 적합할 수 있습니다. 하지만 일반적인 애플리케이션의 INSERT 작업은 대부분 SimpleJdbcInsert로 충분히 처리 가능합니다. 😉


💡 SimpleJdbcInsert 사용 시 고려사항 및 팁

  • 성능: SimpleJdbcInsert는 첫 번째 실행 시 데이터베이스 메타데이터를 로드하는 데 약간의 오버헤드가 발생할 수 있습니다. 하지만 이는 한 번만 발생하는 과정이며, 이후의 INSERT 작업에는 영향을 미치지 않으므로 일반적인 경우 성능에 대한 걱정은 크게 하지 않으셔도 됩니다.
  • 메타데이터 의존성: SimpleJdbcInsert는 데이터베이스의 메타데이터(테이블/컬럼 이름 등)에 의존합니다. 만약 사용하시는 JDBC 드라이버가 메타데이터를 제대로 제공하지 않거나, 데이터베이스 스키마가 매우 유동적으로 변경되는 환경이라면 usingColumns()와 같이 명시적으로 컬럼을 지정하는 것이 더 안정적일 수 있습니다.
  • 복합 키 또는 비정수형 키: executeAndReturnKey()는 주로 단일 Number 타입의 키를 반환하는 데 적합합니다. 복합 키나 UUID와 같은 비정수형 자동 생성 키를 처리해야 한다면, execute(parameters, keyHolder) 메서드를 사용하고 KeyHolder에서 적절한 타입으로 변환하여 가져와야 합니다.
  • 대량 Batch INSERT: SimpleJdbcInsertexecuteBatch() 메서드를 통해 Map[] 또는 SqlParameterSource[] 배열을 받아 대량의 데이터를 효율적으로 한 번에 삽입하는 Batch INSERT 기능도 제공합니다.

SimpleJdbcInsert는 Spring JDBC의 강력한 기능 중 하나로, INSERT 작업을 훨씬 더 간결하고 직관적으로 만들어줍니다. 특히 반복적인 보일러플레이트 코드를 줄이고 자동 생성 키 처리를 간소화하는 데 큰 장점이 있으니, 다음 프로젝트에서 INSERT 작업을 하실 때 꼭 활용해 보시길 바랍니다! 여러분의 코드가 더욱 깔끔해질 것입니다. 😉

혹시 더 궁금한 점이나 다른 Spring JDBC 기능에 대해 알고 싶은 것이 있다면 언제든지 질문해주세요!

profile
Small Big Cycle

0개의 댓글