소개
Odoo에서 양식을 저장할 때 어떤 칸이 붉게 변한 적이 있다면, 그건 바로 ‘필수 필드’ 기능을 경험한 것입니다. 이 기능은 Odoo 데이터 모델의 기초 중 하나로, 업무 흐름 전반에서 데이터 완전성을 확보하는 가장 간단하면서도 강력한 수단입니다.
영업팀용 설정을 하든, 자체 모델을 설계하든, 기술적 Odoo 개발 작업을 하든 required 속성의 동작 원리를 이해하면 더 신뢰할 수 있는 프로세스를 설계할 수 있습니다.
이 문서는 Odoo 프레임워크에서 필드가 어떻게 동작하는지, Odoo Studio나 Python 코드로 어떻게 설정하는지, 어떤 상황에서 사용하면 좋은지, 그리고 피해야 할 실수를 모두 다룹니다.
Odoo에서 ‘필수 필드’란 무엇인가
Odoo에서 required은 특정 필드에 값이 없으면 레코드를 저장하지 못하게 하는 필드 단위 제약입니다. 텍스트, 숫자, 선택(selection), many2one, 날짜 등 거의 모든 필드 유형에 적용할 수 있습니다.
이 속성은 핵심 데이터 모델의 일부이며 커스터마이즈 시 가장 자주 쓰이는 설정 중 하나입니다. 필드를 필수로 지정하는 것은 데이터베이스에 불완전하거나 일관성 없는 데이터가 들어오는 것을 막는 1차 방어선입니다.
인터페이스에서 보이는 방식
Odoo UI에서는 필수 필드가 선택 필드와 시각적으로 구분됩니다. 편집 모드에서 필수 필드는 화면상에 표시로 구분되고, 사용자가 빈 칸으로 저장을 시도하면 해당 필드가 붉게 강조되며 경고가 뜹니다.
이 피드백은 웹 인터페이스 전반에 걸쳐 일관되게 제공되어, 사용자들이 즉시 문제를 인지하고 불완전한 레코드를 제출할 가능성을 줄여줍니다.
정적 필수와 동적 필수의 차이
Odoo에서 필드를 필수로 만드는 방식은 두 가지입니다. 하나는 항상 값이 필요하도록 하는 '정적 필수'이고, 다른 하나는 다른 필드 값이나 상태에 따라 조건적으로 필수로 바뀌는 '동적 필수'입니다.
두 방식 모두 구현 현장에서 널리 사용됩니다. 어떤 것을 선택할지는 비즈니스 로직에 따라 결정됩니다.
필드 동작 방식
기술적 관점에서 required 속성의 동작 원리를 이해하면 올바르게 적용하고 문제가 생겼을 때 디버깅하기가 훨씬 쉬워집니다.
애플리케이션 수준의 제약
많은 사용자가 놀라는 사실 한 가지: required 속성은 데이터베이스 수준이 아니라 애플리케이션(ORM) 수준에서 동작합니다. 즉 레코드 생성·수정 시 ORM이 값을 검사하고, 그 결과에 따라 저장을 차단합니다.
기본적으로 required=True를 설정한다고 해서 PostgreSQL 컬럼에 자동으로 NOT NULL 제약이 추가되지는 않습니다. 검증 로직은 Python 내부, 즉 Odoo ORM 레이어에서 수행됩니다.
따라서 데이터베이스에 직접 값을 넣는 경우(ORM을 통하지 않는 경우) 이 제약에 걸리지 않습니다. ORM이나 API를 통해 Odoo 필드를 다루는 것이 이 보호를 받는 유일한 방법입니다.
제약을 위반하면 어떤 일이 발생하나
사용자가 필수 필드를 비워두고 저장을 시도하면 두 가지 일이 일어납니다:
- 인터페이스에서 해당 필드가 붉게 강조되고, Odoo가 검증 경고를 표시합니다.
- 필드가 채워질 때까지 저장 작업은 차단됩니다.
프로그램적으로 검증을 트리거하면(예: XML-RPC API나 서버 액션을 통해) Odoo는 어떤 필드가 비었는지 알려주는 ValidationError를 발생시킵니다.
도메인(조건식)을 이용한 동적 필수 설정
Odoo에서는 뷰 수준 표현식을 사용해 required를 조건부로 만들 수 있습니다. 이전 버전(예: Odoo 16까지)은 뷰 XML의 attrs를 통해 설정합니다.
예시: <field name="x_delivery_date" attrs="{'required': [('order_type', '=', 'delivery')]}" />
Odoo 17 이후에는 필드 태그에 직접 조건식을 넣는 더 간단한 문법을 지원합니다.
예시: <field name="x_delivery_date" required="order_type == 'delivery'" />
이들 조건은 모델이 아니라 뷰 레이어에 살아 있습니다. 즉 모델 수준의 required=True는 항상 제약을 적용하지만, 뷰 수준의 표현식은 특정 UI 상황에서만 유효하다는 중요한 차이가 있습니다.
ORM 및 API와의 상호작용
Odoo ORM에서 create()나 write()를 호출하면 ORM이 모델에 정의된 모든 required=True 필드를 확인합니다. 필수가 없거나 False로 들어오면 ValidationError가 발생합니다.
XML-RPC 같은 API를 통해 레코드를 생성할 때도 동일합니다. 모델 정의상 필수로 표시된 필드는 create 호출 시 데이터에 반드시 포함되어야 하며, 누락되면 호출이 실패합니다.
비즈니스 적용 사례
표준 Odoo 전반에 걸쳐 필수 필드는 널리 사용되며, 커스터마이즈 시에도 큰 가치를 제공합니다. 아래는 실제 업무에서 필수 필드가 효과를 발휘하는 다섯 가지 사례입니다.
1. CRM: 리드에 고객 세그먼트 필수화
영업팀이 파이프라인으로 옮기기 전에 모든 리드에 고객 세그먼트를 할당하도록 보장하고 싶을 때가 있습니다. 필수가 없으면 영업 담당자가 이를 생략해 이후에 세그먼트별 리드 분석이 불가능해집니다.
CRM 리드 폼에 '고객 세그먼트' 선택 필드를 필수로 설정하면 입력 시점에 반드시 데이터가 들어오게 되어 누락을 방지할 수 있습니다.
2. 영업: 주문서에 배송지 필수화
실물 배송이 있는 회사에서는 배송지가 필수적입니다. 일부 Odoo 설정에서는 배송지가 기본적으로 필수가 아니어서 배송지 없이 주문이 확정되는 경우가 있습니다.
판매 주문 폼에서 배송지 필드를 필수로 하면 물류팀에 정보가 전달되기 전 주문 확정을 막아 이행 과정에서 발생하는 오류를 줄일 수 있습니다.
3. 재고: 입고 시 로트/시리얼 넘버 필수화
식품, 제약, 전자제품 등 규제가 있는 산업에서는 입고 시 로트나 시리얼 번호 추적이 필수입니다. Odoo는 제품 추적 설정을 통해 재고 이동 중 로트/시리얼 번호 입력을 사실상 강제할 수 있습니다.
입고 폼에 품질검사 참고번호 같은 커스텀 필드를 추가하고 이를 필수로 지정하면 창고팀이 입고 시 반드시 기록하게 만들어 추적성을 확보할 수 있습니다.
4. 회계: 매입 전표에 비용센터 필수화
재무팀은 지출을 예산 단위(비용센터)로 반드시 분류해야 하는 경우가 많습니다. 강제하지 않으면 담당자가 필드를 비워 두어 재무 보고에 공백이 생깁니다.
매입 전표 폼에 비용센터를 가리키는 many2one 필드를 필수로 추가하면 전표를 기표하기 전에 반드시 분류가 이루어지도록 할 수 있어 데이터 완결성이 향상됩니다.
5. 인사: 온보딩 전 근로계약 유형 필수화
HR 팀은 직원 레코드가 확정되기 전에 계약 유형을 반드시 기록하기를 원할 수 있습니다. 직원 폼에 계약 유형을 필수로 설정하면 바쁜 온보딩 과정에서 핵심 정보가 빠지는 일을 막을 수 있습니다.
필드 생성 및 커스터마이즈 방법
Odoo에서 필드를 필수로 만드는 방법은 크게 두 가지입니다: 코드 없이 가능한 Odoo Studio를 사용하는 방법과, 세부 제어가 가능한 Python 코드를 작성하는 방법입니다. 상황에 맞게 선택하면 됩니다.
Odoo Studio 사용하기
Odoo Studio는 개발 지식 없이 필드를 설정할 수 있는 내장 도구입니다. Studio를 열어 폼에서 필드를 선택하면 오른쪽 속성 패널에 'Required' 토글이 나타납니다.
이 토글을 켜면 뷰 수준에서 필드가 필수로 표시되고, 모델 레벨에도 해당 설정이 저장됩니다. 간단한 케이스에서는 가장 빠른 방법으로 표준 필드나 Studio로 추가한 커스텀 필드 모두에 적용할 수 있습니다.
다만 Studio로는 정적 필수 제약만 설정할 수 있다는 한계가 있습니다. 다른 필드 값에 따라 동적으로 필수가 바뀌어야 한다면 뷰 XML을 직접 수정하거나 기술적 방법을 사용해야 합니다.
기술적 방법: Python 필드 정의
커스텀 모듈에서 필드를 선언할 때는 필드 정의에 required=True를 추가하면 됩니다. 이것이 Odoo Python 필드 개발에서의 표준 패턴입니다.
예시(개념적): from odoo import fields, models
class SaleOrder(models.Model):
_inherit = 'sale.order'
x_customer_segment = fields.Selection(
selection=[
('smb', 'SMB'),
('enterprise', 'Enterprise'),
('public', 'Public Sector'),
],
string='Customer Segment',
required=True,
)
x_cost_center_id = fields.Many2one(
comodel_name='account.analytic.account',
string='Cost Center',
required=True,
)
이 방식은 모델 수준에서 제약을 강제하므로 어떤 뷰나 인터페이스로 레코드가 생성되든 해당 규칙이 적용됩니다. 뷰만 수정하는 것보다 우회가 불가능합니다.
뷰 XML에서 동적 필수 구현하기
특정 조건에서만 필수가 적용되어야 한다면 모델이 아니라 뷰 수준에 조건식을 넣으세요. Odoo 16에서는 다음과 같은 방식입니다.
예시: <field name="x_cost_center_id"
attrs="{'required': [('order_type', '=', 'invoiced')]}" />
Odoo 17에서는 문법이 더 간단합니다.
예시: <field name="x_cost_center_id"
required="order_type == 'invoiced'" />
이 설정은 특정 뷰에서만 유효한 제약입니다. 따라서 API나 다른 뷰로 레코드를 만드는 경우에는 적용되지 않을 수 있습니다.
API로 필드를 생성하고 필수로 표시하기
XML-RPC API를 통해 프로그램적으로 필드를 생성할 때 ir.model.fields에 required 값을 포함하면 필드를 생성하면서 필수 제약을 함께 정의할 수 있습니다. 이는 자동화된 배포 워크플로에서 유용합니다.
예시(개념적): models.execute_kw(ODOO_DB, uid, ODOO_API_KEY,
'ir.model.fields', 'create',
[{
'name': 'x_customer_segment',
'field_description': 'Customer Segment',
'model_id': model_id,
'ttype': 'selection',
'selection': "[('smb', 'SMB'), ('enterprise', 'Enterprise')]",
'required': True,
'state': 'manual',
}]
)
이렇게 하면 필드 생성과 동시에 필수 제약을 적용할 수 있어 대규모 자동화 배포 시 편리합니다.
권장 관행
필드를 필수로 만드는 건 겉보기엔 단순하지만 현명하게 사용하려면 몇 가지 원칙을 지켜야 합니다. 다음 권장 관행들이 시간 절약과 사용자 불만을 줄이는 데 도움됩니다.
1. 진짜로 필요한 경우에만 필드를 필수로 만들자
필드를 불필요하게 필수로 만드는 것은 흔한 실수입니다. 필요한 정보가 입력 시점에 항상 준비되지 않는다면 사용자는 임시값을 넣거나 우회하는 방법을 찾아 데이터가 오염될 수 있습니다.
필수로 지정하기 전에 그 정보가 실제로 입력 시점에 항상 존재하는지 질문하세요. 만약 그렇지 않다면 생성 시점이 아니라 확인(confirmation) 시점에 필수화하거나 동적 필수 설정을 고려하세요.
2. 항상 필수로 하기보다 단계별 검증을 사용하라
영업기회나 제조 주문처럼 단계가 많은 워크플로우에서는 모든 필드를 처음부터 필수로 하기보다 특정 단계에서 검증을 수행하는 편이 좋습니다. Python 제약이나 자동화 액션을 사용해 레코드가 특정 단계로 넘어갈 때 검사하도록 구현하세요.
이 패턴은 사용자 경험 측면에서 더 유연하고 실용적입니다.
3. 합리적일 때는 기본값을 함께 제공하라
대부분 경우에 유효한 기본값이 있다면 필수 필드에 default를 설정하세요. 사용자의 입력 부담을 줄이면서 필드가 비어있지 않도록 보장할 수 있습니다.
4. 중요한 데이터는 모델 수준에서 강제하라
회계, 규제 식별자처럼 반드시 필요하고 회피하면 안 되는 데이터는 뷰 수준이 아니라 Python 모델 정의에서 required=True로 강제하세요. 뷰 수준 제약은 API나 다른 뷰를 통해 우회될 수 있습니다.
5. 사용자에게 필수 변경사항을 사전 안내하라
이미 작업 중인 사용자에게 폼에 새 필수를 추가하면 편집 시 갑자기 검증 오류가 발생할 수 있습니다. 배포 전에 팀에게 변경사항을 공지하고 영향을 문서화하세요.
6. 빈 값과 부분 데이터로 충분히 테스트하라
새 필수를 적용하기 전에는 웹 UI, API, 자동화된 통합 시나리오까지 포함해 빈 값과 부분 데이터로 전체 워크플로를 테스트하세요. 이는 책임 있는 Odoo 구현의 기본입니다.
흔한 실수와 주의사항
경험 많은 구현자들도 필수 필드로 인해 문제를 겪습니다. 무엇을 주의해야 할지 알면 디버깅 시간이 크게 줄어듭니다.
실수 1: 이미 레코드가 많은 모델에 필수 속성 추가하기
이미 수천 건의 레코드가 존재하는 모델에 required=True를 추가하면, 기존 레코드에 값이 없을 경우 이후 편집 시 저장이 불가능해져 문제가 발생합니다.
기존 레코드의 값 유무를 먼저 확인하고, 비어 있다면 배포 전에 데이터 마이그레이션으로 값을 채운 뒤 제약을 적용하세요.
실수 2: 뷰 수준과 모델 수준 필수 혼동하기
뷰에서 필드를 필수로 설정해도 그것은 그 뷰에서만 유효합니다. API, 다른 뷰, 데이터 임포트 등은 뷰 수준 제약을 우회할 수 있습니다.
하드 제약이 필요하면 Python 필드 정의에서 required=True로 설정하세요. 이 점을 오해하면 프로덕션에서 데이터 품질 문제가 생기기 쉽습니다.
실수 3: 자동화·스케줄 액션에서의 누락
자동화 액션이나 스케줄러가 코드로 레코드를 생성할 때는 모든 필수 필드를 제공해야 합니다. 필수 필드 추가 전에 자동화 코드를 검토하지 않으면 해당 작업이 실패하거나 오류를 내기 시작합니다.
필수 필드를 추가한 후에는 자동으로 레코드를 생성하는 모든 코드 경로를 점검하세요.
실수 4: 임포트 파일에 필수 필드 누락
CSV나 Odoo 임포트 도구로 데이터를 가져올 때 필수 필드가 빠져 있으면 임포트가 실패합니다. 이는 올바른 동작이지만 사용자에게는 놀라울 수 있습니다.
임포트 템플릿에 모든 필수 필드를 포함시키고, 데이터 임포트 가이드에 어떤 필드가 필수인지 명확히 문서화하세요.
실수 5: 복잡한 검증을 required로 대신하려는 시도
required는 단지 필드가 비어있지 않은지를 검사할 뿐, 값의 유효성이나 필드 간 관계까지 확인하지 않습니다. 날짜가 미래여야 한다거나 두 필드의 조합이 일관되어야 하는 등 복잡한 검증은 Python의 @api.constrains 같은 제약으로 구현해야 합니다.
필수 속성만으로 비즈니스 로직을 억지로 구현하면 유지보수가 어렵고 유연성이 떨어지는 시스템이 됩니다.
맺음말
요약하면, '필수 필드'는 단순하지만 데이터 품질에 큰 영향을 미치는 도구입니다. 적절히 사용하면 중요한 정보를 적재적소에 확보할 수 있고, 잘못 사용하면 사용자 불만과 데이터 오염을 초래합니다.
핵심은 모델 수준과 뷰 수준의 차이를 이해하고, 정말로 강제해야 할 필드를 신중히 선택하며 자동화·API 연동까지 고려해 미리 대비하는 것입니다.
Odoo 개발 가이드를 따르든, 커스터마이즈 프로젝트를 진행하든, 혹은 Odoo Studio에서 폼을 수정하든 required 속성은 확실히 이해할 가치가 있는 설정입니다. 제대로 설계된 구현과 그렇지 않은 구현을 나누는 작은 차이입니다.
Odoo 도입에 도움이 필요하신가요?
저희 Dasolo는 기업이 Odoo를 도입·커스터마이즈·최적화하는 일을 도와드립니다. 데이터 모델 설계, 검증 규칙 구성, 커스텀 모듈 개발 등 모든 프로젝트에 기능적·기술적 전문성을 제공합니다.
필수 필드나 Odoo 설정의 다른 부분에 대해 질문이 있으시면 언제든 도와드리겠습니다. 문의하기 어떤 솔루션을 만들고 계신지 함께 이야기해봅시다.