From a0fc67adae37091cd6e7e03bb9e64747bbd10be5 Mon Sep 17 00:00:00 2001 From: caadiq Date: Thu, 22 Jan 2026 16:30:39 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20Low=20=EC=9A=B0=EC=84=A0=EC=88=9C?= =?UTF-8?q?=EC=9C=84=20=EC=BD=94=EB=93=9C=20=ED=92=88=EC=A7=88=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - constants에 GROUP_INFO 상수 추가 (데뷔일, 팬덤명) - PC Home에서 멤버 수 동적 계산 (API 기반) - mobile/Layout.jsx 컴포넌트 분리 (Header.jsx, BottomNav.jsx) - 미사용 유틸리티 함수는 관리자 페이지용으로 유지 Co-Authored-By: Claude Opus 4.5 --- docs/code-improvements.md | 32 +++++---- docs/migration.md | 2 + .../src/components/mobile/BottomNav.jsx | 45 +++++++++++++ .../src/components/mobile/Header.jsx | 26 ++++++++ .../src/components/mobile/Layout.jsx | 66 +------------------ frontend-temp/src/components/mobile/index.js | 2 + frontend-temp/src/constants/index.js | 9 +++ frontend-temp/src/pages/home/pc/Home.jsx | 22 +++++-- 8 files changed, 122 insertions(+), 82 deletions(-) create mode 100644 frontend-temp/src/components/mobile/BottomNav.jsx create mode 100644 frontend-temp/src/components/mobile/Header.jsx diff --git a/docs/code-improvements.md b/docs/code-improvements.md index a401b5f..05d2f62 100644 --- a/docs/code-improvements.md +++ b/docs/code-improvements.md @@ -943,12 +943,14 @@ export function decodeHtmlEntities(text) { --- -## 4. 낮은 우선순위 (Low) +## 4. 낮은 우선순위 (Low) ✅ 완료 -### 4.1 하드코딩된 값 +### 4.1 하드코딩된 값 ✅ **파일**: `pages/home/pc/Home.jsx`, `pages/home/mobile/Home.jsx` +**상태**: ✅ 완료 - `GROUP_INFO` 상수 추가, 멤버 수는 API에서 동적 계산 + ```javascript // 하드코딩된 데뷔일 const debutDate = new Date('2018-01-24'); @@ -959,51 +961,57 @@ const debutDate = new Date('2018-01-24'); { value: '2018.01.24', label: '데뷔일' }, ``` -**해결방법**: 상수 또는 API로 이동 +**해결방법**: 상수 + 동적 계산 ```javascript // constants/index.js에 추가 export const GROUP_INFO = { + NAME: 'fromis_9', + NAME_KR: '프로미스나인', DEBUT_DATE: '2018-01-24', + DEBUT_DATE_DISPLAY: '2018.01.24', FANDOM_NAME: 'flover', }; -// 또는 멤버 수는 API에서 동적으로 계산 -const activeMembers = members.filter(m => !m.is_former); -const memberCount = activeMembers.length; +// 멤버 수는 API에서 동적으로 계산 +const activeMemberCount = members.filter(m => !m.is_former).length; ``` --- -### 4.2 미사용 유틸리티 함수 +### 4.2 미사용 유틸리티 함수 ✅ (유지) **파일**: `utils/format.js`, `utils/schedule.js` +**상태**: ✅ 검토 완료 - 관리자 페이지 마이그레이션에서 사용 예정으로 유지 + **미사용 함수 목록**: - `formatViewCount()` - 검색 결과 0건 - `formatFileSize()` - 검색 결과 0건 - `groupSchedulesByDate()` - 검색 결과 0건 (정의부만) - `countByCategory()` - 검색 결과 0건 (정의부만) -**권장**: 관리자 페이지 마이그레이션 후 재검토, 여전히 미사용이면 삭제 +**결정**: 관리자 페이지 마이그레이션 후 재검토, 여전히 미사용이면 삭제 --- -### 4.3 mobile/Layout.jsx 컴포넌트 분리 +### 4.3 mobile/Layout.jsx 컴포넌트 분리 ✅ **파일**: `components/mobile/Layout.jsx` +**상태**: ✅ 완료 - Header.jsx, BottomNav.jsx로 분리 + **문제**: 한 파일에 3개 컴포넌트 존재 - `MobileHeader` - `MobileBottomNav` - `Layout` -**권장**: 별도 파일로 분리 +**해결**: 별도 파일로 분리 ``` components/mobile/ -├── Layout.jsx +├── Layout.jsx # 분리된 컴포넌트 import ├── Header.jsx # MobileHeader ├── BottomNav.jsx # MobileBottomNav -└── index.js +└── index.js # Header, BottomNav export 추가 ``` --- diff --git a/docs/migration.md b/docs/migration.md index 4f43464..7ff6850 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -277,6 +277,8 @@ function App() { ### Mobile 컴포넌트 (components/mobile/) - [x] Layout.jsx +- [x] Header.jsx (MobileHeader) +- [x] BottomNav.jsx (MobileBottomNav) - [x] Calendar.jsx - [x] ScheduleCard.jsx - [x] ScheduleListCard.jsx diff --git a/frontend-temp/src/components/mobile/BottomNav.jsx b/frontend-temp/src/components/mobile/BottomNav.jsx new file mode 100644 index 0000000..f1fbae0 --- /dev/null +++ b/frontend-temp/src/components/mobile/BottomNav.jsx @@ -0,0 +1,45 @@ +import { NavLink, useLocation } from 'react-router-dom'; +import { Home, Users, Disc3, Calendar } from 'lucide-react'; + +/** + * 모바일 하단 네비게이션 + */ +function MobileBottomNav() { + const location = useLocation(); + + const navItems = [ + { path: '/', label: '홈', icon: Home }, + { path: '/members', label: '멤버', icon: Users }, + { path: '/album', label: '앨범', icon: Disc3 }, + { path: '/schedule', label: '일정', icon: Calendar }, + ]; + + return ( + + ); +} + +export default MobileBottomNav; diff --git a/frontend-temp/src/components/mobile/Header.jsx b/frontend-temp/src/components/mobile/Header.jsx new file mode 100644 index 0000000..9a9fe60 --- /dev/null +++ b/frontend-temp/src/components/mobile/Header.jsx @@ -0,0 +1,26 @@ +import { NavLink } from 'react-router-dom'; + +/** + * 모바일 헤더 컴포넌트 + * @param {string} title - 페이지 제목 (없으면 fromis_9) + * @param {boolean} noShadow - 그림자 숨김 여부 + */ +function MobileHeader({ title, noShadow = false }) { + return ( +
+
+ {title ? ( + {title} + ) : ( + + fromis_9 + + )} +
+
+ ); +} + +export default MobileHeader; diff --git a/frontend-temp/src/components/mobile/Layout.jsx b/frontend-temp/src/components/mobile/Layout.jsx index 9d44951..a4d65d3 100644 --- a/frontend-temp/src/components/mobile/Layout.jsx +++ b/frontend-temp/src/components/mobile/Layout.jsx @@ -1,70 +1,8 @@ -import { NavLink, useLocation } from 'react-router-dom'; -import { Home, Users, Disc3, Calendar } from 'lucide-react'; import { useEffect } from 'react'; +import MobileHeader from './Header'; +import MobileBottomNav from './BottomNav'; import '@/mobile.css'; -/** - * 모바일 헤더 컴포넌트 - */ -function MobileHeader({ title, noShadow = false }) { - return ( -
-
- {title ? ( - {title} - ) : ( - - fromis_9 - - )} -
-
- ); -} - -/** - * 모바일 하단 네비게이션 - */ -function MobileBottomNav() { - const location = useLocation(); - - const navItems = [ - { path: '/', label: '홈', icon: Home }, - { path: '/members', label: '멤버', icon: Users }, - { path: '/album', label: '앨범', icon: Disc3 }, - { path: '/schedule', label: '일정', icon: Calendar }, - ]; - - return ( - - ); -} - /** * 모바일 레이아웃 컴포넌트 * @param {React.ReactNode} children - 페이지 컨텐츠 diff --git a/frontend-temp/src/components/mobile/index.js b/frontend-temp/src/components/mobile/index.js index 4533f85..cc5f621 100644 --- a/frontend-temp/src/components/mobile/index.js +++ b/frontend-temp/src/components/mobile/index.js @@ -1,5 +1,7 @@ // 레이아웃 export { default as Layout } from './Layout'; +export { default as Header } from './Header'; +export { default as BottomNav } from './BottomNav'; // 일정 컴포넌트 export { default as Calendar } from './Calendar'; diff --git a/frontend-temp/src/constants/index.js b/frontend-temp/src/constants/index.js index 7b52724..f821698 100644 --- a/frontend-temp/src/constants/index.js +++ b/frontend-temp/src/constants/index.js @@ -57,3 +57,12 @@ export const MONTH_NAMES = [ '1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월', ]; + +/** 그룹 정보 */ +export const GROUP_INFO = { + NAME: 'fromis_9', + NAME_KR: '프로미스나인', + DEBUT_DATE: '2018-01-24', + DEBUT_DATE_DISPLAY: '2018.01.24', + FANDOM_NAME: 'flover', +}; diff --git a/frontend-temp/src/pages/home/pc/Home.jsx b/frontend-temp/src/pages/home/pc/Home.jsx index 86d1d89..c91ae00 100644 --- a/frontend-temp/src/pages/home/pc/Home.jsx +++ b/frontend-temp/src/pages/home/pc/Home.jsx @@ -1,8 +1,10 @@ +import { useMemo } from 'react'; import { motion } from 'framer-motion'; import { Link } from 'react-router-dom'; import { Calendar, ArrowRight, Music } from 'lucide-react'; import { useMembers, useAlbums, useUpcomingSchedules } from '@/hooks'; import { ScheduleCard } from '@/components/pc'; +import { GROUP_INFO } from '@/constants'; /** * PC 홈 페이지 @@ -18,10 +20,18 @@ function Home() { // 다가오는 일정 (3개) const { data: upcomingSchedules = [] } = useUpcomingSchedules(3); + // 활동 멤버 수 + const activeMemberCount = useMemo( + () => members.filter((m) => !m.is_former).length, + [members] + ); + // D+Day 계산 - const debutDate = new Date('2018-01-24'); - const today = new Date(); - const dDay = Math.floor((today - debutDate) / (1000 * 60 * 60 * 24)) + 1; + const dDay = useMemo(() => { + const debutDate = new Date(GROUP_INFO.DEBUT_DATE); + const today = new Date(); + return Math.floor((today - debutDate) / (1000 * 60 * 60 * 24)) + 1; + }, []); return (
@@ -67,10 +77,10 @@ function Home() { }} > {[ - { value: '2018.01.24', label: '데뷔일' }, + { value: GROUP_INFO.DEBUT_DATE_DISPLAY, label: '데뷔일' }, { value: `D+${dDay.toLocaleString()}`, label: 'D+Day' }, - { value: '5', label: '멤버 수' }, - { value: 'flover', label: '팬덤명' }, + { value: String(activeMemberCount || '-'), label: '멤버 수' }, + { value: GROUP_INFO.FANDOM_NAME, label: '팬덤명' }, ].map((stat, index) => (