계산된 필드가 무한 재계산을 일으킴(Odoo)

발행: (2026년 1월 9일 오후 02:00 GMT+9)
6 분 소요
원문: Dev.to

Source: Dev.to

(번역을 원하는 본문을 제공해 주시면, 해당 내용을 한국어로 번역해 드리겠습니다.)

Problem Statement

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

왜 이 문제가 Odoo에서 발생하는가

  • 계산된 필드가 자신에게 의존한다
  • compute 메서드가 다른 필드에 쓰기를 수행한다
  • write()가 compute 메서드 내부에서 사용된다
  • 의존성이 너무 넓거나 잘못되었다
  • store=True가 잘못 사용된다
  • 부모 ↔ 자식 필드가 서로 의존한다

결과

  • 느린 폼
  • 높은 CPU 사용량
  • UI 정지
  • 운영 중 무작위 충돌

Step 1 – Identify the Problematic Computed Field

Common Symptoms

  • Form view keeps loading → 폼 뷰가 계속 로드됩니다
  • Server CPU spikes → 서버 CPU 급증
  • Logs show repeated “Computing field …” messages → 로그에 반복되는 “Computing field …” 메시지가 표시됩니다
  • Issue disappears when the field is removed → 필드를 제거하면 문제가 사라집니다

Enable logs:

--log-level=debug

Look for:

Computing field 

repeating continuously.

단계 2 – 일반적인 WRONG 패턴 식별

WRONG 1: 필드가 자신에게 의존

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

무한 재계산을 유발합니다.

WRONG 2: Compute 안에서 다른 필드 쓰기

@api.depends('price', 'qty')
def _compute_amount(self):
    for rec in self:
        rec.amount = rec.price * rec.qty
        rec.discount = rec.amount * 0.1  # BAD

각 쓰기 작업이 다시 재계산을 트리거합니다.

WRONG 3: Compute 안에서 write() 사용

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

재귀와 DB 쓰기를 발생시킵니다.

Step 3 – 올바르고 최소한의 종속성 정의

올바른 종속성 선언

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

규칙

  • 원본 필드에만 의존
  • 계산된 필드 자체를 포함하지 않음
  • @api.depends('*') 사용 금지

Step 4 – Compute 메서드 순수하게 유지 (부작용 없음)

올바른 패턴

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
  • 쓰기 없음
  • ORM 호출 없음
  • 안전하고 예측 가능

Step 5 – 논리를 여러 계산 필드로 분리

좋은 설계

amount = fields.Float(compute='_compute_amount', store=True)
discount = fields.Float(compute='_compute_discount', store=True)

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

@api.depends('amount')
def _compute_discount(self):
    for rec in self:
        rec.discount = rec.amount * 0.1
  • 순환 의존성 없음
  • 깔끔한 분리

Step 6 – store=True를 반드시 필요할 때만 사용

BAD

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

store=True는 다음 경우에만 사용하세요:

  • 검색 도메인에서 사용될 때
  • 보고서 / group by에서 사용될 때

GOOD

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

필드가 UI 전용이며 검색이나 그룹화에 사용되지 않을 때.

단계 7 – UI 로직에 @api.onchange 사용 (계산이 아님)

잘못된 예

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

올바른 예

@api.onchange('qty')
def _onchange_qty(self):
    self.price = self.qty * 10
  • 재계산 없음
  • UI 전용 동작

Step 8 – 부모‑자식 의존성 루프 수정

BAD (숨겨진 루프)

@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 – 계산된 필드 대신 제약 조건을 사용하여 검증하기

BAD

@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")
  • 재계산 없음
  • 적절한 검증 레이어

Step 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 or 0.0) * (rec.qty or 0.0)
  • 순수 계산
  • 올바른 의존성
  • 프로덕션 안전

결론

Odoo 개발에서 무한 재계산 문제는 거의 항상 데이터를 쓰는 순수하지 않은 계산 필드, 자신에게 의존하는 필드, 혹은 비즈니스 로직과 계산 로직을 혼합한 경우에 발생합니다. 해결책은 엄격한 규율입니다: 순수한 compute 메서드, 최소한의 의존성, ORM 쓰기 금지, UI 로직과 검증 로직의 적절한 분리. 계산 필드를 읽기 전용 계산으로 다루면 Odoo의 ORM은 규모가 커져도 빠르고 안정적이며 예측 가능하게 유지됩니다.

0 조회
Back to Blog

관련 글

더 보기 »