필드 마스크를 사용한 업데이트

Google Ads API에서는 필드 마스크를 사용하여 업데이트가 이루어집니다. 필드 마스크에는 업데이트로 변경하려는 모든 필드가 나열되며, 지정된 필드가 필드 마스크에 없으면 서버로 전송되더라도 무시됩니다.

FieldMaskUtil

필드 마스크를 생성하는 데 권장되는 방법은 특정 세부정보를 숨기고 엔티티 필드에 적용한 변경사항을 모니터링하여 필드 마스크를 자동으로 생성할 수 있는 내장 필드 마스크 유틸리티를 사용하는 것입니다.

다음 예에서는 캠페인을 업데이트하기 위한 필드 마스크를 생성하는 방법을 보여줍니다.

campaign = client.resource.campaign
campaign.resource_name = client.path.campaign(customer_id, campaign_id)

mask = client.field_mask.with campaign do
  campaign.status = :PAUSED
  campaign.network_settings = client.resource.network_settings do |ns|
    ns.target_search_network = false
  end
end

이 코드는 먼저 빈 Campaign 객체를 만든 다음 업데이트되는 캠페인을 API에 알리기 위해 리소스 이름을 설정합니다.

이 예에서는 캠페인에서 client.field_mask.with 메서드를 사용하여 업데이트를 포함하는 블록을 시작합니다. 이 블록이 끝나면 유틸리티가 블록 후 캠페인의 현재 상태를 블록 전 캠페인의 초기 상태와 비교하고 변경된 필드를 열거하는 필드 마스크를 자동으로 생성합니다. 다음과 같이 변이 호출을 위해 필드 마스크를 생성할 때 작업에 필드 마스크를 제공할 수 있습니다.

operation = client.operation.campaign
operation.update = campaign
operation.update_mask = mask

이 방법은 복잡한 작업을 빌드하고 모든 단계를 세부적으로 제어하려는 경우에 권장됩니다. 하지만 대부분의 경우 리소스 이름 (또는 기존 리소스 인스턴스)을 Ruby 라이브러리 팩토리 메서드에 전달할 수 있습니다.

campaign_resource_name = client.path.campaign(customer_id, campaign_id)

operation =
  client.operation.update_resource.campaign(campaign_resource_name) do |c|
    c.status = :PAUSED
    c.network_settings = client.resource.network_settings do |ns|
      ns.target_search_network = false
    end
  end

리소스 이름 문자열이 제공되면 이 메서드는 resource_name이 채워진 새 캠페인 리소스를 자동으로 만들고, 블록 내에서 변경한 내용을 기반으로 필드 마스크를 구성하고, 업데이트 작업을 빌드하고, update 및 update_mask이 이미 채워진 최종 작업을 반환합니다. 리소스 이름 문자열 대신 기존 Campaign 프로토 인스턴스를 전달하여 캠페인의 시작 상태를 지정할 수도 있습니다. 이 패턴은 업데이트 작업을 지원하는 모든 리소스에 적용됩니다.

수동으로 필드 마스크 만들기

라이브러리 유틸리티를 사용하지 않고 처음부터 필드 마스크를 만들려면 Google::Protobuf::FieldMask를 만들고 변경하려는 모든 필드의 이름으로 채워진 배열을 만든 후 배열을 필드 마스크의 paths 필드에 할당합니다.

mask = Google::Protobuf::FieldMask.new
mask.paths = ['status', 'name']

메시지 필드 및 하위 필드 업데이트

MESSAGE 필드에는 하위 필드 (예: MaximizeConversions에는 target_cpa_micros, cpc_bid_ceiling_micros, cpc_bid_floor_micros의 세 개가 있음)가 있을 수도 있고 전혀 없을 수도 있습니다 (예: ManualCpm).

정의된 하위 필드가 없는 메시지 필드

하위 필드로 정의되지 않은 MESSAGE 필드를 업데이트할 때는 앞에서 설명한 대로 FieldMaskUtil를 사용하여 필드 마스크를 생성합니다.

하위 필드가 정의된 메시지 필드

메시지에서 하위 필드를 명시적으로 설정하지 않고 하위 필드로 정의된 MESSAGE 필드를 업데이트할 때는 처음부터 필드 마스크를 만든 이전 예와 마찬가지로 FieldMask에 변경 가능한 MESSAGE 하위 필드를 각각 수동으로 추가해야 합니다.

일반적인 예로 새 입찰 전략에서 필드를 설정하지 않고 캠페인의 입찰 전략을 업데이트하는 경우가 있습니다. 다음 예에서는 입찰 전략의 하위 필드를 설정하지 않고 MaximizeConversions 입찰 전략을 사용하도록 캠페인을 업데이트하는 방법을 보여줍니다.

이 예에서는 FieldMaskUtil의 내장 비교를 사용해도 원하는 목표를 달성할 수 없습니다.

다음 코드는 maximize_conversions이 포함된 필드 마스크를 생성합니다. 하지만 Google Ads API는 필드가 실수로 삭제되는 것을 방지하기 위해 이러한 동작을 허용하지 않으며 FieldMaskError.FIELD_HAS_SUBFIELDS 오류를 생성합니다.

# Creates a campaign with the proper resource name.
campaign = client.resource.campaign do |c|
  c.resource_name = client.path.campaign(customer_id, campaign_id)
end

# Update the maximize conversions field within the update block, so it's
# captured in the field mask.
operation = client.operation.update_resource.campaign(campaign) do |c|
  c.maximize_conversions = client.resource.maximize_conversions
end

# Sends the operation in a mutate request that results in a
# FieldMaskError.FIELD_HAS_SUBFIELDS error because empty MESSAGE fields cannot
# be included in a field mask.
response = client.service.campaign.mutate_campaigns(
  customer_id: customer_id,
  operations: [operation]
)
# Create the operation directly from the campaign's resource name. Don't do
# anything in the block so that the field mask starts empty. You can modify
# other fields in this block, except the message field intended to have a
# blank subfield.
campaign_resource_name = client.path.campaign(customer_id, campaign_id)
operation = client.operation.update_resource.campaign(campaign_resource_name) {}

# Manually add the maximize conversions subfield to the field mask so the API
# knows to clear it.
operation.update_mask.paths << 'maximize_conversions.target_cpa_micros'

# This operation succeeds.
response = client.service.campaign.mutate_campaigns(
  customer_id: customer_id,
  operations: [operation]
)

필드 지우기

일부 필드는 명시적으로 지울 수 있습니다. 이전 예와 마찬가지로 이러한 필드를 필드 마스크에 명시적으로 추가해야 합니다. 예를 들어 MaximizeConversions 입찰 전략을 사용하는 캠페인이 있고 target_cpa_micros 필드가 0보다 큰 값으로 설정되어 있다고 가정해 보겠습니다.

proto3에서 선택사항이 아닌 스칼라 필드를 기본값 (0)으로 설정하는 것은 새 메시지 인스턴스에서 설정하지 않은 것과 구별할 수 없습니다. 따라서 FieldMaskUtil는 maximize_conversions.target_cpa_micros 대신 필드 마스크에 maximize_conversions를 추가하여 FieldMaskError.FIELD_HAS_SUBFIELDS 오류를 발생시킵니다.

# Create a campaign object representing the campaign you want to change.
campaign = client.resource.campaign do |c|
  c.resource_name = client.path.campaign(customer_id, campaign_id)
end

# The field mask in this operation includes 'maximize_conversions',
# but not 'maximize_conversions.target_cpa_micros', so it results in an
# error.
operation = client.operation.update_resource.campaign(campaign) do |c|
  c.maximize_conversions = client.resource.maximize_conversions do |mc|
    mc.target_cpa_micros = 0
  end
end

# Operation fails because the field mask is invalid.
response = client.service.campaign.mutate_campaigns(
  customer_id: customer_id,
  operations: [operation]
)
# Create a campaign including the maximize conversions fields right away, since
# they are manually added to the field mask.
campaign = client.resource.campaign do |c|
  c.resource_name = client.path.campaign(customer_id, campaign_id)
  c.maximize_conversions = client.resource.maximize_conversions do |mc|
    mc.target_cpa_micros = 0
  end
end

# Create the operation with an empty field mask. You can add a block here with
# other changes that are automatically added to the field mask.
operation = client.operation.update_resource.campaign(campaign) {}

# Add the field to the field mask so the API knows to clear it.
operation.update_mask.paths << 'maximize_conversions.target_cpa_micros'

# Operation succeeds because the correct field mask is specified.
response = client.service.campaign.mutate_campaigns(
  customer_id: customer_id,
  operations: [operation]
)

자동 비교 접근 방식은 Google Ads API 프로토콜 버퍼에서 optional로 정의된 필드에 대해 의도한 대로 작동합니다. target_cpa_micros은 MaximizeConversions의 optional 필드가 아니므로 update_mask.paths에 경로를 명시적으로 추가하여 이를 지워야 합니다.