1- import { useEffect } from 'react' ;
1+ import { useEffect , useState } from 'react' ;
22import * as S from '../styles/StepStartStyled' ;
33import AuthButton from './AuthButton' ;
44import { useAuthStore } from '@repo/auth/stores/useAuthStore' ;
@@ -8,13 +8,20 @@ import signIn from '@/assets/Icons/signIn.png';
88
99import { isKakaoInApp , isAndroid , isIOS } from '@/utils/detect' ;
1010import { useSearchParams , useNavigate } from 'react-router' ;
11+ import Cookies from 'js-cookie' ;
12+ import { usePublicApi } from '@repo/api/hooks/usePublicApi' ;
1113
1214export default function StepStart ( ) {
13- const { setStep } = useAuthStore ( ) ;
15+ const { setStep, setToken } = useAuthStore ( ) ;
1416 const { handleLogin } = useSocialLogin ( ) ;
1517 const { isMobile } = useMediaQueries ( ) ;
1618 const [ searchParams ] = useSearchParams ( ) ;
1719 const navigate = useNavigate ( ) ;
20+ const [ showViewerLogin , setShowViewerLogin ] = useState ( false ) ;
21+ const [ viewerId , setViewerId ] = useState ( '' ) ;
22+ const [ viewerPw , setViewerPw ] = useState ( '' ) ;
23+ const [ viewerError , setViewerError ] = useState < string | null > ( null ) ;
24+ const { post } = usePublicApi ( ) ;
1825
1926 const type = searchParams . get ( 'type' ) as 'KAKAO' | 'GOOGLE' | null ;
2027
@@ -51,6 +58,47 @@ export default function StepStart() {
5158 }
5259 } ;
5360
61+ // SW융합 교육원 뷰어역할로 로그인 (
62+ const handleViewerLogin = async ( ) => {
63+ const validId = import . meta. env . VITE_UNION_VIEWER_ID ;
64+ const validPw = import . meta. env . VITE_UNION_VIEWER_PW ;
65+
66+ if ( ! validId || ! validPw ) {
67+ setViewerError ( '뷰어 계정 환경변수(VITE_UNION_VIEWER_ID/PW)가 설정되지 않았습니다.' ) ;
68+ return ;
69+ }
70+
71+ if ( viewerId !== validId || viewerPw !== validPw ) {
72+ setViewerError ( 'ID 또는 비밀번호가 올바르지 않습니다.' ) ;
73+ return ;
74+ }
75+
76+ try {
77+ // 임시 토큰 발급 (테스트용)
78+ const userId = Number ( import . meta. env . VITE_UNION_VIEWER_NUM ) ;
79+ type TokenDTO = { accessToken : string ; refreshToken : string } ;
80+ const res = await post < { status : number ; data : TokenDTO } > ( `/auth/token/${ userId } ` ) ;
81+ const tokenWrapper = res as unknown as { status ?: number ; data ?: TokenDTO } | { data ?: { data ?: TokenDTO } } ;
82+ const tokenData = ( tokenWrapper as unknown as { data ?: { data ?: TokenDTO } } ) ?. data ?. data || ( tokenWrapper as unknown as { data ?: TokenDTO } ) ?. data ;
83+ const accessToken = tokenData ?. accessToken as string | undefined ;
84+ const refreshToken = tokenData ?. refreshToken as string | undefined ;
85+
86+ if ( accessToken && refreshToken ) {
87+ Cookies . set ( 'refreshToken' , refreshToken , { secure : true , sameSite : 'Strict' } ) ;
88+ setToken ( accessToken ) ; // Authorization 즉시 활성화
89+ Cookies . set ( 'limitWrite' , 'true' , { secure : true , sameSite : 'Strict' } ) ;
90+ }
91+ } catch {
92+ // 토큰 발급 실패해도 뷰어 세션으로 조회만 시도
93+ }
94+
95+ // 뷰어 플래그 제거
96+
97+ setViewerError ( null ) ;
98+ setShowViewerLogin ( false ) ;
99+ navigate ( '/home' ) ;
100+ } ;
101+
54102 // step=input 감지해서 자동 인증 단계 진입
55103useEffect ( ( ) => {
56104 const step = searchParams . get ( 'step' ) ;
@@ -107,6 +155,83 @@ const handleVerifyClick = () => {
107155 />
108156 </ S . LinkWrapper >
109157 </ S . GapWrapper >
158+
159+ { /* 뷰어 전용 로그인 토글 */ }
160+ < div style = { { marginTop : isMobile ? '16px' : '24px' } } >
161+ < button
162+ style = { {
163+ background : 'transparent' ,
164+ border : 'none' ,
165+ color : '#666' ,
166+ textDecoration : 'underline' ,
167+ cursor : 'pointer'
168+ } }
169+ onClick = { ( ) => setShowViewerLogin ( ( v ) => ! v ) }
170+ >
171+ SW융합 교육원 뷰어 로그인
172+ </ button >
173+ </ div >
174+
175+ { showViewerLogin && (
176+ < div style = { {
177+ marginTop : isMobile ? '12px' : '16px' ,
178+ marginBottom : isMobile ? '12px' : '16px' ,
179+ display : 'flex' ,
180+ flexDirection : 'column' ,
181+ alignItems : 'center' ,
182+ gap : '8px'
183+ } } >
184+ < input
185+ placeholder = "ID"
186+ value = { viewerId }
187+ onChange = { ( e ) => setViewerId ( e . target . value ) }
188+ style = { {
189+ width : isMobile ? '175px' : '190px' ,
190+ height : isMobile ? '32px' : '36px' ,
191+ border : '1px solid #ccc' ,
192+ borderRadius : '6px' ,
193+ padding : '6px 10px' ,
194+ fontFamily : 'Pretendard Variable'
195+ } }
196+ />
197+ < input
198+ placeholder = "비밀번호"
199+ type = "password"
200+ value = { viewerPw }
201+ onChange = { ( e ) => setViewerPw ( e . target . value ) }
202+ style = { {
203+ width : isMobile ? '175px' : '190px' ,
204+ height : isMobile ? '32px' : '36px' ,
205+ border : '1px solid #ccc' ,
206+ borderRadius : '6px' ,
207+ padding : '6px 10px' ,
208+ fontFamily : 'Pretendard Variable'
209+ } }
210+ />
211+ { viewerError && (
212+ < div style = { { color : '#ff4d4f' , fontSize : isMobile ? '12px' : '13px' } } >
213+ { viewerError }
214+ </ div >
215+ ) }
216+ < button
217+ onClick = { handleViewerLogin }
218+ style = { {
219+ marginTop : '4px' ,
220+ width : isMobile ? '175px' : '190px' ,
221+ height : isMobile ? '36px' : '40px' ,
222+ backgroundColor : '#29d4a7' ,
223+ color : '#fff' ,
224+ border : 'none' ,
225+ borderRadius : '999px' ,
226+ cursor : 'pointer' ,
227+ fontFamily : 'Pretendard Variable' ,
228+ fontWeight : 600
229+ } }
230+ >
231+ 뷰어로 시작하기
232+ </ button >
233+ </ div >
234+ ) }
110235 </ S . Container >
111236 ) ;
112237}
0 commit comments