커뮤니티

간단한 모듈을 만들어 봅시다. #1 (Feat. 공홈 질문 Style)

2025.03.25 15:59
226
5
💡
팁: 이 게시글은 구 XE버전의 모듈 만들기를 안내하고 있습니다.
Rhymix2 부터 동작하는 모듈 구조는 본 튜토리얼이 끝나면 공부해봅시다!
이 예제에서는 실제 서비스에 적용할 정도의 미려한 튜토리얼이 아닙니다.
서비스 적용 전 검토 할 사항이 많으니 참고 용도로만 사용해주세요.
 

안녕하세요, 라이믹스와 함께 밥벌이를 하고 있는 얼음조각티라고 합니다.

최근에는 라이믹스로 Ubuntu에서 작동되는 시스템과 연동하여, 자동화 서비스 등을 제공하고 있는데요.

 

최근에 AI를 활용한 SFT 모듈을 만들다가, 모듈 만들기 기초를 알려드려보고자 작성하게 되었습니다.

먼저 이 게시글에서는 포에시스에서 제공하는 모듈 생성기를 통해 모듈의 가장 기초를 만들었다고 가정하고 시작합니다.

 

포에시스 모듈 생성기로 모듈 먼저 만들기 

 

모듈의 구조

XE와 라이믹스는 아래의 파일 구조를 가지고 있습니다.

image.pngimage.png

(왼쪽, XE/라이믹스의 초창기 모듈 구조 / 오른쪽  MVC패턴 (출처: 큰돌의 터전))

 

XE와 라이믹스는 MVC패턴을 기반으로 하고 있습니다.

따라서 Model, View, Controller를 활용하여 모듈을 구성하여 기능을 만들 수 있는데요.

 

간단히 이야기 하면,

Model은 View로 데이터베이스의 값을 넘겨주는 역할을 합니다.

View는 사용자가 보는 페이지 입니다.

Controller는 사용자가 컨트롤러를 조작하면 Model로 부터 값을 가져오거나 변형하는 조작 액션이 일어나는 컨트롤러입니다.

 

 

공홈의 Q&A 기능을 만든다고 하였을때 구조를 파해쳐볼까요?

 

image.png

 

쉽게 이렇게 표현할 수 있겠습니다. 버튼을 보여주는 것은 View의 영역에서 보여줄 것이고요.

게시글의 해결 or 미해결의 상태는 Model에서 값을 보여줄것이고 버튼을 눌러 해결상태나 미해결상태로 되돌리는것은 Controller에서 조작 해 줄 것입니다.

 

그렇다면 우리가 만든 모듈 구조에서 당장 사용할 파일이 무엇인지 감이 오시나요?

image.png

 

우리는 이 여섯개의 파일중 3개의 파일만으로 이 기능을 구현할 수 있답니다.

물론, 3개의 파일 다음에도 xml쿼리를 사용해야 하기 때문에, queries 파일도 작성해야하지만, 그건 어렵지 않습니다.

 

기능 구현을 위한 첫 걸음, DB 구성하기

제 개인적인 신념은 어떤 프로그램이나 기능을 구현할 때, DB를 가장 먼저 구성해야한다 생각합니다.

그래야 나중에 기능 추가를 하더라도 DB 구성에서 크게 벗어나지 않고 확장하거나 이를 구성할때 유리하게 유지보수 할 수 있기 때문이라고 생각합니다.

 

먼저 schemas폴더에 모듈이름_solved.xml 으로 파일을 만듭니다.

그리고 그 xml파일은 아래와 같이 구성합니다.

 

<table name="모듈이름_solved">

    <column name="document_srl" type="BIGINT" notnull="true" primary="true" />

    <column name="solved" type="varchar" size="20"/>

</table>

 

간단히 게시판의 번호와, 해결 여부를 저장하는데요. 그러면 이제 이 모듈을 설정할때에 DB에 아래와 같이 구성된답니다.

image.png

벌써부터 두근거리지 않나요?! rx_모듈이름_solved라니.. 뭔가 벌써부터 모듈이 만들어진것 같죠!?

이제, DB구성은 끝났으니 다음에는 DB에서 게시글의 해결 여부를 체크해야겠습니다

 

너.. 해결상태가 뭐니? Model에 물어봐도 되겠니?

먼저, 모듈이름.model.php를 열고 아래와 같이 작성합니다.

 

public static function getDocumentSolved($document_srl){

        // 게시글의 상태를 한꺼번에 가져옵니다.

        $args = new stdClass();

        $args->document_srl = $document_srl;

 

        $output = executeQuery('모듈이름.getSolvedStatus', $args);

        return $output;

    }

 

 

Model에 View 또는 게시판의 read.html 나 list.html에서 모듈Model에 접근하여 이 게시글의 상태가 어떻게 되는지 물어보는 단계입니다. executeQuery()를 사용하여 queries 폴더의 getSolvedStatus.xml 을 만들어서 데이터를 가져올 것이고요.

 

XML쿼리.. 어려운데..ㅠㅠ 어떻게 해야하죠?

XML쿼리 생각보다 처음엔 어려울 수 있습니다. DB쿼리문도 짜기 어려운데 XML쿼리라뇨.

근데! 생각보다 익숙해지면 쿼리문 짜는거보다 간단할 수 있습니다 :)

 

이번에는 쿼리 폴더에 getSolvedStatus.xml 파일을 만든 후 아래 처럼 작성해줍니다.

<query id="getSolvedStatus" action="select">

    <tables>

        <table name="모듈이름_solved" />

    </tables>

    <columns>

        <column name="*" />

    </columns>

    <conditions>

        <condition operation="equal" column="document_srl" var="document_srl" filter="number" notnull="notnull" />

    </conditions>

</query>

 

XML쿼리문을 조금 알려드리자면 conditions 문에 document_srl을 $args의 변수값 document_srl을 받아 "동일한"값이 있을때 전체 컬럼의 값을 가져오는 쿼리입니다.

DB쿼리문으로 하면 [ SELECT * FROM `rx_모듈이름_solved` WHERE `document_srl` = ? ] 가 되겠네요.

 

여기까지 오신 당신! 벌써 DB에서 게시글 상태가 해결상태인지 아닌지를 알 수 있는 상태가 되었군요 =ㅅ=+

 

이얍! 게시글 상태여 바뀌어라!!

모듈이름.controller.php 파일을 열어 아래와 같이 작성해볼까요?

 

public function procChecktoSolved()

    {

        //현재 로그인 한 회원 정보를 가져온다.

        $logged_info = Context::get('logged_info');

        if(!$logged_info){

            return new BaseObject(-1, '잘못된 요청입니다. [ERR.403]');

        }

 

        // Context를 사용하여 GET/POST 값 받아야 함

        $document_srl = Context::get('document_srl');

        $status = Context::get('status');

 

        // 필수 값이 없을 경우 오류 반환

        if (!$document_srl || !$status) {

            return new BaseObject(-1, '잘못된 요청입니다. [ERR.404]');

        }

 

        $args = new stdClass();

        $args->document_srl = $document_srl;

 

        // 이미 체크한 데이터가 있는지 확인

        $exists = executeQuery('모듈이름.getSolvedStatus', $args);

 

        if ($exists->data) {

            // 기존 데이터가 있으면 업데이트

            $args->solved = $status;

            executeQuery('모듈이름.updateSolvedStatus', $args);

            //return new BaseObject(0, '업데이트 성공');

        } else {

            // 데이터가 없으면 새로 삽입

            $args->solved = $status;

            executeQuery('모듈이름.insertSolvedStatus', $args);

            //return new BaseObject(0, '데이터 추가 성공');

        }

       

    }

 

여기에 사용된 update와 insert쿼리문은 파일로 첨부했어요. 읽어보시고 그 구조를 잘 이해하시길 바래요 *^^*

다운로드 : 튜토리얼.zip

 

사용자와 맞닿아 조작하는 controller를 사용하려면?

지금 당장 버튼을 만들어 조작하면, 아마 act_404_notfound 에러가 날것이에요.

모듈이름/conf/module.xml 파일을 열어 아래와 같이 작성해주세요.

<actions> </actions> 사이에 

<action name="procSetStatus" type="controller" method="POST" />

를 넣어주시면 됩니다 :)

 

이렇게 되면 /index.php?module=모듈이름&act=procChecktoSolved 요청을 POST로만 받을 수 있어요.

게시판에 새로운 자바 스크립트 파일을 만들거나, 기존에 사용되는 js 파일에 아래와 같이 작성해주세요.

 

function setDocumentStatus(document_srl, status) {

    if (status === "Y") {

        if (!confirm("해결완료 표시하시겠습니까?")) return;

    } else {

        if (!confirm("미해결 상태로 되돌리시겠습니까?")) return;

    }

    exec_json("모듈명.procChecktoSolved", { document_srl: document_srl, status: status }, function (data) {

        location.reload();

    });

 

}

 

setDocumentStatus(게시글번호, 상태값) 이 onclick인 버튼을 눌렀을 때 모듈명.procChecktoSolved를 실행하고, POST파라미터로 document_srl과 status 값을 보내 처리할거에요. 처리한 후에는 새로고침 할거고요.

 

마지막, 게시판 스킨에 모듈을 얹어보기

게시판 read.html 에 아래와 같은 코드로 내가 만든 모듈의 model과 값을 가져오도록 해보세요.

 

{@

            //나만의 모듈

            $o모듈이름Model = getModel('모듈이름');

   

       

            //게시글 Status 정보

            $isSolved_raw = $o모듈이름Model->getDocumentSolved($oDocument->document_srl);

            $isSolved = $isSolved_raw->data;

        }

 

그리고 버튼은 아래와 같이 하면 된답니다.

 

<!--@if($isSolved->solved == 'Y')-->

                        <a href="#" onclick="setComplete({$oDocument->document_srl}, 'N')" class="voted"><i class="xi-rollback"></i><span class="count is-solved">Return to processing</span> </a>

                        <!--@else-->

                        <a href="#" onclick="setComplete({$oDocument->document_srl}, 'Y')" class="voted"><i class="xi-message"></i><span class="count">Check Solved</span> </a>

                        <!--@end-->

 

 

물론 이 버튼을 보이게 할 사람은 템플릿의 cond문을 사용하여 cond="$oDocument->isEditable()" 으로 게시글이 수정 가능한 사람으로 한정하면 되겠지요?

 

관리자와, 게시글 작성자만 보일테니까요.

이렇게 하면, 상태값에 따라 해결 완료로 처리하거나, 처리중으로 바꾸는 등의 작업이 가능하답니다.

 

 

마치며

사실 별거 아닌 아~~주 간단한 모듈입니다.

DB구조를 이해하고, xml쿼리도 이해할 수 있으며, MVC패턴을 이해하기에는 이만한 예제가 없다고 생각했습니다.

사내 인트라넷에서 일부 코드를 조금 더 다듬어 게시글 작성자와 관리자에게만 이 버튼을 노출하고, act실행권한을 주는등의 일부 코드 수정이 필요하겠지만, 처음부터 끝까지 이 구조를 이해하며 나만의 확장 모듈을 만들어보는건 어떨까요?

 

앞으로는 코어 수정 No~ 확장모듈 OK!

 

- 잘못된 정보가 있다면 알려주세요. 즉시 확인하고 수정하겠습니다.

- 본 예제는 XE와 라이믹스에서 구동가능하도록 작성된 예제입니다.

- 모든 모듈 이름은 영문으로 작성되어야 합니다.

- 코드 편집기도 CKEditor에 붙여줬으면 좋겠습니다 ㅜ

 

:)

댓글 15

  • 2025.03.25 16:46 #1905010

    알려주신 대로 한번 시도 해 보겠습니다. 상세한 팁 감사합니다. 

  • 2025.03.25 17:26 #1905014

    수고하셨습니다~

    아마 쉽게 쓰려다보니 일부러 한정한듯하긴한데
    DB table 생성은 모듈이름과 무관하게 가능합니다.   rx_모듈이름_solved  로 꼭 할 필요는 없는거죠 
    따라서  rx_document_solved  이렇게 해도 되고,    prefix 값인   rx_  이것만 지켜주면 무관합니다. 
    ( XE 쓰다가 라이믹스로 업데이트한경우는 xe_  로 되어있습니다 )

  • 2025.03.25 17:45 #1905018

    이거 혹시 제한이 혹시 없어졌나요?    


    제가 기억하기로 controller 에 사용하는 함수명은.

    proc+모듈명+함수명  형태로 해야할거예요.  각각은 대문자로 시작해야하고
    procSetStatus  라든지,   procChecktoSolved    는 아마그래서 안 될거예요

    proc모듈명SetStatus 나 proc모듈명ChecktoSolved 가 되어야겠죠    
    ( post 로 넘길때는  module.xml 에  method="POST"  를 굳이 안 써줘도 되긴합니다 )

    혹시나 라이믹스에서 규칙이 바뀌어서  모듈명을 안 써도 되는거면 알려주세요~

  • 2025.03.25 18:51 #1905035

    어느 모듈인지 분명하지 않은 경우: 반드시 disp모듈명 또는 proc모듈명으로 시작해야 찾아갈 수 있습니다.

      - mid나 module이 주어지지 않은 경우

      - mid나 module이 주어졌지만, 다른 모듈인 경우 (예: mid=board, module=admin 등)

      - document_srl만 던져주고 알아서 찾으라고 하는 경우

     

    어느 모듈인지 분명한 경우: 함수명을 뭐라고 하든 상관없습니다.

      - module.act 형식으로 exec_json() 한 경우

      - module이 정확하게 주어진 경우

      - mid가 주어졌고, 해당 mid가 내 모듈이 맞는 경우

        - 위의 두 사례에서는 act에 해당하는 짧은주소(route)를 사용해도 됩니다.

      - action forward로 등록된 act인 경우

      - 단, 해당 정보와 일치하지 않는 다른 변수(module, mid, document_srl)를 함께 넘기면 충돌이 일어나서 "어느 모듈인지 분명하지 않은 경우"로 회귀할 수 있습니다. 대체로 다른 모듈에 소속된 document_srl을 집어넣었다가 이런 사태가 일어나곤 합니다.

  • 2025.03.25 19:41 #1905050

    그냥 조금 길더라도 무조건 써주는게 좋겠네요.
    함수명  써주면 무조건 에러는 안 난다는 의미니까.. 
    주소에서 꼭 줄여야할경우 AF 를 사용하고

  • 2025.03.25 20:23 #1905067

    주소창에 노출되는 GET 함수명들은 좀 줄이고 싶은 유혹이 들죠.

    POST는 뭐... 상관없으니까요. ㅎㅎ

  • 2025.03.25 19:58 #1905063

    네.

    exec_json에 특정 모듈을 명시하여 보내는 경우에는 proc에 모듈명이 있지 않아도 됩니다.

    disp도 마찬가지입니다.

     

  • 2025.03.25 18:07 #1905024

    이야!

    아직 완전히 이해는 어렵지만 그래도 모듈의 구성에 대해서 많이 알았네요.

    모듈 생성기와 이글을 참조하여 간단한 것이라도 만들어 봐야 겠네요.

    감사합니다.

  • 2025.03.25 18:21 #1905029

    한가지 질문 드립니다.

     

    처음 모듈을 만들때 schemas폴더 .xml 에 regdate를 넣지 않았다가 나중에 필요에 의해서 삽입시

    <column name="regdate" type="char" size="14" notnull="notnull" index="idx_regdate" />

     

    테이블에 regdate 란이 만들어지지 않고 아래의 오류가 출력되면서 입력 자체가 되지를 않습니다. 

    SQLSTATE[42S22]: Column not found: 1054 Unknown column 'regdate' in 'field list' 

     

    이런 경우는 어떻게 하면 테이블에 regdate 란이 만들어 지는지 궁금합니다.

  • 2025.03.25 19:38 #1905044

    class.php 파일에서 칼럼을 업데이트해줘야합니다
    순수 Core 대로라면

     

    function checkUpdate()  내부에

    $oDB = &DB::getInstance();

    if(!$oDB->isColumnExists('테이블명', '칼럼명')) return true;

     

    function moduleUpdate()   함수에

    $oDB = &DB::getInstance();            

    if(!$oDB->isColumnExists('테이블명', '칼럼명')) {

                $oDB->addColumn('테이블명', '칼럼명', '데이터타입' );
    //         $oDB->addColumn('테이블명', '칼럼명', '데이터타입','글자수','기본값',필수여부 );   //  글자수, 기본값, 필수(true-NOTNULL,false-NULL)  까지도 가능
    }

     

     

    modules/document/documents.class.php  같은 파일 확인해보면 이해되실거예요

  • 2025.03.25 19:42 #1905054

    아니면 스키마 파일의 이름을 한번 바꿔주고 관리자 페이지에서 업데이트 해주는 방법도 있습니다.

  • 2025.03.25 19:57 #1905059

    다른 분들이 말씀해주신 것 처럼 하셔도 됩니다.

    근데 혼자 쓸 것이고 고려하지 않으실거라면.. 그냥 DB테이블에 컬럼만 만드셔도 됩니다.

  • 2025.03.26 01:43 #1905084

    님이 만들어 주신 모듈에 날짜를 넣어 볼려고 하니 그렇네요.

    저야 DB 테이블 삭제하고 다시 생성하면 되는데 처음 받아서 설치한 후 업데이트 하는 분들이 오류가 난다고 해서 어떻게 하면 되는지 궁금해서 질문 드린겁니다.

    답변 주신분들 감사합니다.

    이름 바꾸기가 쉽네요 ㅎㅎ

  • 2025.03.26 04:21 #1905096

    모듈을 배포하는 형태라면 해당 방식은 당연히 지양해야되지만, 배포할 모듈이 아니라면 언제든지 해당 방식으로 사용하셔도 된다고 봅니다.

    단점은 이미 만들어진 컬럼을 삭제하는건 해당방식으로 불가능해서, 스키마 작성에 항상 신중해야 한다는 정도죠.

  • 2025.03.26 02:39 #1905088

    감사합니다.

    모듈을 만들고 싶은 마음이 샘솟네요.

     

    저도 간단한 것 부터 도전해보겠습니다.

    #1~ 쭉~ 많은 정보 부탁드립니다.