소개
Odoo 필드 정의를 살펴보다 보면 index=True 같은 속성을 본 적이 있을 것입니다. 코드상으로는 아주 짧은 한 줄이지만, 대량의 데이터에서 검색·필터링·정렬 성능에 눈에 띄는 차이를 만드는 설정입니다.
이 가이드는 Odoo의 데이터 모델에서 색인 필드가 무엇인지, 데이터베이스에 어떤 영향을 주는지, 그리고 언제 적용하는 것이 합리적인지 실무 관점에서 설명합니다. 커스텀 모듈을 설계하거나 기존 구현을 검토할 때 올바른 판단을 내리는 데 도움이 될 것입니다.
Odoo에서 색인(indexed)된 필드란 무엇인가
Odoo ORM에서 필드에 index=True를 지정하면 모듈 설치 또는 업데이트 시 PostgreSQL에 해당 컬럼에 대한 인덱스 생성 명령을 내리도록 Odoo가 처리합니다.
다음은 Python으로 필드를 정의할 때 흔히 보이는 간단한 예입니다.
class SaleOrder(models.Model):
_name = 'sale.order'
reference = fields.Char(string='Reference', index=True)
state = fields.Selection([...], index=True)
partner_id = fields.Many2one('res.partner', index=True)
사용자 화면에서는 색인 여부를 알 수 있는 표시가 없습니다. 폼에 값을 넣거나 리스트를 보는 사용자는 해당 필드가 인덱스인지 아닌지 전혀 인지하지 못합니다. 인덱싱은 전적으로 데이터베이스 레벨의 최적화입니다.
색인은 성능에 영향을 줍니다. 인덱스가 있으면 PostgreSQL이 수천, 수백만 건이 쌓인 테이블에서도 조건에 맞는 레코드를 훨씬 빠르게 찾아냅니다. 인덱스가 없으면 DB는 테이블의 모든 행을 순차적으로 검사해야 하지만, 인덱스가 있으면 정렬된 구조를 따라 목표 레코드를 직접 찾아갑니다.
어떤 Odoo 필드 타입이 index=True를 지원하는가
대부분의 스칼라 필드 타입은 index 속성을 지원합니다.
- Char, Text 계열 필드
- Integer 및 Float 필드
- Date, Datetime 필드
- Selection 필드
- Many2one 필드(특히 자주 색인됨)
- Boolean 필드
One2many, Many2many 같은 관계형 필드는 직접적인 단일 컬럼이 아니므로 index가 의미가 없고, 저장되지 않는 계산 필드는 DB 컬럼이 없어 색인할 수 없습니다.
필드가 어떻게 동작하는가
모듈 초기화나 업그레이드 시 Odoo는 필드 정의를 읽어 데이터베이스 스키마를 동기화합니다. index=True가 지정된 필드에 대해서는 PostgreSQL에 인덱스 생성 SQL을 실행합니다.
기본적으로 PostgreSQL은 B-트리 인덱스를 생성합니다. B-트리는 등가 비교(=), 범위 검색(>, <, BETWEEN), 정렬에 적합해 Odoo의 일반적인 도메인 검색과 리스트 뷰 필터에 널리 유용합니다.
Odoo ORM과의 상호작용
Odoo ORM은 파이썬 도메인 표현식을 SQL WHERE 절로 변환합니다. 예를 들어 [('state', '=', 'sale')]는 WHERE state = 'sale'가 되고, state 필드에 인덱스가 있으면 PostgreSQL이 테이블 전체를 훑지 않고도 조건을 빠르게 해결합니다.
Many2one 필드는 대표적인 사례입니다. sale.order의 partner_id는 관련 파트너의 정수 ID를 저장합니다. 고객별로 주문을 필터링할 때 WHERE partner_id = X 쿼리가 실행되며, 인덱스가 있으면 수십만 건의 주문이 있어도 조회 속도가 유지됩니다.
인덱스와 쓰기 성능
인덱스는 비용이 따릅니다. 레코드 생성·수정·삭제 시마다 PostgreSQL은 관련 인덱스도 갱신해야 하므로 인덱스가 많은 테이블에서는 쓰기 비용이 증가합니다. 대부분의 경우 읽기 성능을 위해 한두 개의 인덱스 추가가 합리적이지만, 모든 필드를 무작정 색인하는 것은 바람직하지 않습니다.
index='trigram' 옵션
Odoo 16 이상에서는 index에 불리언뿐 아니라 문자열 'trigram'을 지정할 수 있습니다. 이 경우 PostgreSQL의 pg_trgm 확장을 활용한 GIN trigram 인덱스가 생성되어 ILIKE 기반의 부분 일치 검색을 빠르게 처리합니다. 제품명이나 거래처명처럼 부분 문자열로 검색이 많은 필드에 유용합니다.
name = fields.Char(string='Product Name', index='trigram')
Odoo 표준 모듈들도 부분 검색이 잦은 텍스트 필드에 이 옵션을 적용해 성능을 확보합니다.
업무상 활용 사례
실무 워크플로우에서 인덱스가 유용한 다섯 가지 예시를 정리합니다.
1. CRM: 담당자별 리드 필터링
CRM에서는 담당자별 파이프라인 필터링이 자주 이뤄집니다. crm.lead의 user_id는 기본적으로 색인되어 있어 수천 건의 리드가 있어도 담당자 필터가 빠릅니다. 커스텀으로 사용자나 팀을 참조하는 Many2one을 추가하면 동일한 논리로 색인하는 것이 좋습니다.
2. 영업: 주문 상태별 검색
sale.order의 state 필드는 색인되어 있어, 확정 주문만 보거나 배송 대기 주문만 필터링할 때 대량 주문을 처리하는 회사에서도 리스트 로딩이 빠릅니다. 자주 필터링되는 선택형 필드는 색인 후보입니다.
3. 재고: 제품별 이동 추적
유통·제조 환경에서는 재고 이동 기록이 급격히 늘어납니다. stock.move의 product_id가 색인되어 있으면 특정 제품의 모든 이동을 신속히 조회할 수 있어 재고 추적 리포트의 성능을 확보합니다.
4. 회계: 거래처별 분개 조회
회계 담당자는 특정 고객·공급사별로 분개를 자주 조회합니다. account.move.line의 partner_id 색인은 수년치 회계 데이터가 쌓인 회사에서 외상채권·채무 보고서가 타임아웃 없이 동작하도록 돕습니다.
5. 커스텀 모듈: 참조 필드(추적성)
커스텀 모듈에서 프로젝트 코드나 외부 문서 번호처럼 조회·필터링 대상이 되는 참조 필드를 추가했다면, 사용자가 자주 검색한다는 전제하에 index=True를 넣어두면 데이터가 커져도 조회 성능을 유지할 수 있습니다.
색인 필드 만들기 및 커스터마이징
파이썬(커스텀 모듈 개발)에서의 적용
Python 코드로 필드를 선언할 때 index=True를 추가하는 것은 간단합니다. 필드 선언 시 키워드 인자로 넣어주면 됩니다.
from odoo import models, fields
class ProjectTask(models.Model):
_inherit = 'project.task'
x_external_ref = fields.Char(
string='External Reference',
index=True,
help='Reference number from the external system'
)
필드를 추가한 뒤에는 odoo-bin -u your_module_name로 모듈을 업그레이드하거나 앱 메뉴에서 업데이트해야 DB에 인덱스가 생성됩니다. 업그레이드를 수행해야만 실제 인덱스가 물리적으로 만들어집니다.
기존 필드에 인덱스를 추가하려면 상속하여 재정의하는 방법이 있지만, 원래 필드 동작에 의도치 않은 영향을 주지 않도록 주의가 필요합니다.
Odoo Studio에서의 처리
Odoo Studio로는 비개발자도 UI에서 필드를 만들 수 있지만, Studio 인터페이스에서는 필드 생성 시 인덱스 토글을 제공하지 않습니다. Studio로 만든 필드는 기본적으로 index=True가 설정되지 않습니다.
Studio에서 만든 필드를 색인해야 할 경우, 가장 깔끔한 방법은 해당 변경을 코드 기반의 모듈로 옮겨 index=True를 명시하는 것입니다. 보통 개발자가 담당하는 작업입니다.
PostgreSQL에서 직접 인덱스 추가하기
운영 중인 데이터베이스를 모듈 업그레이드 없이 최적화해야 하는 상황에서는 DBA가 SQL로 직접 인덱스를 생성할 수 있습니다.
CREATE INDEX CONCURRENTLY idx_sale_order_partner_id
ON sale_order (partner_id);
CONCURRENTLY 옵션은 인덱스 생성 중 테이블 잠금을 피해 가용성을 유지하는 데 중요합니다. 다만 코드 정의와 DB 상태가 불일치하지 않도록 모듈 정의와 함께 조율해야 합니다. 이후 모듈 업그레이드 시 인덱스 관리가 어떻게 될지 확인해야 합니다.
권장 사항(모범 사례)
도메인 검색에 자주 등장하는 필드에 인덱스 달기
리스트 필터, 자동 액션, 예약작업, 계산 필드 의존성 등에서 도메인에 자주 사용되는 필드는 색인 후보입니다. 대표적으로 Many2one, 상태(state), 식별 코드·참조 필드가 해당됩니다.
Odoo의 자체 관례 따르기
index=True를 언제 쓸지 판단할 때 가장 좋은 참고는 Odoo 자체 표준 모듈입니다. sale.order, account.move, stock.move 등 표준 정의를 보면 Odoo 개발자들이 어떤 필드를 색인했는지 알 수 있으며, 이는 실제 사용 패턴과 성능 경험에 기반한 결정입니다.
대량 데이터 모델의 Many2one은 항상 색인하라
장기간에 걸쳐 레코드가 많이 쌓이는 모델(분개, 재고 이동, 판매 주문 행 등)에서는 필터링에 사용되는 Many2one 필드를 반드시 색인하는 것이 좋습니다. 쓰기 부하가 있는 테이블에 인덱스를 하나 더 두는 비용은 대부분 읽기 성능 향상으로 충분히 상쇄됩니다.
텍스트 검색 필드에는 트라이그램 인덱스 고려
Odoo 16 이상에서 제품명·거래처명·문서 참조처럼 부분 문자열 검색이 자주 발생한다면 기본 B-트리 대신 index='trigram'을 고려하세요. Trigram 인덱스는 ILIKE 기반의 패턴 매칭에 최적화되어 있습니다.
인덱스가 실제로 사용되는지 확인하기
인덱스 추가 후에는 PostgreSQL의 EXPLAIN ANALYZE로 쿼리 실행 계획을 확인해 인덱스가 사용되는지 검증하세요. 플래너가 여전히 순차 스캔을 선택하면 테이블이 너무 작거나 쿼리 조건이 인덱스와 맞지 않을 수 있습니다.
인덱싱 결정에 대한 문서화
커스텀 모듈을 만들 때는 왜 특정 필드를 색인했는지 간단히 주석으로 남기세요. 향후 개발자나 컨설턴트가 의도를 이해하고 리팩터링 중 실수로 인덱스를 제거하는 것을 방지할 수 있습니다.
자주 빠지는 함정
모든 필드에 무작정 색인 달기
학습 초기에 흔히 하는 실수는 ‘안전하게’ 모든 필드에 index=True를 다는 것입니다. 이는 저장 공간 증가와 모든 쓰기 작업의 오버헤드로 이어져, 쓰기 성능 저하를 초래할 수 있습니다.
작은 테이블에 색인 추가하기
수백 건 수준의 작은 테이블에서는 PostgreSQL 플래너가 순차 스캔이 더 빠르다고 판단하는 경우가 많습니다. 인덱스는 테이블이 수천 건 이상으로 커졌을 때 진가를 발휘합니다. 작은 룩업 테이블이나 거의 채워지지 않는 커스텀 모델에 불필요한 인덱스를 추가하는 것은 의미가 없습니다.
index=True 추가 후 모듈 업그레이드 누락
Python에 index=True를 추가하는 것만으로는 DB에 인덱스가 생성되지 않습니다. 반드시 -u module_name로 모듈을 업그레이드하거나 백엔드에서 업데이트해야 합니다. 이 과정을 잊어버려서 성능 개선이 되지 않는 경우가 자주 발생합니다.
B-트리 인덱스가 ILIKE('%키워드%')를 가속하지는 않는다
B-트리 인덱스는 패턴 앞에 와일드카드가 있는 ILIKE '%keyword%' 형태를 가속하지 못합니다. 선행 와일드카드 검색을 빠르게 하려면 Odoo 16 이상에서 index='trigram'을 사용하거나 Full-Text Search 같은 다른 방안을 검토해야 합니다.
저장된 계산 필드(store=True)를 고려하지 않는 실수
저장된 계산 필드(예: store=True)는 DB 컬럼을 가지므로 색인할 수 있습니다. 종종 이를 간과해 필터 성능 개선 기회를 놓치곤 합니다. 계산된 값이 보고서나 뷰의 도메인에 자주 쓰인다면 색인 적용을 고려하세요.
맺음말
index=True는 사소한 필드 속성이지만 데이터가 커질수록 DB 성능에 실질적인 영향을 줍니다. 적절히 사용하면 검색·리스트·리포트가 빠르게 유지되지만, 남용하면 불필요한 오버헤드를 초래합니다.
핵심 요약은 단순합니다: 도메인 필터에 자주 쓰이는 필드, 특히 대량 데이터 모델의 Many2one 필드는 색인하세요. Odoo 표준 모듈의 관행을 따르고, 작은 테이블이나 절대 필터되지 않는 필드는 과도하게 색인하지 마세요. Odoo 16 이상에서는 부분 텍스트 검색 대상에 index='trigram'을 고려하십시오.
커스텀 개발 시작 단계에서 올바른 인덱스 전략을 세우는 것이 운영 중 느린 쿼리를 진단하고 해결하는 것보다 훨씬 쉽습니다.
Odoo 도입을 진행 중이신가요?
Dasolo에서는 Odoo 도입, 커스터마이징, 성능 최적화를 지원합니다. 커스텀 모듈 개발부터 기존 인스턴스 성능 개선, 새로운 프로젝트 기획까지 실무 중심의 기술 역량을 제공합니다.
느린 쿼리 문제, 복잡한 커스터마이징, Odoo 개발 모범 사례에 대한 조언이 필요하다면 도움을 드리겠습니다. Dasolo 팀에 문의하세요 현재 진행 중인 작업이나 고민거리를 알려주시면 함께 살펴보겠습니다.