6 KiB
6 KiB
fromis_9 개선 작업 계획서
코드 리뷰(2026-06) 결과를 바탕으로 한 단계별 개선 계획. 범위는 fromis_9 프론트+백엔드(Flutter 앱 제외).
원칙
- 작고 확실한 것 → 성능 → 캐시 → (보류) 리팩터링 순서. 한 번에 다 건드리지 않음.
- 웨이브 단위로 커밋. 각 항목은 변경 + 검증을 짝으로 진행.
- 백엔드 변경 후
docker restart fromis9-backend로 부팅/동작 확인, 프론트는 HMR 로그로 컴파일 확인. - 회귀 위험이 보이면 멈추고 보고.
웨이브 1 — 버그·보안 핫픽스 (작고 안전)
리스크 낮음, 코드 소량. 한 커밋으로 묶음.
1-1. X봇 생성 ReferenceError (확정 버그)
- 파일:
backend/src/routes/admin/x-bots.js - 문제:
exclude_managed_channels가 POST body 구조분해(180-188)·스키마(160-171)에 없는데 INSERT(209)에서 사용 → ES strict mode라 X봇 생성 시ReferenceError크래시. - 변경: body 스키마에
exclude_managed_channels추가 + 구조분해에exclude_managed_channels = true기본값 추가(컬럼 기본값 1과 일치). - 검증: 백엔드 재시작 후 X봇 추가 1건 → 정상 생성 확인.
1-2. 죽은/깨진 API 제거
- 파일:
frontend/src/api/admin/schedules.js,frontend/src/api/public/schedules.js,frontend/src/hooks/common/useMemberData.js(+hooks/common/index.js),frontend/src/api/client.js(JSDoc) - 문제:
createSchedule/updateSchedule(백엔드 라우트 없음, 미사용),useMemberDetail→getMemberByName(미정의, 호출 시 throw). - 변경: 미사용 함수/훅 삭제. export 정리.
- 검증:
grep으로 참조 0 확인 + 프론트 빌드(HMR) 에러 없음.
1-3. /api/bots 정보 노출
- 파일:
backend/src/app.js(또는 해당 라우트), 프론트api/admin/bots.js - 선행 확인: 프론트가
/api/bots를fetchAuthApi(토큰)로 부르는지fetchApi로 부르는지 확인.- 토큰으로 부르면 → 라우트에
preHandler: [fastify.authenticate]추가. - 토큰 없이 부르면 → 인증 추가 시 관리자 페이지가 깨지므로, 우선 응답에서
errorMessage등 민감필드만 제거.
- 토큰으로 부르면 → 라우트에
- 검증: 관리자 봇 페이지 정상 로드 + 무인증 호출 시 민감정보 미노출 확인.
1-4. 방어 코드 2종 (덤)
- 스케줄러 JSON.parse 가드 —
backend/src/plugins/scheduler.js:weekly_schedule_config/title_filters/default_member_ids/auto_schedule_config/text_filters파싱을safeParse(value, fallback)로 감싸 깨진 값 하나가startAll전체를 막지 않도록. - YouTube fetch 타임아웃 —
backend/src/services/youtube/api.js:og.js의AbortController+setTimeout패턴 적용(업스트림 행 방지). - 검증: 백엔드 재시작 후 봇 정상 기동, YouTube 동기화 로그 정상.
커밋: fix: 웨이브1 버그·보안 핫픽스 (X봇 크래시/죽은 API/정보노출/방어코드) — 항목별로 나눠 커밋해도 됨.
웨이브 2 — 체감 성능 (가성비 최고)
모바일 일정 페이지의 "필터/스크롤마다 전체 리렌더 + 날짜 스트립 재스크롤" 제거.
2-1. selectedDate 객체 안정화
- 파일:
frontend/src/pages/mobile/schedule/Schedule.jsx:40(PC도 동일 패턴 있으면 함께) - 변경:
const selectedDate = storedSelectedDate || new Date()→useMemo(() => storedSelectedDate || getTodayKST_Date(), [storedSelectedDate])또는 스토어 초기값을 오늘로 시드. (다수useMemo/effect의 의존성이라 매 렌더 재실행 원인) - 검증: 렌더 시
daysInMonth/selectedDateSchedules재계산·scrollIntoView반복이 사라지는지(React DevTools 또는 동작 체감).
2-2. 카드 onClick memo 복구
- 파일: 모바일
Schedule.jsx(887/907/925), PCSchedule.jsx(589/593/621/625/645), 관련 카드 컴포넌트 - 변경: 인라인
onClick={() => navigate(...)}제거. 카드가onClick(schedule)을 호출하도록 시그니처 통일하고, 페이지에서useCallback으로 안정적 핸들러 1개 전달. PChandleScheduleClick도useCallback. - 검증: 필터 토글·스크롤 시 카드들이 불필요하게 리렌더되지 않는지 확인(엔트런스 애니메이션 재생 안 됨으로 체감).
커밋: perf(mobile-schedule): selectedDate 안정화 + 카드 onClick memo 복구
웨이브 3 — 캐시 (신중히, 단독)
3-1. 월별 일정 캐시 + 무효화
- 파일:
backend/src/services/schedule.js(getMonthlySchedules),backend/src/routes/schedules/index.js,backend/src/utils/cache.js - 변경:
getMonthlySchedules를getOrSet(redis, cacheKeys.scheduleMonthly(y,m), fn, TTL.MEDIUM)로 래핑. - 핵심: 일정 생성/수정/삭제 경로 전부(events/variety/concert/x/youtube/bot sync/삭제)에서 해당 월 캐시 무효화(
invalidatePattern(redis,'schedule:monthly:*')) 호출 누락 없이 추가. ← 이게 빠지면 stale 발생. - 검증: 일정 추가/수정/삭제 후 캘린더 즉시 반영 확인. 캐시 적중 시 응답 빨라지는지 확인.
커밋: perf(schedule): 월별 일정 Redis 캐시 + 쓰기 시 무효화
보류 (지금 안 함 — 큰 리팩터링)
당장 아픈 문제가 아니므로 해당 영역 작업 시 곁들이는 것을 권장:
- 봇 라우트 3종(festival/x/youtube) CRUD 팩토리화
- 모바일
ScheduleDetail.jsx(1304줄) →sections/분리(PC 구조와 일치) - React Query 캐시 키 팩토리 중앙화 + 공통 데이터 훅(
useScheduleData등) 실제 사용 - PC/모바일
ScheduleCard·BirthdayCard·DebutCard공통 파생 훅(useScheduleCardData) getActiveMemberCount중복 통합, 매직넘버/Nitter URL config 이전- 타임존(KST) 처리 통일, 멤버 PUT 트랜잭션화,
saveTweetDUP catch - JWT 수명 단축, CORS
credentials제거, Gemini 키 헤더 전달
진행 현황
- 웨이브 1 (X봇 버그/죽은 API/정보노출/JSON가드/fetch 타임아웃)
- 웨이브 2
- 웨이브 3