CAP for Java

Action에서 @On 이름 틀리면? — 핸들러가 묵살된다 #shorts #SAP #CAP

▶ YouTube에서 보기

CAP Java에서 @On 어노테이션 이름이 틀리면 생기는 일

CAP for Java에서 커스텀 Action 핸들러를 구현할 때 @On 어노테이션의 이름이 CDS 서비스 정의와 정확히 일치하지 않으면 핸들러가 전혀 실행되지 않습니다. 오류 메시지도 없고 예외도 발생하지 않습니다. Action을 호출하면 빈 응답만 반환됩니다. 이 침묵의 실패를 디버깅하는 데 많은 시간을 낭비할 수 있습니다.

문제 시나리오: 핸들러 이름 불일치

// CDS 서비스 정의
service OrdersService {
  entity Orders as projection on db.Orders;

  // Action 이름: confirmOrder (camelCase)
  action confirmOrder(orderId: String) returns Orders;
}
// 잘못된 핸들러: 이름 대소문자 불일치
@Component
@ServiceName("OrdersService")
public class OrdersHandler implements EventHandler {

    // "ConfirmOrder" — 대문자 C, 등록 안 됨
    @On(event = "ConfirmOrder", service = "OrdersService")
    public void handleConfirmOrder(ActionEventContext ctx) {
        // 이 코드는 절대 실행되지 않음
        // 오류도 없음, 그냥 무시됨
        ctx.setResult(Map.of("status", "confirmed"));
    }
}
// 올바른 핸들러: CDS와 정확히 동일한 이름
@Component
@ServiceName("OrdersService")
public class OrdersHandler implements EventHandler {

    // "confirmOrder" — CDS와 동일
    @On(event = "confirmOrder", service = "OrdersService")
    public void handleConfirmOrder(ActionEventContext ctx) {
        // 정상 실행됨
        String orderId = (String) ctx.getParameterInfo().getParam("orderId");
        // 비즈니스 로직
        ctx.setResult(processOrder(orderId));
    }
}

올바른 Action 핸들러 구조

// CDS 정의
service ShipmentService {
  entity Shipments as projection on db.Shipments;
  action dispatchShipment(
    shipmentId : String,
    carrierId  : String,
    trackingNo : String
  ) returns Shipments;
  action cancelShipment(
    shipmentId : String,
    reason     : String
  ) returns Shipments;
}
@Component
@ServiceName("ShipmentService")
public class ShipmentHandler implements EventHandler {

    @Autowired
    PersistenceService persistenceService;

    // Action 이름이 CDS와 완전 일치해야 함
    @On(event = "dispatchShipment", service = "ShipmentService")
    public void onDispatchShipment(ActionEventContext ctx) {
        // 파라미터 읽기
        var params = ctx.getParameterInfo();
        String shipmentId = (String) params.getParam("shipmentId");
        String carrierId  = (String) params.getParam("carrierId");
        String trackingNo = (String) params.getParam("trackingNo");

        // 유효성 검사
        if (shipmentId == null || carrierId == null) {
            throw new ServiceException(ErrorStatuses.BAD_REQUEST,
                "shipmentId와 carrierId는 필수입니다.");
        }

        // 비즈니스 로직
        var update = Update.entity(Shipments_.class)
            .data(Map.of(
                "status", "DISPATCHED",
                "carrierId", carrierId,
                "trackingNo", trackingNo,
                "dispatchedAt", LocalDate.now().toString()
            ))
            .byId(shipmentId);

        persistenceService.run(update);

        // 업데이트된 엔티티 반환
        var result = persistenceService.run(
            Select.from(Shipments_.class).byId(shipmentId)
        );
        ctx.setResult(result.single(Shipments.class));
    }

    @On(event = "cancelShipment", service = "ShipmentService")
    public void onCancelShipment(ActionEventContext ctx) {
        String shipmentId = (String) ctx.getParameterInfo().getParam("shipmentId");
        String reason = (String) ctx.getParameterInfo().getParam("reason");

        var update = Update.entity(Shipments_.class)
            .data(Map.of(
                "status", "CANCELLED",
                "cancelReason", reason,
                "cancelledAt", LocalDate.now().toString()
            ))
            .byId(shipmentId);

        persistenceService.run(update);

        var result = persistenceService.run(
            Select.from(Shipments_.class).byId(shipmentId)
        );
        ctx.setResult(result.single(Shipments.class));
    }
}

핸들러 등록 확인 방법

// 서버 시작 로그에서 핸들러 등록 확인
// 로그 레벨을 DEBUG로 설정 (application.properties)
// logging.level.com.sap.cds=DEBUG

// 정상 등록 시 로그:
// [INFO] Registered handler 'dispatchShipment' for service 'ShipmentService'
// [INFO] Registered handler 'cancelShipment' for service 'ShipmentService'

// 누락 시: 해당 로그가 없음

// 테스트로 확인 (가장 확실한 방법)
@SpringBootTest
class ShipmentHandlerTest {

    @Autowired
    private ShipmentService shipmentService;

    @Test
    void testDispatchShipmentHandlerRegistered() {
        // Action이 올바르게 처리되면 오류 없이 결과 반환
        assertDoesNotThrow(() -> {
            shipmentService.run(
                ActionInput.create("dispatchShipment")
                    .param("shipmentId", "SHP-001")
                    .param("carrierId", "DHL")
                    .param("trackingNo", "DHL123456")
            );
        });
    }
}

핵심 체크리스트

  • @On의 event 이름이 CDS 서비스의 Action 이름과 대소문자까지 동일한가
  • @ServiceName이 CDS 서비스 이름과 일치하는가
  • 서버 시작 로그에서 핸들러 등록 확인
  • 단위 테스트로 Action 핸들러 동작 검증

공식 문서

CAP Java Action 핸들러 구현 가이드는 cap.cloud.sap/docs/java/event-handlers에서 확인하세요.

댓글 0

아직 댓글이 없습니다.