์ ๊ท ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๊ฐ ์ฝ๋ ๊ตฌ์กฐ๋ฅผ ๋น ๋ฅด๊ฒ ํ์ํ ์ ์๋๋ก, ์ค์ ๊ตฌํ ํ์ผ ๊ธฐ์ค์ผ๋ก ์ ๋ฆฌํ ๋ฌธ์์
๋๋ค.
1. ๋ถํธ์คํธ๋ฉ๊ณผ ์ฑ ์
ธ
ํ์ผ
์ญํ
src/main.jsx
React ์ฑ ์ํธ๋ฆฌํฌ์ธํธ. #root์ <App /> ๋ง์ดํธ
src/App.jsx
์ ์ญ Provider(ThemeProvider, NetworkStatusProvider, PwaInstallProvider, AuthProvider) + ์ต์์ ๋ผ์ฐํ
๊ตฌ์ฑ
src/layout/AppLayout.jsx
๊ณตํต ๋ ์ด์์(์ ๊ทผ์ฑ skip-link, Header, main, Footer, OfflineGate)
src/components/Header/Header.jsx
์ ์ญ ๋ด๋น๊ฒ์ด์
/ํ
๋ง ํ ๊ธ/์ธ์ฆ ์ํ UI. ๊ณต์ง ์์ญ์ ํ๊ต ์๊ฐ ์ง์
์ , ์ปค๋ฎค๋ํฐ ๋ณด๋ feature flag, ํ๊ต ์ํ ์ ๋ณด ์ ํธ๋ฆฌํฐ ๋งํฌ(QR/์ค๋๋ง์ค ์นด๋/์คํฌ์ธ ๋ฆฌ๊ทธ ๋ฑ) ํฌํจ
src/components/Footer/Footer.jsx
์ธ๋ถ ๋งํฌ/๋ฒ์ ๋ฌธ์ ๋งํฌ/์ด์ ์ ๋ณด
src/components/pwa/OfflineGate.jsx
์คํ๋ผ์ธ ์ ์ ์ฒด ํ๋ฉด ์ค๋ฒ๋ ์ด์ ์ฌ์๋ ํ๋ฆ ์ ๊ณต
src/pages/CommunityPage.jsx
์ปค๋ฎค๋ํฐ ํ๋ธ ํ์ด์ง. ๋ชจ๋ ๋ณด๋ ์นด๋๋ฅผ ๊ทธ๋ฆฌ๋๋ก ๋์ดํ๋ฉฐ, ํ์ฌ ํ์ฑ ๋ณด๋๋ฅผ ํ์ด๋ผ์ดํธ
2. ์ฃผ์ ๋๋ ํฐ๋ฆฌ ๋งต
๋๋ ํฐ๋ฆฌ
์ญํ
src/pages
๋ผ์ฐํธ ๋จ์ ํ๋ฉด ์ปดํฌ๋ํธ
src/components
์ฌ์ฌ์ฉ UI ์ปดํฌ๋ํธ
src/api
๋ฐฑ์๋ ์ฐ๋ ๋ชจ๋ + FastAPI/๊ธ์ ์๋ฆผ ํด๋ผ์ด์ธํธ
src/features
๊ธฐ๋ฅ ๋จ์ data/hook/utils ๋ฌถ์
src/context
์ ์ญ ์ํ ์ปจํ
์คํธ(Theme/NetworkStatus/PWA/Auth)
src/pwa
์คํ๋ผ์ธ/์ค์น ์ํ, Firebase Web Push, ์ค์น ๊ธฐ๊ธฐ ์๋ณ ์ ํธ
src/security
URL/HTML/CSV/์ค๋ฌธ ์คํค๋ง sanitize ์ ์ฑ
src/analytics
Zaraz ์ด๋ฒคํธ ์ ์ก ๋ํผ
src/config
ํ๊ฒฝ๋ณ์ ํ์ฑ ๋ฐ ์์ ๋
ธ์ถ
src/styles
์ ์ญ ์คํ์ผ ํ ํฐ/๋ ์ด์์ CSS
3. ๋ผ์ฐํธ ํธ๋ฆฌ (์ค์ ์ฝ๋ ๊ธฐ์ค)
3.1 ์ต์์ ๋ผ์ฐํธ (src/App.jsx)
๊ฒฝ๋ก
์์
/
MainPage
/login
LoginPage
/signup
SignUpPage
/notices/*
NoticesPage (src/pages/Notices/index.jsx)
/community/*
CommunityRouter
/school-info/*
SchoolInfoRouter (src/pages/SchoolLifeInfo/index.jsx)
/privacy
PrivacyPolicyPage
/terms
TermsOfServicePage
*
NotFoundPage
/privacy, /terms๋ ์ ์ ๋ฒ์ ๋ฌธ์ ํ์ด์ง์ด๋ฉฐ, ์๋ฒ ๋ฐ์ดํฐ fetch ์์ด anchor ๊ธฐ๋ฐ ๋ชฉ์ฐจ์ ๋งจ ์๋ก ์คํฌ๋กค ํฌํผ๋ฅผ ๋ ๋๋งํฉ๋๋ค.
3.2 ๊ณต์ง ๋ผ์ฐํธ (src/pages/Notices/index.jsx)
๊ฒฝ๋ก
์์
/notices
Navigate -> /notices/school
/notices/budget/*
BudgetBoardPage
/notices/lost-found
LostFoundListView
/notices/lost-found/new
LostFoundComposeView
/notices/lost-found/:id
LostFoundDetailView (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/notices/school-info/*
SchoolInfoRouter
/notices/* (budget, lost-found, school-info ์ธ ๊ฒฝ๋ก)
NoticeCenterPage
3.2.1 ํ๊ต/ํ์ํ ๊ณต์ง ์๋ธ๋ผ์ฐํธ (src/pages/Notices/NoticeCenter/NoticeCenterPage.jsx)
๊ฒฝ๋ก
์์
/notices/school
ListView
/notices/council
ListView
/notices/:category/new
ComposeView(mode=create) (school, council๋ง ํ์ฉ)
/notices/:category/:id
DetailView (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/notices/:category/:id/edit
ComposeView(mode=edit) (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/notices/* (invalid path)
NotFoundPage
3.2.2 ์์ฐ ๊ณต๊ฐ ์๋ธ๋ผ์ฐํธ (src/pages/Notices/BudgetBoard/BudgetBoardPage.jsx)
๊ฒฝ๋ก
์์
/notices/budget
Navigate -> /notices/budget/:budgetYear/:budgetMonth (GET /api/notices/budget/settings์ ๊ธฐ๋ณธ ์ฐ/์ ์ฌ์ฉ)
/notices/budget/:budgetYear/:budgetMonth
BudgetListView
/notices/budget/:budgetYear/:budgetMonth/new
BudgetComposeView(mode=create)
/notices/budget/:budgetYear/:budgetMonth/:id
BudgetDetailView (budgetYear, id ์ซ์ ๊ฒฝ๋ก, budgetMonth ํ์ฉ๊ฐ ๊ฐ๋)
/notices/budget/:budgetYear/:budgetMonth/:id/edit
BudgetComposeView(mode=edit) (budgetYear, id ์ซ์ ๊ฒฝ๋ก, budgetMonth ํ์ฉ๊ฐ ๊ฐ๋)
/notices/budget/* (invalid path)
NotFoundPage
์์ฐ ๊ณต๊ฐ ๋ผ์ฐํธ ๋ฉ๋ชจ:
ํ๊ณ ์ฌ์ดํด์ 03~12, 01, 02 ๊ณ ์ ์์๋ฅผ ์ฌ์ฉํฉ๋๋ค.
URL์ budgetYear๋ ๋ฌ๋ ฅ ์ฐ๋๊ฐ ์๋๋ผ ํ๊ณ์ฐ๋ ์์ ์ฐ๋์
๋๋ค.
BudgetBoardPage๋ settings ์๋ต์ ์ฐ๋ ๋ฒ์๋ฅผ ๋ฒ์ด๋ ๊ฒฝ๋ก๋ฅผ ์ฆ์ 404 ํ๋ฉด์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค.
3.2.3 ํ๊ต ์๊ฐ ๋ผ์ฐํธ (src/pages/Notices/SchoolInfo/index.jsx)
๊ฒฝ๋ก
์์
/notices/school-info
Navigate -> /notices/school-info/bshs-info
/notices/school-info/bshs-info
BeomseoInfoPage
/notices/school-info/cshs-info
CSHSInfoPage
/notices/school-info/* (invalid path)
NotFoundPage
ํ๊ต ์๊ฐ ๋ผ์ฐํธ ๋ฉ๋ชจ:
SchoolInfoTabs๊ฐ ๋ฒ์๊ณ ยท์ฒ์๊ณ ์๊ฐ ํญ์ ๋ผ๋ฒจ, ๊ฒฝ๋ก, ํ์ฑ ์ํ ํ์ ์ ํ๊ณณ์์ ๊ด๋ฆฌํฉ๋๋ค.
BeomseoInfoPage๋ ๊ณต์ ํํ์ด์ง์ ๊ต์ก ๋ฐฉํฅ, ํ๊ต์์ง, ํ๊ตํํฉ, ์ฐํ, ์ค์๋ ๊ธธ์ ์ ์ ์ฝํ
์ธ ๋ก ๋ณด์ฌ์ฃผ๊ณ ๊ฐ ์๋ฌธ ์ถ์ฒ ๋งํฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋ ํ๊ต ์๊ฐ ํ์ด์ง๋ ๋ฐฑ์๋ API๋ฅผ ํธ์ถํ์ง ์์ผ๋ฉฐ, ์ ์ SEO/prerender ์ ๋ณด๋ src/seo/policy.js์์ ๊ด๋ฆฌํฉ๋๋ค.
3.3 ์ปค๋ฎค๋ํฐ ๋ผ์ฐํธ (src/pages/Community/index.jsx)
์ธ์ฑ ๊ฐ์น PICK!, ๋์๋ฆฌ ๋ชจ์ง, ์ ํ๊ณผ๋ชฉ ๋ณ๊ฒฝ, BOSPI, ์คํฐ๋ ์ ๋ฒ์, ์ํ์ฌํ ๋ผ์ฐํธ๋ ๊ฐ ๋ณด๋ feature flag๊ฐ ์ผ์ง ๋ฐฐํฌ์์๋ง ๋ฑ๋ก๋ฉ๋๋ค.
graph TD
CR["CommunityRouter"] -->|index| DEF["Navigate โ /community/free"]
CR --> F["free/*"]
CR --> VP["value-pick/*"]
CR --> CLB["club-recruit/*"]
CR --> SUB["subjects/*"]
CR --> PET["petition/*"]
CR --> SUR["survey/*"]
CR --> VOT["vote/*"]
CR --> BOS["bospi"]
CR --> SWB["study-with-beomseo"]
CR --> FT["field-trip"]
CR --> LF["lost-found/*"]
CR --> GM["gomsol-market/*"]
CR -->|"*"| FALL["NotFoundPage"]
F --> F1["FreeBoardListView"]
F --> F2["FreeBoardComposeView(create)"]
F --> F3["FreeBoardDetailView"]
F --> F4["FreeBoardComposeView(edit)"]
VP --> VP1["ValuePickListView"]
VP --> VP2["ValuePickComposeView(create)"]
VP --> VP3["ValuePickDetailView"]
VP --> VP4["ValuePickComposeView(edit)"]
CLB --> CLB1["ClubRecruitListPage"]
CLB --> CLB2["ClubRecruitComposePage"]
CLB --> CLB3["ClubRecruitDetailPage"]
SUB --> SUB1["SubjectsListPage"]
SUB --> SUB2["SubjectComposePage"]
SUB --> SUB3["SubjectDetailPage"]
PET --> PET1["PetitionListView"]
PET --> PET2["PetitionComposeView"]
PET --> PET3["PetitionDetailView"]
SUR --> SUR1["SurveyExchangeListView"]
SUR --> SUR2["SurveyExchangeComposePage"]
SUR --> SUR3["SurveyExchangeDetailView"]
SUR --> SUR4["SurveyExchangeComposePage(edit)"]
SUR --> SUR5["SurveyResultsView"]
VOT --> VOT1["VoteListView"]
VOT --> VOT2["VoteComposeView"]
VOT --> VOT3["VoteDetailView"]
BOS --> BOS1["BospiPage"]
SWB --> SWB1["StudyWithBeomseoPage"]
FT --> FT1["FieldTripPage (Hub)"]
FT --> FT2["FieldTripClassBoardPage"]
FT --> FT3["FieldTripPostDetailPage"]
LF --> LF1["LostFoundListView"]
LF --> LF2["LostFoundComposeView"]
LF --> LF3["LostFoundDetailView"]
GM --> GM1["GomsolMarketListView"]
GM --> GM2["GomsolMarketComposeView"]
GM --> GM3["GomsolMarketDetailView"]
Loading
๊ฒฝ๋ก
์์
/community
Navigate โ /community/free
/community/free
FreeBoardListView
/community/free/new
FreeBoardComposeView(mode=create)
/community/free/:id
FreeBoardDetailView (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/community/free/:id/edit
FreeBoardComposeView(mode=edit) (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/community/value-pick
ValuePickListView
/community/value-pick/new
ValuePickComposeView(mode=create)
/community/value-pick/:id
ValuePickDetailView (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/community/value-pick/:id/edit
ValuePickComposeView(mode=edit) (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/community/club-recruit
ClubRecruitListPage
/community/club-recruit/new
ClubRecruitComposePage
/community/club-recruit/:id
ClubRecruitDetailPage (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/community/subjects
SubjectsListPage
/community/subjects/new
SubjectComposePage
/community/subjects/:id
SubjectDetailPage (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/community/petition
PetitionListView
/community/petition/new
PetitionComposeView
/community/petition/:id
PetitionDetailView (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/community/survey
SurveyExchangeListView
/community/survey/new
SurveyExchangeComposePage
/community/survey/:id
SurveyExchangeDetailView (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/community/survey/:id/edit
SurveyExchangeComposePage (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/community/survey/:id/results
SurveyResultsView (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/community/vote
VoteListView
/community/vote/new
VoteComposeView
/community/vote/:id
VoteDetailView (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/community/bospi
BospiPage
/community/study-with-beomseo
StudyWithBeomseoPage
/community/field-trip
FieldTripPage (FieldTripHubPage re-export)
/community/field-trip/classes/:classId
FieldTripClassBoardPage
/community/field-trip/classes/:classId/new
FieldTripClassBoardPage
/community/field-trip/classes/:classId/posts/:postId
FieldTripPostDetailPage
/community/field-trip/classes/:classId/posts/:postId/edit
FieldTripClassBoardPage
/community/lost-found
LostFoundListView
/community/lost-found/new
LostFoundComposeView
/community/lost-found/:id
LostFoundDetailView (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/community/gomsol-market
GomsolMarketListView
/community/gomsol-market/new
GomsolMarketComposeView
/community/gomsol-market/:id
GomsolMarketDetailView (id ์ซ์ ๊ฒฝ๋ก๋ง ํ์ฉ)
/community/* (invalid path)
NotFoundPage
3.4 ํ๊ต ์ํ ์ ๋ณด ๋ผ์ฐํธ (src/pages/SchoolLifeInfo/index.jsx)
๊ฒฝ๋ก
์์
/school-info
SchoolInfoHub
/school-info/qr-generator
QrCodeGeneratorPage
/school-info/shany-card-generator
ShanyCardGeneratorPage
/school-info/timetable
TimetableDownloadPage
/school-info/evaluation-plans
EvaluationPlansPage
/school-info/meal
MealPage
/school-info/calendar
AcademicCalendarPage
/school-info/sports-league
Navigate โ /school-info/sports-league/2026-spring-grade2-boys-soccer
/school-info/sports-league/:categoryId
SportsLeagueCategoryPage
/school-info/* (invalid path)
NotFoundPage
4. ๊ธฐ๋ฅ๋ณ ์์ง ์ฌ๋ผ์ด์ค ํ์
๊ธฐ๋ฅ
ํ์ด์ง ๋ ์ด์ด
์ปดํฌ๋ํธ ๋ ์ด์ด
API ๋ ์ด์ด
๊ณต์ง/์์ฐ ๊ณต๊ฐ
src/pages/Notices/*
src/components/notices/*
src/api/notices.js
๊ณต์ง(ํ๊ต ์๊ฐ)
src/pages/Notices/SchoolInfo/*
SchoolInfoTabs, ํ์ด์ง๋ณ CSS Module
์์ (์ ์ ํ๊ต ์๊ฐ ์ฝํ
์ธ + ๊ณต์ ์ถ์ฒ ๋งํฌ)
์์ ๊ฒ์ํ
src/pages/Community/FreeBoard/*
src/components/freeboard/*
src/api/community.js
์ธ์ฑ ๊ฐ์น PICK!
src/pages/Community/ValuePick/*
src/components/Community/ValuePick/*
src/api/valuePick.js
๋์๋ฆฌ ๋ชจ์ง
src/pages/Community/ClubRecruit/*
src/components/clubRecruit/*
src/api/clubRecruit.js
์ ํ๊ณผ๋ชฉ ๋ณ๊ฒฝ
src/pages/Community/Subjects/*
src/components/subjects/*
src/api/subjectChanges.js
์ฒญ์
src/pages/Community/Petition/*
src/components/petition/*
src/api/petition.js
์ค๋ฌธ ํ์์ด
src/pages/Community/SurveyExchange/*
src/components/survey/*
src/api/survey.js
ํฌํ
src/pages/Community/Vote/*
src/components/vote/*
src/api/vote.js
BOSPI
src/pages/Community/Bospi/BospiPage.jsx
BospiPage.module.css์ ํ์ฌ ์ง์/์์ธก/๋ญํน/์ง์๋ณด๋ ์น์
src/api/bospi.js
์คํฐ๋ ์ ๋ฒ์
src/pages/Community/StudyWithBeomseo/StudyWithBeomseoPage.jsx
StudyWithBeomseoPage.module.css์ ์ ์ํ/์์ฝ ๊ณต๊ฐ ์น์
src/api/studyWithBeomseo.js
์ํ์ฌํ ์ด๋ฒคํธ
src/pages/Community/FieldTrip/* (FieldTripHubPage, FieldTripClassBoardPage, FieldTripPostDetailPage)
src/components/fieldTrip/*, src/features/fieldTrip/*
src/api/fieldTrip.js
๋ถ์ค๋ฌผ
src/pages/Notices/LostFound/*
src/components/lostfound/*
src/api/lostFound.js
๊ณฐ์๋ง์ผ
src/pages/Community/GomsolMarket/*
src/components/gomsolmarket/*
src/api/gomsolMarket.js
ํ๊ต ์ํ ์ ๋ณด(QR ์ฝ๋ ์์ฑ๊ธฐ)
src/pages/SchoolLifeInfo/QrCodeGenerator/QrCodeGeneratorPage.jsx
QrCodeGeneratorPage.module.css, react-qrcode-logo
์์ (๋ธ๋ผ์ฐ์ ์บ๋ฒ์ค ๊ธฐ๋ฐ ์์ฑ/๋ค์ด๋ก๋)
ํ๊ต ์ํ ์ ๋ณด(์ค๋๋ง์ค ์นด๋ ์์ฑ๊ธฐ)
src/pages/SchoolLifeInfo/ShanyCardGenerator/ShanyCardGeneratorPage.jsx
ShanyCardGeneratorPage.module.css, react-shany-card-generator
์์ (๋ธ๋ผ์ฐ์ Blob ๊ธฐ๋ฐ ์์ฑ/๋ค์ด๋ก๋)
ํ๊ต ์ํ ์ ๋ณด(์๊ฐํ)
src/pages/SchoolLifeInfo/TimetableDownload/*
src/components/timetable/*
์์ (src/components/timetable/timetableTemplates.json ์ ์ ํ
ํ๋ฆฟ ์ฌ์ฉ)
ํ๊ต ์ํ ์ ๋ณด(ํ๊ฐ๊ณํ์)
src/pages/SchoolLifeInfo/EvaluationPlans/*
์์
์์ (public/evaluation-plans/* ์ ์ HWP ์ฌ์ฉ)
ํ๊ต ์ํ ์ ๋ณด(์ค๋์ ๊ธ์)
src/pages/SchoolLifeInfo/Meal/MealPage.jsx
src/components/MealCard/*, src/features/meals/*
src/api/meals.js, src/api/mealNotifications.js
ํ๊ต ์ํ ์ ๋ณด(ํ์ฌ ์บ๋ฆฐ๋)
src/pages/SchoolLifeInfo/AcademicCalendar/AcademicCalendarPage.jsx
src/components/AcademicUpcomingCard/*, src/features/academicCalendar/*
์์ (src/features/academicCalendar/data.js ์ ์ ๋ฐ์ดํฐ ์ฌ์ฉ)
ํ๊ต ์ํ ์ ๋ณด(์คํฌ์ธ ๋ฆฌ๊ทธ ๋ฌธ์์ค๊ณ/๋ผ์ธ์
/๊ฐ์ธ ์์)
src/pages/SchoolLifeInfo/SportsLeagueCategory/SportsLeagueCategoryPage.jsx
src/features/sportsLeague/* (useSportsLeagueLive, usePlayersStore, TeamLineupPanel, PlayerRankingPanel)
src/api/sportsLeague.js
4.1 ๊ณต์ง/์์ฐ ๊ณต๊ฐ ํ์ผ ๊ตฌ์ฑ
ํ์ผ
์ญํ
src/pages/Notices/index.jsx
/notices/*๋ฅผ NoticeCenterPage์ BudgetBoardPage๋ก ๋ถ๊ธฐ
src/pages/Notices/NoticeCenter/NoticeCenterPage.jsx
ํ๊ต/ํ์ํ ๊ณต์ง ์ ์ฉ ์
ธ๊ณผ ์ค์ฒฉ ๋ผ์ฐํธ
src/pages/Notices/BudgetBoard/BudgetBoardPage.jsx
์์ฐ ๊ณต๊ฐ ์ค์ ๋ก๋, ์ฐ๋ ์ ํ, ์ ํญ, ์์ฐ ๊ณต๊ฐ ์ค์ฒฉ ๋ผ์ฐํธ
src/pages/Notices/BudgetBoard/BudgetListView.jsx
category='budget', budgetYear, budgetMonth ํํฐ ๊ธฐ๋ฐ ์๋ณ ๋ฆฌ์คํธ
src/pages/Notices/BudgetBoard/BudgetDetailView.jsx
์์ฐ ๊ณต๊ฐ ์์ธ, ๋๊ธ/๋ฐ์/์ฒจ๋ถ ์ฌ์ฌ์ฉ
src/pages/Notices/BudgetBoard/BudgetComposeView.jsx
๊ฒฝ๋ก ๊ธฐ๋ฐ ํ๊ณ์ฐ๋/์์ ๊ณ ์ ํ ์์ฑยท์์ ํ๋ฉด
src/pages/Notices/BudgetBoard/budgetUtils.js
03~02 ํ๊ณ ์ฌ์ดํด ๊ณ์ฐ ๋ฐ ๋ผ๋ฒจ/๊ฒฝ๋ก ์ ํธ
src/pages/Notices/SchoolInfo/index.jsx
/notices/school-info/* ํ์ ํ๊ต ์๊ฐ ๋ผ์ฐํฐ์ ์๋ชป๋ ํ๊ต ์๊ฐ ๊ฒฝ๋ก 404 ์ฒ๋ฆฌ
src/pages/Notices/SchoolInfo/SchoolInfoTabs.jsx
๋ฒ์๊ณ ยท์ฒ์๊ณ ์๊ฐ ํญ๊ณผ ํ์ฑ ๊ฒฝ๋ก ํ์
src/pages/Notices/SchoolInfo/BeomseoInfoPage.jsx
๋ฒ์๊ณ ๊ต์ก ๋ฐฉํฅ, ์์ง, ํํฉ, ์ฐํ, ์์น, ๊ณต์ ์ถ์ฒ ๋งํฌ๋ฅผ ์ ์ ๋ ๋๋ง
src/pages/Notices/SchoolInfo/CSHSInfoPage.jsx
์ฒ์๊ณ ์๊ฐ ์นด๋์ ๋ค์ ํ๊ต ํ์ฌ D-Day ๋ ๋๋ง
src/components/notices/NoticeToolbar.jsx
showAttributeFilters, sortOptions, searchPlaceholder๋ก budget ๋ณด๋ ํ์ฅ
src/components/notices/NoticeList.jsx
emptyStateProps, cardProps๋ก budget ์ ์ฉ ๋น์ด ์์/์นด๋ ๋ ๋๋ง ์ฌ์ฌ์ฉ
src/components/notices/NoticeCard.jsx
hideBadges, hideTags๋ก ์์ฐ ๊ณต๊ฐ ์นด๋ ํํ ๋จ์ํ
src/components/notices/EmptyState.jsx
createPath, title, description override ์ง์
4.2 ์คํฌ์ธ ๋ฆฌ๊ทธ feature ์ฌ๋ผ์ด์ค (src/features/sportsLeague/*)
ํ์ผ
์ญํ
data.js
๊ธฐ๋ณธ/์ด์ ์นดํ
๊ณ ๋ฆฌ ID, API ๋ก๋ฉ ์ ๋์ฒด ์นดํ
๊ณ ๋ฆฌ ๋ชฉ๋ก, ์ด๋ฒคํธ ํ
ํ๋ฆฟ, ์ด์์ง ์ญํ ์์
useSportsLeagueLive.js
category ๋ณ๊ฒฝ ์ ๊ธฐ์กด snapshot ์ด๊ธฐํ + snapshot ์กฐํ + SSE ๊ตฌ๋
+ ์ด๋ฒคํธ CRUD orchestration
usePlayersStore.js
์ ์ ๋ผ์ธ์
/๊ฐ์ธ ์์ ์ ์ฉ ์ํ ํ
(getPlayers, add/remove/stat)
TeamLineupPanel.jsx
ํ๋ณ ๋ผ์ธ์
ํญ UI, ํ ์ ํ/์ ์ ์ถ๊ฐยท์ญ์
PlayerRankingPanel.jsx
๊ฐ์ธ๋ณ ์์ ํญ UI, ๋์ /์ด์์คํธ ์ ๋ ฌ ๋ฐ inline +/- ์กฐ์
utils.js
๊ฒฝ๊ธฐ ์ ๋ ฌ, ํ์ฌ/๋ค์ ๊ฒฝ๊ธฐ ํ๋ณ, ์์ ๊ณ์ฐ, tone/label ํฌํผ
src/context/AuthContext.jsx
์ธ์
์ด๊ธฐํ: authApi.getMe()
์ธ์ฆ ์ก์
: login, register, logout
๋ง๋ฃ ์ด๋ฒคํธ ๊ตฌ๋
: AUTH_EXPIRED_EVENT ์์ ์ ์ฌ์ฉ์ ์ํ ์ด๊ธฐํ
๋ถ์ ์ด๋ฒคํธ ์ฐ๊ฒฐ: ๋ก๊ทธ์ธ/ํ์๊ฐ์
์ฑ๊ณต/์คํจ ํธ๋ํน ํธ์ถ
src/context/ThemeContext.jsx
ํ
๋ง ์ํ(light/dark) ์ ์ง
localStorage + ์์คํ
ํ
๋ง ๊ฐ์ง
document.documentElement[data-theme] ๋๊ธฐํ
src/context/NetworkStatusContext.jsx
๋ธ๋ผ์ฐ์ online/offline ์ด๋ฒคํธ์ app:network-request-failed ์ปค์คํ
์ด๋ฒคํธ๋ฅผ ํจ๊ป ๊ตฌ๋
/api/health ์ฌํ์ธ ๊ฒฐ๊ณผ๋ก ์ค์ API ๋๋ฌ ๊ฐ๋ฅ ์ฌ๋ถ ํ์
OfflineGate๊ฐ ์ฌ์ฉํ isOffline, lastSource, recheckConnection() ์ ๊ณต
src/context/PwaInstallContext.jsx
beforeinstallprompt, appinstalled, display-mode: standalone ์ํ๋ฅผ ํตํฉ ๊ด๋ฆฌ
iOS Safari ์๋ ์ค์น ๊ฒฝ๋ก(isIosManualInstall)์ ์ผ๋ฐ ์ค์น ํ๋กฌํํธ ๊ฒฝ๋ก๋ฅผ ๋ถ๋ฆฌ
์ค์น CTA๊ฐ ์ฌ์ฉํ canInstall, promptInstall(), helpOpen ์ํ ์ ๊ณต
๊ตฌ๋ถ
์์ค ์ค๋ธ ํธ๋ฃจ์ค ํ์ผ
์ค๋ช
๊ณตํต HTTP ํด๋ผ์ด์ธํธ
src/api/auth.js
Axios ์ธ์คํด์ค, CSRF ํค๋, 401 refresh ์ฌ์๋, transport ์คํจ ์ ์คํ๋ผ์ธ ์ด๋ฒคํธ ๋ฐํ
FastAPI HTTP ํด๋ผ์ด์ธํธ
src/api/fastapiClient.js
FastAPI origin ์ ์ฉ Axios ์ธ์คํด์ค, CSRF ํค๋, transport ์คํจ ์ ํ
๊ธฐ๋ฅ API
src/api/*.js
๊ธฐ๋ฅ๋ณ endpoint ๋ํ ๋ฐ ์๋ต ์ ๊ทํ
์๋ต ์ ๊ทํ
src/api/normalizers.js
ํ์ด์ง๋ค์ด์
/์
๋ก๋ URL ๋ณด์
์์ธ ๋ฉ์๋/์๋ํฌ์ธํธ๋ frontend-api-reference.md ๋ฅผ ์ฐธ๊ณ ํฉ๋๋ค.
7. ๋ณด์ ๊ฒฝ๊ณ ํ์ผ ์งํฉ (src/security/*)
ํ์ผ
์ฑ
์
src/security/urlPolicy.js
์ธ๋ถ ๋งํฌ/์คํ์ฑํ
/์์
URL ์์ ์ฑ ๊ฒ์ฆ
src/security/htmlSanitizer.js
DOMPurify ๊ธฐ๋ฐ ๋ฆฌ์น HTML sanitize
src/security/surveySchemaSanitizer.js
์ค๋ฌธ ์คํค๋ง์ link/src ํ๋ sanitize
src/security/csvSanitizer.js
CSV formula injection ๋ฐฉ์ด
src/components/security/SafeHtml.jsx
sanitize ์ดํ dangerouslySetInnerHTML ๋ ๋๋ง ๊ฒฝ๊ณ
8. ๋ ๊ฑฐ์/ํธํ ๋ ์ด์ด
ํ์ผ
๋ชฉ์
src/pages/MainPage/index.js
MainPage.jsx ์ง์
์ ์ฌ-export
์ ์ฝ๋์์๋ ํด๋ ๊ธฐ๋ฐ ์ํธ๋ฆฌ(src/pages/Notices/index.jsx, src/pages/SchoolLifeInfo/index.jsx)๋ฅผ ์ฐ์ ์ฌ์ฉํฉ๋๋ค.
9. ์จ๋ณด๋ฉ ๊ถ์ฅ ์ฝ๊ธฐ ์์
src/main.jsx
src/App.jsx
src/layout/AppLayout.jsx
src/context/AuthContext.jsx
src/api/auth.js
src/security/urlPolicy.js + src/security/htmlSanitizer.js
src/pages/Community/index.jsx
์์์ ๊ธฐ๋ฅ 1๊ฐ ์์ง ์ฌ๋ผ์ด์ค(page + component + api) ๋๊น์ง ์ถ์
10. ๋ณ๊ฒฝ ์ ๋๊ธฐํ ๊ท์น
๋ผ์ฐํธ ๋ณ๊ฒฝ: ๋ณธ ๋ฌธ์ + frontend-architecture.md ๋์ ๊ฐฑ์
๋ผ์ฐํธ ๋ณ๊ฒฝ: ์ด์ Nginx SPA allowlist๋ ํจ๊ป ๊ฐฑ์
API ๋ณ๊ฒฝ: frontend-api-reference.md ๋์ ๊ฐฑ์
ํธ๋ํน ๋ณ๊ฒฝ: analytics-tracking.md ๋์ ๊ฐฑ์
์ญํ ํ์ ๋ก์ง(src/utils/roleDisplay.js) ๋ณ๊ฒฝ: ๋ณธ ๋ฌธ์ ๊ฐฑ์
ํ๊ฒฝ๋ณ์ ์ถ๊ฐ/๋ณ๊ฒฝ: README.md ํ๊ฒฝ ๋ณ์ ํ + src/config/env.js ํจ๊ป ๊ฐฑ์
PR ์ ์ต์ข
์ ๊ฒ: team-checklist.md
11. ๊ณต์ UI ์ปดํฌ๋ํธ
src/components/ ์ค ๊ธฐ๋ฅ ๋ณด๋ ์ธ์ ๊ณต์ ์ปดํฌ๋ํธ์
๋๋ค.
๋๋ ํฐ๋ฆฌ
์ญํ
์ฌ์ฉ ์์น
AnnouncementCard/
๋ฉ์ธ ํ์ด์ง ๊ณต์ง ์นด๋
MainPage
CountdownWidget/
D-Day ์นด์ดํธ๋ค์ด ์์ ฏ
MainPage
MealCard/
๊ธ์ ์ ๋ณด ์นด๋
MainPage
QuickLinkCard/
๋ฐ๋ก๊ฐ๊ธฐ ์นด๋
MainPage
RoleName/
์ญํ ๊ธฐ๋ฐ ๋๋ค์ ๋ ๋๋ง
๊ฒ์ํ ์์ธ/๋ชฉ๋ก ์ ์ญ
security/SafeHtml.jsx
sanitize ํ dangerouslySetInnerHTML ๋ ๋๋ง ๊ฒฝ๊ณ
๋ฆฌ์น HTML ์ถ๋ ฅ ์ ์ญ
11.1 ์๊ฐํ ๋ค์ด๋ก๋ ๋ชจ๋
ํ๊ต ์ํ ์ ๋ณด > ์๊ฐํ ๋ค์ด๋ก๋ ๊ธฐ๋ฅ์ ์ ์ ํ
ํ๋ฆฟ + SVG ๋ ๋๋ง ์กฐํฉ์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
ํ์ผ
์ญํ
src/pages/SchoolLifeInfo/TimetableDownload/TimetableDownloadPage.jsx
ํ๋
/๋ฐ ์ ํ, ์
๋ ฅ ์ํ, ๋ค์ด๋ก๋ ์ก์
orchestration
src/components/timetable/TimetableControls.jsx
ํ๋
/๋ฐ ๋๋กญ๋ค์ด๊ณผ ์ ํ๊ณผ๋ชฉ ์
๋ ฅ ํผ
src/components/timetable/TimetablePreview.jsx
๋ฏธ๋ฆฌ๋ณด๊ธฐ ์นด๋์ SVG ํ๋ฆฌ๋ทฐ ๋ํผ
src/components/timetable/TimetableSvg.jsx
์๊ฐํ SVG ๋ ๋๋ง
src/components/timetable/exportTimetablePng.js
SVG โ PNG ๋ค์ด๋ก๋
src/components/timetable/timetableUtils.js
ํ
ํ๋ฆฟ ์กฐํ, ๊ธ์ ํฌ๊ธฐ ๊ณ์ฐ, ํฐํธ ๋ก๋ฉ ์ ํธ
src/components/timetable/timetableTemplates.json
๋ฐ๋ณ ์๊ฐํ ํ
ํ๋ฆฟ ๋ฐ์ดํฐ
11.2 ํ๊ฐ๊ณํ์ ๋ค์ด๋ก๋ ๋ชจ๋
ํ๊ต ์ํ ์ ๋ณด > ํ๊ฐ๊ณํ์ ๋ค์ด๋ก๋ ๊ธฐ๋ฅ์ ๋ฐฑ์๋ API ์์ด frontend/public/evaluation-plans/์ HWP ์๋ณธ ํ์ผ์ ์ง์ ์ ๊ณตํฉ๋๋ค.
ํ์ผ
์ญํ
src/pages/SchoolLifeInfo/EvaluationPlans/EvaluationPlansPage.jsx
ํ๋
๋ณ ๋ค์ด๋ก๋ ์นด๋์ ๊ณต๊ณต๋๋ฆฌ ์ 3์ ํ ๊ณ ์ง ๋ ๋๋ง
src/pages/SchoolLifeInfo/EvaluationPlans/EvaluationPlansPage.module.css
๋ค์ด๋ก๋ ์นด๋, ๊ณต๊ณต๋๋ฆฌ ๊ณ ์ง, ๋ฐ์ํ ๋ ์ด์์
public/evaluation-plans/*.hwp
ํ๊ต์๋ฆฌ๋ฏธ ์๋ณธ ํ๊ฐ๊ณํ์ ํ์ผ
public/kogl/img_opentype03.jpg
๊ณต๊ณต๋๋ฆฌ ์ 3์ ํ ํ์ ๋งํฌ
ํ๊ฐ๊ณํ์ HWP ํ์ผ๊ณผ ๊ณต๊ณต๋๋ฆฌ ๋งํฌ๋ GPL-3.0 ์ฝ๋ ๋ผ์ด์ ์ค๊ฐ ์๋๋ผ ๋ฃจํธ์ THIRD_PARTY_NOTICES.md์ ๋ช
์ํ ๊ณต๊ณต๋๋ฆฌ ์ 3์ ํ ์กฐ๊ฑด์ ๋ฐ๋ฆ
๋๋ค.
11.3 QR ์ฝ๋ ์์ฑ๊ธฐ ๋ชจ๋
ํ๊ต ์ํ ์ ๋ณด > QR ์ฝ๋ ์์ฑ๊ธฐ ๊ธฐ๋ฅ์ ๋ฐฑ์๋ API ์์ด ๋ธ๋ผ์ฐ์ ์์ QR ์์ฑ๊ณผ ํ์ผ ๋ค์ด๋ก๋๋ฅผ ๋๋ด๋ ํด๋ผ์ด์ธํธ ์ ์ฉ ์ ํธ๋ฆฌํฐ์
๋๋ค.
ํ์ผ
์ญํ
src/pages/SchoolLifeInfo/QrCodeGenerator/QrCodeGeneratorPage.jsx
QR ๋ด์ฉ, ์ค๋ฅ ๋ณด์ ๋จ๊ณ, ์์, ๋ก๊ณ , ํจํด, ๋ ์คํ์ผ, ๋ค์ด๋ก๋ ํ์ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ QRCode ์ปดํฌ๋ํธ์ props๋ก ์ ๋ฌ
src/pages/SchoolLifeInfo/QrCodeGenerator/QrCodeGeneratorPage.module.css
์ข์ธก ์ค์ ํจ๋, ์ฐ์ธก sticky ๋ฏธ๋ฆฌ๋ณด๊ธฐ, ์ปฌ๋ฌ ์
๋ ฅ, ํ ๊ธ/๋ผ๋์ค, ๋ค์ด๋ก๋ ๋ฒํผ ๋ฐ์ํ ์คํ์ผ
src/pages/SchoolLifeInfo/index.jsx
/school-info/qr-generator ์ง์ฐ ๋ก๋ฉ ๋ผ์ฐํธ ๋ฑ๋ก
src/pages/SchoolLifeInfo/SchoolInfoHub/SchoolInfoHub.jsx
ํ๊ต ์ํ ์ ๋ณด ํ๋ธ ์นด๋ ๋ฑ๋ก
src/components/Header/Header.jsx
์ ์ญ ํ๊ต ์ํ ์ ๋ณด ๋ฉ๋ด ๋งํฌ ๋ฑ๋ก
src/seo/policy.js
QR ์์ฑ๊ธฐ ์ ์ SEO/sitemap/prerender ๋ฉํ๋ฐ์ดํฐ ๋ฑ๋ก
๋์ ๋ฉ๋ชจ:
react-qrcode-logo๊ฐ QR ์บ๋ฒ์ค๋ฅผ ๋ ๋๋งํ๊ณ ์ฐธ์กฐ(ref)์ download() ๋ฉ์๋๋ก PNG/JPG/WebP ์ ์ฅ์ ์๋ํฉ๋๋ค.
๋ค์ด๋ก๋ API๊ฐ ์คํจํ๋ฉด ์บ๋ฒ์ค toDataURL() ๊ธฐ๋ฐ ๋์ฒด ๊ฒฝ๋ก๋ก ๊ฐ์ ํ์ผ ํ์์ ์์ฑํฉ๋๋ค.
๊ธฐ๋ณธ ๋ก๊ณ ๋๋ ์
๋ก๋ ๋ก๊ณ ๊ฐ ์ ํ๋๋ฉด ์ค์ ๋ชจ๋ ์์ค์ ๋ณด์ํ๊ธฐ ์ํด ์ค๋ฅ ๋ณด์ ๋จ๊ณ๊ฐ 4๋จ๊ณ๋ก ๊ณ ์ ๋ฉ๋๋ค.
์ฌ์ฉ์ ์
๋ก๋ ๋ก๊ณ ๋ FileReader.readAsDataURL()๋ก ๋ณํํด ์๋ฒ ์
๋ก๋ ์์ด ๋ฏธ๋ฆฌ๋ณด๊ธฐ์ ๋ค์ด๋ก๋์ ์ฌ์ฉํฉ๋๋ค.
11.4 ์ค๋๋ง์ค ์นด๋ ์์ฑ๊ธฐ ๋ชจ๋
ํ๊ต ์ํ ์ ๋ณด > ์ค๋๋ง์ค ์นด๋ ์์ฑ๊ธฐ ๊ธฐ๋ฅ์ ๋ฐฑ์๋ API ์์ด ๋ธ๋ผ์ฐ์ ์์ ์ค๋๋ง์ค ์คํ์ผ ์ด๋ฆ ๋ ์ด์ด์ ์นด๋ ํฉ์ฑ ์ด๋ฏธ์ง๋ฅผ ์์ฑํฉ๋๋ค.
ํ์ผ
์ญํ
src/pages/SchoolLifeInfo/ShanyCardGenerator/ShanyCardGeneratorPage.jsx
์์ฑ ๋ชจ๋, ๋ ์ด๋ฆฌํฐ, ํ
์คํธ, ์
๋ก๋ ์ด๋ฏธ์ง, ์ด๋ฆ ์์น, ํ์ผ ํ์, ๋ฐฐ์จ, ๋ฐฐ๊ฒฝ์ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ react-shany-card-generator ๋ ๋๋ง/๋ค์ด๋ก๋ API๋ฅผ ํธ์ถ
src/pages/SchoolLifeInfo/ShanyCardGenerator/ShanyCardGeneratorPage.module.css
ํ๋ฆฌ์
๋ฐ, ์ค์ ํจ๋, sticky ๋ฏธ๋ฆฌ๋ณด๊ธฐ, ์
๋ก๋ ์
๋ ฅ, ์ฌ๋ผ์ด๋, ์ํ ๋ฉ์์ง ๋ฐ์ํ ์คํ์ผ
src/pages/SchoolLifeInfo/index.jsx
/school-info/shany-card-generator ์ง์ฐ ๋ก๋ฉ ๋ผ์ฐํธ ๋ฑ๋ก
src/pages/SchoolLifeInfo/SchoolInfoHub/SchoolInfoHub.jsx
ํ๊ต ์ํ ์ ๋ณด ํ๋ธ ์นด๋ ๋ฑ๋ก
src/components/Header/Header.jsx
์ ์ญ ํ๊ต ์ํ ์ ๋ณด ๋ฉ๋ด ๋งํฌ ๋ฑ๋ก
src/seo/policy.js
์ค๋๋ง์ค ์นด๋ ์์ฑ๊ธฐ ์ ์ SEO/sitemap/prerender ๋ฉํ๋ฐ์ดํฐ ๋ฑ๋ก
package.json / package-lock.json
react-shany-card-generator์ ์ ์ด ์์กด์ฑ html-to-image ์ถ๊ฐ
๋์ ๋ฉ๋ชจ:
์ด๋ฆ ๋ ์ด์ด ๋ชจ๋๋ ShanyCardNameLayer ๋ฏธ๋ฆฌ๋ณด๊ธฐ์ renderCardNameBlob() ๋ค์ด๋ก๋ ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์นด๋ ํฉ์ฑ ๋ชจ๋๋ ์
๋ก๋ํ ์ด๋ฏธ์ง ํ์ผ์ ShanyCardPreview์ renderShanyCardBlob()์ ์ ๋ฌํ๋ฉฐ, ์ด๋ฏธ์ง ํฌ๊ธฐ ์์์ X/Y ์์น๋ฅผ ๋ณด์ ํฉ๋๋ค.
ensureShanyCardFontElements()๋ฅผ ํ์ด์ง ๋ง์ดํธ ์ ํธ์ถํด ๋ฏธ๋ฆฌ๋ณด๊ธฐ์ export ์ด๋ฏธ์ง์ ํฐํธ ํํ์ ๋ง์ถฅ๋๋ค.
ํ์ผ๋ช
์ ์นด๋ ์ด๋ฆ์ ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค๋ ํ์ผ ์์คํ
์์ ๋ฌธ์ ๊ฐ ๋๋ ๋ฌธ์๋ฅผ -๋ก ์นํํฉ๋๋ค.
์ถ๋ ฅ ๋ฐฐ์จ, ์บก์ฒ ๋ฐฐ์จ, ์ด๋ฆ ๋ฐฐ์จ์ UI์์๋ 0~500% ๋ฒ์๋ฅผ ์ ๊ณตํ์ง๋ง ์ค์ ๋ ๋๋ง์๋ 0 ํฌ๊ธฐ ์ด๋ฏธ์ง๊ฐ ์๊ธฐ์ง ์๋๋ก ์ต์ ์์ ๋ฐฐ์จ๋ก ๋ณด์ ํฉ๋๋ค.
11.5 ๊ณต์ง ์์ญ ํ๊ต ์๊ฐ ๋ชจ๋
๊ณต์ง์ฌํญ > ํ๊ต ์๊ฐ๋ ๊ณต์ง CRUD์ ๋ณ๊ฐ๋ก ์ด์๋๋ ์ ์ ์๊ฐ ํ๋ฉด์
๋๋ค. ๊ณต์ง ๋๋กญ๋ค์ด์์๋ /notices/school-info๋ก ์ง์
ํ๊ณ , ๋ผ์ฐํฐ๋ ๊ธฐ๋ณธ๊ฐ์ ๋ฒ์๊ณ ์๊ฐ(/notices/school-info/bshs-info)๋ก ์ด๋์ํต๋๋ค.
ํ์ผ
์ญํ
src/pages/Notices/SchoolInfo/index.jsx
๊ธฐ๋ณธ ๊ฒฝ๋ก ๋ฆฌ๋ค์ด๋ ํธ, ๋ฒ์๊ณ /์ฒ์๊ณ ์๊ฐ ๋ผ์ฐํธ, ํ๊ต ์๊ฐ ์ ์ฉ 404 ๊ตฌ์ฑ
src/pages/Notices/SchoolInfo/SchoolInfoTabs.jsx
๋ฒ์๊ณ ์ ์ฒ์๊ณ ํญ์ ๋จ์ผ ๊ฒฝ๋ก ๋ชฉ๋ก๊ณผ ํ์ฑ ํ์
src/pages/Notices/SchoolInfo/BeomseoInfoPage.jsx
๋ฒ์๊ณ ๊ณต์ ์๋ฃ๋ฅผ ํ์์ฉ ์น์
์ผ๋ก ์ฌ๊ตฌ์ฑํ๊ณ ์๋ฌธ ์ถ์ฒ ๋งํฌ ์ ๊ณต
src/pages/Notices/SchoolInfo/CSHSInfoPage.jsx
์ฒ์๊ณ ์๊ฐ ๋ฌธ๊ตฌ, ํต์ฌ ๊ฐ์น ์นด๋, ๋ค์ ํ๊ต ํ์ฌ D-Day ์ ๊ณต
src/seo/policy.js
๋ ์๊ฐ ๊ฒฝ๋ก์ title, description, breadcrumbs, sitemap/prerender ์ค์ ๊ณผ ๋ฒ์๊ณ HighSchool JSON-LD
๋์ ๋ฉ๋ชจ:
์ด ๋ชจ๋์ ๋ฐฑ์๋ API๋ฅผ ํธ์ถํ์ง ์์ผ๋ฏ๋ก ๋ฐฐํฌ ์ ์ ์ ์ฝํ
์ธ ์ ๊ณต์ ์ถ์ฒ ๋งํฌ๋ฅผ ์ฝ๋ ๋ฆฌ๋ทฐ์์ ํจ๊ป ํ์ธํฉ๋๋ค.
main.jsx์ prerender preload ๋ชฉ๋ก์ ํ๊ต ์๊ฐ ๋ผ์ฐํฐ์ ๋ ํ์ด์ง๊ฐ ํฌํจ๋์ด Suspense ๊ฒฝ๊ณ๋ฅผ ์ ์ HTML ์์ฑ ์ ํด์ํฉ๋๋ค.
์ด์ Nginx allowlist์๋ /notices/school-info, /notices/school-info/bshs-info, /notices/school-info/cshs-info๋ฅผ ์ถ๊ฐํด์ผ ์ง์ URL ์ง์
์ด 404๋ก ์คํ๋์ง ์์ต๋๋ค.
12. ์ ํธ๋ฆฌํฐ & ์ค์ ํ์ผ
VITE_* ํ๊ฒฝ๋ณ์๋ฅผ ์ฝ์ด ํ์
์ด ๋ณด์ฅ๋ ์์๋ก ๋
ธ์ถํฉ๋๋ค.
Export
ํ๊ฒฝ๋ณ์
ํ์
๊ธฐ๋ณธ๊ฐ
APP_NAME
VITE_APP_NAME
string
beomseo.in
API_BASE_URL
VITE_API_URL
string
http://localhost:5000
FASTAPI_BASE_URL
VITE_SPORTS_LEAGUE_API_URL
string
API_BASE_URL fallback
VALUE_PICK_BOARD_ENABLED
VITE_VALUE_PICK_BOARD_ENABLED
boolean
true
CLUB_RECRUIT_BOARD_ENABLED
VITE_CLUB_RECRUIT_BOARD_ENABLED
boolean
true
SUBJECT_CHANGES_BOARD_ENABLED
VITE_SUBJECT_CHANGES_BOARD_ENABLED
boolean
true
BOSPI_BOARD_ENABLED
VITE_BOSPI_BOARD_ENABLED
boolean
true
STUDY_WITH_BEOMSEO_BOARD_ENABLED
VITE_STUDY_WITH_BEOMSEO_BOARD_ENABLED
boolean
true
FIELD_TRIP_BOARD_ENABLED
VITE_FIELD_TRIP_BOARD_ENABLED
boolean
true
COMMUNITY_BOARD_FEATURE_FLAGS
(์ ๋ณด๋ ํ๋๊ทธ ๋ฌถ์)
Readonly<Record<string, boolean>>
๋ชจ๋ true
UPLOAD_MAX_ATTACHMENTS
VITE_UPLOAD_MAX_ATTACHMENTS
number
5
UPLOAD_MAX_IMAGES
VITE_UPLOAD_MAX_IMAGES
number
5
UPLOAD_MAX_FILE_SIZE_MB
VITE_UPLOAD_MAX_FILE_SIZE_MB
number
10
UPLOAD_MAX_FILE_SIZE_BYTES
(๊ณ์ฐ๊ฐ)
number
10 * 1024 * 1024
FIELD_TRIP_VIDEO_MAX_SIZE_MB
VITE_FIELD_TRIP_VIDEO_MAX_SIZE_MB
number
500
PETITION_THRESHOLD_DEFAULT
VITE_PETITION_THRESHOLD_DEFAULT
number
50
ALLOWED_ASSET_HOSTS
VITE_ALLOWED_ASSET_HOSTS
string[]
[]
src/pwa/firebaseMessaging.js
Firebase Web Push์ foreground ์๋ฆผ์ ๋ด๋นํฉ๋๋ค.
Export
์ญํ
isFirebaseMessagingConfigured()
ํ์ VITE_FIREBASE_* ๊ฐ ์ถฉ์กฑ ์ฌ๋ถ
isFirebaseMessagingSupported()
ํ์ฌ ๋ธ๋ผ์ฐ์ /์๋น์ค์์ปค ํ๊ฒฝ ์ง์ ์ฌ๋ถ
getCurrentFirebaseMessagingToken()
๊ธฐ์กด FCM token ์กฐํ
requestFirebaseMessagingPermissionAndToken()
๊ถํ ์์ฒญ + token ๋ฐ๊ธ
deleteCurrentFirebaseMessagingToken()
ํ์ฌ ๊ธฐ๊ธฐ์ token ํด์
startFirebaseForegroundMessageListener()
foreground ๋ฉ์์ง๋ฅผ ๋ธ๋ผ์ฐ์ ์๋ฆผ์ผ๋ก ํ์
src/pwa/mealNotificationInstallationId.js
localStorage์ ์ ์ฅ๋๋ ๋ธ๋ผ์ฐ์ /PWA ์ธ์คํด์ค ์๋ณ์ ๊ด๋ฆฌ
๊ธ์ ์๋ฆผ ๊ตฌ๋
API์ installationId ์
๋ ฅ๊ฐ ์์ค
์ญํ ๋ฌธ์์ด์ ์ ๊ทํํ๊ณ , ์ ๋์ด/CSS ํด๋์ค๋ฅผ ๋ฐํํ๋ ์ ํธ๋ฆฌํฐ์
๋๋ค.
Export
์ค๋ช
getRoleDisplay({ role, nickname, showPrefix, prefixOverride })
์ ๊ทํ๋ ์ญํ ์ ๋ฐ๋ผ displayPrefix, ariaLabel, roleClassName, safeNickname ๋ฐํ
default
getRoleDisplay์ ๋์ผ
์ง์ ์ญํ : admin([๊ด๋ฆฌ์]), student_council([ํ์ํ]), teacher([๊ต์ฌ]), student(์ ๋์ด ์์)
๋ณ์นญ ์ง์: council, studentcouncil, student-council, student council โ student_council
13. ์คํ์ผ ์์คํ
ํ์ผ
src/styles/ ๋๋ ํฐ๋ฆฌ๋ ์ ์ญ CSS ํ ํฐ๊ณผ ๋ ์ด์์์ ๊ด๋ฆฌํฉ๋๋ค.
flowchart TD
V["variables.css\n๋์์ธ ํ ํฐ (์์/ํ์ดํฌ/๊ฐ๊ฒฉ)"] --> P["primitives.css\n๊ธฐ๋ณธ ์์ ๋ฆฌ์
/์คํ์ผ"]
P --> G["globals.css\n์ ํธ๋ฆฌํฐ ํด๋์ค/์ ์ญ ์ปดํฌ๋ํธ"]
V --> L["layout.css\n๋ ์ด์์ ๊ทธ๋ฆฌ๋/์ปจํ
์ด๋"]
Loading
ํ์ผ
์ญํ
variables.css
CSS custom property (์์, ํ์ดํฌ๊ทธ๋ํผ, ๊ฐ๊ฒฉ, ๊ทธ๋ฆผ์ ๋ฑ) โ ๋ผ์ดํธ/๋คํฌ ํ
๋ง ๋ชจ๋ ์ ์
primitives.css
HTML ์์ ๋ฆฌ์
+ ๊ธฐ๋ณธ ํ์ดํฌ ์คํ์ผ
globals.css
์ ํธ๋ฆฌํฐ ํด๋์ค, ๊ณตํต ์ปดํฌ๋ํธ ์คํ์ผ
layout.css
์ต์์ ๋ ์ด์์ ๊ทธ๋ฆฌ๋ ๊ท์น