Java & Spring/JPA

[Spring] JPA 변경 감지

nippycloud 2026. 2. 13. 09:56

 

게시판 프로젝트 - Board 수정 상황

사용자가 수정 html에서 데이터를 입력하면 백엔드에서 dto로 해당 데이터를 받아 Board 데이터를 수정한다.

 

 

1. Entity

@Getter
@Entity
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Board {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "board_id")
    private Long id;

    @NotBlank
    private String title;

    @NotBlank
    private String content;
    @NotBlank
    private String password;

    @CreatedDate
    private LocalDateTime createdAt;

    public Board(String password, String title, String content) {
        this.password = password;
        this.title = title;
        this.content = content;
        createdAt = LocalDateTime.now();
    }

    public void update(String title, String content) {
        this.title = title;
        this.content = content;
    }
}

 

 

2. 수정 DTO

@Data
@NoArgsConstructor
@AllArgsConstructor
public class BoardUpdateDto {
    private String title;
    private String content;
}

 

 

핵심 : JPA의 변경 감지는 반드시 트랜잭션 안에서 발생한다.

 

 

수정 로직 - 잘못된 예시

@PostMapping("/update/{boardId}")
public String updateBoard(@PathVariable Long boardId,
                          @ModelAttribute BoardUpdateDto dto) {
    // 변경 감지로 update
    Board board = boardService.readDetail(boardId); // 변경하려는 board 조회
    board.update(dto.getTitle(), dto.getContent()); // 변경

    return "redirect:/board/" + boardId;
}

 

board.update로 데이터를 변경하지만 "트랜잭션" 안에서 데이터를 변경하지 않기 때문에 DB에서 실제로 데이터 변경이 일어나지 않는다.

 

수정 로직 - 올바른 예시

// 컨트롤러 엔드 포인트
@PostMapping("/update/{boardId}")
public String updateBoard(@PathVariable Long boardId,
                          @ModelAttribute BoardUpdateDto dto) {
    // 변경 감지로 update
    Board board = boardService.update(boardId, dto);

    return "redirect:/board/" + boardId;
}



// 서비스 계층 메서드 : @Transactional - 트랜잭션 안에서 변경 감지를 다룬다..
@Transactional
public Board update(Long boardId, BoardUpdateDto dto) {
    Board findBoard = boardRepository.findById(boardId).orElse(null);
    if (findBoard == null) {
        throw new NoSuchElementException("No Board Data");
    }

    // 영속성 컨텍스트에서 영속화된 엔티티를 확인 후 변경이 있다면 쓰기 지연 저장소에 update 쿼리를 넣는다.
    // 트랜잭션 종료 시 쓰기 지연 저장소의 update 쿼리가 DB에 전달된다.
    findBoard.update(dto.getTitle(), dto.getContent());

    return findBoard;
}



// 리포지토리
@Repository
public interface BoardRepository extends JpaRepository<Board, Long> {
}

 

 

@Transactional
public Board update(Long boardId, BoardUpdateDto dto) {
    Board findBoard = boardRepository.findById(boardId).orElse(null);
    if (findBoard == null) {
        throw new NoSuchElementException("No Board Data");
    }

    // 영속성 컨텍스트에서 영속화된 엔티티를 확인 후 변경이 있다면 쓰기 지연 저장소에 update 쿼리를 넣는다.
    // 트랜잭션 종료 시 쓰기 지연 저장소의 update 쿼리가 DB에 전달된다.
    findBoard.update(dto.getTitle(), dto.getContent());

    return findBoard;
}

 

 

트랜잭션 안에서 데이터를 변경하기 때문에 데이터 변경 시점에 영속성 컨텍스트에서 변경 감지를 확인 쓰기 지연 저장소에 update SQL을 넣어둔다.

트랜잭션이 종료되는 시점에 쓰기 지연 저장소들의 쿼리를 DB에 전달하여 수정을 반영한다.

'Java & Spring > JPA' 카테고리의 다른 글

[Spring Data Jpa] 스프링 부트 페이징 처리  (0) 2026.04.25
[Spring] JPA 5  (1) 2026.03.03
[Spring] JPA 4  (0) 2026.02.12
[Spring] JPA 3  (0) 2026.02.05
[Spring] JPA 2  (0) 2026.01.28