From b97d4e3d60cb3286724c262ce59b2077e1f19b03 Mon Sep 17 00:00:00 2001 From: durumi99 Date: Mon, 19 Jan 2026 23:44:30 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20FCM=20=EC=95=8C=EB=A6=BC=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=B2=84=ED=8A=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyPage/FCMTestButton/FCMTestButton.tsx | 101 ++++++++++++++++++ src/pages/MyPage/MyPage.tsx | 2 + 2 files changed, 103 insertions(+) create mode 100644 src/components/MyPage/FCMTestButton/FCMTestButton.tsx diff --git a/src/components/MyPage/FCMTestButton/FCMTestButton.tsx b/src/components/MyPage/FCMTestButton/FCMTestButton.tsx new file mode 100644 index 0000000..d6cde8b --- /dev/null +++ b/src/components/MyPage/FCMTestButton/FCMTestButton.tsx @@ -0,0 +1,101 @@ +import styled from '@emotion/styled'; +import { useState } from 'react'; + +const FCMTestButton = () => { + const [loading, setLoading] = useState(false); + const [result, setResult] = useState(''); + + const testFCMPush = async () => { + try { + setLoading(true); + setResult(''); + + const token = localStorage.getItem('access_token'); + + if (!token) { + setResult('❌ 로그인이 필요합니다'); + alert('❌ 로그인이 필요합니다'); + return; + } + + const response = await fetch('https://api.humanzipyo.com/notification/test-fcm', { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + const data = await response.json(); + + if (response.ok) { + setResult('✅ 테스트 알림 발송 성공!\n앱에서 알림을 확인하세요.'); + alert('✅ 테스트 알림 발송 성공!\n앱에서 알림을 확인하세요.'); + } else { + setResult(`❌ 실패: ${data.message || '알 수 없는 오류'}`); + alert(`❌ 실패: ${data.message || '알 수 없는 오류'}`); + } + } catch (error) { + const message = error instanceof Error ? error.message : '알 수 없는 오류'; + setResult(`❌ 에러: ${message}`); + alert(`❌ 에러: ${message}`); + } finally { + setLoading(false); + } + }; + + return ( + + + {loading ? '🔄 발송 중...' : '📱 FCM 알림 테스트'} + + {result && {result}} + + ); +}; + +export default FCMTestButton; + +const Container = styled.div` + margin: 20px 0; + padding: 16px; + background-color: ${({ theme }) => theme.colors.sub_gray1}; + border-radius: 8px; + border: 1px dashed ${({ theme }) => theme.colors.sub_gray5}; +`; + +const TestButton = styled.button` + width: 100%; + padding: 12px 16px; + background-color: #007aff; + color: white; + border: none; + border-radius: 8px; + font-size: 16px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + + &:hover { + opacity: 0.9; + } + + &:active { + transform: scale(0.98); + } + + &:disabled { + opacity: 0.6; + cursor: not-allowed; + } +`; + +const ResultText = styled.pre` + margin-top: 12px; + padding: 12px; + background-color: ${({ theme }) => theme.colors.sub_gray2}; + border-radius: 4px; + font-size: 13px; + line-height: 1.5; + white-space: pre-wrap; + color: ${({ theme }) => theme.colors.text1}; +`; diff --git a/src/pages/MyPage/MyPage.tsx b/src/pages/MyPage/MyPage.tsx index 430f544..b8b9785 100644 --- a/src/pages/MyPage/MyPage.tsx +++ b/src/pages/MyPage/MyPage.tsx @@ -2,6 +2,7 @@ import { useNavigate } from 'react-router-dom'; import useAuthInfo from '@hooks/useAuthInfo'; import { webPath } from '@router/index'; import ConfirmModal from '@components/Modal/Confirm/ConfirmModal'; +import FCMTestButton from '@components/MyPage/FCMTestButton/FCMTestButton'; import { fetchAuthLogout } from '@controllers/auth/api'; import { useExperimentStatusQuery } from '@controllers/experiment/query'; import { useBookmarkListQuery } from '@controllers/preference/query'; @@ -172,6 +173,7 @@ const MyPage = () => { )} +