# M-Project 개발 가이드 ## 개요 M-Project의 신규 페이지 및 기능 개발을 위한 표준 가이드입니다. --- ## 📋 기본 규칙 ### 1. 파일 구조 규칙 ``` frontend/ ├── [page-name].html # 메인 HTML 파일 ├── static/ │ ├── js/ │ │ ├── modules/ │ │ │ └── [page-name]/ # 페이지별 모듈 │ │ │ ├── [page-name].js │ │ │ ├── components/ # 페이지 전용 컴포넌트 │ │ │ └── utils/ # 페이지 전용 유틸리티 │ │ ├── components/ # 공통 컴포넌트 │ │ ├── core/ # 핵심 시스템 │ │ └── utils/ # 공통 유틸리티 │ └── css/ │ └── [page-name].css # 페이지별 스타일 (필요시) ``` ### 2. 네이밍 규칙 - **파일명**: kebab-case (`user-management.html`) - **클래스명**: PascalCase (`UserManagementModule`) - **함수명**: camelCase (`loadUserData`) - **변수명**: camelCase (`currentUser`) - **상수명**: UPPER_SNAKE_CASE (`API_BASE_URL`) - **CSS 클래스**: kebab-case (`user-list-item`) ### 3. 권한 체크 규칙 - 모든 페이지는 권한 체크를 구현해야 함 - 페이지별 권한명은 `[category]_[action]` 형식 사용 - 예: `users_manage`, `reports_view`, `projects_create` --- ## 🚀 신규 페이지 개발 단계 ### 1단계: 권한 정의 ```javascript // backend/routers/page_permissions.py의 DEFAULT_PAGES에 추가 'new_feature': {'title': '새 기능', 'default_access': false} ``` ### 2단계: HTML 템플릿 생성 ```html 새 기능 - 작업보고서
``` ### 3단계: 페이지 모듈 생성 ```javascript // static/js/modules/new-feature/new-feature.js class NewFeatureModule extends BasePageModule { constructor(options = {}) { super(options); this.data = []; this.currentView = 'list'; } /** * 모듈 초기화 */ async initialize() { try { this.showLoading('main-content', '새 기능을 로드하는 중...'); // 데이터 로드 await this.loadData(); // UI 렌더링 this.render(); // 이벤트 바인딩 this.bindEvents(); this.initialized = true; } catch (error) { console.error('NewFeatureModule 초기화 실패:', error); this.showError('main-content', '새 기능을 로드할 수 없습니다.'); } } /** * 데이터 로드 */ async loadData() { try { // API 호출 예시 this.data = await NewFeatureAPI.getAll(); } catch (error) { console.error('데이터 로드 실패:', error); throw error; } } /** * UI 렌더링 */ render() { const container = document.getElementById('main-content'); container.innerHTML = this.generateHTML(); } /** * HTML 생성 */ generateHTML() { return `

새 기능

새 기능에 대한 설명

${this.generateDataList()}
`; } /** * 데이터 목록 HTML 생성 */ generateDataList() { if (this.data.length === 0) { return `

데이터가 없습니다.

`; } return `
${this.data.map(item => this.generateDataRow(item)).join('')}
제목 생성일 액션
`; } /** * 데이터 행 HTML 생성 */ generateDataRow(item) { return ` ${item.title} ${new Date(item.created_at).toLocaleDateString()} `; } /** * 이벤트 바인딩 */ bindEvents() { const addBtn = document.getElementById('add-btn'); if (addBtn) { this.addEventListener(addBtn, 'click', () => this.showAddModal()); } } /** * 항목 추가 모달 표시 */ showAddModal() { // 모달 구현 console.log('새 항목 추가 모달 표시'); } /** * 항목 수정 */ editItem(id) { console.log('항목 수정:', id); } /** * 항목 삭제 */ async deleteItem(id) { if (confirm('정말 삭제하시겠습니까?')) { try { await NewFeatureAPI.delete(id); await this.loadData(); this.render(); } catch (error) { alert('삭제에 실패했습니다.'); } } } } // API 정의 const NewFeatureAPI = { getAll: () => apiRequest('/new-feature/'), get: (id) => apiRequest(`/new-feature/${id}`), create: (data) => apiRequest('/new-feature/', { method: 'POST', body: JSON.stringify(data) }), update: (id, data) => apiRequest(`/new-feature/${id}`, { method: 'PUT', body: JSON.stringify(data) }), delete: (id) => apiRequest(`/new-feature/${id}`, { method: 'DELETE' }) }; // 전역 인스턴스 생성 window.newFeatureModule = null; // 페이지 매니저에 모듈 등록 if (window.pageManager) { window.pageManager.createPageModule = function(pageId, options) { switch (pageId) { case 'new_feature': window.newFeatureModule = new NewFeatureModule(options); return window.newFeatureModule; // ... 기존 케이스들 default: return null; } }; } ``` ### 4단계: 공통 헤더에 메뉴 추가 ```javascript // static/js/components/common-header.js의 initMenuItems()에 추가 { id: 'new_feature', title: '새 기능', icon: 'fas fa-star', url: '/new-feature.html', pageName: 'new_feature', color: 'text-yellow-600', bgColor: 'bg-yellow-50 hover:bg-yellow-100' } ``` --- ## 🎨 UI/UX 가이드라인 ### 1. 색상 팔레트 - **Primary**: Blue (blue-600, blue-700) - **Success**: Green (green-600, green-700) - **Warning**: Yellow (yellow-600, yellow-700) - **Danger**: Red (red-600, red-700) - **Info**: Purple (purple-600, purple-700) - **Gray**: Gray (gray-500, gray-600, gray-700) ### 2. 컴포넌트 스타일 ```css /* 버튼 */ .btn-primary { @apply px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors; } .btn-secondary { @apply px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-colors; } .btn-success { @apply px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors; } .btn-danger { @apply px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors; } /* 입력 필드 */ .input-field { @apply w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500; } /* 카드 */ .card { @apply bg-white rounded-lg shadow-sm border border-gray-200; } .card-header { @apply px-6 py-4 border-b border-gray-200; } .card-body { @apply px-6 py-4; } ``` ### 3. 반응형 디자인 - **Mobile First**: 모바일부터 디자인 시작 - **Breakpoints**: sm(640px), md(768px), lg(1024px), xl(1280px) - **Grid System**: Tailwind CSS Grid 사용 --- ## 🔧 API 개발 가이드 ### 1. 백엔드 라우터 생성 ```python # backend/routers/new_feature.py from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from typing import List from database.database import get_db from database.models import User from routers.auth import get_current_user router = APIRouter(prefix="/api/new-feature", tags=["new-feature"]) @router.get("/") async def get_all_items( current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """모든 항목 조회""" # 구현 내용 pass @router.post("/") async def create_item( item_data: dict, current_user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """새 항목 생성""" # 구현 내용 pass ``` ### 2. 메인 앱에 라우터 등록 ```python # backend/main.py from routers import new_feature app.include_router(new_feature.router) ``` --- ## 📝 코드 품질 가이드 ### 1. 에러 처리 ```javascript // 좋은 예 try { const data = await API.getData(); this.processData(data); } catch (error) { console.error('데이터 로드 실패:', error); this.showError('데이터를 불러올 수 없습니다.'); } // 나쁜 예 const data = await API.getData(); // 에러 처리 없음 ``` ### 2. 로딩 상태 관리 ```javascript // 좋은 예 async loadData() { this.showLoading('data-container'); try { const data = await API.getData(); this.renderData(data); } catch (error) { this.showError('data-container', '데이터 로드 실패'); } } // 나쁜 예 async loadData() { const data = await API.getData(); // 로딩 상태 없음 this.renderData(data); } ``` ### 3. 메모리 관리 ```javascript // 좋은 예 - BasePageModule 사용 class MyModule extends BasePageModule { bindEvents() { const button = document.getElementById('my-button'); this.addEventListener(button, 'click', this.handleClick.bind(this)); } // cleanup()은 BasePageModule에서 자동 처리 } // 나쁜 예 - 수동 이벤트 관리 class MyModule { bindEvents() { document.getElementById('my-button').addEventListener('click', this.handleClick); // 이벤트 리스너 제거 코드 없음 } } ``` --- ## 🧪 테스트 가이드 ### 1. 기능 테스트 체크리스트 - [ ] 페이지 로드 정상 작동 - [ ] 권한 체크 정상 작동 - [ ] CRUD 기능 정상 작동 - [ ] 에러 처리 정상 작동 - [ ] 반응형 디자인 정상 작동 - [ ] 브라우저 호환성 확인 ### 2. 성능 테스트 - [ ] 페이지 로드 시간 < 3초 - [ ] API 응답 시간 < 1초 - [ ] 메모리 누수 없음 - [ ] 모바일 성능 최적화 --- ## 📚 참고 자료 ### 1. 기존 모듈 참고 - `static/js/components/common-header.js` - 공통 컴포넌트 예시 - `static/js/core/page-manager.js` - 페이지 관리 예시 - `static/js/core/permissions.js` - 권한 시스템 예시 ### 2. 외부 라이브러리 - **Tailwind CSS**: https://tailwindcss.com/docs - **Font Awesome**: https://fontawesome.com/icons - **FastAPI**: https://fastapi.tiangolo.com/ ### 3. 코딩 컨벤션 - **JavaScript**: Airbnb Style Guide - **Python**: PEP 8 - **HTML/CSS**: Google Style Guide --- **작성일**: 2025-10-25 **버전**: 1.0 **작성자**: AI Assistant > 이 가이드는 프로젝트 발전에 따라 지속적으로 업데이트됩니다.