Apex 트리거 시작하기
학습 목표
이 유닛을 완료하면 다음을 수행할 수 있습니다.
- Salesforce 개체에 대한 트리거를 작성할 수 있습니다.
- 트리거 컨텍스트 변수를 사용할 수 있습니다.
- 트리거에서 클래스 메서드를 호출할 수 있습니다.
- 트리거의 sObject
addError()
메서드를 사용하여 저장 작업을 제한할 수 있습니다.
시작하기 전에
Apex 트리거는 유용하고 재미있으며 훌륭합니다. 이 모듈을 통해 이러한 기능을 시작할 수도 있으며 다른 Salesforce 기능도 알 수 있기 때문에 Apex 트리거의 성능을 확인할 수도 있습니다. 이 모듈을 최대한 활용하려면 먼저 다음 모듈을 확인하는 것이 좋습니다.
Apex 트리거 작성하기
Apex 트리거를 사용하면 삽입, 업데이트 또는 삭제와 같은 Salesforce의 레코드에 대한 이벤트 전후에 맞춤형 작업을 수행할 수 있습니다. 데이터베이스 시스템이 트리거를 지원하는 것과 같이 Apex는 레코드 관리를 위한 트리거 지원을 제공합니다.
일반적으로 트리거를 사용하여 특정 조건에 따라 작업을 수행하거나 관련 레코드를 수정하거나 특정 작업이 발생하지 않도록 제한합니다. 트리거를 사용하여 SOQL 및 DML 실행 또는 맞춤형 Apex 메서드 호출을 포함하여 Apex에서 할 수 있는 모든 작업을 수행할 수 있습니다.
트리거를 사용하여 Salesforce 사용자 인터페이스에서 포인트 앤 클릭 도구로 수행 불가능한 작업을 수행할 수 있습니다. 예를 들어 필드 값의 유효성을 검사하거나 레코드의 필드를 업데이트하는 경우 유효성 검사 규칙과 플로를 사용합니다. 성능과 확장이 중요하거나, 포인트 앤 클릭 도구용으로는 논리가 너무 복잡하거나, CPU에 부담을 주는 작업을 실행할 때는 Apex 트리거를 사용합니다.
계정 또는 연락처와 같은 최상위 표준 개체, 맞춤형 객체 및 일부 표준 하위 개체에 대해 트리거를 정의할 수 있습니다. 트리거는 생성 시 기본적으로 활성화됩니다. Salesforce는 지정된 데이터베이스 이벤트가 발생할 때 자동으로 활성 트리거를 실행합니다.
트리거 구문
트리거 정의의 구문은 클래스 정의의 구문과 다릅니다. 트리거 정의는 trigger
키워드로 시작합니다. 그런 다음 트리거 이름, 트리거가 연결된 Salesforce 개체 및 트리거가 실행되는 조건이 따릅니다. 트리거의 구문은 다음과 같습니다.
trigger TriggerName on ObjectName (trigger_events) { code_block }
삽입, 업데이트, 삭제 및 삭제 취소 작업 전후에 트리거를 실행하려면 쉼표로 구분된 목록에 여러 트리거 이벤트를 지정합니다. 지정할 수 있는 이벤트는 다음과 같습니다.
before insert
before update
before delete
after insert
after update
after delete
after undelete
트리거 예제
이 간단한 트리거는 계정을 삽입하고 디버그 로그에 메시지를 쓰기 전에 실행됩니다.
- Developer Console에서 File(파일) | New(새로 만들기) | Apex Trigger(Apex 트리거)를 클릭합니다.
- 트리거 이름에
HelloWorldTrigger
를 입력한 다음 sObject에 대한 Account(계정)를 선택합니다. Submit(제출)을 클릭합니다. - 기본 코드를 다음으로 바꿉니다.
trigger HelloWorldTrigger on Account (before insert) { System.debug('Hello World!'); }
- 저장하려면 Ctrl+S를 누릅니다.
- 트리거를 테스트하려면 계정을 만듭니다.
- Debug(디버그) | Open Execute Anonymous Window(익명 실행 창 열기)를 클릭합니다.
- 새 창에서 다음을 추가한 다음 Execute(실행)를 클릭합니다.
Account a = new Account(Name='Test Trigger'); insert a;
- 디버그 로그에서
Hello World!
구문을 찾습니다. 또한 로그는 트리거가 실행되었음을 보여줍니다.
트리거 유형
트리거에는 두 가지 유형이 있습니다.
- Before triggers - 데이터베이스에 저장하기 전에 레코드 값을 업데이트하거나 유효성을 검사하는 데 사용됩니다.
- After triggers - 시스템에서 설정한 필드 값(예: 레코드의
Id
또는LastModifiedDate
필드)에 액세스 및 다른 레코드의 변경 사항에 영향을 미치는 데 사용됩니다. after trigger가 발생한 레코드는 읽기 전용입니다.
컨텍스트 변수 사용
트리거를 발생한 레코드에 액세스하려면 컨텍스트 변수를 사용합니다. 예를 들어 Trigger.new
에는 트리거 삽입 또는 업데이트에 삽입된 모든 레코드가 포함됩니다. Trigger.old
에서 트리거 업데이트에 업데이트되기 전의 sObject 이전 버전 또는 트리거 삭제에서 삭제된 sObject 목록을 제공합니다. 하나의 레코드가 삽입되거나 API 또는 Apex를 통해 많은 레코드가 대량으로 삽입될 경우 트리거가 발생할 수 있습니다. 따라서 Trigger.new
와 같은 컨텍스트 변수에 하나의 레코드만 또는 여러 레코드를 포함할 수 있습니다. Trigger.new
를 반복하여 각 개별 sObject를 가져올 수 있습니다.
이 예제는 HelloWorldTrigger
예시 트리거의 수정된 버전입니다. for 루프에서 각 계정을 반복하고 각각에 대한 Description
(설명) 필드를 업데이트합니다.
trigger HelloWorldTrigger on Account (before insert) { for(Account a : Trigger.new) { a.Description = 'New description'; } }
일부 다른 컨텍스트 변수는 업데이트 또는 기타 이벤트로 인해 트리거가 실행되었는지 여부를 나타내는 부울 값을 반환합니다. 이러한 변수는 트리거가 여러 이벤트를 결합할 때 유용합니다. 다음의 예를 확인해 보세요.
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 | Apex 코드의 현재 컨텍스트가 Visualforce 페이지, 웹 서비스 또는 |
isInsert | Salesforce 사용자 인터페이스, Apex 또는 API에서 삽입 작업으로 인해 이 트리거가 실행된 경우 |
isUpdate | Salesforce 사용자 인터페이스, Apex 또는 API에서 업데이트 작업으로 인해 이 트리거가 실행된 경우 |
isDelete | Salesforce 사용자 인터페이스, Apex 또는 API에서 삭제 작업으로 인해 이 트리거가 실행된 경우 |
isBefore | 레코드가 저장되기 전에 이 트리거가 실행된 경우 |
isAfter | 모든 레코드가 저장된 후에 이 트리거가 실행된 경우 |
isUndelete | Recycle Bin(휴지통)에서 레코드를 복구한 후 이 트리거가 실행된 경우 |
new | sObject 레코드의 새 버전 목록을 반환합니다. 이 sObject 목록은 |
newMap | sObject 레코드의 새 버전에 대한 ID 지도입니다. 이 지도는 |
old | sObject 레코드의 이전 버전 목록을 반환합니다. 이 sObject 목록은 |
oldMap | sObject 레코드의 이전 버전에 대한 ID 지도입니다. 이 지도는 |
operationType | 현재 작업에 해당하는 System.TriggerOperation 유형의 열거형을 반환합니다.
|
size | 이전 및 새 트리거 호출의 총 레코드 수입니다. |
트리거에서 클래스 메서드 호출하기
트리거에서 공용 유틸리티 메서드를 호출할 수 있습니다. 다른 클래스의 메서드를 호출하면 코드 재사용이 가능하고 트리거 크기가 줄어들며 Apex 코드의 유지 관리가 향상됩니다. 또한 개체 지향 프로그래밍을 사용할 수 있습니다.
다음 예제 트리거는 트리거에서 정적 메서드를 호출하는 방법을 보여줍니다. 삽입 이벤트로 인해 트리거가 실행된 경우 예제에서 EmailManager
클래스에 대한 정적 sendMail()
메서드를 호출합니다. 이 유틸리티 메서드는 지정된 수신자에게 이메일을 전송하고 삽입된 연락처 레코드 수를 포함합니다.
- Developer Console에서 File(파일) | New(새로 만들기) | Apex Class(Apex 클래스)를 클릭합니다.
EmailManager
를 입력하고 OK(확인)를 클릭합니다.- 기본 클래스 본문을 아래에 제시된
EmailManager
클래스 예시로 변경합니다.public class EmailManager { // Public method public static void sendMail(String address, String subject, String body) { // Create an email message object Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); String[] toAddresses = new String[] {address}; mail.setToAddresses(toAddresses); mail.setSubject(subject); mail.setPlainTextBody(body); // Pass this email message to the built-in sendEmail method // of the Messaging class Messaging.SendEmailResult[] results = Messaging.sendEmail( new Messaging.SingleEmailMessage[] { mail }); // Call a helper method to inspect the returned results inspectResults(results); } // Helper method private static Boolean inspectResults(Messaging.SendEmailResult[] results) { Boolean sendResult = true; // sendEmail returns an array of result objects. // Iterate through the list to inspect results. // In this class, the methods send only one email, // so we should have only one result. for (Messaging.SendEmailResult res : results) { if (res.isSuccess()) { System.debug('Email sent successfully'); } else { sendResult = false; System.debug('The following errors occurred: ' + res.getErrors()); } } return sendResult; } }
- Developer Console에서 File(파일) | New(새로 만들기) | Apex Trigger(Apex 트리거)를 클릭합니다.
- 트리거 이름에
ExampleTrigger
를 입력한 다음 sObject에 대한 Contact(연락처)를 선택합니다. Submit(제출)을 클릭합니다. - 기본 코드를 다음으로 바꾼 다음
sendMail()
의 이메일 주소 자리 표시자 텍스트를 이메일 주소로 수정합니다.trigger ExampleTrigger on Contact (after insert, after delete) { if (Trigger.isInsert) { Integer recordCount = Trigger.new.size(); // Call a utility method from another class EmailManager.sendMail('Your email address', 'Trailhead Trigger Tutorial', recordCount + ' contact(s) were inserted.'); } else if (Trigger.isDelete) { // Process after delete } }
- 저장하려면 Ctrl+S를 누릅니다.
- 트리거를 테스트하려면 연락처를 만듭니다.
- Debug(디버그) | Open Execute Anonymous Window(익명 실행 창 열기)를 클릭합니다.
- 새 창에서 다음을 추가한 다음 Execute(실행)를 클릭합니다.
Contact c = new Contact(LastName='Test Contact'); insert c;
- 디버그 로그에서 트리거가 실행되었는지 확인합니다. 로그 끝 부분에서 유틸리티 메서드(
DEBUG|Email sent successfully
)로 작성된 디버그 메시지를 찾습니다. - 이제 본문 텍스트
1 contact(s) were inserted
(1개의 연락처가 삽입되었습니다)가 포함된 이메일을 받았는지 확인하세요.
새 트리거가 적용되면 연락처를 한 개 이상 추가할 때마다 이메일이 전송됩니다!
관련 레코드 추가하기
트리거는 종종 트리거 컨텍스트의 레코드(이 트리거를 발생시킨 레코드)와 관련된 레코드에 액세스하고 관리하는 데 사용됩니다.
이 트리거는 기회가 이미 계정과 연결되어 있지 않은 경우 새 계정 또는 업데이트된 각 계정에 대한 관련 기회를 추가합니다. 트리거는 먼저 SOQL 쿼리를 수행하여 트리거가 실행된 계정에 대한 모든 하위 기회를 가져옵니다. 다음으로 트리거는 Trigger.new
의 sObject 목록을 반복하여 각 계정 sObject를 가져옵니다. 계정에 관련된 기회 sObject가 없는 경우 for 루프는 기회를 만듭니다. 트리거가 새로운 기회를 생성한 경우 최종 명령문이 이를 삽입합니다.
- Developer Console을 사용하여 다음 트리거를 추가합니다(
HelloWorldTrigger
예제의 단계를 따르지만 트리거 이름에 대해AddRelatedRecord
를 사용합니다).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; } }
- 트리거를 테스트하려면 Salesforce 사용자 인터페이스에서 계정을 만들고
Apples & Oranges
(사과 및 오렌지)로 이름을 지정합니다. - 계정 페이지의 기회 관련 목록에서 새 기회를 찾습니다. 트리거가 이 기회를 자동으로 추가했습니다.
트리거 예외 사용하기
특정 조건이 충족되면 레코드가 저장되지 않도록 하는 것과 같이 특정 데이터베이스 작업에 대한 제한을 추가해야 하는 경우가 있습니다. 트리거에 레코드가 저장되지 않도록 하려면 문제가 되는 sObject에 대한 addError()
메서드를 호출합니다. addError()
메서드는 트리거 내부에서 치명적인 오류를 발생시킵니다. 오류 메시지는 사용자 인터페이스에 표시되고 기록됩니다.
다음 트리거는 관련 기회가 있는 경우 계정이 삭제되는 것을 방지합니다. 기본적으로 계정을 삭제하면 모든 관련 레코드가 계단식으로 삭제됩니다. 이 트리거는 기회의 계단식 삭제를 방지합니다. 이 트리거를 직접 사용해 보세요. 이전 예제를 실행한 경우 조직에 관련 기회가 있는 Apples & Oranges
라는 계정이 있습니다. 이 예제에서 해당 샘플 계정을 사용합니다.
- Developer Console을 사용하여 다음 트리거를 추가합니다.
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.'); } }
- Salesforce 사용자 인터페이스에서
Apples & Oranges
계정 페이지로 이동하여 Delete(삭제)를 클릭합니다. - 확인 팝업창에서 OK(확인)를 클릭합니다.
Cannot delete account with related opportunities
(관련 기회가 있는 계정을 삭제할 수 없습니다)라는 맞춤형 오류 메시지로 유효성 검사 오류를 찾습니다.
AccountDeletion
트리거를 비활성화합니다. 이 트리거를 활성 상태로 두면 과제를 확인할 수 없습니다.
- Setup(설정)에서
Apex Triggers
를 검색합니다. - Apex Triggers(Apex 트리거) 페이지에서
AccountDeletion
트리거 옆에 있는 Edit(편집)을 클릭합니다. - Is Active(활성)를 선택 해제합니다.
- Save(저장)를 클릭합니다.
트리거 및 콜아웃
Apex를 사용하면 외부 웹 서비스를 호출하고 Apex 코드를 외부 웹 서비스와 통합할 수 있습니다. 외부 웹 서비스에 대한 Apex 호출을 콜아웃이라고 합니다. 예를 들어 주식 시세 서비스에 콜아웃을 만들어 최신 시세를 얻을 수 있습니다. 트리거에서 호출할 경우 호출은 트리거 프로세스가 외부 서비스의 응답을 기다리는 동안 작업을 차단하지 않도록 비동기적으로 수행되어야 합니다. 비동기 호출은 백그라운드 프로세스에서 수행되며 외부 서비스를 반환할 경우 응답이 수신됩니다.
트리거에서 콜아웃을 만들려면 비동기적으로 실행되는 클래스 메서드를 호출합니다. 이러한 메서드를 future 메서드라고 하며 @future(callout=true)
주석을 추가합니다. 이 예제 클래스에는 콜아웃을 만드는 future 메서드가 포함되어 있습니다.
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); } }
이 예제는 콜아웃을 비동기적으로 만들기 위해 클래스의 메서드를 호출하는 트리거를 보여줍니다.
trigger CalloutTrigger on Account (before insert, before update) { CalloutClass.makeCallout(); }
이 섹션에서는 콜아웃의 개요만 제공하며 콜아웃을 자세히 다루지는 않습니다. 자세한 내용은 Apex 개발자 가이드의 Apex를 사용하여 콜아웃 호출을 참조하세요.
리소스