나중에 후회하게 될 Salesforce 데이터 모델 설계 실수
요약
Salesforce 데이터 모델 설계 시 초기 요구사항에만 맞춘 결정이 향후 비즈니스 확장 시 막대한 비용을 초래할 수 있음을 경고합니다. 특히 Account 객체를 남용하여 다양한 엔티티를 레코드 유형으로만 구분할 때 발생하는 자동화 및 관계 로직의 복잡성을 지적합니다.
핵심 포인트
- 데이터 모델 결정은 되돌리기 매우 어렵고 비용이 많이 듦
- Account 객체에 모든 엔티티를 몰아넣는 설계는 지양해야 함
- 레코드 유형만으로는 복잡한 자동화와 공유 규칙을 감당하기 어려움
- 엔티티 간 관계 모델이 모호해지면 보고 및 자동화에 문제 발생
Salesforce에서의 데이터 모델 (Data model) 결정은 다른 대부분의 기술적 결정과는 다른 특정한 성격을 가지고 있습니다.
결정하기는 쉽습니다. 하지만 되돌리기는 어렵습니다. 그리고 잘못된 결정을 내렸을 때의 결과는 즉시 나타나지 않습니다. 6개월 후, 혹은 18개월 후에 나타납니다. 비즈니스가 데이터 모델이 수용하도록 설계되지 않은 무언가를 수행해야 할 때, 그리고 그것을 변경하는 비용이 갑자기 눈에 띄게 커질 때 그 결과가 드러납니다.
저는 이 패턴을 인식할 수 있을 만큼 충분히 많은 사례를 목격해 왔습니다. 구현은 빠르게 진행되고, 데이터 모델은 현재의 요구사항에 맞춰 구축되며, 라이브(Go-live) 시점에는 모두가 만족합니다. 그러다 비즈니스가 진화합니다. 비즈니스는 언제나 그렇듯 항상 진화하기 마련입니다. 그러면 출시 당시에는 완벽하게 타당해 보였던 데이터 모델이 비즈니스와 다음에 해야 할 일 사이를 가로막는 장애물이 됩니다. 완전히 망가진 것은 아닙니다. 단지 변경하는 데 비용이 많이 들 뿐입니다.
이러한 상황을 만드는 실수들은 대개 극적이지 않습니다. 맥락상 합리적으로 보였던 작은 결정들이 쌓여 제약 사항이 되는 것입니다. 이러한 실수들이 일관되게 나타나는 지점은 다음과 같습니다.
모든 것에 Account 객체 사용하기
Salesforce의 Account 객체는 강력하고 유연합니다. 또한 플랫폼에서 가장 남용되는 객체이기도 합니다.
그 유혹은 이해할 만합니다. Account는 이미 존재하고, 다른 모든 것과 관계를 맺고 있습니다. 따라서 파트너, 벤더, 자회사, 혹은 Lead(리드)라고 하기에는 다소 모호한 잠재 고객과 같이 새로운 조직 카테고리를 추적해야 할 때, Record Type(레코드 유형)을 사용하여 Account 객체에 넣는 것이 깔끔하게 느껴집니다. 구현도 빠르고, 사용자들에게도 익숙합니다. 그러면 끝이라고 생각하게 됩니다.
문제는 비즈니스 요구사항이 레코드 유형 (Record Type) 차별화만으로는 수용할 수 없는 방식으로 해당 카테고리들을 다르게 처리해야 할 때 발생합니다. 서로 다른 페이지 레이아웃 (Page Layout)을 통해 서로 다른 필드 가시성을 제어하는 것은 괜찮습니다. 하지만 Account 유형에 따른 서로 다른 자동화 로직 (Automation Logic)은 상황을 복잡하게 만듭니다. 서로 다른 공유 규칙 (Sharing Rules), 서로 다른 테리토리 할당 (Territory Assignments), 서로 다른 롤업 계산 (Rollup Calculations) 등, 각각은 개별적으로 관리 가능할지 모르지만, 이들이 모이면 자동화 과정에 수많은 조건부 분기 (Conditional Branches)를 가진 Account 객체가 생성되어, 도대체 언제 무엇이 실행되는지 아무도 완전히 이해하지 못하는 상황이 벌어집니다.
더 깊은 문제는 관계 로직 (Relationship Logic)입니다. Contact는 Account와 연결됩니다. Opportunity도 Account와 연결됩니다. Activity, Case, Contract 모두 Account와 연관되어 있습니다. Account가 개념적으로 서로 다른 여러 엔티티 (Entities)를 담게 되면, 레코드 간의 관계가 모호해집니다. 이 Contact가 고객(Customer) Account의 연락처인가요, 아니면 공급업체(Vendor) Account의 연락처인가요? 이들이 서로 다른 레코드 유형을 가진 동일한 객체일 때, 관계 모델은 보고 (Reporting), 자동화 (Automation)
무언가가 무엇인지 설명하는 선택 목록 (Picklist) 값 — "Closed Won", "Active Customer", "Qualified" — 과 누군가가 무엇을 했는지 설명하는 선택 목록 (Picklist) 값 — "Sent Proposal Email", "Left Voicemail", "Scheduled Demo Call" — 사이에는 차이가 있습니다. 첫 번째 유형의 값은 레코드의 상태 (State)를 알려줍니다. 두 번째 유형은 어떤 활동 (Activity)이 일어났는지를 알려주며, 이는 활동 (Activity) 레코드가 담당해야 할 영역입니다.
선택 목록 (Picklist) 값이 프로세스 단계 (Process steps)를 인코딩하게 되면, 시간이 흐름에 따라 여러 가지 문제가 발생합니다. 영업 또는 서비스 프로세스의 모든 새로운 단계가 새로운 값으로 추가되기 때문에 선택 목록 (Picklist)이 계속해서 커집니다. 또한, 값이 상태 (States)가 아닌 동작 (Actions)을 나타내기 때문에 보고 (Reporting)가 복잡해집니다. 예를 들어 "제안 단계 (Proposal stage)"가 수십 개의 동작 기반 값들과 뒤섞여 있으면, "현재 제안 단계에 있는 기회 (Opportunities)가 몇 개인가?"와 같은 간단한 질문에 답하기 어려워집니다. 선택 목록 (Picklist) 값을 기반으로 실행되는 자동화 (Automation)는 값에 암묵적인 순서 가정이 포함되어 있기 때문에 취약해집니다.
해결책은 상태 (State) — 레코드가 무엇인지 — 와 활동 (Activity) — 레코드에 어떤 일이 일어났는지 — 를 명확하게 구분하는 것입니다. 상태 (State)는 선택 목록 (Picklists)에 넣습니다. 활동 (Activity)은 활동 (Activity) 레코드, 작업 (Tasks), 또는 별도의 추적 필드 (Tracking fields)에 넣습니다. 데이터 모델 설계 (Data model design) 단계에서 이러한 구분을 명확히 하면, 시간이 지날수록 Salesforce 조직 (Orgs)의 보고 (Reporting)를 점점 더 어렵게 만드는 선택 목록 (Picklist) 비대화 (Bloat)를 방지할 수 있습니다.
다대다 관계 (Many-to-Many Relationships)의 영향 무시하기
Salesforce의 표준 관계 (Relationship) 유형은 조회 (Lookup)와 마스터-세부 사항 (Master-detail)입니다. 이는 일대다 (One-to-many) 관계입니다. 깔끔하고 단순하지만, 상당수의 실제 비즈니스 요구 사항을 충족하기에는 불충분합니다.
다대다 관계 (Many-to-many relationships) — 여러 계정 (Accounts)에 속한 연락처 (Contact), 여러 케이스 (Cases)에 연결된 자산 (Asset), 여러 캠페인 (Campaigns)과 연관된 제품 (Product) — 를 구현하려면 교차 객체 (Junction objects)가 필요합니다. Salesforce는 커스텀 교차 객체 (Custom junction objects)를 통해 이를 지원하지만, 이를 제대로 구현하려면 즉각적으로 드러나지 않는 영향들을 깊이 고민해야 합니다.
연결 객체 (junction object)의 각 마스터-상세 (master-detail) 관계에서 어떤 객체가 마스터인지가 소유권 (ownership), 공유 (sharing), 그리고 연쇄 삭제 (cascade delete) 동작에 있어 매우 중요합니다. 이를 잘못 설정한다고 해서 구현 시점에 눈에 보이는 문제가 발생하지는 않습니다. 하지만 실제 데이터가 쌓이고, 실제 사용자들이 구현 단계에서 예상하지 못한 방식으로 연결 레코드를 사용하게 될 때, 데이터 무결성 (data integrity) 문제와 공유 동작의 예기치 못한 상황을 초래하게 됩니다.
이를 올바르게 수행하는 팀은 데이터 모델 설계 단계에서 연쇄 삭제 (cascade delete) 시나리오를 명시적으로 검토하며 시간을 할애합니다. 만약 연결 객체가 객체 A (Object A)와 마스터-상세 (master-detail) 관계를 맺고 있다면, 객체 A의 레코드를 삭제할 때 모든 연결 레코드가 연쇄적으로 삭제됩니다. 그리고 이를 통해 객체 B (Object B)와의 관계에도 영향을 미칠 수 있으며, 이는 실제 데이터에 적용되기 전에 반드시 이해되어야 하는 부분입니다.
이러한 작업은 흥미롭지 않습니다. 구현 일정이 촉박하고 테스트 데이터가 있는 샌드박스 (sandbox) 환경에서 모든 것이 올바르게 작동하는 것처럼 보일 때 흔히 생략되는 작업입니다. 하지만 바로 이런 작업이 실제 데이터 볼륨이 있는 운영 환경 (production)에서 데이터 무결성 사고를 일으키는 원인이 됩니다.
커스텀 객체 확산 (Custom Object Proliferation)
거의 모든 Salesforce 구현 과정의 어느 시점에, 누군가는 특정 사항을 추적하기 위해 커스텀 객체 (custom object)가 필요하다고 결정합니다. 그다음 또 다른 객체가 필요해지고, 또 그다음이 이어집니다. 각각의 결정은 개별적으로는 합리적입니다. 하지만 그 누적된 결과는 30개의 커스텀 객체, 객체 간의 불분명한 관계, 중복된 데이터, 그리고 일관성 없는 데이터 아키텍처 (data architecture)를 가진 조직 (org)이 됩니다.
커스텀 객체의 확산이 발생하는 이유는 커스텀 객체를 추가하는 즉각적인 비용은 낮지만, 장기적인 비용은 뒤로 미뤄지기 때문입니다. 새로운 객체를 만드는 것은 빠릅니다. 비용은 모든 객체가 서로 어떻게 연관되어 있는지 이해해야 할 때, 여러 객체에 걸친 보고서 (report)를 작성하려 할 때, 데이터 모델을 이해해야 하는 새로운 관리자 (admin)를 온보딩할 때, 또는 여러 객체의 레코드를 건드리는 프로세스를 자동화하려 할 때 비로소 나타납니다.
이를 방지하는 규율은 새로운 커스텀 객체 (Custom Object)를 생성하기 전에, 해당 데이터가 정말로 별도의 객체를 필요로 하는지, 아니면 기존 객체에 추가 필드나 자식 관계 (Child Relationship)를 통해 포함되는 것이 적절한지 질문하는 것입니다. 커스텀 객체는 데이터가 고유한 생명주기 (Lifecycle), 고유한 관계 (Relationships), 그리고 고유한 보고 (Reporting) 요구사항을 가질 때 의미가 있습니다. 기존 객체를 "어지럽히지" 않기 위해 단순히 몇 개의 필드를 추가하려는 목적으로 커스텀 객체를 사용하는 것은 적절하지 않습니다.
기존 객체에 추가 필드가 많아져 발생하는 혼란은 불필요한 커스텀 객체로 인해 발생하는 아키텍처 (Architectural) 차원의 혼란보다 훨씬 비용이 적게 듭니다. 페이지 레이아웃 (Page Layouts)과 필드 수준 보안 (Field-level Security)은 필드 가시성을 제어할 수 있습니다. 하지만 무분별하게 늘어난 객체 모델 (Object Model)에는 그에 상응하는 정리 메커니즘이 없습니다.
한때 한 사람에게만 의미 있었던 필드 명명 규칙
이 문제는 아키텍처적인 측면보다는 운영적인 측면에 가깝지만, 팀들에게 지속적으로 고통을 안겨줍니다.
Salesforce 필드에는 생성 시 설정되며 변경할 수 없는 API 이름 (API Name)이 있습니다. 레이블 (Label) — 즉 사용자가 보는 이름 — 은 변경할 수 있습니다. 하지만 자동화 (Automation), 코드 (Code), 그리고 통합 (Integrations)에서 참조하는 API 이름은 변경할 수 없습니다. 팀이 특정 방식으로 생각하며 "Initial_Contact_Date__c"로 생성한 필드는, 5년 뒤 그 개념이 다른 것으로 진화하더라도 모든 Flow, 모든 Apex 클래스 (Apex Class), 모든 API 통합 (API Integration), 그리고 모든 데이터 내보내기 (Data Export)에서 여전히 "Initial_Contact_Date__c"로 남아있게 됩니다.
필드 생성 시점에 API 이름에 대해 신중하게 생각하지 않는 팀은, 필드의 목적은 진화했지만 API 이름은 변경할 수 없었기 때문에 필드가 실제로 저장하는 내용과 API 이름이 더 이상 일치하지 않는 필드들을 쌓아가게 됩니다. 이는 최초 구현 팀 이후에 해당 조직 (Org)에서 작업하는 모든 개발자와 관리자 (Admin)에게 혼란을 야기합니다. 또한 코드를 읽기 어렵게 만들고, 데이터 내보내기 결과를 해석하기 어렵게 만듭니다.
필드 생성을 시작하기 전에 확립할 가치가 있는 관례는 다음과 같습니다: API 이름은 필드의 용도가 진화함에 따라 정확성을 유지할 수 있을 만큼 충분히 일반적(generic)이어야 하며, 레이블(label) 없이도 의미를 파악할 수 있을 만큼 충분히 구체적(specific)이어야 하고, 팀이 합의한 명명 규칙(naming convention)과 일관되어야 합니다. 이미 수백 개의 필드가 생성된 후에 이러한 관례를 확립하는 것은, 좀처럼 완전히 이루어지기 어려운 사후 수정(retrofitting) 작업이 됩니다.
데이터 모델 설계 단계에서 보고(Reporting)를 고려하지 않는 문제
보고(Reports)는 비즈니스가 Salesforce의 데이터로부터 가치를 얻는 메커니즘입니다. 만약 데이터 모델이 비즈니스에 필요한 보고를 지원하지 못한다면, 그 데이터 모델은 제 역할을 다하지 못한 것입니다.
이는 당연한 소리처럼 들립니다. 하지만 데이터 모델 설계 과정에서는 데이터 검색(data retrieval)보다는 데이터 입력(data entry)과 자동화(automation)에 집중하는 경향이 있어, 보고의 중요성이 지속적으로 과소평가됩니다. 데이터 입력을 깔끔하게 만드는 관계(Relationships)가 항상 보고를 깔끔하게 만드는 것은 아닙니다. 저장 효율성을 위해 정규화(normalized)된 데이터는 때때로 보고를 위해 역정규화(denormalized)되어야 할 수도 있습니다. 프로세스의 세부 사항을 아주 세밀한 수준(granular level)으로 캡처하는 필드들이 비즈니스 대시보드(dashboards)가 요구하는 방식대로 집계(aggregate)되지 않을 수도 있습니다.
이를 방지하는 규율은 데이터 모델 설계와 함께 보고 요구사항을 검토하는 것입니다. 모델이 구축된 후가 아니라 설계 단계에서 이루어져야 합니다. 이 조직이 답변할 수 있어야 하는 가장 중요한 질문 10가지는 무엇인가? 제안된 데이터 모델이 해당 보고서들을 지원할 수 있는가? 만약 그렇지 않다면, 무엇을 변경해야 하는가? 등을 자문해야 합니다.
이러한 논의는 구현(implementation) 단계에서 진행하기가 더 어렵습니다. 왜냐하면 비즈니스 이해관계자들은 무엇이 가능한지 실제로 보기 전까지는 보고 요구사항을 명확히 설명하지 못하는 경우가 많기 때문입니다. 이 문제를 잘 처리하는 구현 방식은 피드백 루프(feedback loop)를 생성합니다. 즉, 제안된 모델을 기반으로 예비 보고서를 작성하고, 이를 이해관계자들과 함께 검토하여, 데이터 모델이 운영 환경(production)에 배포된 후가 아니라 확정되기 전에 모델의 격차(gaps)를 식별하는 방식입니다.
결론
Salesforce 데이터 모델 실수는 시스템을 망가뜨리기 때문이 아니라, 시스템을 제약하기 때문에 비용이 많이 듭니다. 조직(org)은 여전히 작동합니다. 다만 요구사항이 진화함에 따라 비즈니스가 필요로 하는 방식으로 작동하지 않을 뿐입니다. 실제 데이터가 들어있고 그에 의존하는 실제 프로세스가 존재하는 데이터 모델을 변경하는 것은 진정으로 어려운 작업입니다.
이러한 문제를 방지하는 결정은 구현(implementation) 단계에서 내려지며, 종종 당시에는 이론적으로 느껴지는 대화 속에서 이루어집니다. 'Account(계정)의 정확한 정의는 무엇인가?', '이 Picklist(선택 목록)는 실제로 어떤 상태를 나타내는가?', '비즈니스에서 실행해야 할 보고서(reports)는 무엇인가?'와 같은 질문들입니다. 이러한 질문들은 화면(screens)과 자동화(automation)를 구축하는 가시적인 진척도보다 덜 시급하게 느껴질 수 있습니다. 하지만 이 질문들이 훨씬 더 중요합니다.
데이터 모델 설계를 단순한 사전 작업이 아니라, 그 위에 구축된 모든 것이 유지될 수 있는지를 결정하는 근간(foundation)이자 최우선 순위의 결과물(deliverable)로 취급하는 **Salesforce 개발 회사(Salesforce development company)**인 Hyperlink InfoSystem과 파트너 관계를 맺는 것이, 시스템 가동(go-live) 18개월 후의 결과를 바꿉니다.
당신을 괴롭히는 데이터 모델 실수는 당시에는 작은 결정처럼 느껴졌던 것들입니다. 그리고 그것들은 거의 항상 그렇습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기