Skip to content

Commit 249df4d

Browse files
authored
Merge pull request #78 from DMU-DebugVisual/sunwoong
로그인 필요 시 안내 및 코드방송 UI 중앙 배치
2 parents 7fb02a2 + ef6153d commit 249df4d

File tree

19 files changed

+311
-134
lines changed

19 files changed

+311
-134
lines changed

src/App.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,16 @@
3636
transform: rotate(360deg);
3737
}
3838
}
39+
/* 전체 텍스트 선택 방지 */
40+
* {
41+
-webkit-user-select: none; /* Safari */
42+
-moz-user-select: none; /* Firefox */
43+
-ms-user-select: none; /* IE10+ */
44+
user-select: none; /* 표준 */
45+
}
46+
47+
/* 단, Monaco Editor(IDE)는 선택 허용 */
48+
.monaco-editor,
49+
.monaco-editor * {
50+
user-select: text !important;
51+
}

src/App.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import MyCommunity from "./components/mypage/MyCommunity";
1919
import ScrollToTop from "./components/common/ScrollToTop";
2020
import CommunityWrite from "./components/community/CommunityWrite";
2121
import VisualizationModal from "./components/ide/VisualizationModal";
22-
import PostDetail from "./components/community/PostDetail"; // ✅ 게시글 상세 컴포넌트 추가
22+
import PostDetail from "./components/community/PostDetail"; // ✅ 게시글 상세 컴포넌트
2323
import CodecastLive from "./components/codecast/codecastlive/CodecastLive";
2424

2525
function AppContent() {
@@ -30,6 +30,7 @@ function AppContent() {
3030
const [isLoginModalOpen, setIsLoginModalOpen] = useState(false);
3131

3232
const isSignupPage = location.pathname === "/signup";
33+
const isIdePage = location.pathname.startsWith("/ide"); // ✅ IDE 페이지 판별
3334

3435
useEffect(() => {
3536
const token = localStorage.getItem('token');
@@ -75,7 +76,7 @@ function AppContent() {
7576
<Route path="/ide/:language/:filename" element={<IDE />} />
7677
<Route path="/community" element={<Community />} />
7778
<Route path="/community/write" element={<CommunityWrite />} />
78-
<Route path="/community/post/:id" element={<PostDetail />} /> {/* ✅ 상세 페이지 라우팅 추가 */}
79+
<Route path="/community/post/:id" element={<PostDetail />} /> {/* ✅ 상세 페이지 */}
7980
<Route path="/broadcast" element={<Codecast />} />
8081
<Route path="/startbroadcast" element={<StartCodecast />} />
8182
<Route path="/broadcast/live" element={<CodecastLive />} />
@@ -88,7 +89,8 @@ function AppContent() {
8889
</Route>
8990
</Routes>
9091

91-
{!isSignupPage && <Footer />}
92+
{/* ✅ 푸터는 회원가입/IDE 페이지에서는 숨김 */}
93+
{(!isSignupPage && !isIdePage) && <Footer />}
9294

9395
{isLoginModalOpen && (
9496
<Login

src/components/codecast/Codecast.css

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
33
.broadcast-container {
44
background-color: #ffffff;
55
min-height: 100vh;
6-
padding: 100px 20px;
6+
display: flex; /* 가운데 정렬용 */
7+
justify-content: center; /* 가로 가운데 */
8+
align-items: center; /* 세로 가운데 */
9+
flex-direction: column; /* 위아래 배치 */
10+
padding: 20px; /* 좌우만 여백 */
711
text-align: center;
812
}
913

14+
1015
.broadcast-header {
1116
margin-bottom: 30px;
1217
}

src/components/community/Community.css

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,18 @@ html {
136136
}
137137

138138
.community-page {
139-
display: flex;
139+
display: flex !important;
140140
gap: 24px;
141-
max-width: 1280px;
142-
margin: 100px auto 40px;
141+
width: 100% !important; /* ✅ 전체 폭 차지 */
142+
max-width: 1280px !important;
143+
margin: 100px auto 40px !important;
143144
padding: 0 20px;
144145
font-family: "Noto Sans KR", sans-serif;
145-
justify-content: center;
146+
justify-content: flex-start !important; /* ✅ 왼쪽 정렬 */
147+
align-items: flex-start !important; /* ✅ 위에서부터 배치 */
146148
}
147149

150+
148151
/* 좌측 사이드바 */
149152
.sidebar-left {
150153
flex-shrink: 0;

src/components/community/Community.jsx

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useEffect, useState } from "react";
2-
import {useNavigate} from "react-router-dom";
2+
import { useNavigate } from "react-router-dom";
33
import "./Community.css";
44

55
const API_BASE = "http://52.79.145.160:8080";
@@ -24,19 +24,18 @@ export default function Community() {
2424
setError("");
2525

2626
const token = localStorage.getItem("token");
27-
if (!token) {
28-
// 🚨 비회원 접근 차단
29-
alert("로그인이 필요합니다.");
30-
navigate("/");
31-
return;
27+
28+
// ✅ 토큰이 있으면 Authorization 헤더 추가
29+
const headers = {
30+
Accept: "application/json",
31+
};
32+
if (token) {
33+
headers.Authorization = `Bearer ${token}`;
3234
}
3335

3436
const res = await fetch(`${API_BASE}/api/posts`, {
3537
method: "GET",
36-
headers: {
37-
Accept: "application/json",
38-
Authorization: `Bearer ${token}`,
39-
},
38+
headers,
4039
signal: controller.signal,
4140
credentials: "include",
4241
});
@@ -56,7 +55,6 @@ export default function Community() {
5655
.sort((a, b) => {
5756
const diff = getTime(b.createdAt) - getTime(a.createdAt);
5857
if (diff !== 0) return diff;
59-
// createdAt이 같거나 비어있으면 id 내림차순으로 보정
6058
return (b.id ?? 0) - (a.id ?? 0);
6159
});
6260

@@ -155,12 +153,11 @@ export default function Community() {
155153
<div
156154
key={post.id}
157155
className="post-card"
158-
onClick={() => navigate(`/community/post/${post.id}`)} // ← id 사용
156+
onClick={() => navigate(`/community/post/${post.id}`)}
159157
style={{ cursor: "pointer" }}
160158
>
161159
<div className="post-meta">
162160
<div className="title-row">
163-
{/* 상태값 없으면 뱃지 숨김 */}
164161
{post.status ? (
165162
<span className={`badge ${post.status === "해결됨" ? "badge-solved" : ""}`}>
166163
{post.status}
@@ -190,7 +187,6 @@ export default function Community() {
190187
</div>
191188
)}
192189

193-
{/* 기존 페이징 UI는 유지 (서버 페이징 스펙 나오면 연결) */}
194190
<div className="pagination-wrapper">
195191
<div className="page-numbers">
196192
<button className="page-button active">1</button>

src/components/community/CommunityWrite.jsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const ALLOWED_TAGS = [
1313
"JAVA","C","CPP","JPA","JAVASCRIPT","PYTHON","OOP","BIGDATA","SPRING","TYPESCRIPT","ML"
1414
];
1515

16-
// ✅ 흔한 표기 → ENUM 매핑(한국어/소문자/동의어 흡수)
16+
// ✅ 흔한 표기 → ENUM 매핑
1717
const TAG_SYNONYM = {
1818
js: "JAVASCRIPT", javascript: "JAVASCRIPT", 자바스크립트: "JAVASCRIPT",
1919
java: "JAVA", 자바: "JAVA",
@@ -31,15 +31,15 @@ function normalizeToEnumTag(raw) {
3131
if (!raw) return null;
3232
const k = raw.replace(/^#/, "").trim();
3333
const keyLC = k.toLowerCase();
34-
if (TAG_SYNONYM[keyLC]) return TAG_SYNONYM[keyLC]; // 동의어 우선
34+
if (TAG_SYNONYM[keyLC]) return TAG_SYNONYM[keyLC];
3535
const upper = k.toUpperCase();
3636
return ALLOWED_TAGS.includes(upper) ? upper : null;
3737
}
3838

3939
// 입력 문자열 → ENUM 배열(중복 제거, 최대 10개)
4040
function parseTagsInput(input) {
4141
const list = input
42-
.split(/[#,,\s]+/) // 쉼표/공백/# 구분
42+
.split(/[#,,\s]+/)
4343
.map(normalizeToEnumTag)
4444
.filter(Boolean);
4545
return Array.from(new Set(list)).slice(0, 10);
@@ -59,11 +59,12 @@ export default function CommunityWrite() {
5959
const [content, setContent] = useState(defaultGuide);
6060
const [submitting, setSubmitting] = useState(false);
6161

62-
// 비회원 접근 차단(알림은 로그인 페이지에서 처리)
62+
// 비회원 접근 차단: 알림 + 커뮤니티 페이지로 이동
6363
useEffect(() => {
6464
const token = localStorage.getItem("token");
6565
if (!token) {
66-
navigate("/", { replace: true, state: { reason: "auth-required", from: location.pathname } });
66+
alert("로그인이 필요합니다.");
67+
navigate("/community", { replace: true, state: { from: location.pathname } });
6768
}
6869
}, [navigate, location.pathname]);
6970

@@ -77,7 +78,7 @@ export default function CommunityWrite() {
7778
const handleSubmit = async () => {
7879
const token = localStorage.getItem("token");
7980
if (!token) {
80-
navigate("/login", { replace: true, state: { reason: "auth-required", from: location.pathname } });
81+
// 🚨 여기서는 다시 알림 필요 없음 → 이미 진입 차단됨
8182
return;
8283
}
8384

@@ -88,10 +89,8 @@ export default function CommunityWrite() {
8889
return;
8990
}
9091

91-
// ✅ 태그 정규화/검증 → ENUM 배열
9292
const tagArray = parseTagsInput(tags);
9393

94-
// 사용자가 뭔가 입력했는데 결과가 0개면 허용값 아님
9594
if (tags.trim() && tagArray.length === 0) {
9695
alert(`지원하는 태그만 사용할 수 있어요.\n허용값: ${ALLOWED_TAGS.join(", ")}`);
9796
return;
@@ -114,10 +113,6 @@ export default function CommunityWrite() {
114113
body: JSON.stringify(payload),
115114
});
116115

117-
if (res.status === 401 || res.status === 403) {
118-
navigate("/login", { replace: true, state: { reason: "auth-required", from: location.pathname } });
119-
return;
120-
}
121116
if (!res.ok) {
122117
const ct = res.headers.get("content-type") || "";
123118
const msg = ct.includes("application/json") ? (await res.json()).message : await res.text();

src/components/header/Header.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,13 @@
109109
overflow: hidden;
110110
text-overflow: ellipsis;
111111
max-width: 200px;
112+
cursor: pointer; /* ✅ 마우스 올리면 포인터 */
113+
transition: color 0.2s ease, text-decoration 0.2s ease; /* ✅ hover 효과 부드럽게 */
114+
}
115+
116+
.user-nickname:hover {
117+
color: #6a1b9a; /* ✅ hover 시 보라색 */
118+
text-decoration: underline; /* ✅ hover 시 밑줄 */
112119
}
113120

114121
.user-icon {
@@ -423,4 +430,13 @@
423430

424431
.dark-mode .pagination button:hover:not(.active) {
425432
background-color: #444;
433+
}
434+
.dark-mode .user-nickname {
435+
color: #b88eff;
436+
}
437+
438+
/* ✅ 다크모드 hover 스타일 */
439+
.dark-mode .user-nickname:hover {
440+
color: #ffffff; /* hover 시 흰색 */
441+
text-decoration: underline; /* 밑줄 유지 */
426442
}

src/components/ide/IDE.css

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ body {
6666

6767
.sidebar {
6868
width: 280px;
69-
border-right: 1px solid var(--border);
69+
border: none; /* ✅ 테두리 완전히 제거 */
7070
display: flex;
7171
flex-direction: column;
7272
transition: width 0.3s ease, transform 0.3s ease;
73-
background-color: var(--element);
73+
background-color: var(--bg); /* ✅ 회색톤으로 변경 */
7474
color: var(--text);
7575
height: 100vh;
7676
z-index: 10;
@@ -92,7 +92,7 @@ body {
9292
flex-direction: column;
9393
height: 100vh;
9494
width: 100%;
95-
background-color: var(--element);
95+
background-color: var(--bg); /* ✅ 내부도 동일하게 */
9696
border-right: none; /* 🔧 중복 테두리 제거 */
9797
overflow: hidden;
9898
}
@@ -492,12 +492,13 @@ body.dark-mode .active-file-name {
492492
color: #60a5fa;
493493
}
494494

495-
/* 인증 섹션 - 정렬 수정 */
496495
.auth-section {
497496
display: flex;
498497
flex-direction: column;
499498
height: 100%;
500499
padding: 0;
500+
color: var(--text); /* ✅ 라이트/다크 모드 대응 */
501+
font-size:13px;
501502
}
502503

503504
.auth-header {

src/components/ide/IDE.jsx

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,19 +1300,12 @@ int main() {
13001300
<span className="auth-icon">🔐</span>
13011301
<span className="auth-title">계정 접속</span>
13021302
</div>
1303-
<div className="auth-content">
1304-
<p className="auth-message">아직 계정이 없으신가요?</p>
1305-
<div className="auth-buttons">
1306-
<Link to="/login" className="auth-button login-button">
1307-
<span className="button-icon">🔑</span>
1308-
로그인
1309-
</Link>
1310-
<Link to="/signup" className="auth-button signup-button">
1311-
<span className="button-icon">✏️</span>
1312-
회원가입
1313-
</Link>
1303+
<div className="auth-section">
1304+
<div className="auth-content sidebar-guest-message">
1305+
<p>🔒 로그인 후 이용 가능합니다</p>
13141306
</div>
13151307
</div>
1308+
13161309
</div>
13171310
)}
13181311
</div>

src/components/mainpage/Main.css

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.fullpage-container {
2+
height: auto; /* ✅ 전체 페이지 높이 자동 */
3+
overflow: visible; /* ✅ 윈도우 스크롤 사용 */
4+
}
5+
6+
.page-section {
7+
height: 100vh; /* 각 섹션은 한 화면 크기 */
8+
width: 100%;
9+
}
10+
11+
.page-section.last-section {
12+
height: auto; /* 마지막 섹션은 콘텐츠 크기 */
13+
min-height: 100vh;
14+
}

0 commit comments

Comments
 (0)