선상 낚시 예약 서비스의 사이트 관리자(Admin) 와 사용자 사이트.
| 프론트엔드 | Nuxt 3 (Vue 3), SCSS, flatpickr (DatePicker) |
| 백엔드 | CodeIgniter 4 (PHP) |
| DB | MySQL / MariaDB |
| 폰트 | Pretendard (로컬, woff2 + woff) |
| 외부 API | Daum 우편번호, Google Geocoding |
| 암호화 | CI4 Encryption (AES-256-CTR + HMAC-SHA512) |
/site-manager (로그인 폼, 좌우 50/50 레이아웃)POST /api/auth/login, POST /api/auth/logout, GET /api/auth/check/site-manager/dashboard 로 이동admin_users (id, username, password, password_changed_at, name, email, phone, role, status, login_attempts, last_failed_login, last_login, deleted_YN, created_at, updated_at)admin_permissions (id, admin_id FK CASCADE, permission VARCHAR, UNIQUE(admin_id, permission)) — 1:N 메뉴 권한list / create / detail / edit (/site-manager/admin/...)GET /api/admin (검색 search_field=username/name/email + role/status 필터 + deleted=1로 삭제 관리자만 분기)GET /api/admin/check-username (아이디 중복, soft 삭제된 계정 제외 → 재사용 가능)GET /api/admin/:id (permissions 같이 응답)POST /api/admin (생성 + permissions 동기화)PUT /api/admin/:id (role/permissions 변경은 슈퍼관리자만 가드)DELETE /api/admin/:id (soft delete + 토큰 무효화, permissions는 보존)POST /api/admin/:id/restore (복구 — username/email 충돌 검사)DELETE /api/admin/:id/hard (영구 삭제 — 이미 soft 삭제된 계정만, 본인 차단)POST /api/admin/:id/password, POST /api/admin/:id/unlocksuper_admin, admin (디폴트 admin)active, inactive, suspendedpermissions) enum: admin / field / fishing / challenge / quest / item / species / user (= admin.vue menuItems id와 동일)
'all' 문자열로 반환deleted_YN='Y'만 보여줌 → 선택 복구 / 선택 영구 삭제+N 더보기 (list 최대 2개, detail 전체 표시)fishing_field (id, name, weight, status_YN, deleted_YN, created_at, updated_at)GET /api/field/list, GET/POST/PUT/DELETE /api/field/:idfishing_area (id, name, deleted_YN, created_at, updated_at)GET /api/area/list, GET/POST/PUT/DELETE /api/area/:idGET /api/area/:id/places — 해당 지역에 속한 onboard + fishing UNION ALL (필드명 JOIN), limit 모드(detail 미리보기 8개) / page 모드(전체보기 페이지네이션)409 Conflict)/area/places/:id): 페이지네이션 + 6컬럼(번호/구분/이름/주소/상태/등록일) + 역순 번호 표시onboard (id, field_id, area_id, name, area_detail, tonnage, capacity, zip_code, address, address_detail, address_refer, lat, lng, bank_code, account_number(암호화), account_holder, partnership_YN, status_YN, deleted_YN, created_at, updated_at)onboard_photos (FK → onboard, ON DELETE CASCADE, hard delete)GET /api/onboard/list (분야·지역 JOIN, 검색·필터·페이지네이션)GET /api/onboard/:id (사진 목록 포함, 계좌번호 복호화)POST /api/onboard (계좌번호 암호화)PUT /api/onboard/:id (계좌번호 재암호화)DELETE /api/onboard/:id (soft delete)POST /api/onboard/:id/photos (다중 사진 업로드, MIME 검증)DELETE /api/onboard/photo/:photoId (파일 + DB hard delete)fishing (id, field_id, area_id, name, operating_hours, fish_species, zip_code, address, address_detail, address_refer, lat, lng, bank_code, account_number(암호화), account_holder, partnership_YN, status_YN, deleted_YN, created_at, updated_at)fishing_photos (FK → fishing, ON DELETE CASCADE, hard delete)GET /api/fishing/list (분야·지역 JOIN, 검색·필터·페이지네이션)GET /api/fishing/:id (사진 목록 포함, 계좌번호 복호화)POST /api/fishing (계좌번호 암호화)PUT /api/fishing/:id (계좌번호 재암호화)DELETE /api/fishing/:id (soft delete)POST /api/fishing/:id/photos (다중 사진 업로드, MIME 검증)DELETE /api/fishing/photo/:photoId (파일 + DB hard delete)operating_hours (운영시간 자유 텍스트), fish_species (주요 어종, 콤마 구분 VARCHAR)fs.fish_species LIKE 매칭item (id, name, type, point, file_name, file_path, status_YN, deleted_YN, created_at, updated_at)GET /api/item/list (검색·구분 필터·페이지네이션)GET /api/item/:idPOST /api/item (multipart — 이미지와 텍스트 한 번에)PUT /api/item/:id (텍스트 필드 수정, JSON)POST /api/item/:id/image (이미지 교체, multipart, 기존 파일 자동 삭제)DELETE /api/item/:id/image (이미지 제거)DELETE /api/item/:id (soft delete)세 개의 1뎁스 메뉴(어종구분 / 챌린지 어종관리 / 퀘스트 어종관리), 같은 코드 패턴 공유.
species_type (id, name, sort_order, status_YN, deleted_YN, created_at)GET /api/species/list, POST /api/species/bulk-save (creates + updates + deletes 트랜잭션)species_challenge (id, type_id NULL, name, min, max, round1_min/max ~ round5_min/max, deleted_YN, created_at)GET /api/species-challenge/list (species_type JOIN, 구분 필터 + 어종명 검색 + 기간 검색), POST /api/species-challenge/bulk-savetype_id IS NULL)species_quest (구조 동일)일괄 저장 (3)), 변경 없으면 버튼 숨김Teleport to="body" + Transition, 하단 중앙, 자동 dismiss)maxlength는 number에서 무먹어서 @input 핸들러로 자르기) + spinner 화살표 숨김.env (Nuxt 루트)NUXT_PUBLIC_API_BASE=... # 백엔드 base URL
NUXT_PUBLIC_IMAGE_BASE=... # 이미지 호스트 (운영 도메인)
NUXT_PUBLIC_MEDIA_BASE=...
NUXT_PUBLIC_GOOGLE_MAP_KEY=... # Google Geocoding 키 (위도, 경도 가져오는 데에 사용[타 API 이용시 별도 키 필요하여 구 피싱포엠 형식 따름])
.env (backend/)encryption.key = hex2bin:... # 계좌번호 암호화 키 (분실 시 복호화 불가)
backend/app/Config/App.php — appTimezone = 'Asia/Seoul'
backend/public/uploads/{도메인}/ (기존 패턴과 통일, 웹 직접 서빙)
backend/public/uploads/onboard/backend/public/uploads/fishing/backend/public/uploads/item/NUXT_PUBLIC_IMAGE_BASE 환경변수NUXT_PUBLIC_API_BASE의 origin 자동 추출 → 로컬에서 imageBase 비워도 이미지 표시됨ERD 새로 짜면서 기존 비즈니스 컨트롤러(Branch, Showroom, Service, Brochure, Event, News, Notice, IR, Advisor, Popup, Basic, SalesStaff, BranchManager, Test) 전부 삭제. 남긴 것: Auth / Admin / BaseApi / Ping / Dashboard / Upload / Home.
deleted_YN)hardDelete)는 슈퍼관리자가 "삭제 관리자 관리" 모드에서만 수행ON UPDATE CURRENT_TIMESTAMP 부여하므로 created_at은 DEFAULT CURRENT_TIMESTAMP만, updated_at은 NULL DEFAULT NULL로 정의VARCHAR(255) 권장<style scoped> 안 만들고 app/assets/scss/admin.scss 에 통합 (일관성 + 관리 단순화)INT/INT UNSIGNED/BIGINT)이 정확히 같아야 함. CHARSET/COLLATE도 동일해야 함.admin--modal-overlay + .admin--alert-modal admin--form-modal 조합. AdminAlertModal/비밀번호 변경 모달 등 전부 같은 톤(헤더 primary 네이비 배경, 본문 라운드 input, footer .admin--btn 체계)<Teleport to="body"> — 부모 컨테이너의 transform/overflow가 position: fixed를 가두는 경우 대비. 알림 모달은 .admin--alert-overlay { z-index: 10010 }로 다른 모달 위에 표시localStorage.admin_user.permissions 기반으로 사이드바 메뉴 필터링