카테고리 없음

[Springboot] 업데이트 처리 방법

늘이 2024. 11. 29. 11:35

 

 

방법1: Repository를 통한 업데이트

import com.douzone.shinhanaireport.registrationstatement.domain.RegistrationStatement;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

public interface RegistrationStatementRepository extends JpaRepository<RegistrationStatement, String>, JpaSpecificationExecutor<RegistrationStatement> {

    // 가장 큰 ID 조회
    @Query("SELECT MAX(rs.id) FROM RegistrationStatement rs WHERE rs.id LIKE :datePart")
    Optional<String> findMaxIdByDate(String datePart);

    @Transactional
    @Modifying
    @Query("UPDATE RegistrationStatement r SET r.titleName = :titleName WHERE r.id = :id")
    void updateTitleName(@Param("id") String id, @Param("titleName") String titleName);

    @Transactional
    @Modifying
    @Query("UPDATE RegistrationStatement r SET r.status = :status WHERE r.id = :id")
    void updateStatus(@Param("id") String id, @Param("status") String status);

    @Transactional
    @Modifying
    @Query("UPDATE RegistrationStatement r SET r.stepFlag = :stepFlag WHERE r.id = :id")
    void updateStepFlag(@Param("id") String id, @Param("stepFlag") String stepFlag);

    @Transactional
    @Modifying
    @Query("UPDATE RegistrationStatement r SET r.workIndexNumber = :workIndexNumber WHERE r.id = :id")
    void updateWorkIndexNumber(@Param("id") String id, @Param("workIndexNumber") String workIndexNumber);
}

장점

  1. 명시적 SQL
    • SQL 문장이 명확히 보이고 이해하기 쉽습니다.
  2. 간단한 작업
    • 단순 업데이트에 작업에 적합합니다.
  3. 성능 최적화
    • 데이터 조회 없이 직접 업데이트 가능하므로 성능이 향상됩니다.

 

 

단점

  1. 코드 중복
    • 쿼리가 많아지면 유지보수가 어렵고 중복 코드가 많아질 수 있습니다.
  2. 객체지향 위배
    • 비즈니스 로직이 Repository 계층으로 이동하여 객체지향성이 약화됩니다.
  3. 동적 업데이트 어려움
    • 복잡한 조건에 따라 다양한 속성을 업데이트하기 어렵습니다.

 

 

 

 

방법2: Entity 메서드를 통한 업데이트

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@IdClass(ContentId.class)
@Table(name = "BONDREPORT_D")
public class Content {

    @Id
    @Column(name = "ID")
    private String id;

    @Id
    @Column(name = "ORIGIN_FG")
    private String originFlag;

    @Id
    @Column(name = "INDEX_NB")
    private String indexNumber;

    @Column(name = "ORDER_NB")
    private String orderNumber;

    @Column(name = "INDEX_NM")
    private String indexName;

    @Column(name = "INDEX_DC", columnDefinition = "LONGTEXT") // 추가
    private String indexDescription;

    @Column(name = "CONTENT_DC", columnDefinition = "LONGTEXT")
    private String contentDescription;

    @Column(name = "COM_ST")
    private String completeStatus;

    @Column(name = "INSERT_ID")
    private String insertID;

    @Column(name = "INSERT_DT")
    private LocalDateTime insertDate;

    @Column(name = "INSERT_IP")
    private String insertIP;

    @Column(name = "MODIFY_ID")
    private String modifyID;

    @Column(name = "MODIFY_DT")
    private LocalDateTime modifyDate;

    @Column(name = "MODIFY_IP")
    private String modifyIP;


    // 목차명 업데이트
    public void updateIndexName(String newIndexName) {
        this.indexName = newIndexName;
        this.modifyDate = LocalDateTime.now();
    }

    // 목차 순서 업데이트
    public void updateOrderNumber(String newOrderNumber) {
        this.orderNumber = newOrderNumber;
        this.modifyDate = LocalDateTime.now();
    }

    // 최종본 반영 업데이트
    public void updateCompleteStatus(String contentDescription) {
        this.contentDescription = contentDescription;
        this.completeStatus = "1";
        this.modifyDate = LocalDateTime.now();
    }

    // 수정자 정보 업데이트
    public void updateModifyInfo(String modifyID, String modifyIP) {
        this.modifyID = modifyID;
        this.modifyIP = modifyIP;
        this.modifyDate = LocalDateTime.now();
    }
}

장점

  1. 객체지향적 설계
    • 상태를 변경하는 책임이 엔티티 내부로 이동하여 코드가 더 명확해집니다.
    • Entity가 자신의 상태를 직접 관리하기 때문에 도메인 로직이 응집성을 갖게 됩니다.
  2. 중복 제거
    • 각 업데이트 메서드가 공통적으로 포함하는 수정자 정보를 Entity 내부에서 처리하므로 중복이 줄어듭니다.
  3. 테스트 가능성
    • 서비스 메서드가 Entity와의 상호작용만 테스트하면 되므로 단위 테스트 작성이 간단합니다.

 

 

단점

  1. 추가 조회 비용
    • 업데이트하려면 항상 엔티티를 먼저 조회해야 하므로 단순 쿼리 방식보다 성능이 떨어질 수 있습니다.
  2. 병행 업데이트 문제
    • 동일한 데이터를 여러 곳에서 동시에 업데이트하려고 하면 동시성 문제가 발생할 가능성이 있습니다.

 

 

 

1. 두 방식의 비교

구분Repository를 통한 업데이트Entity 메서드를 통한 업데이트

장점 - SQL 쿼리를 직접 작성하므로 복잡한 조건 처리 가능
- 성능 최적화 가능(필드만 업데이트)
- 비즈니스 로직과 데이터 조작이 한 곳에 모여 있어 가독성이 높음
- 재사용성 우수
단점 - 로직이 분산됨(Repository에 쿼리, Service에 로직 분리됨)
- 코드 중복 발생 가능
- Entity의 책임이 과도하게 커질 수 있음
- 모든 필드 업데이트로 인한 성능 저하 가능성
사용 사례 - 특정 필드만 업데이트할 때
- 복잡한 조건에 따른 업데이트가 필요할 때
- 단순한 업데이트
- Entity 상태와 동작이 밀접하게 관련된 경우
주요 특징 - Repository 인터페이스에 @Query 또는 @Modifying 어노테이션 사용
- 데이터베이스에 직접 반영
- Entity 클래스에서 업데이트 메서드 구현
- Service 레이어에서 Entity를 조회 후 업데이트

 

 

두 방식 모두 특정 상황에서 유용하기 때문에 혼합 사용이 가능합니다. 다만, 각 방식의 사용 기준을 명확히 정의해야 합니다.

  • Entity 메서드 방식: Entity 상태와 밀접한 간단한 업데이트(예: updateCompleteStatus)를 처리.
  • Repository 방식: 복잡한 SQL 조건이나 대량 업데이트 처리.

혼합 사용이 관리 부담이 크다고 느껴진다면, 팀 내 표준을 정해 한 가지 방식으로 통일하는 것이 좋습니다. 복잡성이 높은 프로젝트에서는 Repository 방식으로 통일하는 것이 일반적입니다.

 

 

위에서 설명한 작업은 간단한 CRUD 작업에 해당한다고 볼 수 있습니다. 주요 이유는 다음과 같습니다:

간단한 CRUD 작업의 특징과 비교

  1. Create, Read, Update, Delete 작업:
    • 요청 데이터에 따라 특정 컬럼(예: indexName, orderNumber, completeStatus)을 업데이트하거나 단순 조회 작업만 수행합니다.
    • 복잡한 비즈니스 로직이 필요하지 않고, 데이터베이스와 단순히 데이터를 주고받는 작업입니다.
  2. 비즈니스 로직이 단순함:
    • 별도로 추가 계산이나 복잡한 상태 관리 없이 입력받은 데이터를 그대로 업데이트하거나 반환합니다.
    • 업데이트 대상 조건(예: id, originFlag, indexNumber)이 명확합니다.
  3. 한정된 데이터 조작:
    • 한정된 필드(indexName, orderNumber, completeStatus, 수정 정보 등`)만 조작합니다.
    • 다른 도메인 객체와의 복잡한 관계 연산은 포함되지 않습니다.
  4. Entity의 상태 관리가 필요하지 않음:
    • 엔티티의 상태 변화에 따른 추가 작업(예: 상태 변경에 따른 다른 연산, 이벤트 발생 등)이 없습니다.
    • 단순히 데이터베이스 필드 값을 업데이트하는 작업입니다.

복잡한 작업에 해당하지 않는 이유

  • 상태 변화에 따른 추가 작업 없음:
    • 특정 상태 변화(예: completeStatus가 1로 변경)에서 추가적인 비즈니스 규칙 적용이나 연산이 필요하지 않습니다.
  • 도메인 로직과의 연관성 약함:
    • Content 엔티티 자체의 값만 변경하고, 다른 엔티티와의 상호작용이 없습니다.
  • 정교한 검증 필요 없음:
    • 입력값의 간단한 유효성 검증 외에 데이터 일관성이나 복잡한 검증이 요구되지 않습니다.

결론

현재 작업은 단순한 CRUD 작업으로 볼 수 있습니다.
따라서 이 경우 Repository 기반 접근 방식이 적합하며, 성능적으로도 더 유리합니다.
단, 프로젝트 내에서 객체지향 설계를 일관되게 유지하고 싶다면 Entity 메서드 방식을 선택할 수도 있습니다.