계산된 필드가 무한 재계산을 유발함 (odoo)

발행: (2026년 1월 7일 오후 05:41 GMT+9)
5 min read
원문: Dev.to

Source: Dev.to

Problem Statement

잘못 설계된 계산된 필드는 잘못된 의존성 선언(@api.depends)으로 인해 불필요한 재계산이나 무한 루프를 유발하여 성능 문제와 불안정한 동작을 초래합니다.

Step 1 – 문제 있는 계산된 필드 식별

Typical symptoms

  • 폼 뷰가 로드되지 않음
  • 레코드 열 때 CPU 급증
  • 로그에 반복적인 재계산 표시
  • 레코드 생성/업데이트 후 서버가 멈춤

Enable logs

--log-level=debug

Look for repeating messages

Computing field x_field_name

Step 2 – Identify common WRONG patterns (most bugs come from here)

1. Computed field depends on itself

@api.depends('total_amount')
def _compute_total_amount(self):
    for rec in self:
        rec.total_amount = rec.price * rec.qty

Result: infinite recomputation. → 결과: 무한 재계산.

2. Writing to other fields inside compute

@api.depends('price', 'qty')
def _compute_total(self):
    for rec in self:
        rec.total = rec.price * rec.qty
        rec.subtotal = rec.total * 0.9  # BAD

3. Using write() inside compute

@api.depends('line_ids.amount')
def _compute_amount(self):
    for rec in self:
        rec.write({'total': sum(rec.line_ids.mapped('amount'))})

Result: recursive recompute + database writes. → 결과: 재귀적 재계산 + 데이터베이스 쓰기.

Step 3 – 올바른 의존성 정의하기

@api.depends('price', 'qty')
def _compute_total(self):
    for rec in self:
        rec.total = rec.price * rec.qty

규칙

  • 원본 필드에만 의존합니다.
  • 계산된 필드 자체에 의존하지 않도록 합니다.
  • @api.depends('*') 사용을 피합니다.

Step 4 – 계산 내부에서 다른 필드를 절대 수정하지 않음

별도 계산 필드

total    = fields.Float(compute='_compute_total',    store=True)
subtotal = fields.Float(compute='_compute_subtotal', store=True)

@api.depends('price', 'qty')
def _compute_total(self):
    for rec in self:
        rec.total = rec.price * rec.qty

@api.depends('total')
def _compute_subtotal(self):
    for rec in self:
        rec.subtotal = rec.total * 0.9

장점: 깔끔하고 예측 가능하며, 루프가 없음.

Step 5 – store=True필요할 때만 사용하세요

BAD (불필요한 저장)

total = fields.Float(compute='_compute_total', store=True)

store=True는 다음과 같은 경우에만 사용합니다:

  • 자주 검색되는 경우
  • 그룹화나 보고에 사용되는 경우

GOOD (일시적인 UI 값)

total = fields.Float(compute='_compute_total')

store=True는 재계산 비용을 증가시킵니다.

Step 6 – Compute에서 ORM 쓰기를 피하기 (@api.onchange 사용)

WRONG (compute)

@api.depends('qty')
def _compute_price(self):
    for rec in self:
        rec.price = rec.qty * 10

RIGHT (onchange)

@api.onchange('qty')
def _onchange_qty(self):
    self.price = self.qty * 10

UI 전용 로직에는 onchange를 사용하세요 – 재계산 루프가 없습니다.

Step 7 – 필요할 때 @api.depends_context 사용

If the computation depends on company, language, or user:

@api.depends_context('company')
@api.depends('amount')
def _compute_tax(self):
    for rec in self:
        rec.tax = rec.amount * rec.company_id.tax_rate

Prevents unnecessary recomputation.

Step 8 – One2many / Many2one 루프 수정

COMMON LOOP

@api.depends('line_ids.total')
def _compute_total(self):
    for rec in self:
        rec.total = sum(rec.line_ids.mapped('total'))

line.total가 부모를 다시 참조한다면 → 무한 루프가 발생합니다.

SAFE VERSION

@api.depends('line_ids.price', 'line_ids.qty')
def _compute_total(self):
    for rec in self:
        rec.total = sum(line.price * line.qty for line in rec.line_ids)

Step 9 – 검증을 위해 compute 대신 SQL 제약 조건 사용

BAD (검증을 위한 compute)

@api.depends('qty')
def _compute_check(self):
    if self.qty < 0:
        raise ValidationError("Invalid")

GOOD (제약 조건)

@api.constrains('qty')
def _check_qty(self):
    for rec in self:
        if rec.qty < 0:
            raise ValidationError("Quantity cannot be negative")

10단계 – 최종 안전 계산 필드 템플릿 (모범 사례)

total = fields.Float(
    compute='_compute_total',
    store=True,
)

@api.depends('price', 'qty')
def _compute_total(self):
    for rec in self:
        rec.total = rec.price * rec.qty if rec.price and rec.qty else 0.0

핵심 포인트

  • 계산 메서드 내부에서 쓰기를 하지 않습니다.
  • 재귀 호출이 없습니다.
  • 최소한의 올바른 의존성.
  • 프로덕션에 안전합니다.

결론

Odoo에서 무한 재계산 문제는 거의 항상 데이터를 쓰거나 자신에게 의존하거나 정의가 불명확한 종속성을 가진 순수하지 않은 계산 필드 때문에 발생합니다. 해결책은 간단하지만 엄격합니다:

  1. 계산 메서드를 순수하게 유지합니다.
  2. 최소이며 정확한 종속성을 선언합니다.
  3. 계산 내부에서 ORM 쓰기를 피하고, 대신 @api.onchange 또는 제약조건을 사용합니다.
  4. UI‑전용 로직과 저장 로직을 분리합니다.

이러한 규칙을 적용하면 루프가 사라지고 성능이 향상되며 Odoo 모듈이 안정적이고 유지보수하기 쉬워집니다.

Calculations instead of business logic containers, performance stabilizes, recomputation stops, and your Odoo system becomes predictable and scalable.
Back to Blog

관련 글

더 보기 »

Logger 모듈을 사용한 FastAPI 기본 로깅

Application이 Production에서 충돌할 때 Production에서 애플리케이션이 충돌했으며 사용자는 다음에 무엇을 해야 할지 확신하지 못했습니다. 나는 작년에 이와 같은 상황을 프로젝트에서 경험했습니다.