[Salesforce] Apex Triggers - Get Started with Apex Triggers



Get Started with Apex Triggers

https://trailhead.salesforce.com/content/learn/modules/apex_triggers/apex_triggers_intro


Apex 트리거 작성하기

  • Salesforce 레코드에 대한 삽입, 수정, 삭제와 같은 이벤트 전후에 사용자 지정 작업을 수행할 수 있음
  • Salesforce 사용자 인터페이스로 수행할 수 없는 작업을 처리하려면 트리거 사용 (ex. 레코드의 필드 값 검증)
  • 성능과 확장성이 중요할 때나 로직이 복잡할 때 사용하는 것이 좋음


Trigger Syntax

trigger TriggerName on ObjectName (trigger_events) {
   code_block
}
  • trigger_events 부분에 사용 가능
    • before insert
    • before update
    • before delete
    • after insert
    • after update
    • after delete
    • after undelete
  • Before triggers -> 데이터베이스 저장 전 수정하거나 검증할 때 사용
  • After triggers
    • 시스템에 의해 세팅된 값에 접근할 때(ex. Id, LastModifiedDate)
    • 다른 레코드의 변경 사항에 영향을 미칠 때
    • 읽기 전용으로 레코드 들고있음




Context 변수 사용하기

  • 트리거를 실행시킨 레코드에 접근하려면 context 변수 필요함
  • Trigger.new -> insert/update 트리거에 의해 생성된 레코드
  • Trigger.old -> update 트리거에 의해 수정된 sObject의 수정 전 내용
  • Bulk로도 처리 가능해서 여러 레코드를 포함하고 있을 수 있음


예시 코드

trigger ContextExampleTrigger on Account (before insert, after insert, after delete) {
    if (Trigger.isInsert) {
        if (Trigger.isBefore) {
            // Process before insert
        } else if (Trigger.isAfter) {
            // Process after insert
        }
    }
    else if (Trigger.isDelete) {
        // Process after delete
    }
}
  • isExecuting
  • isInsert
  • isUpdate
  • isDelete
  • isBefore
  • isAfter
  • isUndelete
  • new
    • sObject 레코드들의 새 버전 내용 목록
    • insert, update, undelete 사용 가능
    • before 트리거일 때 레코드 수정 가능
  • newMap
    • sObject 레코드들의 ID와 새 버전 내용으로 Map 구성
    • before update, after insert, after update, after undelete 사용 가능
  • old
    • sObject 레코드들의 구 버전 내용 목록
    • update, delete 사용 가능
  • oldMap
    • sObject 레코드들의 ID와 구 버전 내용으로 Map 구성
    • update, delete 사용 가능
  • operationType
    • BEFORE_INSERT, BEFORE_UPDATE, … 와 같은 ENUM 값
  • size
    • 트리거 호출 시 처리된 레코드 수
    • 200개 이상의 코드를 포함한 DML 작업은 배치로 처리 (현재 배치에 포함된 레코드 수만 나옴)




트리거에서 클래스 메서드 호출하기

  1. Custom Notifications 생성
  2. 클래스 작성
    public with sharing class CustomContactNotification {
     // Public method
     public static void notifyUsers(Set<String> recipientsIds, Integer recordCount) {
         // Get the ID for the custom notification type created in Setup
     		CustomNotificationType notificationType =
             [SELECT Id, DeveloperName
              FROM CustomNotificationType
              WHERE DeveloperName='New_Contact_Notification'];
         // Create a new custom notification
         Messaging.CustomNotification notification = new Messaging.CustomNotification();
         // Set the contents for the notification
         notification.setTitle('Trailhead Trigger Tutorial');
         notification.setBody(recordCount + ' contact(s) were inserted.');
         // Set the notification type and target
         notification.setNotificationTypeId(notificationType.Id);
         // '000000000000000AAA' is a dummy targetId value
         notification.setTargetId('000000000000000AAA');
        // Send the notification
        try {
            notification.send(recipientsIds);
            System.debug('Custom notification sent successfully.');
        }
        catch (Exception e) {
            System.debug('Problem sending notification: ' + e.getMessage());
         }
     }
    }
    
  3. 트리거 작성
    trigger ContactNotificationTrigger on Contact (after insert, after delete) {
     if (Trigger.isInsert) {
         Integer recordCount = Trigger.new.size();
         // Set the recipientIDs to the current user
         Set<String> recipientIDs = new Set<String>{UserInfo.getUserId()};
         // Call a utility method from another class
         CustomContactNotification.notifyUsers(recipientIDs, recordCount);
     }
     else if (Trigger.isDelete) {
         // Process after delete
     }
    }
    
  4. 실행
    Contact c = new Contact(LastName='Test Contact');
    insert c;
    

    • 알림 온거 확인~




관련 레코드 추가하기

  • 보통 트리거는 context에 있는 레코드와 관련된 레코드에 액세스하고 관리하는 데 주로 사용됨


예시 코드 (Account를 생성하고 관련된 Opportunity가 없으면 생성)

trigger AddRelatedRecord on Account(after insert, after update) {
    List<Opportunity> oppList = new List<Opportunity>();
    // Get the related opportunities for the accounts in this trigger
    Map<Id,Account> acctsWithOpps = new Map<Id,Account>(
        [SELECT Id,(SELECT Id FROM Opportunities) FROM Account WHERE Id IN :Trigger.new]);
    // Add an opportunity for each account if it doesn't already have one.
    // Iterate through each account.
    for(Account a : Trigger.new) {
        System.debug('acctsWithOpps.get(a.Id).Opportunities.size()=' + acctsWithOpps.get(a.Id).Opportunities.size());
        // Check if the account already has a related opportunity.
        if (acctsWithOpps.get(a.Id).Opportunities.size() == 0) {
            // If it doesn't, add a default opportunity
            oppList.add(new Opportunity(Name=a.Name + ' Opportunity',
                                       StageName='Prospecting',
                                       CloseDate=System.today().addMonths(1),
                                       AccountId=a.Id));
        }
    }
    if (oppList.size() > 0) {
        insert oppList;
    }
}




Trigger Exceptions 사용하기

  • addError() 메서드를 사용하여 특정 조건에 충족하면 저장 불가하게 할 수 있음


예시 코드 (관련 레코드 존재할 때 삭제 방지)

  1. 트리거 작성
    trigger AccountDeletion on Account (before delete) {
     // Prevent the deletion of accounts if they have related opportunities.
     for (Account a : [SELECT Id FROM Account
                      WHERE Id IN (SELECT AccountId FROM Opportunity) AND
                      Id IN :Trigger.old]) {
         Trigger.oldMap.get(a.Id).addError(
             'Cannot delete account with related opportunities.');
     }
    }
    
  2. Salesforce user interface에서 Account 삭제
    • 삭제 불가능 메시지 표시 확인~
  3. trigger disable 하려면 Setup - Apex Triggers에서 is active 체크해제 필요




Triggers and Callouts

  • Apex를 사용하면 외부 웹 서비스를 호출해 Apex 코드와 통합할 수 있음
    • ex. 주식 시세 서비스 콜아웃으로 최신 시세 가져오기
  • 트리거에서 콜아웃을 수행할 때 외부 서비스의 응답을 기다리는 동안 비동기처리 필요
    • 비동기 콜아웃은 백그라운드에서 실행되며, 외부 서비스에서 응답 반환되면 수신됨
  • @future(callout=true) 어노테이션 사용
public class CalloutClass {
    @future(callout=true)
    public static void makeCallout() {
        HttpRequest request = new HttpRequest();
        // Set the endpoint URL.
        String endpoint = 'http://yourHost/yourService';
        request.setEndPoint(endpoint);
        // Set the HTTP verb to GET.
        request.setMethod('GET');
        // Send the HTTP request and get the response.
        HttpResponse response = new HTTP().send(request);
    }
}




Hands-on

  • Account의 Match Billing Address 옵션이 선택된 경우, Shipping Postal Code를 Billing Postal Code와 일치 시키기
  1. Account 객체에 Match Billing Address 필드 추가하기
  2. trigger 작성
trigger AccountAddressTrigger on Account (before insert, before update) {

    for(Account a : Trigger.new) {
        if (a.Match_Billing_Address__c == true) {
			a.ShippingPostalCode = a.BillingPostalCode;
        }
    }

}

다른 글