소개
Odoo 개발을 시작하면 반드시 마주치는 개념이 하나 있습니다: 상속. Odoo의 ORM은 모델들끼리 필드와 동작을 공유하고 확장할 수 있게 설계되어 있어, 불필요한 데이터 중복이나 반복 코드를 줄이는 데 초점이 맞춰져 있습니다.
상속된 필드는 이 체계의 핵심입니다. 한 모델이 실제로는 다른 모델에 저장된 필드를 그대로 노출하거나 사용하게 만드는 방식이 바로 그것입니다. 이 메커니즘을 이해하면 Odoo의 데이터 구조가 훨씬 직관적으로 느껴집니다.
이 글에서는 상속된 필드의 개념, 상속을 만들어내는 세 가지 방식, 데이터베이스 관점에서의 동작, 그리고 실무 커스터마이징에서의 활용법을 설명합니다.
Odoo에서 '상속된 필드'란 무엇인가?
Odoo ORM에서 어떤 필드가 '상속된' 것으로 간주되려면, 모델이 세 가지 상속 방식 중 하나를 통해 다른 모델의 필드에 접근할 수 있어야 합니다. 기존 필드를 처음부터 다시 정의하는 대신, 자식 모델이 그 필드를 재사용합니다.
이 방식 덕분에 Odoo 데이터 모델은 간결하고 일관성을 유지합니다. 예를 들어 고객명(name) 같은 핵심 필드는 파트너(res.partner)에 한 번만 정의되며, 판매 주문, CRM 기록, 청구서 등 여러 곳에서 동일한 원본 데이터를 참조합니다.
Odoo에서 지원하는 세 가지 상속 유형
Odoo는 서로 다른 방식으로 필드를 처리하는 세 가지 모델 상속 유형을 지원합니다.
1. 클래식(직접) 상속
가장 흔히 쓰이는 패턴입니다. _inherit를 사용하되 새로운 _name을 선언하지 않으면 기존 모델을 그대로 확장합니다. 새 테이블을 만들지 않으며, 추가한 필드들은 원래 모델의 테이블에 바로 추가됩니다.
표준 모델에 필드를 추가할 때 대부분 이 방법을 사용합니다. 예컨대 CRM 모듈은 res.partner에 CRM 관련 필드를 클래식 상속으로 더하고, 그 필드들은 파트너 테이블에 함께 저장됩니다.
2. 프로토타입 상속
_inherit와 _name을 동시에 쓰는 패턴입니다. 부모 모델의 구조를 복사해 새 모델을 만들고, 별도의 데이터베이스 테이블을 갖습니다. 부모의 필드들이 자식 모델에 복제되기 때문에 자식의 변경은 부모에 영향을 주지 않습니다.
일상적인 단순 커스터마이징에서는 덜 쓰이지만, 기존 모델 구조를 그대로 따르면서 완전히 별도의 모델을 만들고 싶을 때 유용합니다.
3. 위임(Delegate) 상속
가장 특이한 형태로, _inherits로 정의합니다. 자식 모델은 Many2one 필드로 부모 모델과 연결하고, 부모의 모든 필드를 자식의 것처럼 투명하게 노출합니다. 실제 데이터는 자식 테이블이 아닌 부모 테이블에 저장됩니다.
개발자들이 엄밀히 말하는 '상속된 필드'는 보통 이 위임 상속을 의미합니다. 필드가 복제되는 대신 관계를 통해 읽고 쓰기가 이뤄집니다.
표준 Odoo에서 잘 알려진 예는 res.users와 res.partner 관계입니다. 모든 사용자는 파트너이기도 해서 이름, 이메일, 전화번호 등은 파트너 레코드에 저장되며 사용자 모델은 이를 위임 방식으로 참조합니다.
필드의 동작 원리
위임 상속의 내부 동작
모델에서 _inherits를 사용하면 ORM은 두 테이블 사이에 투명한 다리 역할을 만듭니다. 자식 레코드에서 상속된 필드를 읽으면 ORM이 Many2one 링크를 따라 부모 레코드를 찾아 그 값을 반환합니다.
자식 모델에서 상속된 필드에 쓰기 작업을 하면 ORM이 부모 레코드에 직접 값을 씁니다. 개발자 관점에서는 해당 필드가 자식의 고유 필드처럼 느껴지지만, 데이터베이스 관점에서는 값이 부모 테이블에 위치합니다.
이 방식의 중요한 결과는 데이터 중복이 없다는 것입니다. 파트너의 이름을 고치면 그 파트너를 참조하는 모든 곳—예를 들어 그 파트너를 위임한 사용자 레코드에도—바로 반영됩니다.
클래식 상속과 데이터베이스
클래식 상속에서는 새 필드가 원래 모델의 데이터베이스 테이블에 추가됩니다. 별도의 테이블은 생기지 않으므로 확장된 모델과 원래 모델은 DB 수준에서 동일한 엔터티입니다. Odoo 커스터마이징에서 필드를 추가하는 가장 깔끔하고 흔한 방식입니다.
연결(related) 필드는 가벼운 상속처럼 활용 가능
related 필드는 연산(computed) 필드의 한 종류로, 관계 체인을 통해 연결된 레코드의 값을 읽어옵니다. 모델 상속과 완전히 동일하진 않지만, 모델 구조를 바꾸지 않고도 다른 모델의 데이터를 노출할 때 자주 쓰입니다.
예를 들어 판매 주문에 partner_country_id라는 필드를 만들어 partner_id.country_id를 읽어오게 할 수 있습니다. 그 필드는 판매주문 상에서는 네이티브 필드처럼 동작하지만 값은 파트너에서 옵니다.
관련 필드는 store=True로 데이터베이스에 저장할 수 있는데, 검색·필터 성능은 좋아지지만 저장 용량이 늘고 원본이 바뀔 때 재계산이 필요합니다.
런타임에서 필드가 어떻게 결정되는가
Odoo는 모델 정의를 불러올 때 모든 상속 필드를 포함한 전체 필드 맵을 해석합니다. 모델이 사용 준비가 완료되면 각 필드가 어떤 소스에서 오는지(자기 테이블인지, 위임된 부모 테이블인지, 혹은 관련 체인인지) 이미 매핑되어 있습니다. 이 해석은 서버 시작 시 한 번 이루어지고 캐시되어 성능을 높입니다.
비즈니스 적용 사례
상속된 필드는 단순한 기술 개념이 아닙니다. 데이터 중복 없이 서로 다른 앱 영역에서 일관된 정보를 유지하게 해주는 실무적 해법입니다.
1. CRM과 영업: 연락처 정보 공유
영업 담당자가 CRM에서 리드를 만들면 고객명, 전화, 이메일 같은 연락처 정보는 파트너 레코드를 통해 제공됩니다. 파트너 정보가 바뀌면 그 파트너와 연결된 모든 리드, 견적, 주문에 즉시 반영됩니다.
이것은 클래식 상속과 관련 필드가 함께 작동한 예입니다. CRM 모듈은 res.partner에 CRM 전용 필드를 더하고, 판매 주문은 관련 필드를 통해 파트너 연락처를 보여줍니다.
2. 제품과 변형(Variants) 관리
제품 모델은 위임 상속을 사용해 제품 템플릿과 개별 변형을 깔끔하게 분리합니다. product.product(변형) 모델은 product.template에 대해 _inherits를 사용해 연결하고, 공통 필드는 템플릿에, 변형 전용 필드는 변형 레코드에 둡니다.
이 구조 덕분에 티셔츠의 색상 변형이 50개 있어도 이름·설명 같은 공통 정보는 템플릿에 한 번만 저장하면 됩니다.
3. 사용자와 연락처
모든 Odoo 사용자는 연락처에 해당합니다. res.users는 _inherits = {'res.partner': 'partner_id'}로 설정되어 있어 사용자는 파트너의 이름, 이메일, 전화, 주소, 프로필 사진 등을 자동으로 상속받습니다. 직원 이메일을 바꾸면 사용자 설정과 연락처 양쪽에 동시에 반영됩니다.
4. 재고: 이동 라인과 제품 정보
재고 모듈의 출고/입고 이동 라인은 제품 설명을 템플릿에서 이어받아 표시합니다. 창고 관리자는 피킹리스트에서 중복 없이 최신 제품 정보를 바로 확인할 수 있습니다.
5. 회계: 청구서 라인
회계의 회계분개 라인은 제품과 파트너를 참조합니다. 청구서 라인에 보이는 제품명, 계정 코드, 세금 설정 등은 제품·파트너 모델의 값을 연결을 통해 가져온 것입니다. 회계팀은 판매팀이 제품에 설정한 값과 일치하는 데이터를 보게 됩니다.
상속된 필드 만들기 및 커스터마이징
Odoo Studio 사용하기
Odoo Studio는 코드 없이 모델과 뷰를 커스터마이즈할 수 있는 도구입니다. Studio에서 새 필드를 추가하면 내부적으로는 클래식 상속 방식으로 모델 정의를 확장합니다.
Studio는 또한 관련 필드(related field)를 만들 수 있게 합니다. ‘Related Field’ 타입을 선택하면 현재 모델에서 접근 가능한 관계 체인의 어느 필드든 가리킬 수 있습니다. 예컨대 판매주문에서 고객의 국가나 VAT 번호를 파트너에서 바로 읽어오는 필드를 만들 수 있습니다.
많은 비즈니스 사용자와 기능 컨설턴트에게 Studio는 필드를 추가하기에 적합한 도구입니다. 필드 명명, DB 마이그레이션, 뷰 배치 등을 자동으로 처리합니다.
개발자용 기술 커스터마이징(Python)
개발자가 모듈을 만들 때는 Odoo ORM의 models.Model 클래스를 이용해 파이썬으로 상속 필드를 정의합니다.
기존 모델에 커스텀 필드를 추가하는 클래식 상속 예:
class CrmLead(models.Model):
_inherit = 'crm.lead'
x_contract_value = fields.Float(
string='Estimated Contract Value'
)
기존 모델의 필드를 공유하는 새 모델을 만드는 위임 상속 예:
class EmployeeProfile(models.Model):
_name = 'hr.employee.profile'
_inherits = {'res.partner': 'partner_id'}
partner_id = fields.Many2one(
'res.partner',
required=True,
ondelete='cascade'
)
employee_id = fields.Many2one(
'hr.employee',
string='Employee'
)
연결 필드로 링크된 값 노출하기 예:
class SaleOrder(models.Model):
_inherit = 'sale.order'
partner_country_id = fields.Many2one(
related='partner_id.country_id',
string='Customer Country',
store=True
)
XML-RPC API를 통한 원격 구성
필드는 Odoo의 XML-RPC API와 ir.model.fields 모델을 사용해 프로그래매틱하게도 추가할 수 있습니다. Dasolo의 원격 구성 노트북들이 이 방식을 사용합니다. Studio와 동등한 작업을 서버 접속 없이 자동화된 방식으로 수행할 수 있습니다.
API로 관련 필드를 추가하려면 적절한 타입(예: ttype='many2one')으로 ir.model.fields 레코드를 만들고 related 파라미터로 관계 경로를 지정하면 됩니다. 자동화된 배포나 초기 설정 과정에 적합한 방법입니다.
권장 실무(베스트 프랙티스)
작업에 맞는 상속 유형 선택하기
기존 모델에 단순히 필드나 메소드를 추가하려면 클래식 상속을 쓰세요. 가장 간단하고 대부분의 모듈이 이 방식을 따릅니다. 반면 어떤 비즈니스 개념이 연락처를 확장하되 독립적인 레코드 집합이 필요하다면 위임 상속이 적합합니다.
읽기 전용 노출에는 관련 필드를 권장
단지 폼이나 리스트에 다른 레코드의 값을 표시하려면 관련 필드가 위임 상속보다 깔끔합니다. 구조적 의존성을 만들지 않으면서 이해·유지 보수가 쉽습니다.
관련 필드에 store=True를 쓸 때 주의
관련 필드를 DB에 저장하면 검색·필터가 빨라지지만 복사본이 생기기 때문에 원본과 일시적으로 불일치가 발생할 수 있습니다. 대량 데이터에서 필터·정렬이 필요할 때만 신중히 사용하세요.
커스텀 필드는 반드시 x_ 접두어 사용
표준 Odoo 모델에 필드를 추가할 때는 필드명을 x_로 시작하거나 모듈 네임스페이스를 사용하세요. 이렇게 하면 향후 Odoo 버전에서 동일한 이름의 필드와 충돌하는 일을 예방할 수 있습니다.
상속 체인을 문서화하라
복잡한 커스터마이징에서는 필드의 출처와 이유를 간단히 메모로 남기세요. 판매주문의 x_country_code가 실제로 partner_id.country_id.code에서 온다는 것을 문서화해 두면 이후 유지보수에 큰 도움이 됩니다.
삭제 시 연쇄(ondelete) 처리 올바르게 설정
위임 상속에서는 자식 생성 시 부모 레코드가 자동으로 생성됩니다. 자식을 삭제할 때 부모도 함께 삭제되어야 한다면 _inherits의 Many2one에 ondelete='cascade'를 설정해 고아 레코드가 남지 않게 하세요.
자주 발생하는 실수들
세 가지 상속을 혼동하는 실수
초보 개발자가 가장 자주 범하는 실수는 클래식 확장을 의도했는데 _inherit와 함께 _name을 넣어 버려 전혀 새로운 모델을 만드는 경우입니다. 새 모델을 만들 의도가 없다면 _name을 생략했는지 반드시 확인하세요.
_inherits에서 부모 레코드 생성 누락
위임 상속으로 자식 레코드를 만들 때 모든 상황에서 부모가 자동 생성되는 것은 아닙니다. API나 스크립트로 레코드를 만들면 ORM이 부모 생성을 처리하도록 하거나 먼저 부모를 생성해 그 ID를 전달해야 합니다. 이 단계를 빼면 DB 제약 오류가 발생합니다.
상속으로 필드 타입을 변경하려는 시도
상속을 통해 기존 필드의 타입을 바꾸는 것은 허용되지 않습니다. 라벨이나 도메인, 도움말 같은 속성은 바꿀 수 있지만 Char를 Integer로 바꾸는 것은 불가능합니다. 시도하면 모듈 설치 오류가 납니다.
저장된 관련 필드의 남용
성능 때문에 모든 관련 필드에 store=True를 붙이는 유혹이 있지만, DB 크기를 키우고 유지보수 부담을 늘립니다. 원본 필드가 자주 바뀌면 재계산 작업이 많아집니다. 대규모 필터·집계가 실제 필요할 때만 사용하세요.
상속 필드가 자식 모델의 접근 제약을 따를 것이라는 오해
위임으로 상속된 필드는 부모 모델에 저장됩니다. 자식 모델에 설정한 접근 권한이 자동으로 부모 필드에 적용되지 않을 수 있습니다. 자식 모델 접근을 제한하면 부모 모델을 통해 해당 값에 접근할 수 있는 경우가 있으므로 위임 상속을 쓸 때는 양쪽 모델의 보안 규칙을 반드시 검토하세요.
맺음말
상속된 필드는 Odoo 아키텍처의 기본 요소입니다. 사용자와 파트너, 제품 템플릿과 변형, 판매 주문과 고객 연락처 같은 관계들이 모두 상속된 필드를 활용해 데이터 일관성을 유지하고 중복을 피합니다.
어떤 상속 방식을 쓸지, 언제 관련 필드를 대신 사용할지, 그리고 이들 메커니즘이 DB에서 어떻게 동작하는지를 알면 Odoo 커스터마이징 업무를 훨씬 효율적으로 할 수 있습니다. 더 깔끔한 코드, 더 나은 데이터 모델 설계, 그리고 예상 못한 필드 문제를 디버그하는 시간을 줄일 수 있습니다.
핵심 메시지는 단순합니다: Odoo ORM은 필드를 한 곳에 저장하고 여러 곳에서 접근하도록 설계되어 있습니다. 이것이 바로 상속된 필드의 원리이며, 수십 개 모듈이 서로 얽혀 있어도 데이터 모델을 일관되게 유지하는 비결입니다.
Odoo 도입 지원이 필요하신가요?
Dasolo는 기업들이 Odoo를 도입·커스터마이즈·최적화하도록 도와드립니다. 맞춤 모듈을 새로 만들든, 표준 모델을 확장하든, 또는 데이터 모델 동작이 이상할 때 원인을 찾든, 현장 경험이 풍부한 저희 팀이 빠르게 진행하고 비용이 큰 실수를 피하도록 도와드립니다.
Odoo 데이터 모델, 커스텀 필드 전략, 기술적 구현 관련 질문이 있다면 상황을 같이 검토해 드리겠습니다. 문의하기 귀사의 프로젝트에 맞는 최적의 접근법을 함께 찾아보겠습니다.