diff --git a/client/src/App.js b/client/src/App.js
index 3167f4f..c13d920 100644
--- a/client/src/App.js
+++ b/client/src/App.js
@@ -6,6 +6,7 @@ import Footer from "./components/Footer/Footer.js";
import Main from "./components/Main/Main.js";
import Diagnosis from "./components/Diagnosis/Diagnosis.js";
import Community from "./components/Community/Community.js";
+import CardNews from "./components/Community/CardNews.js";
import Mypage from "./components/Mypage/Mypage.js";
import Login from "./components/Login/Login.js";
import Signup from "./components/Signup/Signup.js";
@@ -14,7 +15,7 @@ import SelfTest from "./components/Mypage/SelfTest.js";
import EmotionChart from "./components/Mypage/EmotionChart.js";
import EditProfile from "./components/Mypage/EditProfile.js";
import LoginExtraInfo from "./components/LoginExtraInfo/LoginExtraInfo.js";
-import KakaoCallback from "./components/Login/KakaoCallback.js";
+import KakaoCallback from "./components/Login/KaKaoCallback.js";
function App() {
return (
@@ -27,6 +28,7 @@ function App() {
} />
} />
} />
+ } />
} />
} />
} />
@@ -35,7 +37,7 @@ function App() {
} />
} />
} />
- } />
+ } />
diff --git a/client/src/Images/cardnews/cn1.png b/client/src/Images/cardnews/cn1.png
new file mode 100644
index 0000000..f00e809
Binary files /dev/null and b/client/src/Images/cardnews/cn1.png differ
diff --git a/client/src/Images/cardnews/cn2.png b/client/src/Images/cardnews/cn2.png
new file mode 100644
index 0000000..9c86598
Binary files /dev/null and b/client/src/Images/cardnews/cn2.png differ
diff --git a/client/src/Images/cardnews/cn3.png b/client/src/Images/cardnews/cn3.png
new file mode 100644
index 0000000..b97cc85
Binary files /dev/null and b/client/src/Images/cardnews/cn3.png differ
diff --git a/client/src/Images/cardnews/cn4.png b/client/src/Images/cardnews/cn4.png
new file mode 100644
index 0000000..2671448
Binary files /dev/null and b/client/src/Images/cardnews/cn4.png differ
diff --git a/client/src/Images/cardnews/cn5.png b/client/src/Images/cardnews/cn5.png
new file mode 100644
index 0000000..c552a99
Binary files /dev/null and b/client/src/Images/cardnews/cn5.png differ
diff --git a/client/src/Images/cardnews/cn6.png b/client/src/Images/cardnews/cn6.png
new file mode 100644
index 0000000..97ea511
Binary files /dev/null and b/client/src/Images/cardnews/cn6.png differ
diff --git a/client/src/components/Community/CardNews.js b/client/src/components/Community/CardNews.js
new file mode 100644
index 0000000..5d2f3b2
--- /dev/null
+++ b/client/src/components/Community/CardNews.js
@@ -0,0 +1,173 @@
+import React, { useState, useEffect, useCallback } from "react";
+import styles from "./Community.module.css";
+
+import cn1 from "../../Images/cardnews/cn1.png";
+import cn2 from "../../Images/cardnews/cn2.png";
+import cn3 from "../../Images/cardnews/cn3.png";
+import cn4 from "../../Images/cardnews/cn4.png";
+import cn5 from "../../Images/cardnews/cn5.png";
+import cn6 from "../../Images/cardnews/cn6.png";
+
+const items = [
+ {
+ title: "기록의 중요성",
+ date: "2025.04.15",
+ desc: "매일의 감정을 기록하세요. 정신건강을 관리하는 첫 걸음입니다!",
+ img: cn1,
+ content: [
+ "하루 3줄만 적어도 스트레스 인식 능력이 향상돼요.",
+ "감정·사건·신체반응(예: 심장 두근거림)을 함께 적으면 트리거 파악이 쉬워집니다.",
+ "주 1회 ‘돌아보기’로 무엇이 도움 됐는지 체크하면 자기조절감이 커집니다.",
+ "툴 팁: 날짜/기분(1~5)/한 줄 요약 포맷으로 시작하면 오래 갑니다."
+ ]
+ },
+ {
+ title: "사회적 연결과 행복",
+ date: "2025.04.10",
+ desc: "사회적 관계가 우리의 행복과 정신 건강에 미치는 영향에 대해 알아보세요.",
+ img: cn2,
+ content: [
+ "짧은 대화라도 고립감을 낮추고 회복탄력성을 높여요.",
+ "‘정기적 연결 루틴’(주 1회 전화, 월 1회 만남)을 달력에 고정하세요.",
+ "관계의 질이 중요합니다. 소진되는 관계는 빈도를 줄이고 지지적 관계를 늘리세요.",
+ "감사 메시지 1통이 우울·불안 완화에 유의미한 효과가 있다는 연구가 많습니다."
+ ]
+ },
+ {
+ title: "마음을 위한 운동",
+ date: "2025.04.05",
+ desc: "운동이 마음 건강에 미치는 긍정적인 영향에 대해 알아보세요.",
+ img: cn3,
+ content: [
+ "주 3회 20~30분의 가벼운 유산소만으로도 기분이 개선돼요.",
+ "‘너무 바쁘면 5분’ 규칙: 5분만 걸어도 시작 장벽이 낮아집니다.",
+ "햇빛 노출+걷기 조합은 수면 리듬 안정에 도움을 줍니다.",
+ "체크리스트: 운동 전/후 기분 점수 기록 → 동기 유지에 큰 도움."
+ ]
+ },
+ {
+ title: "휴가와 정신건강",
+ date: "2025.04.03",
+ desc: "휴가가 정신 건강에 미치는 긍정적인 영향에 대해 알아보세요. 자주 쉬어가며 스트레스를 줄이세요.",
+ img: cn4,
+ content: [
+ "짧은 마이크로바케이션(반나절~1일)도 번아웃 예방에 효과적입니다.",
+ "‘완전 오프’ 구간(알림 off, 업무앱 로그아웃)을 명확히 만드세요.",
+ "휴식 계획에는 ‘회복 활동(자연, 수면, 가벼운 운동)’을 포함시키세요.",
+ "복귀 전날 30분 정리 루틴을 두면 복귀 스트레스가 줄어듭니다."
+ ]
+ },
+ {
+ title: "수면과 감정 균형",
+ date: "2025.03.30",
+ desc: "잠을 충분히 자는 것이 감정 균형에 미치는 영향을 알아보세요.",
+ img: cn5,
+ content: [
+ "취침·기상 시간 고정이 최우선(주말 포함 ±1시간 이내).",
+ "취침 2시간 전 스크린 타임 줄이고, 카페인은 오후 2시 이후 피하세요.",
+ "‘걱정 리스트’는 잠자리 전에 종이에 적고, 침대에서는 오직 수면/휴식만.",
+ "기상 직후 햇빛 노출 10분은 강력한 생체시계 리셋 방법입니다."
+ ]
+ },
+ {
+ title: "자가진단",
+ date: "2025.03.27",
+ desc: "우울증 자가진단을 통해 자신의 마음 상태를 평가해보세요. 초기 증상을 놓치지 마세요.",
+ img: cn6,
+ content: [
+ "2주 이상 지속된 우울감/흥미저하/수면변화가 있다면 평가가 필요합니다.",
+ "자가진단은 ‘출발점’일 뿐, 결과가 높으면 전문가 상담을 검토하세요.",
+ "위험 신호(자해 생각, 극단적 계획)는 즉시 주변에 알리고 도움을 요청하세요.",
+ "기록→상담→생활습관 조정의 3단계 루프로 회복경로를 만드세요."
+ ]
+ }
+];
+
+export default function CardNews() {
+ const [activeIdx, setActiveIdx] = useState(null);
+
+ const handleKey = useCallback((e) => {
+ if (activeIdx === null) return;
+ if (e.key === "Escape") setActiveIdx(null);
+ if (e.key === "ArrowRight") setActiveIdx((i) => (i + 1) % items.length);
+ if (e.key === "ArrowLeft") setActiveIdx((i) => (i - 1 + items.length) % items.length);
+ }, [activeIdx]);
+
+ useEffect(() => {
+ if (activeIdx !== null) {
+ document.addEventListener("keydown", handleKey);
+ document.body.style.overflow = "hidden";
+ return () => {
+ document.removeEventListener("keydown", handleKey);
+ document.body.style.overflow = "";
+ };
+ }
+ }, [activeIdx, handleKey]);
+
+ return (
+
+
+ {items.map((it, i) => (
+
setActiveIdx(i)}
+ role="button"
+ tabIndex={0}
+ onKeyDown={(e) => e.key === "Enter" && setActiveIdx(i)}
+ aria-label={`${it.title} 상세 보기`}
+ >
+
+

+
+
+
+
{it.title}
+
+
+
{it.desc}
+
+
+ ))}
+
+
+ {/* 상세 모달 */}
+ {activeIdx !== null && (
+
setActiveIdx(null)}>
+
e.stopPropagation()} role="dialog" aria-modal="true">
+
+
+
![{items[activeIdx].title}]({items[activeIdx].img})
+
+ {/* 상세 내용 영역 */}
+
+
{items[activeIdx].title}
+
+
+ {items[activeIdx].content.map((line, idx) => (
+ - {line}
+ ))}
+
+
+ {/* ✅ 상세 영역 하단 좌우 네비게이션 */}
+
+
+
+
+
+
+
+)}
+
+ );
+}
diff --git a/client/src/components/Community/Community.js b/client/src/components/Community/Community.js
index 2c42ff8..b934f27 100644
--- a/client/src/components/Community/Community.js
+++ b/client/src/components/Community/Community.js
@@ -1,15 +1,17 @@
import React, { useState, useEffect } from "react";
-import { useSearchParams, useNavigate } from 'react-router-dom';
-import styles from './Community.module.css';
+import { useSearchParams, useNavigate } from "react-router-dom";
+import styles from "./Community.module.css";
+
+import CardNews from "./CardNews";
function Community() {
const [searchParams] = useSearchParams();
const navigate = useNavigate();
- const defaultTab = searchParams.get('tab') || 'notice';
+ const defaultTab = searchParams.get("tab") || "notice";
const [category, setCategory] = useState(defaultTab);
useEffect(() => {
- const newTab = searchParams.get('tab');
+ const newTab = searchParams.get("tab");
if (newTab && newTab !== category) {
setCategory(newTab);
}
@@ -32,26 +34,14 @@ function Community() {
{ title: "웹사이트 디자인 개선 제안", date: "2025.03.27" },
{ title: "새로운 서비스 아이디어", date: "2025.03.22" },
];
- const cardNewsPosts = [
- { title: "건강한 마음 가꾸기", date: "2025.04.15" },
- { title: "스트레스 관리 팁", date: "2025.04.10" },
- { title: "일상 속 소소한 행복", date: "2025.04.05" },
- ];
const tabs = [
{ key: "notice", label: "공지사항", description: "공지사항 소개글" },
{ key: "board", label: "커뮤니티", description: "커뮤니티 소개글" },
{ key: "suggestion", label: "건의사항", description: "건의사항 소개글" },
- { key: "cardNews", label: "카드뉴스", description: "카드뉴스 소개글" },
+ { key: "cardNews", label: "카드뉴스", description: "심리 건강에 도움이 되는 카드뉴스를 확인하세요" },
];
- const currentPosts = {
- notice: noticePosts,
- board: boardPosts,
- suggestion: suggestionPosts,
- cardNews: cardNewsPosts,
- }[category];
-
const currentDescription = {
notice: "공지사항을 확인하세요",
board: "고객님의 문의사항을 해결해 드립니다",
@@ -66,10 +56,17 @@ function Community() {
cardNews: { category: "카드뉴스", since: "2025" },
}[category];
+ const listPosts = {
+ notice: noticePosts,
+ board: boardPosts,
+ suggestion: suggestionPosts,
+ }[category];
+
return (
-
+ {}
+
-
+
+
+ {}
+
-
{tabs.find(t => t.key === category)?.label}
+
{tabs.find((t) => t.key === category)?.label}
{currentDescription}
Category: {currentCategoryInfo.category}
Since: {currentCategoryInfo.since}
-
-
- {category === "cardNews" ? (
- <>
-
-
- >
- ) : (
- <>
+
+ {}
+ {category === "cardNews" ? (
+
+ ) : (
+ <>
+ {}
+
+
{category === "board" && (
<>
@@ -110,19 +109,20 @@ function Community() {
>
)}
- >
- )}
-
-
- {currentPosts.map((post, idx) => (
- -
- {post.title}
- {post.date}
-
- ))}
-
+
+
+
+ {listPosts?.map((post, idx) => (
+ -
+ {post.title}
+ {post.date}
+
+ ))}
+
+ >
+ )}
-
+
);
diff --git a/client/src/components/Community/Community.module.css b/client/src/components/Community/Community.module.css
index 44083c5..6ee1358 100644
--- a/client/src/components/Community/Community.module.css
+++ b/client/src/components/Community/Community.module.css
@@ -18,8 +18,6 @@
min-width: 220px;
}
-
-
.subtitle {
font-size: 14px;
color: #666;
@@ -76,13 +74,8 @@
min-width: 0;
}
-.content {
- display: none;
-}
-
-.contentActive {
- display: block;
-}
+.content { display: none; }
+.contentActive { display: block; }
.content h2 {
margin-bottom: 10px;
@@ -120,13 +113,9 @@
transition: all 0.3s ease;
}
-.actionBtn:hover {
- background-color: #238a7d;
-}
+.actionBtn:hover { background-color: #238a7d; }
-.postList {
- list-style: none;
-}
+.postList { list-style: none; }
.postItem {
display: flex;
@@ -134,53 +123,125 @@
padding: 15px 0;
border-bottom: 1px solid #eee;
}
+.postItem:last-child { border-bottom: none; }
-.postItem:last-child {
- border-bottom: none;
-}
-
-.postTitle {
- font-size: 16px;
- color: #333;
-}
-
-.postDate {
- font-size: 14px;
- color: #999;
-}
+.postTitle { font-size: 16px; color: #333; }
+.postDate { font-size: 14px; color: #999; }
@media (max-width: 1024px) {
- .container {
- max-width: 100%;
- padding: 32px 8px;
- }
- .mainContent {
- gap: 16px;
- }
- .sidebar {
- flex: 0 0 180px;
- min-width: 120px;
- }
- .contentContainer {
- padding: 20px 10px;
- }
+ .container { max-width: 100%; padding: 32px 8px; }
+ .mainContent { gap: 16px; }
+ .sidebar { flex: 0 0 180px; min-width: 120px; }
+ .contentContainer { padding: 20px 10px; }
}
@media (max-width: 768px) {
- .mainContent {
- flex-direction: column;
- gap: 0;
- }
- .sidebar {
- flex: auto;
- min-width: 0;
- margin-bottom: 20px;
- }
- .contentContainer {
- padding: 16px 4px;
- }
- .postItem {
- flex-direction: column;
- gap: 5px;
- }
+ .mainContent { flex-direction: column; gap: 0; }
+ .sidebar { flex: auto; min-width: 0; margin-bottom: 20px; }
+ .contentContainer { padding: 16px 4px; }
+ .postItem { flex-direction: column; gap: 5px; }
+}
+
+/* ========== CardNews ========== */
+.wrapper { display: grid; gap: 16px; }
+
+.header h2 { margin: 0; }
+.header p { margin: 4px 0 0; color: #666; }
+
+.grid {
+ display: grid;
+ gap: 16px;
+ grid-template-columns: repeat(12, 1fr);
+}
+
+.card {
+ grid-column: span 4;
+ background: #fff;
+ border-radius: 16px;
+ overflow: hidden;
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
+ transition: transform .15s ease, box-shadow .15s ease;
+}
+.card:hover { transform: translateY(-3px); box-shadow: 0 10px 22px rgba(0,0,0,.12); }
+
+.thumbWrap { aspect-ratio: 16 / 9; background: #f2f2f2; }
+.thumb { width: 100%; height: 100%; object-fit: cover; display: block; }
+
+.body { padding: 12px 14px 16px; }
+.metaRow { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
+.title { margin: 0; font-size: 18px; font-weight: 700; color: #333; }
+.date { font-size: 12px; color: #777; }
+.desc { margin: 8px 0 0; color: #333; line-height: 1.5; }
+
+@media (max-width: 1024px) { .card { grid-column: span 6; } }
+@media (max-width: 640px) {
+ .grid { gap: 12px; }
+ .card { grid-column: span 12; }
+ .title { font-size: 16px; }
+ .desc { font-size: 14px; }
+}
+
+/* ===== CardNews Modal ===== */
+.modalBackdrop{
+ position: fixed; inset: 0;
+ background: rgba(0,0,0,.45);
+ display: grid; place-items: center;
+ z-index: 1000;
+}
+.modal{
+ width: min(960px, 92vw);
+ max-height: 92vh;
+ background: #fff;
+ border-radius: 18px;
+ overflow: hidden;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 0;
+ position: relative;
+ box-shadow: 0 10px 28px rgba(0,0,0,.2);
+}
+@media (max-width: 768px){ .modal{ grid-template-columns: 1fr; } }
+
+.modalImg{
+ width: 100%; height: 100%;
+ object-fit: cover; background: #eee;
+}
+
+/* 상세 영역을 세로 레이아웃로 만들어 footer가 아래에 고정되도록 */
+.modalBody{
+ padding: 18px;
+ overflow: auto;
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+}
+.modalTitle{ margin: 0 0 4px; font-size: 22px; }
+.modalDate{ color:#777; font-size: 13px; }
+.modalList{ margin: 12px 0 0; padding-left: 18px; line-height: 1.55; }
+
+/* 상세 영역 하단 좌우 네비게이션 */
+.modalFooter{
+ margin-top: auto;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 12px;
+ padding-top: 16px;
+}
+.navBtn{
+ padding: 8px 14px;
+ border: none;
+ border-radius: 10px;
+ background: #222;
+ color: #fff;
+ font-weight: 600;
+ cursor: pointer;
+ box-shadow: 0 4px 10px rgba(0,0,0,.15);
+}
+.navBtn:active { transform: translateY(1px); }
+
+.closeBtn{
+ position:absolute; right:10px; top:10px; width:36px; height:36px;
+ border:none; border-radius:50%; background:rgba(0,0,0,.6); color:#fff;
+ font-size:22px; cursor:pointer;
}