728x90
반응형

@Transactional을 인터페이스에 선언해도 "동작은 합니다" — 하지만 매우 제한적입니다.


📌 자세한 설명

1. Spring의 트랜잭션 처리 방식

Spring AOP(Aspect-Oriented Programming) 기반 트랜잭션 처리에서는 **프록시 객체(proxy)**가 핵심입니다.

  • @Transactional은 프록시 객체가 호출을 감싸서 트랜잭션을 적용합니다.
  • 이 프록시는 구현체(구현 클래스)에 적용된 애노테이션을 기준으로 동작합니다.

📌 인터페이스에 붙인 @Transactional은 기본적으로 무시되며, 구현체 클래스에 붙여야 정상 적용됩니다.

 


2. ✅ 왜 인터페이스에 붙여도 "동작하는 것처럼 보일 때"가 있나?

Spring Data JPA의 @Repository 인터페이스 메서드에 붙인 @Transactional은 예외입니다.

public interface UserRepository extends JpaRepository<User, Long> {

    @Transactional
    @Modifying
    @Query("DELETE FROM User u WHERE u.status = 'INACTIVE'")
    int deleteInactiveUsers();
}
  • 이 경우는 Spring Data 내부에서 메서드 구현을 동적으로 생성하면서 애노테이션을 인식합니다.
  • 하지만 일반적인 @Service나 사용자 정의 인터페이스는 해당되지 않습니다.

❗ 정리: 언제 어디에 붙여야 하나?

위치트랜잭션 적용 여부권장 여부
구현 클래스 (@Service, @Component) ✔ 정상 적용 ✔ 권장
일반 인터페이스 (@Service 아님) ✖ 미적용 ✖ 비권장
⚠️ Spring Data JPA 인터페이스 (@Query, @Modifying 포함) ✔ 일부 적용됨 ⚠ 가능 (제한적)
 
반응형

✅ 올바른 구조 제안

1. 인터페이스에는 @Modifying, @Transactional 사용 X

public interface ItemRepositoryCustom {
    String saveItems(List<ITEM_Dto> list);
}

2. 구현체 클래스에서 트랜잭션 선언

@Repository
@RequiredArgsConstructor
public class ItemRepositoryCustomImpl implements ItemRepositoryCustom {

    private final ProcedureCallService procedureCallService;
    private final Util util;

    @Override
    @Transactional
    public String saveItems(List<ITEM_Dto> list) {
        for (USP_IN_ITEM_ITEM_Dto dto : list) {
            procedureCallService.call_IN_ITEM_INS(
                dto.getCompanyId(),
                "Item",
                LocalDateTime.now(),
                dto.getInType(),
                dto.getAsAgentId(),
                dto.getItemId(),
                dto.getGoodQty(),
                dto.getRecycleQty(),
                dto.getBadQty(),
                0,
                dto.getDescription(),
                util.getLoginUserId(),
                "",
                0
            );
        }
        return "등록 완료";
    }
}

3. 컨트롤러에서 ResponseEntity 처리

 
@PostMapping("/api/items/enter")
public ResponseEntity<String> saveEntering(@RequestBody List<ITEM_Dto> dtoList) {
    String msg = itemRepositoryCustom.saveItems(dtoList);
    return ResponseEntity.ok(msg);
}

✅ 정리

항목잘못된 사용올바른 사용
@Modifying ❌ 인터페이스 + 일반 메서드 ✔ JPQL/Native Query 메서드 위에만 사용
@Transactional ❌ 인터페이스 선언부에 사용 ✔ 구현 클래스에 직접 사용
ResponseEntity ❌ Repository에서 반환 ✔ Controller에서 반환

✅ 결론

🔹 항상 구현 클래스에 @Transactional을 붙이세요.
🔹 인터페이스에 붙이는 건 혼란을 초래할 수 있고, 동작하지 않는 경우가 많습니다.

728x90
반응형

+ Recent posts