모듈 개발 모범 사례
이 문서에는 고품질 XOOPS 모듈 개발을 위한 모범 사례가 통합되어 있습니다. 이러한 지침을 따르면 유지 관리가 가능하고 안전하며 성능이 뛰어난 모듈이 보장됩니다.
아키텍처
섹션 제목: “아키텍처”클린 아키텍처 따르기
섹션 제목: “클린 아키텍처 따르기”코드를 레이어로 구성합니다.
src/├── Domain/ # Business logic, entities├── Application/ # Use cases, services├── Infrastructure/ # Database, external services└── Presentation/ # Controllers, templates단일 책임
섹션 제목: “단일 책임”각 클래스에는 변경해야 할 한 가지 이유가 있어야 합니다.
// Good: Focused classesclass ArticleRepository { /* persistence only */ }class ArticleValidator { /* validation only */ }class ArticleNotifier { /* notifications only */ }
// Bad: God classclass Article { public function save() { } public function validate() { } public function notify() { } public function generatePDF() { }}의존성 주입
섹션 제목: “의존성 주입”종속성을 주입하고 생성하지 마세요.
// Goodpublic function __construct( private readonly ArticleRepositoryInterface $repository) {}
// Badpublic function __construct() { $this->repository = new ArticleRepository();}코드 품질
섹션 제목: “코드 품질”유형 안전
섹션 제목: “유형 안전”엄격한 유형과 유형 선언을 사용하십시오.
<?php
declare(strict_types=1);
final class ArticleService{ public function findById(int $id): ?Article { // ... }
public function create(CreateArticleDTO $dto): Article { // ... }}오류 처리
섹션 제목: “오류 처리”예외를 적절하게 사용하십시오.
// Throw specific exceptionsthrow new ArticleNotFoundException($id);throw new ValidationException($errors);throw new UnauthorizedException('Cannot edit this article');
// Catch at appropriate leveltry { $article = $service->create($dto);} catch (ValidationException $e) { return $this->renderForm($e->getErrors());} catch (UnauthorizedException $e) { return $this->redirectToLogin();}안전이 보장되지 않음
섹션 제목: “안전이 보장되지 않음”가능한 경우 null을 피하세요.
// Use null object patternpublic function getAuthor(): UserInterface{ return $this->author ?? new AnonymousUser();}
// Use Optional/Maybe patternpublic function findById(int $id): ?Article{ // Explicitly nullable return}데이터베이스
섹션 제목: “데이터베이스”쿼리에 Criteria 사용
섹션 제목: “쿼리에 Criteria 사용”$criteria = new CriteriaCompo();$criteria->add(new Criteria('status', 'published'));$criteria->add(new Criteria('category_id', $categoryId));$criteria->setSort('created_at');$criteria->setOrder('DESC');$criteria->setLimit($limit);
$items = $handler->getObjects($criteria);사용자 입력 탈출
섹션 제목: “사용자 입력 탈출”$sql = sprintf( "SELECT * FROM %s WHERE id = %d AND title = %s", $db->prefix('mymodule_items'), intval($id), $db->quoteString($title));거래 사용
섹션 제목: “거래 사용”$db->query('START TRANSACTION');
try { $handler->insert($article); $handler->insert($metadata); $db->query('COMMIT');} catch (\Exception $e) { $db->query('ROLLBACK'); throw $e;}항상 입력 유효성을 검사합니다.
섹션 제목: “항상 입력 유효성을 검사합니다.”use Xmf\Request;
$id = Request::getInt('id', 0);$title = Request::getString('title', '');$data = Request::getArray('data', []);
// Additional validationif (strlen($title) < 5) { throw new ValidationException('Title too short');}CSRF 토큰 사용
섹션 제목: “CSRF 토큰 사용”// In form$form->addElement(new XoopsFormHiddenToken());
// On submitif (!$GLOBALS['xoopsSecurity']->check()) { redirect_header('index.php', 3, 'Invalid token');}권한 확인
섹션 제목: “권한 확인”if (!$helper->isUserAdmin()) { redirect_header('index.php', 3, _NOPERM);}
if (!$permHandler->isGranted('edit', $categoryId)) { throw new UnauthorizedException();}캐싱 사용
섹션 제목: “캐싱 사용”$cache = $helper->getCache();$cacheKey = "articles_{$categoryId}_{$limit}";
$articles = $cache->read($cacheKey);if ($articles === false) { $articles = $handler->getArticles($categoryId, $limit); $cache->write($cacheKey, $articles, 3600);}쿼리 최적화
섹션 제목: “쿼리 최적화”// Use indexes// Add to sql/mysql.sql:// INDEX `idx_status_date` (`status`, `created_at`)
// Select only needed columns$handler->getObjects($criteria, false, true); // asArray = true
// Use pagination$criteria->setLimit($perPage);$criteria->setStart($offset);테스트
섹션 제목: “테스트”단위 테스트 작성
섹션 제목: “단위 테스트 작성”public function testCreateArticle(): void{ $repository = $this->createMock(ArticleRepositoryInterface::class); $repository->expects($this->once())->method('save');
$service = new ArticleService($repository); $dto = new CreateArticleDTO('Title', 'Content');
$article = $service->create($dto);
$this->assertInstanceOf(Article::class, $article);}관련 문서
섹션 제목: “관련 문서”- 클린 코드 - 클린 코드 원칙
- 코드 구성 - 프로젝트 구조
- 테스트 - 테스트 가이드 -../02-핵심 개념/보안/보안-모범 사례 - 보안 가이드