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

발행: (2026년 1월 9일 오후 02:00 GMT+9)
6 min read
원문: 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은 규모가 커져도 빠르고 안정적이며 예측 가능하게 유지됩니다.

Back to Blog

관련 글

더 보기 »

SQLite에서 캐시 효율성

SQLite에서 캐시 효율성 !표지 이미지: A Eficiência do Cache no SQLite https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=aut...

파이썬의 비밀스러운 삶: 게으른 제빵사

왜 파이썬 코드가 메모리를 다 쓰는가, 그리고 `yield`가 이를 어떻게 해결하는가 도서관은 희미하게 오존과 타는 플라스틱 냄새가 났다. 티모시는 그의 앞에 앉아 있었다.