Odoo 클라이언트 사이드 프레임워크 및 JavaScript 활용 가이드

1. 핵심 라이브러리 및 환경 구성

Odoo(과거 OpenERP)의 웹 클라이언트는 강력한 자바스크립트 프레임워크를 기반으로 구축되었습니다. 주요 라이브러리는 다음과 같습니다.

  • jQuery: DOM 조작 및 이벤트 처리를 위해 1.8.3 버전을 기본으로 사용합니다.
  • Underscore.js: 데이터 컬렉션 처리 및 유틸리티 함수를 제공합니다.
  • QWeb: XML 기반의 클라이언트 사이드 템플릿 엔진입니다.

모듈 구성 시 __openerp__.py(또는 최신 버전의 __manifest__.py) 파일에 사용할 리소스를 다음과 같이 정의해야 합니다.

{
    'js': ['static/src/js/custom_script.js'],
    'css': ['static/src/css/style.css'],
    'qweb': ['static/src/xml/templates.xml'],
}

2. 기본 모듈 구조 및 디버깅

Odoo JavaScript 모듈은 인스턴스를 인자로 받는 함수 형태로 정의됩니다. 브라우저에서 스크립트 압축을 해제하고 상세한 로그를 확인하려면 URL 뒤에 ?debug 파라미터를 추가합니다.

openerp.my_custom_module = function(instance) {
    var _t = instance.web._t; // 번역 함수
    var QWeb = instance.web.qweb; // QWeb 인스턴스

    instance.my_custom_module = {}; // 모듈 네임스페이스 정의

    instance.my_custom_module.MainView = instance.web.Widget.extend({
        start: function() {
            console.log("View has been initialized.");
        },
    });

    // 클라이언트 액션 등록
    instance.web.client_actions.add('custom_view.main', 'instance.my_custom_module.MainView');
}

3. 클래스 정의 및 상속 시스템

Odoo는 instance.web.Class를 통해 자체적인 클래스 상속 시스템을 제공합니다. init은 생성자 역할을 하며, this._super()를 통해 부모 메서드를 호출할 수 있습니다.

instance.my_custom_module.BaseLogic = instance.web.Class.extend({
    init: function(user_name) {
        this.user_name = user_name;
    },
    greet: function() {
        console.log("Welcome,", this.user_name);
    },
});

instance.my_custom_module.ExtendedLogic = instance.my_custom_module.BaseLogic.extend({
    greet: function() {
        this._super();
        console.log("Additional greeting from extended class.");
    },
});

var app = new instance.my_custom_module.ExtendedLogic("Dev");
app.greet();

4. 위젯(Widget) 아키텍처

위젯은 UI를 구성하는 기본 단위입니다. this.$el을 통해 해당 위젯의 jQuery 객체에 접근할 수 있으며, 위젯 간의 부모-자식 관계를 형성할 수 있습니다.

instance.my_custom_module.SubWidget = instance.web.Widget.extend({
    start: function() {
        this.$el.html("<p>Child Content</p>");
    },
});

instance.my_custom_module.ParentWidget = instance.web.Widget.extend({
    start: function() {
        this.$el.append("<div class='wrapper'>Parent View</div>");
        var child = new instance.my_custom_module.SubWidget(this);
        child.appendTo(this.$(".wrapper"));
    },
});

5. QWeb 템플릿 엔진 활용

QWeb은 XML 내부에서 JavaScript 로직을 수행할 수 있게 해줍니다. 주요 디렉티브는 다음과 같습니다.

  • t-name: 템플릿 식별자
  • t-esc: 변수 출력 (이스케이프 처리됨)
  • t-raw: HTML 태그를 포함한 원시 출력
  • t-if: 조건문
  • t-foreach / t-as: 반복문
  • t-att-: 속성 바인딩
<?xml version="1.0" encoding="UTF-8"?>
<templates>
    <t t-name="ProductItemTemplate">
        <div class="product-card">
            <h4><t t-esc="widget.title"/></h4>
            <ul>
                <t t-foreach="widget.items" t-as="item">
                    <li t-att-style="'color:' + widget.accentColor">
                        <t t-esc="item"/>
                    </li>
                </t>
            </ul>
        </div>
    </t>
</templates>

6. 이벤트 핸들링 및 속성 감시

위젯 내에서 DOM 이벤트는 events 객체를 통해 선언적으로 바인딩하거나 jQuery를 사용해 직접 바인딩할 수 있습니다. 위젯 자체 이벤트는 triggeron을 사용합니다.

instance.my_custom_module.InteractiveWidget = instance.web.Widget.extend({
    events: {
        "click .action-btn": "handleAction",
    },
    handleAction: function(event) {
        alert("Button clicked!");
        this.trigger("action_complete", {status: "success"});
    },
    start: function() {
        this.on("change:data", this, this.dataChanged);
    },
    dataChanged: function() {
        console.log("Data has been updated to:", this.get("data"));
    }
});

7. 서버와의 통신 (RPC)

Odoo는 서버 측 Python 모델과 통신하기 위해 instance.web.Model 클래스를 제공합니다. 비동기 처리를 위해 Deferred 객체를 사용하며 .then()으로 콜백을 관리합니다.

모델 메서드 호출

var myModel = new instance.web.Model("res.users");
myModel.call("search_read", [ [['active', '=', true]], ['name', 'login'] ]).then(function(users) {
    _.each(users, function(user) {
        console.log("User:", user.name);
    });
});

데이터 쿼리 최적화

query() 메서드를 사용하면 필터링, 정렬, 제한 조건을 체인 형태로 구현할 수 있어 가독성이 향상됩니다.

var model = new instance.web.Model("product.template");
model.query(['name', 'list_price'])
     .filter([['sale_ok', '=', true]])
     .limit(10)
     .all().then(function(products) {
         // 결과 처리
     });

8. 기존 클래스 확장 (include)

이미 정의된 Odoo의 위젯이나 클래스의 기능을 수정해야 할 경우 extend가 아닌 include를 사용합니다. 이는 원본 클래스 자체를 몽키 패치(Monkey Patch)하여 모든 인스턴스에 변경 사항을 적용합니다.

instance.web.Widget.include({
    start: function() {
        console.log("All widgets now log this message on start.");
        return this._super();
    },
});

태그: Odoo JavaScript QWeb OpenERP Frontend-Framework

9월 14일 18:16에 게시됨