Technology/MySQL

INSERT + UPDATE = UPSERT?

ikjo 2022. 7. 15. 04:54

INSERT + UPDATE = UPSERT?

마스터즈 코스 과정 상 구현했었던 스프링 부트 게시판 웹 앱에서 회원가입 기능과 기존 회원 정보를 수정할 수 있는 기능이 있었다. 이때 레파지토리 단에 save라는 하나의 메서드 내에서 회원가입과 사용자 정보 수정을 구분하여 처리해주고 있었는데, Spring JDBC Template를 사용하여 나타내면 다음과 같았었다.

 

    @Override
    public User save(User user) {
        User userInformation;
        try {
            userInformation = findByUserId(user.getUserId())
                .orElseThrow(() -> new NoSuchElementException("해당되는 ID가 없습니다."));
        } catch (NoSuchElementException e) {
            SqlParameterSource parameters = new BeanPropertySqlParameterSource(user);
            jdbcInsert.execute(parameters);
            return user;
        }

        userInformation.update(user);

        SqlParameterSource parameters = new BeanPropertySqlParameterSource(userInformation);
        jdbc.update(UPDATE_USER, parameters);

        return user;
    }

 

사용자가 회원가입을 요청하든 기존 사용자 정보 수정을 요청하든 사용자는 자신의 입력 정보가 담긴 User 객체를 save 메서드 파라미터로 전달하게 된다. 이때 save 메서드 단에서는 사용자 ID로 기존의 사용자 정보를 조회했을 때 존재하지 않는다면 이는 사용자가 회원가입을 요청하는 것으로 판단할 수 있고, 존재한다면 사용자가 기존 정보에 대해 수정을 요청하는 것으로 판단할 수 있었다. 이 과정에서 try-catch문(현재 코드) 외에도 사용자 ID로 사용자 정보 조회 시 null 체크를 통해 구분해줄 수 있었다.

 

하지만 놀랍게도 MySQL 에서는 이러한 상황을 예상(?)하고 유용한 기능 제시하고 있었는데, SQL 상 ON DUPLICATE KEY UPDATE 을 활용하면 다음과 같이 코드를 개선할 수 있다.

 

    @Override
    public User save(User user) {
        SqlParameterSource parameters = new BeanPropertySqlParameterSource(user);
        jdbc.update("INSERT INTO user VALUES (:userId, :password, :name, :email) ON DUPLICATE KEY UPDATE name = :name, email = :email", parameters);

        return user;
    }

 

훨씬 간결해진 것을 확인할 수 있다. 우선 SQL문을 들여다 보면 가장 처음에는 INSERT를 통해 사용자 정보 데이터를 user 테이블에 주입하고 있다. 이때 뒤에 나오는 ON DUPLICATE KEY UPDATE가 중요한데, 이는 기존의 프라이머리 키(Primary Key)나 유니크 키(Unique Key)가 기존 user 테이블에 존재하고 있다면 뒤에 UPDATE문을 통해 특정 칼럼의 값들만 수정하겠다는 의미이다.(여기서는 name과 email에 대한 칼럼 값만 수정하고 있다.)

 

다만, ON DUPLICATE KEY UPDATE 키워드는 MySQL에서는 적용되지만 h2 데이터 베이스를 사용한다면 이 키워드를 사용할 수 없다. 대신 h2에서는 MERGE INTO ~ KEY 키워드를 제공하고 있다. 위와 동일한 기능을 이를 활용하여 코드로 나타내면 다음과 같다.

 

    @Override
    public User save(User user) {
        SqlParameterSource parameters = new BeanPropertySqlParameterSource(user);
        jdbc.update("MERGE INTO user KEY (user_id) VALUES (:userId, :password, :name, :email)", parameters);

        return user;
    }

 

개인적으로 MySQL SQL 보다 좀 더 직관적이고 이해하기 쉬웠다. 하지만 h2 데이터 베이스를 사용할 때는 이 코드를 사용하고 MySQL 데이터 베이스를 사용할 때는 다른 코드를 사용하는 것은 번거로운 일이고 호환성이 떨어지므로 h2 데이터 베이스를 MySQL 모드로 사용하여 ON DUPLICATE KEY UPDATE 키워드를 사용할 수도 있다.