소개
Odoo에서 영업 주문서, 프로젝트 작업, 제조 오더를 열어본 적이 있다면 이미 Date 필드를 사용해본 것입니다. 마감일, 배송일, 송장 납기일, 계약 시작일 등 시간 기반 정보의 대부분이 이 필드 타입으로 관리됩니다.
사용자 입장에서는 Date 필드가 직관적입니다: 클릭하면 달력이 뜨고 날짜를 고르면 끝이죠. 하지만 내부적으로는 더 많은 처리가 일어납니다. Odoo 데이터 모델에서 Date 필드가 어떻게 저장되는지, DateTime 필드와 어떤 점에서 다른지, 그리고 필드를 어떻게 만들고 수정해야 안정적인 워크플로를 설계할 수 있는지를 이해하면 시간대 관련 문제와 같은 흔한 함정을 피할 수 있습니다.
이 가이드는 Odoo의 Date 필드에 대해 알아야 할 핵심 내용을 한데 모았습니다. 저장 방식, UI 표기, ORM 상호작용, 실무 사례와 구현 팁까지 실무에 바로 쓸 수 있도록 정리했습니다.
Odoo의 Date 필드는 무엇인가
Odoo ORM에서 fields.Date는 시간 정보(시, 분, 초)를 포함하지 않는 캘린더 날짜만 저장하도록 설계된 타입입니다. 예컨대 "2026-03-06"은 그 형태 그대로 저장되며 시간이 붙지 않습니다.
Odoo가 사용하는 PostgreSQL에서는 Date 필드가 데이터베이스의 DATE 타입으로 매핑됩니다. 반면 DateTime은 TIMESTAMP로 저장되어 시각 정보까지 포함합니다.
인터페이스에서는 Date 필드가 텍스트 입력과 달력 선택 도구로 표시됩니다. 사용자는 직접 입력하거나 달력에서 선택할 수 있으며, 리스트 뷰에서는 사용자의 로케일에 맞춰 포맷된 텍스트로 보입니다.
커스텀 Python 모듈에서 Date 필드를 정의하면 보통 다음과 같은 형태로 작성됩니다:
from odoo import fields, models
class ProjectTask(models.Model):
_inherit = 'project.task'
x_deadline_confirmed = fields.Date(
string='Confirmed Deadline',
help='The officially confirmed deadline agreed with the customer.',
)
Odoo Studio로 만든 필드는 단순히 Date로 표기되며, 생성 시 자동으로 x_studio_ 접두사가 붙습니다. 반면 Python 코드나 XML-RPC로 만들면 기술적 이름은 개발자가 직접 정합니다.
필드의 동작 원리
Date와 DateTime: 실제로 무엇이 다른가
Date 필드의 핵심은 포함하지 않는 것입니다: 시간 정보가 없습니다. 시·분·초도 없고 시간대 변환도 없으며, 오직 순수한 날짜만 저장됩니다.
반대로 DateTime 필드는 데이터베이스에 UTC 기준으로 타임스탬프를 저장하고, 화면에 표시할 때 사용자의 로컬 시간대로 변환합니다. 이 변환 과정이 ‘하루가 밀렸다’ 같은 혼란의 주된 원인입니다. 대부분의 경우 이런 문제는 Date 필드가 아니라 DateTime 필드에서 발생합니다.
Date 필드를 사용하면 저장된 값이 곧 사용자에게 보이는 값입니다. 예를 들어 2026-03-15로 저장된 값은 전세계 모든 사용자에게 동일하게 2026-03-15로 표시됩니다.
주요 필드 속성
Odoo에서 Date 필드에 설정할 수 있는 중요한 속성들은 다음과 같습니다.
- required: 인터페이스와 모델 레벨에서 필드를 필수로 만듭니다.
- default: 자동 기본값을 설정합니다. 예:
fields.Date.today는 새 레코드의 기본값을 오늘 날짜로 합니다. - index: 데이터베이스 인덱스를 생성하여 필드로 하는 검색과 필터링을 빠르게 합니다.
- compute: 값을 계산해 주는 Python 메서드와 연결합니다.
- store:
compute와 함께 사용하면 계산된 값을 데이터베이스에 저장합니다. - readonly: 사용자 인터페이스에서 직접 수정하지 못하게 합니다.
- copy: 레코드를 복제할 때 값이 복사될지 여부를 제어합니다. 기본값은
True입니다.
뷰에서의 표시 방식
폼 뷰에서는 텍스트 입력과 달력 팝업으로, 리스트 뷰에서는 포맷된 텍스트로 표시됩니다. 검색(필터) 뷰에서는 "이번 주", "이번 달", "이번 분기" 같은 범위 필터와 특정 날짜 이전/이후/당일 같은 비교 연산을 기본 지원합니다.
표시 형식은 사용자의 언어 설정을 따릅니다. 미국 사용자는 MM/DD/YYYY, 유럽권 사용자는 DD/MM/YYYY로 보지만, 내부 저장 값은 항상 ISO 형식(YYYY-MM-DD)입니다.
Odoo ORM과의 상호작용
코드에서 Date 필드를 읽으면 Odoo ORM은 Python의 datetime.date 객체를 반환하고, 값이 없으면 False를 반환합니다. 쓸 때는 datetime.date 객체나 "YYYY-MM-DD" 형식의 문자열을 전달하면 됩니다. XML-RPC는 날짜를 항상 문자열로 주고받습니다.
Date 필드를 다룰 때 복잡한 변환은 거의 필요하지 않습니다. 프레임워크가 포맷과 저장을 자동으로 처리하므로 일상 개발에서 매우 실용적입니다.
업무 적용 사례
Date 필드는 Odoo 전반에 걸쳐 널리 사용됩니다. 실무에서 자주 쓰이는 다섯 가지 예를 소개합니다.
CRM: 계약 시작일과 종료일 관리
영업에서는 계약의 시작일과 종료일을 관리하는 경우가 많습니다. 리드나 계약 모델에 Date 필드를 추가하면 계약의 유효 기간과 만료 시점을 명확히 추적할 수 있습니다.
자동화 규칙과 결합하면 계약 만료가 다가올 때 자동으로 알림 이메일을 보내거나 상태를 변경할 수 있어 갱신 누락을 크게 줄여줍니다.
영업: 고객 요청 배송일
주문 시 고객이 특정 배송일을 요청하는 경우가 많습니다. 영업 주문서에 "고객 요청일" 같은 Date 필드를 두면 운영팀이 명확한 날짜를 기준으로 계획을 세우기 용이합니다.
시간 정보가 필요 없는 상황에서는 Date를 사용하면 타임존 변환으로 인한 혼선을 피할 수 있어 물류·창고팀 간의 날짜 불일치를 막을 수 있습니다.
재고: 로트별 유통기한 관리
식품·의약·화학 분야에서는 로트 단위로 유통기한을 관리해야 합니다. Odoo의 로트(stock.lot)에는 만료일과 권장 소비 기한을 Date 필드로 저장합니다.
이 데이터는 FEFO(유통기한 우선 출고) 전략과 만료 임박 자동 알림을 구동해 규정 준수와 품질 관리를 돕습니다.
회계: 송장 납기일
송장 납기일 역시 Date 필드로 관리됩니다. 납기일 정보는 자동 결제 알림 발송, 연체 송장 식별, 채권·채무 연령 보고서 계산 등에 사용됩니다.
납기일이 잘못 설정되면 현금 흐름 추적과 후속 조치 자동화가 조용히 무너질 수 있으므로 매우 중요한 필드입니다.
인사: 입사, 계약, 자격증 만료일
HR은 입사일, 수습 종료일, 계약 시작·종료일, 자격증 만료일 등을 Date 필드로 광범위하게 사용합니다. 이 값들은 자동 이메일, 알림, 급여 규칙 계산에 직접 연결됩니다.
정확한 Date 데이터가 없으면 HR 자동화의 핵심 트리거가 제대로 작동하지 않으므로 깨끗한 데이터 모델이 필수입니다.
Date 필드 생성 및 커스터마이즈 방법
Odoo 모델에 Date 필드를 추가하는 방법은 기술 환경과 배포 방식에 따라 세 가지가 있습니다.
Odoo Studio로 추가(코드 불필요)
Odoo Studio는 코딩 없이 Date 필드를 추가할 수 있는 가장 쉬운 방법입니다. 표준 폼에 날짜 추적 기능을 빠르게 더하고 싶은 비즈니스 사용자나 컨설턴트에게 권장됩니다.
- 메인 메뉴에서 Odoo Studio를 엽니다.
- 필드를 추가할 폼으로 이동합니다.
- 사이드바에서 Date 필드를 폼으로 드래그합니다.
- 레이블, 필수 여부, 기본값 등을 설정합니다.
- 저장하고 Studio를 닫습니다.
Studio는 필드에 자동으로 x_studio_ 접두사를 붙여 폼 뷰에 추가합니다. 별도의 DB 마이그레이션 작업은 필요하지 않습니다.
커스텀 모듈에서 Python으로 생성
개발자가 버전 관리와 여러 환경에 배포해야 하는 커스터마이즈는 Python 모델 파일에서 필드를 정의하는 방식이 적합합니다.
from odoo import fields, models
class SaleOrder(models.Model):
_inherit = 'sale.order'
x_customer_requested_date = fields.Date(
string='Customer Requested Date',
index=True,
copy=False,
help='Delivery date requested by the customer at time of order.',
)
모델에 필드를 추가한 뒤에는 해당 필드를 뷰 XML에 넣어 인터페이스에 표시해야 합니다. 모듈 설치 또는 업그레이드 시 Odoo가 데이터베이스 칼럼을 생성해 줍니다.
XML-RPC API로 생성
배포 파이프라인이나 원격 구성 스크립트에서 프로그래밍 방식으로 필드를 생성하려면 Odoo XML-RPC API를 사용할 수 있습니다.
field_id = models.execute_kw(
ODOO_DB, uid, ODOO_API_KEY,
'ir.model.fields', 'create',
[{
'name': 'x_customer_requested_date',
'field_description': 'Customer Requested Date',
'model_id': model_id,
'ttype': 'date',
'state': 'manual',
}]
)
Date 필드의 ttype 값은 'date'입니다. DateTime은 'datetime'를 씁니다. 'manual' 상태는 필드가 모듈 설치가 아니라 수동으로 생성되었음을 나타냅니다. 이런 방식으로 원격 구성 자동화가 가능합니다.
권장 관행
1. 시간 정보가 불필요하면 Date를 사용하라
마감일, 계약일, 생년월일, 유통기한처럼 날짜만 중요할 때는 반드시 fields.Date를 사용하세요. DateTime을 쓸 경우 불필요한 시간대 복잡성이 생기고, 서버 설정에 따라 하루가 어긋나는 버그를 유발할 수 있습니다.
2. 의미 있는 기본값을 설정하라
"예상 배송일"이나 "후속일" 같은 필드는 기본값을 오늘 또는 오늘+며칠로 두면 사용자가 편리합니다. Python에서는 default=fields.Date.today처럼 괄호 없이 콜러블을 설정해야 레코드 생성 시점에 평가됩니다.
3. 보고서와 필터에 쓰이는 날짜는 인덱스를 추가하라
사용자가 특정 날짜로 자주 필터링(연체 송장, 갱신 임박, 만료 목록 등)한다면 index=True를 추가하세요. 대량 데이터에서 쿼리 성능이 크게 개선됩니다.
4. 복제 시 날짜를 복사하지 않도록 설정하라
"계약 시작일"이나 "유효기간" 같은 날짜는 레코드 복제 시 복사되면 안 됩니다. copy=False를 쓰면 복제 후 새 레코드에 날짜를 다시 설정하도록 강제해 오래된 날짜가 남는 실수를 막습니다.
5. 시작일/종료일 쌍에는 제약을 걸어라
Start/End 같은 쌍이 있을 때는 @api.constrains로 종료일이 시작일보다 빠르지 않도록 검증하세요. 사전에 데이터 무결성을 확보하면 이후 진단과 수리 비용을 크게 줄일 수 있습니다.
자주 발생하는 실수
Date와 DateTime을 혼동하는 경우
시간 민감한 데이터에서 가장 흔한 실수는 잘못된 필드 타입 선택입니다. DateTime은 UTC로 저장되고 표시 시 변환됩니다. 반면 Date는 변환이 없습니다. DateTime을 썼을 때 UTC+2에서 입력한 3월 15일이 UTC-5 사용자에게는 3월 14일로 보이는 식의 문제는 대부분 이 혼동에서 옵니다.
항상 비즈니스 요구에 꼭 맞는 가장 단순한 타입을 선택하세요.
스케줄드 액션에서 서버 시간대를 고려하지 않은 경우
"오늘"을 기준으로 동작하는 예약 작업이나 리포트는 fields.Date.today()가 서버의 UTC 날짜를 반환한다는 점을 유의해야 합니다. 대부분의 경우 괜찮지만, 서버와 사용자 시간대가 크게 다르면 하루 차이가 발생할 수 있으므로 분산 팀이라면 여러 시간대에서 테스트해 보세요.
날짜 범위 쌍에 대한 제약을 누락한 경우
Start Date와 End Date가 따로 있는 시스템에서 종종 종료일이 시작일보다 빠른 입력을 막지 않아 잘못된 데이터가 들어갑니다. 이렇게 되면 기간 계산, 필터, 리포트가 엉망이 됩니다. 반드시 @api.constrains로 검증하세요.
필터에 쓰이는 날짜에 인덱스를 추가하지 않음
연체 체크, 마감 임박 보기, 연령별 잔액 보고서 등에서 날짜 비교 조건을 자주 사용합니다. 인덱스가 없으면 전체 테이블 스캔이 발생해 대량 데이터 모델(송장, 작업, 재고 이동 등)에서 성능 저하가 눈에 띄게 나타납니다.
문자열(Char)로 날짜를 저장하는 경우
일부 팀은 커스텀 포맷을 유지하거나 달력 선택기를 피하려고 날짜를 Char로 저장하기도 합니다. 하지만 이렇게 하면 정렬, 필터, 날짜 산술, 리포트 기능이 모두 망가집니다. 항상 fields.Date 타입을 사용하세요. 프레임워크와 DB가 포맷과 로컬라이제이션을 처리해 줍니다.
자주 묻는 질문(FAQ)
Odoo에서 Date 필드와 DateTime 필드의 차이는 무엇인가요?
Date 필드는 시각 정보 없이 연·월·일만 저장합니다. DateTime은 시·분·초까지 포함한 타임스탬프를 UTC로 저장하고 사용자의 로컬 시간대로 표시합니다. 하루 단위의 정보만 필요하면 Date, 정확한 시점을 기록해야 한다면 DateTime을 사용하세요.
Date 필드의 기본값을 오늘로 설정하려면 어떻게 하나요?
Python에서는 default=fields.Date.today처럼 괄호 없이 콜러블을 지정합니다. Odoo는 레코드 생성 시점에 이를 평가합니다. Studio에서는 필드 속성에서 "Today"를 기본값으로 선택하면 됩니다.
다른 필드를 기반으로 Date 필드를 계산할 수 있나요?
가능합니다. 필드를 compute='_compute_my_date'로 정의하고, @api.depends()로 재계산을 트리거할 필드를 지정한 메서드를 작성하세요. 내부에서 Python의 datetime.date와 timedelta로 계산하면 되고, 검색·그룹핑·내보내기에 쓰려면 store=True를 추가해 결과를 DB에 저장하세요.
Odoo 도메인에서 날짜 범위로 레코드를 필터하려면 어떻게 하나요?
도메인에서 표준 비교 연산을 사용하면 됩니다. 예: 2026년 3월에 해당하는 레코드를 찾으려면,
[
('x_date_field', '>=', '2026-03-01'),
('x_date_field', '<=', '2026-03-31')
]
도메인에 넣는 날짜 문자열은 항상 ISO 형식(YYYY-MM-DD)을 사용하세요.
특정 조건에서만 Date 필드를 필수로 만들 수 있나요?
기본 Odoo에서는 뷰 수준의 attrs로 조건부 필수 표시를 할 수 있지만 백엔드에서 완전한 강제는 코드가 필요합니다. 진짜 서버측 검증을 원하면 @api.constrains로 제약을 추가해야 합니다. 간단한 경우 Odoo Studio의 조건부 가시성 규칙이 코딩 없이 실용적인 대안입니다.
결론
Date 필드는 Odoo 프레임워크에서 가장 실용적인 필드 타입 중 하나입니다. 이해하기 쉽고 사용하기 간단하며, 알림, 연체 검사, 시간 기반 리포트 같은 중요한 비즈니스 로직을 구동하는 데 충분히 강력합니다.
핵심 요점: 날짜만 필요하면 Date를 사용하고, 기본값과 인덱스는 사용성과 성능을 높이며, 두 날짜가 범위를 이룰 때는 반드시 제약을 추가하세요.
Odoo에서 깔끔하고 견고한 데이터 모델을 만드는 출발점은 이런 작은 설계 결정들입니다. 올바른 필드 타입 선택은 성공적인 Odoo 도입의 기초이며, Date 필드는 그 기반 중 하나입니다.
Dasolo에서는 전사적 프로세스와 부서별 워크플로에 맞게 Odoo를 도입·커스터마이즈·최적화하는 일을 돕고 있습니다. 깔끔한 데이터 모델 설계, 커스텀 필드와 자동화 추가, 또는 모듈 전체 개발 등 어떤 도움이 필요하든 지원해 드립니다. 문의하기 귀사의 Odoo 프로젝트에 대해 이야기해 보겠습니다.