콘텐츠 대표 이미지 - 프레스토샵(PrestaShop) 모듈 개발, A부터 Z까지 완전 정복 튜토리얼 🚀

프레스토샵(PrestaShop) 모듈 개발, A부터 Z까지 완전 정복 튜토리얼 🚀

내 쇼핑몰에 날개를 달아줄 커스텀 기능, 직접 만들어보자!

👋 안녕, 미래의 프레스토샵 마스터!

이커머스 시장에 막 뛰어든 사장님이든, 새로운 스킬을 장착하고 싶은 개발자든 모두 환영해! 🎉
혹시 '프레스토샵'이라는 이름 들어봤어? 전 세계적으로 사랑받는 오픈소스 이커머스 플랫폼이야. 무료인데다 기능도 막강해서, 나만의 온라인 스토어를 꿈꾸는 사람들에게는 거의 '치트키' 수준이지.

그런데 프레스토샵의 진짜 힘은 바로 '모듈'에서 나와.
모듈이 뭐냐고? 쉽게 말해 '레고 블록' 같은 거야. 기본 뼈대(프레스토샵)에 내가 원하는 기능 블록(모듈)을 착착 끼워서 세상에 단 하나뿐인 쇼핑몰을 만드는 거지. 특별한 결제 시스템, 화려한 팝업, 맞춤형 상품 추천 기능... 상상하는 모든 것이 이 모듈을 통해 현실이 될 수 있어.

"코딩... 그거 어려운 거 아니야?" 라고 생각할 수도 있어. 맞아, 쉽지만은 않아. 하지만 걱정 마! 이 튜토리얼은 이제 막 걸음마를 뗀 너를 위해 준비했으니까. 마치 친한 친구가 옆에서 하나하나 알려주듯, 가장 기초적인 'Hello World' 출력부터 시작해서 제법 그럴싸한 기능을 갖춘 모듈을 완성하기까지, 모든 과정을 함께 할 거야.

이 글을 끝까지 따라오고 나면, 넌 더 이상 프레스토샵의 '사용자'가 아니라 '창조자'가 되어 있을 거야. 내 쇼핑몰에 필요한 기능을 직접 만들고, 심지어는 다른 사람들에게 판매해서 수익을 올릴 수도 있지. 멋지지 않아? 자, 그럼 이제 커피 한 잔 타서 자리에 앉아봐. 우리만의 첫 번째 모듈을 만들러 떠나볼까? 💻

🛠️ Part 1: 개발 환경 세팅 - 전쟁터에 나가기 전 총알 장전하기

모든 위대한 여정은 첫걸음부터 시작되지. 모듈 개발의 첫걸음은 바로 '개발 환경 구축'이야. 튼튼한 작업 공간 없이는 멋진 모듈도 나올 수 없어. 약간 지루할 수 있지만, 이 과정을 제대로 해둬야 나중에 골치 아픈 문제들을 피할 수 있으니 집중하자!

필수 준비물 리스트 📝

우리가 모듈을 만들기 위해 필요한 건 딱 세 가지야.

1. 로컬 서버 환경: 내 컴퓨터를 웹서버처럼 만들어주는 프로그램이야. 실제로 웹호스팅에 올리기 전에 내 컴퓨터에서 안전하게 프레스토샵을 설치하고 테스트할 수 있게 해주지. 대표적인 프로그램으로는 XAMPP, WAMP(Windows), MAMP(Mac) 등이 있어. 뭘 선택하든 상관없지만, 이 튜토리얼에서는 가장 대중적인 XAMPP를 기준으로 설명할게.

2. 코드 에디터: 우리가 코드를 작성할 메모장이야. 물론 진짜 메모장에 써도 되지만, 코드를 예쁘게 보여주고 오타도 잡아주는 똑똑한 에디터를 쓰는 게 정신 건강에 이로워. Visual Studio Code (VS Code)는 무료이면서도 강력한 기능으로 전 세계 개발자들의 사랑을 받고 있어. 강력 추천!

3. 최신 버전의 프레스토샵: 당연한 얘기지? 프레스토샵 공식 홈페이지에서 최신 버전을 다운로드해서 준비해두자.

프레스토샵 설치 및 디버그 모드 활성화

XAMPP를 설치하고 Apache 서버와 MySQL(MariaDB)를 실행했다면, 이제 프레스토샵을 설치할 차례야. XAMPP 설치 폴더 안의 `htdocs` 디렉토리에 프레스토샵 압축 파일을 풀고, 웹 브라우저에서 `http://localhost/프레스토샵폴더명`으로 접속해서 설치를 진행하면 돼. 설치 과정은 직관적이라 어렵지 않을 거야.

설치가 끝났다면, 가장 중요한 작업을 해야 해. 바로 '디버그 모드(Debug Mode)'를 켜는 거야.

🚨 잠깐! 디버그 모드는 왜 켜야 할까? 프레스토샵은 기본적으로 성능을 위해 캐시(Cache)라는 걸 사용해. 한번 불러온 정보를 임시로 저장해뒀다가 다음번에 빠르게 보여주는 기술이지. 하지만 개발할 때는 이게 독이 될 수 있어. 코드를 수정했는데도 캐시 때문에 변경 사항이 바로 적용되지 않는 경우가 허다하거든.

디버그 모드를 켜면 이런 캐시 기능이 비활성화되고, 코드에 문제가 생겼을 때 에러 메시지를 아주 친절하고 상세하게 보여줘. 개발자에게는 없어서는 안 될 필수 기능이야!

디버그 모드를 켜는 방법은 간단해.
프레스토샵 관리자 페이지 > 고급 설정(Advanced Parameters) > 성능(Performance) 메뉴로 이동해.

1. 템플릿 컴파일(Template compilation): '강제 컴파일(Force compilation)'로 설정.
2. 캐시(Cache): '아니요(No)'로 설정.
3. 페이지 상단에 있는 디버그 모드(Debug mode)를 '예(Yes)'로 설정하고 저장하면 끝!

이제 우리의 작업 공간은 완벽하게 준비됐어. 총알도 장전했으니, 본격적으로 모듈의 세계로 들어가 보자!

🏗️ Part 2: 모듈의 구조 이해하기 - 첫 모듈 '헬로월드' 만들기

드디어 코딩 시간이야! 🥳 모든 프로그래밍 언어의 첫걸음이 'Hello, World!'를 출력하는 것처럼, 우리도 가장 기본적인, 아무 기능도 없지만 프레스토샵에 '나 여기 있어요!'라고 존재를 알릴 수 있는 모듈부터 만들어 볼 거야. 이 과정을 통해 프레스토샵 모듈이 어떤 뼈대로 이루어져 있는지 자연스럽게 익히게 될 거야.

모듈의 최소 파일 구조

프레스토샵의 모든 모듈은 `modules` 라는 폴더 안에 자기만의 폴더를 가지고 있어. 우리도 `modules` 폴더 안에 `myfirstmodule` 이라는 이름의 폴더를 하나 만들자.

가장 기본적인 모듈이 되기 위해 필요한 파일은 딱 하나야.

`modules/myfirstmodule/myfirstmodule.php`

규칙이 보이지? 모듈 폴더 이름과 그 안의 메인 PHP 파일 이름은 똑같아야 해. 이건 프레스토샵과의 약속이야. 🤙

프레스토샵 모듈의 기본 폴더 구조 /prestashop (루트) /modules /myfirstmodule myfirstmodule.php (핵심 파일)

핵심 파일 작성: `myfirstmodule.php`

이제 `myfirstmodule.php` 파일을 열고 아래 코드를 그대로 입력해보자. 한 줄 한 줄이 어떤 의미인지 자세히 설명해줄게.


<?php
if (!defined('_PS_VERSION_')) {
    exit;
}

class MyFirstModule extends Module
{
    public function __construct()
    {
        $this->name = 'myfirstmodule'; // 1. 모듈의 기술적인 이름 (폴더명과 동일)
        $this->tab = 'front_office_features'; // 2. 관리자 페이지에서 보일 카테고리
        $this->version = '1.0.0'; // 3. 모듈 버전
        $this->author = 'Your Name'; // 4. 제작자 이름
        $this->need_instance = 0; // 5. 모듈 로드 방식 (대부분 0)
        $this->bootstrap = true; // 6. 부트스트랩 사용 여부 (최신 디자인을 위해 true)

        parent::__construct(); // 7. 부모 클래스의 생성자 호출 (필수!)

        $this->displayName = $this->l('My First Module'); // 8. 관리자 페이지에 표시될 모듈 이름
        $this->description = $this->l('This is my very first PrestaShop module. Hello World!'); // 9. 모듈에 대한 설명

        $this->ps_versions_compliancy = array('min' => '1.7', 'max' => _PS_VERSION_); // 10. 호환되는 프레스토샵 버전
    }

    public function install()
    {
        if (!parent::install()) {
            return false;
        }
        return true;
    }

    public function uninstall()
    {
        if (!parent::uninstall()) {
            return false;
        }
        return true;
    }
}
        

코드가 좀 길어 보이지만, 사실은 모듈의 '주민등록증'을 만드는 과정이야.

1. `name`: 모듈의 고유 식별자야. 폴더 이름과 반드시 같아야 하고, 소문자만 사용해야 해.

2. `tab`: 관리자 페이지의 모듈 목록에서 이 모듈이 어떤 카테고리에 속할지를 정해. `front_office_features`는 '프론트 오피스 기능' 카테고리를 의미해.

3. `version`, 4. `author`: 말 그대로 버전과 제작자 정보.

5. `need_instance`: 모듈이 로드될 때마다 새로운 객체를 생성할 필요가 있는지 묻는 건데, 특별한 경우가 아니면 항상 `0`으로 두면 돼.

6. `bootstrap`: 이걸 `true`로 설정하면 관리자 페이지에서 모듈 설정 화면을 만들 때 프레스토샵의 최신 디자인(부트스트랩 기반)을 쉽게 사용할 수 있어. 웬만하면 `true`로 하자.

7. `parent::__construct()`: 이게 정말 중요해! `MyFirstModule`은 프레스토샵의 기본 `Module` 클래스를 상속받아서 만들어졌어. `parent::__construct()`는 부모인 `Module` 클래스가 해야 할 기본적인 설정들을 먼저 처리하라는 명령어거든. 이걸 빼먹으면 모듈이 제대로 동작하지 않아.

8. `displayName`, 9. `description`: 관리자 페이지에서 사람들에게 보여줄 이름과 설명이야. `$this->l()` 함수로 텍스트를 감쌌는데, 이건 나중에 다국어 지원을 위한 장치야. 지금은 '이렇게 하는구나' 정도만 알아두자.

10. `ps_versions_compliancy`: 이 모듈이 어떤 버전의 프레스토샵과 호환되는지 명시하는 부분이야.

마지막으로 `install()`과 `uninstall()` 메소드는 모듈을 설치하거나 제거할 때 실행되는 함수야. 지금은 특별한 작업을 하지 않으니, 부모 클래스의 `install()`, `uninstall()`을 호출해서 기본 작업만 수행하도록 했어.

모듈 설치 및 확인

자, 이제 관리자 페이지로 가서 우리 아기 모듈을 만나보자!

관리자 페이지 > 모듈(Modules) > 모듈 관리자(Module Manager)로 이동해봐.

검색창에 'My First Module'이라고 검색하거나, 'Front Office Features' 카테고리를 찾아보면 방금 우리가 만든 모듈이 짠! 하고 나타날 거야. 옆에 있는 'Install' 버튼을 눌러서 설치해봐.

"설치가 완료되었습니다" 라는 메시지가 뜨면 성공! 🎉 이제 너는 프레스토샵 모듈 개발자로서 첫발을 내디딘 거야. 비록 아무 기능도 없지만, 프레스토샵의 생태계에 새로운 구성원을 등록시킨 거지.

💡 꿀팁: 모듈 아이콘 추가하기 모듈 목록에서 다른 모듈들처럼 예쁜 아이콘을 표시하고 싶다면? 간단해! `myfirstmodule` 폴더 안에 `logo.png` (또는 `logo.gif`) 파일을 넣어주기만 하면 돼. 파일 크기는 32x32 픽셀 정도가 적당해. 사소하지만 이런 디테일이 모듈의 완성도를 높여준다고!

🔌 Part 3: 훅(Hook) 시스템 - 프레스토샵과 소통하는 마법의 연결고리

'헬로월드' 모듈을 성공적으로 설치했지만, 사실 아직 아무 일도 하지 않아. 쇼핑몰 어디에도 우리 모듈의 흔적은 보이지 않지. 이제 우리 모듈이 쇼핑몰의 특정 위치에 무언가를 '보여주거나', 특정 상황에 어떤 행동을 '하도록' 만들어야 해. 이때 필요한 것이 바로 '훅(Hook)'이야.

훅은 프레스토샵의 핵심 개념 중 하나야. 비유하자면, 프레스토샵이라는 거대한 건물 곳곳에 미리 설치된 '콘센트' 같은 거야. 우리 모듈은 '가전제품'이고. 가전제품을 사용하려면 콘센트에 플러그를 꽂아야 하잖아? 마찬가지로 우리 모듈도 프레스토샵의 훅에 '등록'해야만 원하는 위치에서 작동할 수 있어.

훅의 종류: 디스플레이 훅 vs 액션 훅

훅은 크게 두 종류로 나눌 수 있어.

1. 디스플레이 훅 (Display Hooks): 이름 그대로 무언가를 '보여주기' 위한 훅이야. 예를 들어, 홈페이지에 배너를 추가하고 싶다면 `displayHome` 훅을, 모든 페이지 상단에 공지사항을 띄우고 싶다면 `displayHeader` 훅을, 상품 상세 페이지의 특정 위치에 추가 정보를 보여주고 싶다면 `displayProductAdditionalInfo` 훅을 사용하면 돼.

2. 액션 훅 (Action Hooks): 특정 '사건(Event)'이 발생했을 때 어떤 동작을 수행하기 위한 훅이야. 예를 들어, 고객이 주문을 완료했을 때(`actionValidateOrder`) 관리자에게 SMS를 보내거나, 상품 정보가 수정되었을 때(`actionProductUpdate`) 외부 시스템에 데이터를 전송하는 등의 작업을 할 수 있지. 눈에 보이는 결과물은 없지만, 백그라운드에서 중요한 역할을 해.

훅 사용법 3단계: 등록, 구현, 해제

훅을 사용하는 방법은 정해진 3단계 프로세스를 따르면 돼.

1단계: 훅 등록 (Registering a Hook)
모듈을 설치할 때, "나 이 훅을 사용할 거야!"라고 프레스토샵에 알려주는 과정이야. `install()` 메소드 안에 코드를 추가하면 돼.

2단계: 훅 메소드 구현 (Implementing the Hook Method)
훅에 등록했으면, 실제로 그 훅이 호출될 때 어떤 일을 할지 코드로 작성해야 해. `hook + 훅이름` 형식의 함수를 만들면 돼. 예를 들어 `displayHome` 훅을 사용하려면 `public function hookDisplayHome($params)` 라는 함수를 만드는 식이야.

3단계: 훅 해제 (Unregistering a Hook)
모듈을 제거할 때, "나 이제 이 훅 안 쓸 거야!"라고 알려줘서 깨끗하게 정리하는 과정이야. `uninstall()` 메소드 안에 코드를 추가하면 돼.

실전! 홈페이지에 메시지 띄우기

백문이 불여일견! 우리 `myfirstmodule`이 홈페이지에 "Welcome to my shop!"이라는 메시지를 띄우도록 만들어보자. 이걸 하려면 홈페이지 메인 콘텐츠 영역에 해당하는 `displayHome` 훅을 사용해야 해.

먼저, `myfirstmodule.php` 파일의 `install()`과 `uninstall()` 메소드를 수정하자.


// myfirstmodule.php

public function install()
{
    // parent::install()이 먼저 실행되고, 그 결과가 true일 때만 훅을 등록해야 안전해.
    if (!parent::install() || !$this->registerHook('displayHome')) {
        return false;
    }
    return true;
}

public function uninstall()
{
    // uninstall도 마찬가지.
    if (!parent::uninstall() || !$this->unregisterHook('displayHome')) {
        return false;
    }
    return true;
}
        

`$this->registerHook('displayHome')` 코드를 통해 `displayHome` 훅을 사용하겠다고 등록했어. 이제 프레스토샵은 홈페이지를 표시할 때마다 우리 모듈에게 "야, 너 `displayHome`에 할 말 있어?"라고 물어볼 거야.

그럼 이제 대답을 준비해야지. 클래스 맨 아래에 `hookDisplayHome` 함수를 추가하자.


// myfirstmodule.php (클래스 내부에 추가)

public function hookDisplayHome($params)
{
    return "Hello from my first module! Welcome to my shop! 🥳";
}
        

자, 이제 모듈을 재설치해야 해. 관리자 페이지에서 우리 모듈을 'Uninstall' 했다가 다시 'Install' 해봐. (이미 설치된 모듈의 `install` 메소드는 다시 실행되지 않기 때문이야. 'Reset' 버튼을 사용해도 좋아.)

그리고 쇼핑몰 홈페이지로 가보면... 짠! 홈페이지 어딘가에 우리가 작성한 메시지가 보일 거야!

템플릿 파일(.tpl) 사용하기

그런데 PHP 파일 안에서 HTML 코드를 직접 출력하는 건 좀 없어 보이지 않아? 코드가 복잡해지면 관리도 어렵고. 그래서 프레스토샵은 'Smarty'라는 템플릿 엔진을 사용해. 디자인(HTML/CSS)과 로직(PHP)을 분리하기 위해서지.

우리도 템플릿 파일을 사용해서 좀 더 세련되게 메시지를 출력해보자.

1. 먼저, 모듈 폴더 안에 `views/templates/hook` 폴더를 만들어. 그리고 그 안에 `welcome.tpl` 이라는 파일을 생성하자. * 경로: `myfirstmodule/views/templates/hook/welcome.tpl`

2. `welcome.tpl` 파일에 아래 내용을 작성해.


{* welcome.tpl *}
<div style="text-align: center; padding: 20px; background-color: #e3f2fd; border: 1px solid #90caf9; border-radius: 8px; margin: 20px 0;">
    <h3>{$welcome_message}</h3>
    <p>This message is coming from a template file!</p>
</div>
        

`{$welcome_message}` 부분 보이지? 저게 바로 PHP에서 전달받을 변수야.

3. 이제 `myfirstmodule.php`의 `hookDisplayHome` 함수를 수정해서 이 템플릿 파일을 불러오도록 하자.


// myfirstmodule.php

public function hookDisplayHome($params)
{
    // 1. 템플릿에 전달할 변수를 할당한다.
    $this->context->smarty->assign(array(
        'welcome_message' => 'Welcome to my awesome shop! 🚀'
    ));

    // 2. 템플릿 파일을 불러와서 출력한다.
    return $this->display(__FILE__, 'views/templates/hook/welcome.tpl');
}
        

`$this->context->smarty->assign()`은 Smarty 템플릿에 변수를 전달하는 역할을 해. 'welcome_message'라는 이름으로 우리가 원하는 문자열을 넘겨줬지.

`$this->display(__FILE__, '템플릿경로')`는 지정된 템플릿 파일을 렌더링해서 그 결과를 반환하는 함수야.

이제 다시 홈페이지를 새로고침 해봐. 훨씬 예쁘게 스타일링된 메시지 박스가 보일 거야. 이것이 바로 프레스토샵 모듈 개발의 정석적인 방식이야. 로직은 PHP 파일에, 디자인은 TPL 파일에!

⚙️ Part 4: 모듈 설정 페이지 만들기 - 사용자와 소통하는 창구

지금까지 만든 모듈은 우리가 코드에 직접 입력한 메시지만 보여줄 수 있어. 만약 쇼핑몰 관리자가 이 메시지를 직접 바꾸고 싶다면 어떡해야 할까? 코드를 직접 수정하라고 할 순 없잖아. 😅

그래서 필요한 것이 바로 '모듈 설정 페이지'야. 관리자가 관리자 페이지에서 모듈의 동작을 제어할 수 있는 화면이지. 이 페이지를 통해 우리는 사용자가 직접 값을 입력하고, 저장하고, 그 값을 모듈에서 활용하게 만들 수 있어.

`getContent()` 메소드: 설정 페이지의 시작점

모듈 설정 페이지의 모든 것은 `getContent()` 라는 메소드에서 시작돼. 관리자가 모듈 목록에서 'Configure(설정)' 버튼을 누르면 프레스토샵은 바로 이 `getContent()` 함수를 찾아서 실행하거든.

`myfirstmodule.php` 파일에 `getContent()` 메소드를 추가해보자.


// myfirstmodule.php (클래스 내부에 추가)

public function getContent()
{
    $output = ''; // 최종적으로 출력될 HTML을 담을 변수

    // 1. 폼이 전송되었는지 확인
    if (Tools::isSubmit('submitMyFirstModule')) {
        // 2. 폼에서 넘어온 값을 가져옴
        $message = Tools::getValue('MYFIRSTMODULE_MESSAGE');

        // 3. 값 검증 (예: 비어있지 않은지)
        if (!empty($message)) {
            // 4. 데이터베이스에 값을 저장
            Configuration::updateValue('MYFIRSTMODULE_MESSAGE', $message);
            $output .= $this->displayConfirmation($this->l('Settings updated successfully!'));
        } else {
            $output .= $this->displayError($this->l('Message cannot be empty.'));
        }
    }

    // 5. 설정 폼을 화면에 표시
    $output .= $this->renderForm();

    return $output;
}
        

`getContent` 함수의 흐름을 잘 봐.

1. `Tools::isSubmit('submitMyFirstModule')`: 'submitMyFirstModule'이라는 이름의 버튼이 클릭되어서 페이지가 로드된 것인지 확인해. 즉, 사용자가 '저장' 버튼을 눌렀는지 체크하는 거야. 2. `Tools::getValue('MYFIRSTMODULE_MESSAGE')`: 폼에 'MYFIRSTMODULE_MESSAGE'라는 이름의 입력 필드가 있다면 그 값을 가져와. 3. 값 검증: 가져온 값이 유효한지 간단히 확인해. 4. `Configuration::updateValue()`: 이게 핵심이야! 프레스토샵의 설정 값을 저장하는 함수야. `MYFIRSTMODULE_MESSAGE`라는 이름으로 사용자가 입력한 값을 데이터베이스의 `ps_configuration` 테이블에 저장해줘. 우리는 이 이름을 통해 언제든지 저장된 값을 다시 불러올 수 있어. 5. `$this->renderForm()`: 아직 만들진 않았지만, 사용자에게 보여줄 설정 폼 HTML을 생성하는 함수를 호출할 거야.

`HelperForm`으로 손쉽게 폼 만들기

HTML로 폼을 하나하나 만드는 건 꽤 귀찮은 일이야. 그래서 프레스토샵은 `HelperForm`이라는 아주 편리한 도구를 제공해. 우리는 PHP 배열로 폼의 구조만 정의해주면, `HelperForm`이 알아서 프레스토샵 관리자 디자인에 맞는 예쁜 폼을 뚝딱 만들어줘.

이제 `getContent()` 아래에 `renderForm()` 메소드를 만들어보자. 코드가 좀 길지만 구조는 단순하니 겁먹지 마.


// myfirstmodule.php (클래스 내부에 추가)

public function renderForm()
{
    // 1. 폼 필드 정의
    $fields_form[0]['form'] = [
        'legend' => [
            'title' => $this->l('My Module Settings'),
            'icon' => 'icon-cogs',
        ],
        'input' => [
            [
                'type' => 'text',
                'label' => $this->l('Message to display'),
                'name' => 'MYFIRSTMODULE_MESSAGE',
                'required' => true,
                'desc' => $this->l('Enter the message you want to show on the homepage.'),
            ],
        ],
        'submit' => [
            'title' => $this->l('Save'),
            'class' => 'btn btn-default pull-right',
            'name' => 'submitMyFirstModule',
        ],
    ];

    // 2. HelperForm 객체 생성 및 설정
    $helper = new HelperForm();
    $helper->module = $this;
    $helper->name_controller = $this->name;
    $helper->token = Tools::getAdminTokenLite('AdminModules');
    $helper->currentIndex = AdminController::$currentIndex . '&configure=' . $this->name;
    $helper->default_form_language = (int) Configuration::get('PS_LANG_DEFAULT');
    $helper->title = $this->displayName;
    $helper->submit_action = 'submitMyFirstModule';

    // 3. 폼에 기존 값 채우기
    $helper->fields_value['MYFIRSTMODULE_MESSAGE'] = Configuration::get('MYFIRSTMODULE_MESSAGE');

    // 4. 폼 생성
    return $helper->generateForm($fields_form);
}
        

단계별로 살펴보자.

1. 폼 필드 정의: `$fields_form` 배열에 폼의 구조를 정의하고 있어. * `legend`: 폼 상단에 표시될 제목이야. * `input`: 실제 입력 필드들을 배열로 정의해. 우리는 `type`이 `text`이고, 이름(`name`)이 `MYFIRSTMODULE_MESSAGE`인 입력창 하나를 만들었어. * `submit`: 저장 버튼을 정의해.

2. HelperForm 설정: `HelperForm` 객체를 만들고, 폼이 제대로 동작하기 위한 기본 정보들을 설정해주는 부분이야. 대부분은 그냥 공식처럼 사용하면 돼.

3. 기존 값 채우기: `Configuration::get('MYFIRSTMODULE_MESSAGE')`를 사용해서 데이터베이스에 저장된 값을 불러와. 그리고 `HelperForm`에게 이 값을 알려주면, 폼이 로드될 때 입력창에 이전에 저장했던 값이 자동으로 채워져.

4. 폼 생성: `$helper->generateForm()` 함수가 우리가 정의한 배열과 설정값을 바탕으로 최종 HTML을 만들어 반환해.

설정 값을 프론트엔드에 연동하기

이제 마지막 단계야. 관리자가 저장한 메시지를 실제 홈페이지에 보여줘야지! `hookDisplayHome` 메소드를 다시 수정하자.


// myfirstmodule.php

public function hookDisplayHome($params)
{
    // 데이터베이스에서 설정 값을 불러온다.
    $message = Configuration::get('MYFIRSTMODULE_MESSAGE');

    // 템플릿에 전달할 변수를 할당한다.
    $this->context->smarty->assign(array(
        'welcome_message' => $message
    ));

    // 템플릿 파일을 불러와서 출력한다.
    return $this->display(__FILE__, 'views/templates/hook/welcome.tpl');
}
        

이전에는 "Welcome to my awesome shop! 🚀" 이라는 고정된 문자열을 전달했지만, 이제는 `Configuration::get('MYFIRSTMODULE_MESSAGE')`를 통해 관리자가 저장한 동적인 값을 불러와서 전달하도록 바뀌었어.

이제 모든 준비가 끝났어. 관리자 페이지에서 모듈의 'Configure' 버튼을 눌러봐. 우리가 만든 멋진 설정 페이지가 나타날 거야. 입력창에 원하는 메시지를 입력하고 저장한 뒤, 홈페이지에 가보면 그 메시지가 그대로 출력되는 걸 확인할 수 있어!

축하해! 이제 너의 모듈은 사용자와 소통할 수 있는 동적인 모듈로 거듭났어. 이런 설정 기능은 모듈의 활용도를 무한히 높여줘. 색상을 바꾸거나, 노출 여부를 결정하거나, API 키를 입력받는 등 다양한 기능을 구현할 수 있는 기반을 다진 셈이야.

💡 전문가의 길: 모듈 개발에 더 깊이 빠져들고 싶거나, 복잡한 기능을 구현하다 막혔을 땐 어떻게 할까? 혼자 끙끙 앓기보다는 전문가의 도움을 받는 것도 좋은 방법이야. 재능넷 같은 플랫폼에는 실력 있는 프레스토샵 개발자들이 많이 활동하고 있으니, 필요할 때 조언을 구하거나 협업을 통해 프로젝트를 진행하는 것도 현명한 선택이 될 수 있어.

💾 Part 5: 데이터베이스 활용 - 나만의 데이터 창고 만들기

`Configuration`을 이용해 간단한 설정 값을 저장하는 법은 이제 알겠지? 하지만 모듈이 복잡해지면 단순한 키-값 쌍만으로는 부족할 때가 와. 예를 들어, 방문자 로그를 기록하거나, 상품에 대한 고객의 질문과 답변을 관리하거나, 우리 모듈만의 고유한 데이터를 체계적으로 저장해야 할 필요가 생기지.

이럴 때 필요한 것이 바로 '나만의 데이터베이스 테이블'이야. 우리 모듈이 소유하고 관리하는 독립적인 데이터 공간을 만드는 거지.

설치 시 테이블 생성, 제거 시 테이블 삭제

좋은 모듈의 기본은 '깔끔함'이야. 모듈이 설치될 때 필요한 테이블을 스스로 만들고, 제거될 때는 자기가 만들었던 테이블을 깨끗하게 지워서 시스템에 흔적을 남기지 않아야 해. 이 작업은 역시 `install()`과 `uninstall()` 메소드에서 이루어져.

우리 모듈에 간단한 방문자 로그를 남기는 테이블을 추가해본다고 가정해보자. `install()` 메소드를 아래와 같이 수정해.


// myfirstmodule.php

public function install()
{
    if (!parent::install() || !$this->registerHook('displayHome')) {
        return false;
    }

    // SQL 쿼리 작성
    $sql = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'myfirstmodule_log` (
        `id_log` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
        `ip_address` VARCHAR(45) NOT NULL,
        `visit_time` DATETIME NOT NULL,
        PRIMARY KEY (`id_log`)
    ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8;';

    // 쿼리 실행
    if (!Db::getInstance()->execute($sql)) {
        return false;
    }

    return true;
}
        

`parent::install()`이 성공한 후에 SQL 쿼리를 실행하는 로직이 추가됐어.

  • `_DB_PREFIX_`: 프레스토샵 설치 시 설정한 테이블 접두사(보통 `ps_`)를 의미하는 상수야. 이걸 사용해야 다른 프레스토샵 환경에서도 호환성을 유지할 수 있어. 우리 테이블의 최종 이름은 `ps_myfirstmodule_log`가 되겠지.
  • `_MYSQL_ENGINE_`: 프레스토샵이 사용하는 기본 DB 엔진(InnoDB 등)으로 설정해주는 상수야.
  • `Db::getInstance()->execute($sql)`: 프레스토샵의 데이터베이스 클래스를 이용해 SQL 쿼리를 실행하는 명령어. 쿼리 실행에 실패하면 `false`를 반환하기 때문에, 실패 시 모듈 설치도 중단되도록 처리해야 해.

테이블을 만들었으면, 지우는 법도 알아야지. `uninstall()` 메소드도 수정하자.


// myfirstmodule.php

public function uninstall()
{
    if (!parent::uninstall() || !$this->unregisterHook('displayHome')) {
        return false;
    }

    // SQL 쿼리 작성
    $sql = 'DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'myfirstmodule_log`;';

    // 쿼리 실행
    if (!Db::getInstance()->execute($sql)) {
        return false;
    }

    return true;
}
        

이제 모듈을 재설치하면 `ps_myfirstmodule_log` 테이블이 생성되고, 제거하면 흔적도 없이 사라질 거야. 아주 깔끔하지?

데이터 추가 및 조회하기

테이블을 만들었으니 데이터를 넣어봐야지. 홈페이지에 방문할 때마다 로그를 남기도록 `hookDisplayHome`을 수정해보자.


// myfirstmodule.php

public function hookDisplayHome($params)
{
    // --- 로그 기록 로직 추가 ---
    $ip = Tools::getRemoteAddr();
    $now = date('Y-m-d H:i:s');

    // pSQL() 함수로 SQL 인젝션 공격을 방지해야 해. 매우 중요!
    $sql = 'INSERT INTO `' . _DB_PREFIX_ . 'myfirstmodule_log` (`ip_address`, `visit_time`) 
            VALUES (\'' . pSQL($ip) . '\', \'' . pSQL($now) . '\')';
    
    Db::getInstance()->execute($sql);
    // --- 로그 기록 로직 끝 ---

    $message = Configuration::get('MYFIRSTMODULE_MESSAGE');

    $this->context->smarty->assign(array(
        'welcome_message' => $message
    ));

    return $this->display(__FILE__, 'views/templates/hook/welcome.tpl');
}
        
🚨 보안 경고! `pSQL()`을 잊지 마세요! 사용자 입력값이나 외부에서 들어온 데이터를 SQL 쿼리에 직접 넣을 때는 반드시 `pSQL()` 함수로 감싸줘야 해. 이건 SQL 인젝션이라는 아주 위험한 해킹 공격을 막기 위한 필수적인 조치야. `pSQL()`은 문자열에 포함될 수 있는 위험한 특수문자들을 안전하게 처리해줘.

이제 홈페이지를 몇 번 새로고침 한 뒤, phpMyAdmin 같은 DB 관리 툴로 `ps_myfirstmodule_log` 테이블을 열어봐. 방문 기록이 차곡차곡 쌓여있는 걸 볼 수 있을 거야.

고급 기술: ObjectModel로 우아하게 데이터 다루기

매번 SQL 쿼리를 직접 작성하는 건 번거롭고 실수하기도 쉬워. 그래서 프레스토샵은 ORM(Object-Relational Mapping)이라는 멋진 기능을 제공해. 데이터베이스 테이블을 하나의 '객체(Object)'처럼 다룰 수 있게 해주는 기술이지. 이 기능을 사용하려면 `ObjectModel` 클래스를 상속받는 클래스를 만들어야 해.

1. `myfirstmodule/classes` 폴더를 만들고, 그 안에 `MyLog.php` 파일을 생성하자.


<?php
// myfirstmodule/classes/MyLog.php

class MyLog extends ObjectModel
{
    public $id_log;
    public $ip_address;
    public $visit_time;

    public static $definition = array(
        'table' => 'myfirstmodule_log', // 테이블 이름 (접두사 제외)
        'primary' => 'id_log', // Primary Key
        'fields' => array(
            'ip_address' => array('type' => self::TYPE_STRING, 'validate' => 'isIpAddress', 'required' => true),
            'visit_time' => array('type' => self::TYPE_DATE, 'validate' => 'isDate', 'required' => true),
        ),
    );
}
        

`$definition` 배열에 테이블의 구조를 정의해주는 게 핵심이야. 테이블 이름, 기본 키, 그리고 각 필드의 데이터 타입과 유효성 검사 규칙을 명시해.

2. 이제 이 `MyLog` 클래스를 사용하려면, 메인 모듈 파일(`myfirstmodule.php`)에 클래스를 불러오는 코드를 추가해야 해. 생성자(`__construct`) 안에 추가해주자.


// myfirstmodule.php __construct() 메소드 안, parent::__construct() 호출 이후

require_once __DIR__ . '/classes/MyLog.php';
        

3. 이제 `hookDisplayHome`에서 SQL 대신 `ObjectModel`을 사용해보자.


// myfirstmodule.php

public function hookDisplayHome($params)
{
    // --- ObjectModel을 이용한 로그 기록 ---
    $log = new MyLog();
    $log->ip_address = Tools::getRemoteAddr();
    $log->visit_time = date('Y-m-d H:i:s');
    $log->add(); // add() 메소드를 호출하면 데이터베이스에 저장!
    // --- 로그 기록 로직 끝 ---

    // ... (이하 동일)
}
        

어때? 훨씬 깔끔하고 직관적이지 않아? `new MyLog()`로 객체를 만들고, 속성에 값을 할당한 다음, `add()` 메소드를 호출하기만 하면 끝이야. 데이터를 수정할 때는 `update()`, 삭제할 때는 `delete()` 메소드를 사용하면 돼. SQL 쿼리를 한 줄도 쓰지 않았지만, 내부적으로는 안전한 쿼리가 생성되어 실행돼.

`ObjectModel`은 프레스토샵 개발의 생산성을 극적으로 높여주는 강력한 도구야. 복잡한 데이터를 다뤄야 한다면 반드시 익혀두는 것이 좋아.

⚡ Part 6: 고급 테크닉 - 오버라이드와 프론트 컨트롤러

이제 너는 프레스토샵 모듈 개발의 기본기를 모두 갖췄어. 하지만 때로는 훅(Hook)만으로는 해결할 수 없는 문제에 부딪히게 돼. 프레스토샵의 핵심 동작 자체를 바꾸고 싶거나, 모듈을 위한 완전히 새로운 페이지가 필요할 때가 있지. 이럴 때 사용하는 두 가지 강력한 무기가 바로 '오버라이드'와 '프론트 컨트롤러'야.

오버라이드(Override): 핵심 기능을 내 입맛대로 (신중하게!)

오버라이드는 말 그대로 프레스토샵의 핵심 클래스나 컨트롤러의 기능을 '덮어쓰는' 기술이야. 예를 들어, 상품 가격을 계산하는 로직을 바꾸거나, 장바구니에 상품을 담는 규칙을 변경하는 등 아주 깊숙한 부분을 건드릴 수 있어.

⚠️ 경고: 오버라이드는 양날의 검! 오버라이드는 강력한 만큼 위험 부담도 커. 핵심 파일을 직접 수정하는 것보다는 안전하지만, 다른 모듈의 오버라이드와 충돌을 일으킬 수 있고, 프레스토샵 버전이 업데이트될 때 문제가 발생할 수도 있어. 따라서 오버라이드는 훅이나 다른 방법으로 도저히 해결이 불가능할 때, 최후의 수단으로 사용해야 해.

예를 들어, 상품 상세 페이지에서 상품 이름 뒤에 항상 "[BEST]"라는 문구를 붙이고 싶다고 가정해보자. 이건 `Product` 클래스의 `$name` 속성과 관련이 있어.

오버라이드 방법:

1. 프레스토샵 루트 폴더에 `override/classes/` 폴더를 찾아가. (없으면 만들어야 해)

2. 그 안에 `Product.php` 파일을 생성하고 아래 코드를 작성해.


<?php
// override/classes/Product.php

class Product extends ProductCore
{
    public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, Context $context = null)
    {
        // 먼저 부모의 생성자를 호출해서 기본 상품 정보를 로드한다.
        parent::__construct($id_product, $full, $id_lang, $id_shop, $context);

        // 상품 이름 뒤에 텍스트를 추가한다.
        if ($this->id) { // 상품이 실제로 로드되었을 때만
            $this->name = $this->name . ' [BEST]';
        }
    }
}
        

클래스 이름은 `Product`지만, 상속은 `ProductCore`로부터 받아. `ProductCore`가 바로 프레스토샵의 원본 `Product` 클래스야. 우리는 `__construct` (생성자) 메소드를 오버라이드해서, 상품 정보가 로드된 직후에 이름(`$this->name`)을 조작했어.

3. 가장 중요한 단계! 오버라이드 파일을 추가하거나 수정한 후에는 반드시 `cache/class_index.php` 파일을 삭제해야 해. 이 파일을 삭제하면 프레스토샵이 클래스 목록을 다시 스캔하면서 우리가 만든 오버라이드 파일을 인식하게 돼. (디버그 모드에서는 자동으로 처리되기도 해)

이제 쇼핑몰의 아무 상품 페이지나 가보면 상품명 뒤에 "[BEST]"가 붙어있는 걸 볼 수 있을 거야. 이런 식으로 프레스토샵의 거의 모든 핵심 동작을 수정할 수 있어. 하지만 다시 한번 강조할게. 꼭 필요할 때만 신중하게 사용하자!

프론트 컨트롤러(Front Controller): 모듈을 위한 독립 페이지 만들기

내 모듈만을 위한 별도의 페이지가 필요하다면? 예를 들어, 브랜드 소개 페이지, A/S 접수 폼, 특별 이벤트 페이지 같은 것들 말이야. 이럴 때 프론트 컨트롤러를 사용해.

우리 모듈이 지금까지 기록한 방문자 로그 목록을 보여주는 페이지를 만들어보자.

프론트 컨트롤러 생성 방법:

1. `myfirstmodule/controllers/front/` 폴더를 만들고, 그 안에 `log.php` 파일을 생성하자. * 경로: `myfirstmodule/controllers/front/log.php` * `front`는 쇼핑몰 앞단(고객이 보는 화면)을 의미해. 관리자 페이지용 컨트롤러는 `admin` 폴더에 만들어. * `log`는 이 컨트롤러의 이름이 돼.

2. `log.php` 파일에 아래 코드를 작성해.


<?php
// myfirstmodule/controllers/front/log.php

class MyFirstModuleLogModuleFrontController extends ModuleFrontController
{
    public function initContent()
    {
        parent::initContent();

        // 데이터베이스에서 로그 데이터를 가져온다.
        $sql = 'SELECT * FROM `' . _DB_PREFIX_ . 'myfirstmodule_log` ORDER BY `visit_time` DESC LIMIT 10';
        $logs = Db::getInstance()->executeS($sql);

        // Smarty 템플릿에 데이터를 전달한다.
        $this->context->smarty->assign(array(
            'log_entries' => $logs,
        ));

        // 사용할 템플릿 파일을 지정한다.
        $this->setTemplate('module:myfirstmodule/views/templates/front/log_display.tpl');
    }
}
        

클래스 이름의 규칙이 좀 독특하지? `모듈이름` + `컨트롤러이름` + `ModuleFrontController` 형식이야.

  • `MyFirstModuleLogModuleFrontController`: 우리 모듈의 이름은 `myfirstmodule`, 컨트롤러 이름은 `log`니까 이렇게 되는 거야.
  • `initContent()`: 이 메소드가 페이지의 핵심 로직을 담당해. 여기서 데이터를 처리하고, 템플릿에 변수를 넘기고, 어떤 템플릿 파일을 보여줄지 결정해.
  • `Db::getInstance()->executeS($sql)`: `execute`와 달리 `executeS`는 `SELECT` 쿼리의 결과를 배열 형태로 반환해줘.
  • `$this->setTemplate(...)`: 이 페이지를 어떤 템플릿 파일로 그릴지 지정하는 부분이야. `module:모듈이름/경로` 형식으로 써줘야 해.

3. 이제 화면을 그릴 템플릿 파일을 만들어야지. `myfirstmodule/views/templates/front/log_display.tpl` 파일을 생성하자.


{* myfirstmodule/views/templates/front/log_display.tpl *}

{extends file='page.tpl'}

{block name='page_content'}
    <h1>Visitor Log</h1>

    {if $log_entries}
        <table class="table table-bordered">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>IP Address</th>
                    <th>Visit Time</th>
                </tr>
            </thead>
            <tbody>
                {foreach from=$log_entries item=log}
                    <tr>
                        <td>{$log.id_log}</td>
                        <td>{$log.ip_address}</td>
                        <td>{$log.visit_time}</td>
                    </tr>
                {/foreach}
            </tbody>
        </table>
    {else}
        <p>No logs found.</p>
    {/if}
{/block}
        

`{extends file='page.tpl'}` 과 `{block name='page_content'}` 는 이 템플릿이 쇼핑몰의 기본 레이아웃(헤더, 푸터 등)을 그대로 사용하면서, 메인 콘텐츠 영역만 우리가 만든 내용으로 채우겠다는 의미야. 컨트롤러에서 넘겨준 `$log_entries` 배열을 `{foreach}` 루프를 돌면서 테이블로 예쁘게 출력하고 있어.

4. 이제 이 페이지에 어떻게 접속할까? 프레스토샵은 친절하게도 'Pretty URL'을 활성화했다면 자동으로 예쁜 주소를 만들어줘.

`http://내쇼핑몰주소/index.php?fc=module&module=myfirstmodule&controller=log`

이 주소로 접속해봐. 우리가 만든 방문자 로그 페이지가 짠! 하고 나타날 거야. 이제 너는 프레스토샵 안에 너만의 독립적인 페이지를 만들 수 있는 능력까지 갖추게 된 거야!

🏆 Part 7: 마무리 - 좋은 모듈을 위한 체크리스트와 그 너머

와, 정말 긴 여정이었어! 👏 '헬로월드'에서 시작해 훅, 설정 페이지, 데이터베이스, 심지어 오버라이드와 프론트 컨트롤러까지. 이제 너는 프레스토샵 모듈 개발의 핵심적인 개념과 기술을 모두 경험해봤어. 이 정도면 어디 가서 "저 프레스토샵 모듈 좀 만들 줄 압니다"라고 말해도 될 수준이야.

하지만 코드를 '작성'하는 것과 '잘 작성'하는 것은 다른 이야기지. 마지막으로, 너의 모듈을 한 단계 더 업그레이드해 줄 몇 가지 베스트 프랙티스와 팁을 공유할게.

좋은 모듈을 위한 체크리스트 ✅

모듈 개발을 마친 후, 배포하기 전에 아래 항목들을 꼭 확인해보자.

1. 보안은 철저한가? * 사용자 입력값이나 URL 파라미터를 받을 때는 항상 유효성을 검사하고 데이터를 정제(Sanitize)했는가? (`Tools::getValue`, `(int)`, `pSQL()` 등) * SQL 인젝션, XSS(Cross-Site Scripting) 공격에 대비했는가?

2. 코딩 표준을 준수했는가? * 프레스토샵의 코딩 표준(PSR-2 기반)에 맞춰 코드를 작성했는가? (변수명, 함수명, 들여쓰기 등) 일관성 있는 코드는 유지보수를 훨씬 쉽게 만들어.

3. 번역이 가능한가? (다국어 지원) * 화면에 표시되는 모든 문자열을 `$this->l('Your text')` 함수로 감쌌는가? 이렇게 해야 다른 언어로 쉽게 번역할 수 있어.

4. 성능에 문제는 없는가? * 모든 페이지에서 호출되는 훅(예: `displayHeader`)에 무거운 데이터베이스 쿼리나 복잡한 로직을 넣지 않았는가? * 필요하다면 캐시(Cache) 사용을 고려했는가?

5. 다른 환경과의 호환성은? * 멀티스토어(Multi-store) 환경을 고려하여 코드를 작성했는가? (`Context::getContext()->shop` 등) * `$this->ps_versions_compliancy`에 호환되는 프레스토샵 버전을 명확히 기재했는가?

6. 깔끔하게 설치되고 제거되는가? * `install()`에서 생성한 모든 것(테이블, 설정 값, 훅 등록 등)을 `uninstall()`에서 깨끗하게 제거하는가? 시스템에 쓰레기를 남기지 않는 것은 좋은 모듈의 기본 매너야.

이제 어디로 나아갈까?

이 튜토리얼은 너에게 프레스토샵 모듈 개발의 세계로 들어가는 문을 열어줬어. 이제 문 안쪽의 광활한 세계를 탐험하는 것은 너의 몫이야.

더 깊이 파고들 주제들:

  • 결제 모듈 개발: 외부 PG사와 연동하여 새로운 결제 수단을 추가해봐.
  • 배송 모듈 개발: 특정 택배사의 API와 연동하여 배송비를 계산하고 운송장을 출력하는 모듈을 만들어봐.
  • 웹서비스(Webservice) API 활용: 프레스토샵의 데이터를 외부 앱(모바일 앱, ERP 등)과 연동하는 기능을 구현해봐.
  • 관리자 컨트롤러(Admin Controller): 모듈 설정 페이지를 넘어서, 관리자 페이지에 완전히 새로운 메뉴와 기능을 갖춘 페이지를 만들어봐. (`HelperList`, `HelperForm`을 더 깊게 활용)
  • 메일 템플릿 수정 및 발송: 특정 상황에 고객이나 관리자에게 커스텀된 이메일을 보내는 기능을 추가해봐.
💡 최고의 스승은 공식 문서와 커뮤니티! 개발하다 막히는 부분이 있다면, 가장 먼저 찾아봐야 할 곳은 프레스토샵 공식 개발자 문서(DevDocs)야. 모든 클래스와 함수의 사용법이 상세하게 나와 있어. 또한, 프레스토샵 공식 포럼이나 스택 오버플로우 같은 커뮤니티에는 너와 같은 고민을 먼저 했던 개발자들의 질문과 답변이 가득한 보물창고야.

이 튜토리얼을 끝까지 따라온 너의 열정과 노력에 박수를 보내. 🥳
처음에는 모든 것이 낯설고 어려웠겠지만, 이제 너는 프레스토샵이라는 강력한 도구를 자유자재로 확장할 수 있는 힘을 얻었어. 네 상상력을 코드로 옮겨서, 세상에 없던 멋진 기능들을 만들어봐. 네가 만든 작은 모듈 하나가 누군가의 비즈니스를 성공으로 이끄는 결정적인 열쇠가 될 수도 있으니까.

이제 진짜 시작이야. Go build something awesome! 💪

댓글 작성

이 글에 대한 여러분의 생각을 들려주세요

댓글 0
댓글을 불러오는 중입니다.

아직 댓글이 없습니다.