@@ -17,6 +17,7 @@ const Attendance = () => {
1717 const currentDateRef = useRef ( null ) ;
1818
1919 const getSubImage = ( count ) => {
20+ // ✅ 절대 경로(/) 사용으로 이미지 엑박 해결
2021 switch ( count ) {
2122 case 3 :
2223 return "/assets/img/full_coin_green.png" ;
@@ -31,6 +32,7 @@ const Attendance = () => {
3132
3233 // 세션별 상단 이미지 handling
3334 const getBoomImage = ( status ) => {
35+ // ✅ 절대 경로(/) 사용
3436 switch ( status ) {
3537 case "success" :
3638 return "/assets/img/boom-fill-green.png" ;
@@ -41,10 +43,13 @@ const Attendance = () => {
4143 }
4244 } ;
4345
44- // 날짜 기반 주차 계산
46+ // 날짜 기반 주차 계산 (0시 기준 초기화로 오차 제거)
4547 const getWeekFromDate = ( dateStr ) => {
4648 const startDate = new Date ( "2025-12-21" ) ; // 세션 시작일
49+ startDate . setHours ( 0 , 0 , 0 , 0 ) ;
50+
4751 const currentDate = new Date ( dateStr ) ;
52+ currentDate . setHours ( 0 , 0 , 0 , 0 ) ;
4853
4954 // 두 날짜 사이 일수 차이 계산
5055 const diffTime = currentDate . getTime ( ) - startDate . getTime ( ) ;
@@ -54,82 +59,97 @@ const Attendance = () => {
5459 return Math . floor ( diffDays / 7 ) + 1 ;
5560 } ;
5661
62+ // ✅ 화/목/토 요일별 데이터 매핑 로직 적용
5763 const processWeeklyAttendance = ( rawData ) => {
58- const weekSlotMap = new Map ( ) ;
59- // { weekNum: [boolean, boolean, ...] }
60-
61- rawData . forEach ( ( { date, slots } ) => {
62- const week = getWeekFromDate ( date ) ; // 날짜 기준 주차 계산
63- const presentSlots = slots . map ( ( slot ) => slot . status ) ; // T/F 목록 생성
64- const existing = weekSlotMap . get ( week ) || [ ] ;
65- weekSlotMap . set ( week , [ ...existing , ...presentSlots ] ) ;
66- } ) ;
64+ // 화(0), 목(1), 토(2) 인덱스 반환 함수
65+ const getDayIndex = ( dateStr ) => {
66+ const day = new Date ( dateStr ) . getDay ( ) ;
67+ if ( day === 2 ) return 0 ; // 화
68+ if ( day === 4 ) return 1 ; // 목
69+ if ( day === 6 ) return 2 ; // 토
70+ return - 1 ;
71+ } ;
6772
68- console . log ( "주차별 출석 (weekSlotMap):" , weekSlotMap ) ;
73+ // 5주차 x 3세션 빈 데이터 생성
74+ const weeklyData = Array . from ( { length : 5 } , ( _ , i ) => ( {
75+ week : i + 1 ,
76+ classes : [
77+ { count : 0 , image : getSubImage ( 0 ) } , // 화
78+ { count : 0 , image : getSubImage ( 0 ) } , // 목
79+ { count : 0 , image : getSubImage ( 0 ) } // 토
80+ ]
81+ } ) ) ;
6982
70- return Array . from ( { length : 5 } , ( _ , i ) => {
71- const week = i + 1 ;
72- const all9 = weekSlotMap . get ( week ) || [ ] ; // 총 9개의 출석 슬롯 (3번의 출석체크*주차당 3번의 세션)
83+ if ( ! rawData || ! Array . isArray ( rawData ) ) return weeklyData ;
7384
74- const classes = [ 0 , 1 , 2 ] . map ( ( classIdx ) => {
75- // 0,1,2 -> 세션당 3번의 출석체크
76- const slice = all9 . slice ( classIdx * 3 , classIdx * 3 + 3 ) ;
77- const count = slice . filter ( Boolean ) . length ; // 출석 성공(True) 카운트
78- return {
79- image : getSubImage ( count ) ,
80- count,
85+ rawData . forEach ( ( { date, slots } ) => {
86+ const week = getWeekFromDate ( date ) ;
87+ const dayIdx = getDayIndex ( date ) ;
88+
89+ // 유효한 주차(1~5)이고, 화/목/토 세션인 경우만 처리
90+ if ( week >= 1 && week <= 5 && dayIdx !== - 1 ) {
91+
92+ // ✅ DB의 "t"(문자열)와 true(불리언) 모두 출석으로 인정
93+ const presentCount = slots . filter (
94+ ( slot ) => slot . status === true || slot . status === "t"
95+ ) . length ;
96+
97+ weeklyData [ week - 1 ] . classes [ dayIdx ] = {
98+ count : presentCount ,
99+ image : getSubImage ( presentCount ) ,
81100 } ;
82- } ) ;
83-
84- return { week, classes } ; // week: 1, classes: [ {image, count}, ... ]
101+ }
85102 } ) ;
103+
104+ return weeklyData ;
86105 } ;
87106
107+ // 주간 출석 정보 가져오기
88108 const fetchAttendance = async ( ) => {
89109 try {
110+ // ✅ [배포용] 실제 로그인 유저 ID 사용
90111 const user = JSON . parse ( localStorage . getItem ( "user" ) ) ;
91112 const userId = user ?. id ;
113+
92114 if ( ! userId ) return ;
93115
94- // 유저 전체 출석 데이터 불러오기
95116 const res = await api . get ( `/attendance/user` , {
96117 params : { userId } ,
97- withCredentials : true , // 세션 기반 인증 요청처리
118+ withCredentials : true ,
98119 } ) ;
120+
99121 const rawData = res . data . data ;
100- console . log ( "출석 rawData:" , rawData ) ;
101122 const weekly = processWeeklyAttendance ( rawData ) ;
102123 setAttendanceData ( weekly ) ;
103124 } catch ( error ) {
104125 console . error ( "출석 정보 가져오기 실패:" , error ) ;
105126 }
106127 } ;
107128
108- // 세션별 출석체크(총 3번) 진행 정보 불러오기
129+ // 오늘 세션(3회) 출석 정보 가져오기
109130 const fetchTodayAttendance = async ( ) => {
110131 try {
132+ // ✅ [배포용] 실제 로그인 유저 ID 사용
111133 const user = JSON . parse ( localStorage . getItem ( "user" ) ) ;
112134 const userId = user ?. id ;
113- console . log ( "fetchTodayAttendance() called" ) ;
114135
115136 if ( ! userId ) return ;
116137
117- const today = new Date ( ) . toLocaleDateString ( "sv-SE" ) ; // → KST(한국 시간 기준)
138+ const today = new Date ( ) . toLocaleDateString ( "sv-SE" ) ;
118139 const res = await api . get ( `/attendance/user/date` , {
119140 params : { userId, date : today } ,
120- withCredentials : true , // 세션 기반 인증 요청처리
141+ withCredentials : true ,
121142 } ) ;
122143
123- // api 응답 수정에 따라 업데이트
124- // 서버 응답이 순서대로 오지 않을 수 있으므로 order 기준으로 정렬
125144 const slots = ( res . data . data || [ ] ) . sort ( ( a , b ) => a . order - b . order ) ;
126145
127146 const statuses = slots . map ( ( slot ) => {
128- if ( slot . status === true ) return "success" ;
147+ // ✅ DB 데이터 타입 호환성(t/true) 체크
148+ if ( slot . status === true || slot . status === "t" ) return "success" ;
129149 else return "fail" ;
130150 } ) ;
131151
132- // 출석체크 진행안된 것 처리
152+ // 아직 진행되지 않은 출석체크 처리
133153 while ( statuses . length < 3 ) {
134154 statuses . push ( "not_started" ) ;
135155 }
@@ -142,16 +162,14 @@ const Attendance = () => {
142162
143163 useEffect ( ( ) => {
144164 if ( ! currentDateRef . current ) {
145- currentDateRef . current = new Date ( ) . toLocaleDateString ( "sv-SE" ) ; // → KST(한국 시간 기준)
165+ currentDateRef . current = new Date ( ) . toLocaleDateString ( "sv-SE" ) ;
146166 }
147- console . log ( "currentDateRef 할당 갱신:" , currentDateRef . current ) ;
148167
149168 fetchAttendance ( ) ;
150169 fetchTodayAttendance ( ) ;
151170
152- // 10초마다 출석체크 활성화 여부 확인 및 UI 업데이트
171+ // 10초마다 상태 갱신 (미진행 건이 있을 때만)
153172 const interval = setInterval ( ( ) => {
154- // 출석 미진행 상태가 하나라도 있을 때만 호출
155173 setTodayStatuses ( ( prev ) => {
156174 if ( prev . includes ( "not_started" ) ) {
157175 fetchTodayAttendance ( ) ;
@@ -160,28 +178,15 @@ const Attendance = () => {
160178 } ) ;
161179 } , 10000 ) ;
162180
163- // 매 분마다 현재 날짜를 확인해서 달라졌으면 상태 업데이트
181+ // 1분마다 날짜 변경 체크
164182 const dateCheckInterval = setInterval ( ( ) => {
165- const todayStr = new Date ( ) . toLocaleDateString ( "sv-SE" ) ; // → KST(한국 시간 기준)
166- console . log ( "dateCheckInterval 실행 시간:" , new Date ( ) ) ;
167- console . log (
168- "현재 로드해오는 시간:" ,
169- currentDateRef . current ,
170- "| 현재 날짜:" ,
171- todayStr
172- ) ;
173-
183+ const todayStr = new Date ( ) . toLocaleDateString ( "sv-SE" ) ;
184+
174185 if ( todayStr !== currentDateRef . current ) {
175- console . log (
176- "날짜 변경 감지 / 이전:" ,
177- currentDateRef . current ,
178- "→ 현재:" ,
179- todayStr
180- ) ;
181- currentDateRef . current = todayStr ; // 날짜 갱신
182- fetchTodayAttendance ( ) ; // 새로운 날짜 기준으로 다시 가져오기
186+ currentDateRef . current = todayStr ;
187+ fetchTodayAttendance ( ) ;
183188 }
184- } , 60000 ) ; // 60초마다 확인
189+ } , 60000 ) ;
185190
186191 return ( ) => {
187192 clearInterval ( interval ) ;
@@ -190,36 +195,36 @@ const Attendance = () => {
190195 } , [ ] ) ;
191196
192197 const handleChange = ( index , value ) => {
193- // 숫자만 입력 허용
194198 if ( / ^ \d * $ / . test ( value ) ) {
195199 const userCodes = [ ...attendanceCode ] ;
196200 userCodes [ index ] = value ;
197201 setAttendanceCode ( userCodes ) ;
198202 }
199203 } ;
204+
200205 const handleSubmit = async ( ) => {
201206 try {
207+ // ✅ [배포용] 실제 로그인 유저 ID 사용
202208 const user = JSON . parse ( localStorage . getItem ( "user" ) ) ;
203209 const userId = user ?. id ;
210+
204211 if ( ! userId ) return ;
205212
206- // 유저가 입력한 출석 코드 서버에 전달(서버에서 출석코드 체크)
207-
208213 const res = await api . post (
209214 "/attendance/mark" ,
210215 {
211216 userId,
212217 code : attendanceCode [ 0 ] ,
213218 } ,
214219 {
215- withCredentials : true , // 세션 기반 인증 요청처리
220+ withCredentials : true ,
216221 }
217222 ) ;
218223
219224 if ( res . data . success ) {
220225 alert ( res . data . data . message ) ;
221- fetchAttendance ( ) ; // 서버 출석체크 전달 후 UI 반영
222- fetchTodayAttendance ( ) ; // 세션별 상단 이미지 UI 반영
226+ fetchAttendance ( ) ; // 주간 데이터 갱신
227+ fetchTodayAttendance ( ) ; // 오늘 상단 데이터 갱신
223228 } else {
224229 alert ( res . data . message || "출석 실패" ) ;
225230 }
@@ -229,8 +234,6 @@ const Attendance = () => {
229234 }
230235 } ;
231236
232- console . log ( "attendanceData: " , attendanceData ) ;
233-
234237 return (
235238 < div className = { styles . attendance_page } >
236239 < Header />
@@ -251,7 +254,6 @@ const Attendance = () => {
251254 ) }
252255 < div className = { styles . attend_img_container } >
253256 { todayStatuses . map ( ( status , idx ) => {
254- console . log ( `렌더링된 이미지 ${ idx + 1 } :` , getBoomImage ( status ) ) ;
255257 return (
256258 < div className = { styles . boom_icon } key = { idx } >
257259 < img src = { getBoomImage ( status ) } alt = { `attendance-${ idx } ` } />
@@ -268,4 +270,4 @@ const Attendance = () => {
268270 ) ;
269271} ;
270272
271- export default Attendance ;
273+ export default Attendance ;
0 commit comments