Skip to content

Commit 58f9236

Browse files
committed
feat: 커뮤니티 게시판 카드 형태로 변경
1 parent 56baab2 commit 58f9236

13 files changed

Lines changed: 521 additions & 113 deletions

File tree

.env.local

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ API_MOCKING=disabled
1010
GATEWAY_BASE_URL=http://host.docker.internal:8090
1111

1212
# React 리소스 제한
13-
DOCKER_MEMORY_LIMIT=512m
14-
DOCKER_MEMORY_RESERVATION=256m
13+
# Next.js 개발 서버는 컴파일 시 메모리를 많이 사용하므로 최소 1g 권장
14+
DOCKER_MEMORY_LIMIT=1g
15+
DOCKER_MEMORY_RESERVATION=512m
1516
RESTART_POLICY_MAX_RETRIES=3
1617
HEALTHCHECK_INTERVAL=30s
1718
HEALTHCHECK_TIMEOUT=10s

src/app/(main)/community/error.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"use client";
2+
3+
import { useEffect } from "react";
4+
import { AlertCircle, RefreshCw, Home } from "lucide-react";
5+
import { Button } from "@/components/ui/button";
6+
import Link from "next/link";
7+
8+
interface ErrorProps {
9+
error: Error & { digest?: string };
10+
reset: () => void;
11+
}
12+
13+
/**
14+
* Community 페이지 Error Boundary
15+
* 예기치 못한 에러 발생 시 페이지가 죽지 않도록 보호
16+
*/
17+
export default function CommunityError({ error, reset }: ErrorProps) {
18+
useEffect(() => {
19+
// Next.js 서버 로그에 에러 기록
20+
console.error("[Community Error Boundary]", {
21+
message: error.message,
22+
digest: error.digest,
23+
stack: error.stack,
24+
});
25+
}, [error]);
26+
27+
return (
28+
<main className="flex flex-col items-center justify-center min-h-[50vh] px-4">
29+
<div className="bg-white rounded-2xl border border-gray-200 shadow-xl p-8 max-w-md w-full text-center">
30+
<div className="w-16 h-16 rounded-full bg-red-50 flex items-center justify-center mx-auto mb-6">
31+
<AlertCircle className="w-8 h-8 text-red-500" />
32+
</div>
33+
34+
<h2 className="text-xl font-semibold text-gray-900 mb-2">
35+
페이지 로딩 중 오류가 발생했습니다
36+
</h2>
37+
38+
<p className="text-gray-500 mb-6">
39+
커뮤니티 데이터를 받아오지 못 하였습니다.
40+
<br />
41+
잠시 후 다시 시도해주세요.
42+
</p>
43+
44+
<div className="flex flex-col sm:flex-row gap-3 justify-center">
45+
<Button
46+
onClick={reset}
47+
className="gap-2 bg-forest-500 hover:bg-forest-600 rounded-xl"
48+
>
49+
<RefreshCw className="w-4 h-4" />
50+
다시 시도
51+
</Button>
52+
53+
<Link href="/">
54+
<Button
55+
variant="outline"
56+
className="gap-2 rounded-xl border-gray-300 hover:bg-gray-50 w-full"
57+
>
58+
<Home className="w-4 h-4" />
59+
홈으로 이동
60+
</Button>
61+
</Link>
62+
</div>
63+
</div>
64+
</main>
65+
);
66+
}

src/app/api/boards/route.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { BOARDS_ENDPOINT } from "@/lib/api/constants";
22
import { createServerAxios } from "@/lib/api/server";
3+
import { GatewayConfigError } from "@/lib/api/gateway-selector";
34
import { AxiosError } from "axios";
45
import { NextRequest, NextResponse } from "next/server";
56

@@ -17,18 +18,51 @@ export async function GET(request: NextRequest) {
1718
},
1819
});
1920
} catch (error: unknown) {
21+
// 게이트웨이 설정 오류 (환경변수 누락)
22+
if (error instanceof GatewayConfigError) {
23+
console.error("[API /boards] GatewayConfigError:", error.message);
24+
return NextResponse.json(
25+
{
26+
success: false,
27+
code: "GATEWAY_CONFIG_ERROR",
28+
message: "서버 설정 오류가 발생했습니다. 관리자에게 문의하세요.",
29+
},
30+
{ status: 503 }
31+
);
32+
}
33+
2034
// AxiosError 처리(Gateway 통신 오류)
2135
if ((error as AxiosError).isAxiosError) {
2236
const axiosError = error as AxiosError;
2337
const status = axiosError.response?.status ?? 500;
2438
const payload = axiosError.response?.data ?? {
39+
success: false,
2540
message: axiosError.message,
2641
};
42+
43+
// Next.js 서버 로그에 에러 기록
44+
console.error("[API /boards] AxiosError:", {
45+
status,
46+
message: axiosError.message,
47+
url: axiosError.config?.url,
48+
responseData: axiosError.response?.data,
49+
});
50+
2751
return NextResponse.json(payload, { status });
2852
}
2953

3054
// 기타 오류
3155
const err = error instanceof Error ? error : new Error("Unknown error");
32-
return NextResponse.json({ message: err.message }, { status: 500 });
56+
57+
// Next.js 서버 로그에 에러 기록
58+
console.error("[API /boards] Error:", {
59+
message: err.message,
60+
stack: err.stack,
61+
});
62+
63+
return NextResponse.json(
64+
{ success: false, message: err.message },
65+
{ status: 500 }
66+
);
3367
}
3468
}

src/app/api/posts/route.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { POSTS_ENDPOINT } from "@/lib/api/constants";
22
import { createServerAxios } from "@/lib/api/server";
3+
import { GatewayConfigError } from "@/lib/api/gateway-selector";
34
import { AxiosError } from "axios";
45
import { NextRequest, NextResponse } from "next/server";
56

@@ -22,18 +23,51 @@ export async function GET(request: NextRequest) {
2223
});
2324
return NextResponse.json(data, { status: status });
2425
} catch (error: unknown) {
26+
// 게이트웨이 설정 오류 (환경변수 누락)
27+
if (error instanceof GatewayConfigError) {
28+
console.error("[API /posts] GatewayConfigError:", error.message);
29+
return NextResponse.json(
30+
{
31+
success: false,
32+
code: "GATEWAY_CONFIG_ERROR",
33+
message: "서버 설정 오류가 발생했습니다. 관리자에게 문의하세요.",
34+
},
35+
{ status: 503 }
36+
);
37+
}
38+
2539
// AxiosError 처리(Gateway 통신 오류)
2640
if ((error as AxiosError).isAxiosError) {
2741
const axiosError = error as AxiosError;
2842
const status = axiosError.response?.status ?? 500;
2943
const payload = axiosError.response?.data ?? {
44+
success: false,
3045
message: axiosError.message,
3146
};
47+
48+
// Next.js 서버 로그에 에러 기록
49+
console.error("[API /posts] AxiosError:", {
50+
status,
51+
message: axiosError.message,
52+
url: axiosError.config?.url,
53+
responseData: axiosError.response?.data,
54+
});
55+
3256
return NextResponse.json(payload, { status });
3357
}
3458

3559
// 기타 오류
3660
const err = error instanceof Error ? error : new Error("Unknown error");
37-
return NextResponse.json({ message: err.message }, { status: 500 });
61+
62+
// Next.js 서버 로그에 에러 기록
63+
console.error("[API /posts] Error:", {
64+
message: err.message,
65+
stack: err.stack,
66+
});
67+
68+
return NextResponse.json(
69+
{ success: false, message: err.message },
70+
{ status: 500 }
71+
);
3872
}
3973
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"use client";
2+
3+
import { AlertCircle, RefreshCw } from "lucide-react";
4+
import { Button } from "@/components/ui/button";
5+
6+
interface DataFetchErrorProps {
7+
/** 에러 메시지 (기본값: "데이터를 받아오지 못 하였습니다") */
8+
message?: string;
9+
/** 재시도 콜백 함수 */
10+
onRetry?: () => void;
11+
/** 재시도 버튼 표시 여부 */
12+
showRetry?: boolean;
13+
/** 컨테이너 className */
14+
className?: string;
15+
}
16+
17+
/**
18+
* 데이터 fetch 실패 시 표시되는 공통 에러 컴포넌트
19+
*/
20+
export default function DataFetchError({
21+
message = "데이터를 받아오지 못 하였습니다",
22+
onRetry,
23+
showRetry = true,
24+
className = "",
25+
}: DataFetchErrorProps) {
26+
return (
27+
<div
28+
className={`bg-white rounded-2xl border border-gray-200 shadow-xl p-8 ${className}`}
29+
>
30+
<div className="flex flex-col items-center justify-center gap-4 text-center">
31+
<div className="w-12 h-12 rounded-full bg-red-50 flex items-center justify-center">
32+
<AlertCircle className="w-6 h-6 text-red-500" />
33+
</div>
34+
<div>
35+
<p className="text-gray-700 text-lg font-medium">{message}</p>
36+
<p className="text-gray-400 text-sm mt-1">
37+
잠시 후 다시 시도해주세요
38+
</p>
39+
</div>
40+
{showRetry && onRetry && (
41+
<Button
42+
variant="outline"
43+
onClick={onRetry}
44+
className="mt-2 gap-2 rounded-xl border-gray-300 hover:bg-gray-50"
45+
>
46+
<RefreshCw className="w-4 h-4" />
47+
다시 시도
48+
</Button>
49+
)}
50+
</div>
51+
</div>
52+
);
53+
}

0 commit comments

Comments
 (0)