Trigger.new vs Trigger.old — Apex 트리거를 올바르게 이해하기

발행: (2026년 2월 7일 오전 12:06 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

번역을 진행하려면 번역하고자 하는 전체 텍스트를 제공해 주세요. 텍스트를 주시면 요청하신 대로 한국어로 번역해 드리겠습니다.

소개

Apex 트리거를 배울 때 핵심은 Trigger.newTrigger.old를 언제 사용해야 하는지 이해하는 것입니다.

  • Trigger.new – 현재 Salesforce가 저장하려고 하는 데이터입니다.
  • Trigger.old – 변경 전 존재하던 데이터입니다.

이 구분을 염두에 두면 대부분의 사용 시나리오가 논리적으로 이해됩니다.

Trigger.new

무엇을 나타내는가

Trigger.new는 Salesforce가 삽입하거나 업데이트하려고 하는 현재 또는 들어오는 값을 포함합니다.

일반적인 사용 사례

  • 검증
  • 필드 값 설정 또는 수정
  • 업데이트된 값 읽기
  • 새로운 데이터를 기반으로 관련 레코드 생성

다음 상황에서 사용 가능

시나리오사용 가능?
Insert
Update
Undelete

Undelete: 이 트리거 컨텍스트는 레코드가 휴지통에서 복원될 때 실행되며, Trigger.new에는 복구되는 레코드 값이 들어 있습니다.

예시: 검증

for (Contact con : Trigger.new) {
    if (con.Email == null) {
        con.Email.addError('Email is required');
    }
}

검증이 작동하는 이유는 Trigger.new가 그 순간 저장되는 데이터를 나타내기 때문입니다.

Trigger.old

의미

Trigger.old는 변경 레코드의 스냅샷입니다.

일반적인 사용 사례

  • 필드 변경 감지
  • 이전 값에 기반한 조건 로직
  • 중복 작업 방지

사용 가능한 상황

시나리오사용 가능 여부
업데이트
삭제

예시: 변경 감지

for (Opportunity newOpp : Trigger.new) {
    Opportunity oldOpp = Trigger.oldMap.get(newOpp.Id);
    if (newOpp.StageName != oldOpp.StageName) {
        // Stage has changed
    }
}

Trigger.old는 비교를 위한 이전 상태를 제공합니다.

예시: 의미 있는 업데이트 감지

for (Opportunity newOpp : Trigger.new) {
    Opportunity oldOpp = Trigger.oldMap.get(newOpp.Id);
    if (newOpp.StageName == 'Closed Won' &&
        oldOpp.StageName != 'Closed Won') {
        // Stage just changed to Closed Won
    }
}

  • Trigger.oldMapTrigger.newMap은 Id(Id → Record)로 레코드에 빠르게 접근할 수 있게 해줍니다.
  • 대량 작업에서 기존 값과 새 값을 비교할 때는 루프보다 맵을 사용하는 것이 선호됩니다.
  • 로직이 단순히 현재 값이 아니라 변화를 감지해야 할 경우, Trigger.newTrigger.old 모두 필요합니다.

컨텍스트 기반 결정표

요구 사항사용
입력 검증Trigger.new
필드 수정Trigger.new
이전 vs 새 비교Trigger.new + Trigger.old
삭제 전 정리Trigger.old
레코드 저장 차단Trigger.new.addError()

트리거 유형

트리거 유형수행 작업
BeforeTrigger.new 수정
AfterTrigger.new 읽기, Trigger.old와 비교

중요: After 트리거에서는 레코드가 이미 데이터베이스에 커밋되었기 때문에 Trigger.new를 사용해 레코드를 수정할 수 없습니다. 이 단계에서 Trigger.new는 읽기 전용이며, 추가적인 변경은 별도의 DML 작업이 필요합니다.

예시: DML을 사용해 after 트리거에서 업데이트

List<Account> accsToUpdate = new List<Account>();
for (Account acc : Trigger.new) {
    accsToUpdate.add(new Account(
        Id = acc.Id,
        Name = 'Updated Name'
    ));
}
update accsToUpdate;

트리거 컨텍스트 개요

컨텍스트사용 가능한 데이터목적
Before InsertTrigger.new레코드가 아직 존재하지 않음; 저장 전에 필드를 수정
Before UpdateTrigger.new + Trigger.old이전과 새 데이터를 비교; 저장 전에 필드를 수정
After UpdateTrigger.new + Trigger.old레코드가 저장됨; 읽기 전용, 변경 사항 감지
Before DeleteTrigger.old삭제될 레코드; 정리 로직
After DeleteTrigger.old레코드가 삭제됨; 읽기 전용, 감사 또는 관련 로직

데이터 타임라인을 시각화하면 올바른 컨텍스트를 선택하는 것이 자연스럽습니다. 규칙을 외우는 대신 다음과 같이 질문하세요:

“나는 새로운 데이터, 이전 데이터, 혹은 그 사이의 변경을 다루고 있나요?”

답을 얻으면 적절한 컨텍스트(Trigger.new, Trigger.old 또는 둘 다)가 명확해집니다.

모범 사례

  • Bulkify 트리거 – 단일 레코드 대신 Trigger.new와 같은 컬렉션을 반복하여 거버너 제한을 피합니다.
  • Trigger.newMapTrigger.oldMap을 사용해 Id 기반 효율적인 조회를 수행하고 중첩 루프를 피합니다.
Back to Blog

관련 글

더 보기 »