diff --git a/.vscode/settings.json b/.vscode/settings.json
index bf620b9..08fb4f0 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,6 +1,6 @@
{
"tailwindCSS.experimental.configFile": "apps/monitor-web/tailwind.config.ts",
- "cSpell.words": ["jikwon", "supabase"],
+ "cSpell.words": ["jikwon", "junyeol", "supabase"],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
diff --git a/apps/monitor-web/src/assets/icons.constants.ts b/apps/monitor-web/src/assets/icons.constants.ts
index 0bc9b95..86b80ee 100644
--- a/apps/monitor-web/src/assets/icons.constants.ts
+++ b/apps/monitor-web/src/assets/icons.constants.ts
@@ -2,6 +2,7 @@ import AlertIcon from "@/assets/icons/alert.svg?react";
import BaseLogo from "@/assets/icons/base-logo.svg?react";
import Check from "@/assets/icons/check.svg?react";
import Clear from "@/assets/icons/clear.svg?react";
+import EditPencil from "@/assets/icons/edit-pencil.svg?react";
import LoadingSpinner from "@/assets/icons/loading-spinner.svg?react";
export const ICON_MAP = {
@@ -9,5 +10,6 @@ export const ICON_MAP = {
baseLogo: BaseLogo,
check: Check,
clear: Clear,
+ editPencil: EditPencil,
loadingSpinner: LoadingSpinner,
} as const;
diff --git a/apps/monitor-web/src/assets/icons/edit-pencil.svg b/apps/monitor-web/src/assets/icons/edit-pencil.svg
new file mode 100644
index 0000000..f7ccbb6
--- /dev/null
+++ b/apps/monitor-web/src/assets/icons/edit-pencil.svg
@@ -0,0 +1,3 @@
+
diff --git a/apps/monitor-web/src/components/common/display/Badge.tsx b/apps/monitor-web/src/components/common/display/Badge.tsx
index b2a7e64..f4c6feb 100644
--- a/apps/monitor-web/src/components/common/display/Badge.tsx
+++ b/apps/monitor-web/src/components/common/display/Badge.tsx
@@ -1,9 +1,6 @@
import { cn } from "@/utils";
-import {
- API_STATUS_BADGE_STYLES,
- BADGE_BASE_STYLE,
- type ApiStatus,
-} from "./_internal/badge.constants";
+import type { ApiStatus } from "@/types";
+import { API_STATUS_BADGE_STYLES, BADGE_BASE_STYLE } from "./_internal/badge.constants";
/**
* 공통 Badge 컴포넌트입니다.
diff --git a/apps/monitor-web/src/components/common/display/_internal/badge.constants.ts b/apps/monitor-web/src/components/common/display/_internal/badge.constants.ts
index f429578..17c713a 100644
--- a/apps/monitor-web/src/components/common/display/_internal/badge.constants.ts
+++ b/apps/monitor-web/src/components/common/display/_internal/badge.constants.ts
@@ -1,17 +1,6 @@
// TODO(준열) : 디자인 시스템 정립에 따라 기본 스타일 및 API 상태에 따른 스타일 변경 예정
-/**
- * Badge 컴포넌트에서 사용하는 API 상태 타입입니다.
- *
- * @remarks
- * - `healthy`: 정상
- * - `degraded`: 지연
- * - `outage`: 장애
- *
- * @author junyeol
- */
-
-export type ApiStatus = "healthy" | "degraded" | "outage";
+import type { ApiStatus } from "@/types";
/**
* API 상태별 Badge 표시 정보입니다.
diff --git a/apps/monitor-web/src/mock/apiDetail.ts b/apps/monitor-web/src/mock/apiDetail.ts
new file mode 100644
index 0000000..20886dd
--- /dev/null
+++ b/apps/monitor-web/src/mock/apiDetail.ts
@@ -0,0 +1,71 @@
+import type {
+ ApiCheckLog,
+ ApiDetailData,
+ ApiSummaryData,
+ DetailSettingsData,
+ ImpactedFeature,
+} from "../pages/ApiDetail/_types";
+
+export const MOCK_HEADER_DATA: ApiDetailData = {
+ name: "Kakao Map API",
+ statusCode: "404",
+ status: "healthy" as const,
+ category: "map",
+ responseTime: "428ms",
+ lastChecked: "2026-04-24 15:30",
+ successRate: "99%",
+};
+
+export const MOCK_SUMMARY_DATA: ApiSummaryData = {
+ avgResponseTime: 443,
+ maxResponseTime: 1230,
+ minResponseTime: 210,
+ successRate: 99,
+ errorCount: 1,
+ lastErrorAt: "2026-04-24 13:20",
+};
+
+export const MOCK_LOGS: ApiCheckLog[] = [
+ {
+ id: "1",
+ status: "healthy",
+ time: "15:30",
+ fullDate: "2024-05-13",
+ message: "정상 작동 중",
+ statusCode: "HTTP 200",
+ latency: "428ms",
+ },
+ {
+ id: "2",
+ status: "outage",
+ time: "15:20",
+ fullDate: "2024-05-13",
+ message: "Connection Timeout",
+ statusCode: "HTTP 504",
+ latency: "5000ms",
+ },
+ {
+ id: "3",
+ status: "healthy",
+ time: "15:10",
+ fullDate: "2024-05-13",
+ message: "정상 작동 중",
+ statusCode: "HTTP 200",
+ latency: "312ms",
+ },
+];
+
+export const MOCK_FEATURES: ImpactedFeature[] = [
+ { id: "1", name: "지도 표시" },
+ { id: "2", name: "위치 선택" },
+ { id: "3", name: "게시글 작성 시 주소 검색" },
+ { id: "4", name: "내 주변 분실물 조회" },
+];
+
+export const MOCK_SETTINGS: DetailSettingsData = {
+ requestUrl: "https://dapi.kakao.com/...",
+ httpMethod: "GET",
+ checkInterval: "10분",
+ isActive: true,
+ isNotificationEnabled: true,
+};
diff --git a/apps/monitor-web/src/mock/index.ts b/apps/monitor-web/src/mock/index.ts
new file mode 100644
index 0000000..cad9ca7
--- /dev/null
+++ b/apps/monitor-web/src/mock/index.ts
@@ -0,0 +1 @@
+export * from "./apiDetail";
diff --git a/apps/monitor-web/src/pages/ApiDetail/ApiDetail.tsx b/apps/monitor-web/src/pages/ApiDetail/ApiDetail.tsx
index 3fc8840..522a416 100644
--- a/apps/monitor-web/src/pages/ApiDetail/ApiDetail.tsx
+++ b/apps/monitor-web/src/pages/ApiDetail/ApiDetail.tsx
@@ -1,12 +1,37 @@
import { useParams } from "react-router-dom";
+import { MOCK_HEADER_DATA, MOCK_SUMMARY_DATA } from "@/mock";
+import {
+ DetailCheckLogs,
+ DetailHeader,
+ DetailImpactedFeatures,
+ DetailIncidentHistory,
+ DetailResponseChart,
+ DetailSettings,
+ DetailSummaryCards,
+} from "./_components";
const ApiDetail = () => {
const { apiId } = useParams<{ apiId: string }>();
+ console.warn(apiId);
return (
-
-
API 상세 - {apiId}
-
+ <>
+
+
+
+
+
+
+
+
+ >
);
};
diff --git a/apps/monitor-web/src/pages/ApiDetail/_components/DetailCheckLogs.tsx b/apps/monitor-web/src/pages/ApiDetail/_components/DetailCheckLogs.tsx
new file mode 100644
index 0000000..bea1ebd
--- /dev/null
+++ b/apps/monitor-web/src/pages/ApiDetail/_components/DetailCheckLogs.tsx
@@ -0,0 +1,61 @@
+import { cn } from "@/utils";
+import { MOCK_LOGS } from "@/mock";
+import type { ApiStatus } from "@/types";
+
+const STATUS_CONFIG: Record = {
+ healthy: { label: "정상", color: "bg-fg-primary-normal-default" },
+ outage: { label: "장애", color: "bg-[#FF4D4F]" },
+ degraded: { label: "지연", color: "bg-[#FAAD14]" },
+} as const;
+
+const DetailCheckLogs = () => {
+ return (
+
+
+
+ 최근 체크 로그
+
+ 10분 주기
+
+
+
+
+ );
+};
+
+export default DetailCheckLogs;
diff --git a/apps/monitor-web/src/pages/ApiDetail/_components/DetailHeader.tsx b/apps/monitor-web/src/pages/ApiDetail/_components/DetailHeader.tsx
new file mode 100644
index 0000000..55de651
--- /dev/null
+++ b/apps/monitor-web/src/pages/ApiDetail/_components/DetailHeader.tsx
@@ -0,0 +1,105 @@
+import { Badge, BasicButton, Icon } from "@/components";
+import { cn } from "@/utils";
+import type { ApiStatus } from "@/types";
+import type { ApiDetailData } from "../_types";
+
+interface DetailHeaderProps {
+ apiData: ApiDetailData;
+}
+
+const DetailHeader = ({ apiData }: DetailHeaderProps) => {
+ const headerInfoList = [
+ { label: "카테고리", value: apiData.category },
+ { label: "응답", value: apiData.responseTime },
+ {
+ label: "마지막 체크",
+ value: apiData.lastChecked,
+ dateTime: apiData.lastChecked,
+ },
+ { label: "최근 24시간 성공률", value: apiData.successRate },
+ ];
+
+ return (
+
+
+
+
+ {apiData.name}
+
+
+
+
+
+ {headerInfoList.map((info) => (
+
+ ))}
+
+
+
+
+
+
+ 수정
+
+
+
+ );
+};
+
+export default DetailHeader;
+
+const STATUS_CONFIG = {
+ healthy: {
+ label: "정상",
+ color: "bg-fg-primary-normal-default",
+ textColor: "text-fg-primary-normal-default",
+ },
+ outage: {
+ label: "장애",
+ color: "bg-[#FF4D4F]",
+ textColor: "text-[#FF4D4F]",
+ },
+ degraded: {
+ label: "지연",
+ color: "bg-[#FAAD14]",
+ textColor: "text-[#FAAD14]",
+ },
+} as const;
+
+const StatusItem = ({ status }: { status: ApiStatus }) => {
+ const { label, color, textColor } = STATUS_CONFIG[status];
+
+ return (
+
+ );
+};
+
+interface InfoItemProps {
+ label: string;
+ value: string;
+ dateTime?: string;
+}
+
+const InfoItem = ({ label, value, dateTime }: InfoItemProps) => {
+ const Tag = dateTime ? "time" : "span";
+
+ return (
+
+
{label}
+
+
+ {value}
+
+
+
+ );
+};
diff --git a/apps/monitor-web/src/pages/ApiDetail/_components/DetailImpactedFeatures.tsx b/apps/monitor-web/src/pages/ApiDetail/_components/DetailImpactedFeatures.tsx
new file mode 100644
index 0000000..be0b28d
--- /dev/null
+++ b/apps/monitor-web/src/pages/ApiDetail/_components/DetailImpactedFeatures.tsx
@@ -0,0 +1,33 @@
+import { Badge } from "@/components";
+import { MOCK_FEATURES } from "@/mock";
+
+const DetailImpactedFeatures = () => {
+ return (
+
+
+
+ 영향 받는 기능
+
+
+ 이 API에 장애 시 영향을 받는 사용자 기능
+
+
+
+
+
+ {MOCK_FEATURES.map((feature) => (
+ -
+
+
+ ))}
+
+
+
+ );
+};
+
+export default DetailImpactedFeatures;
diff --git a/apps/monitor-web/src/pages/ApiDetail/_components/DetailIncidentHistory.tsx b/apps/monitor-web/src/pages/ApiDetail/_components/DetailIncidentHistory.tsx
new file mode 100644
index 0000000..1b2eec9
--- /dev/null
+++ b/apps/monitor-web/src/pages/ApiDetail/_components/DetailIncidentHistory.tsx
@@ -0,0 +1,14 @@
+const DetailIncidentHistory = () => {
+ return (
+
+ );
+};
+
+export default DetailIncidentHistory;
diff --git a/apps/monitor-web/src/pages/ApiDetail/_components/DetailResponseChart.tsx b/apps/monitor-web/src/pages/ApiDetail/_components/DetailResponseChart.tsx
new file mode 100644
index 0000000..a42eb11
--- /dev/null
+++ b/apps/monitor-web/src/pages/ApiDetail/_components/DetailResponseChart.tsx
@@ -0,0 +1,5 @@
+const DetailResponseChart = () => {
+ return ;
+};
+
+export default DetailResponseChart;
diff --git a/apps/monitor-web/src/pages/ApiDetail/_components/DetailSettings.tsx b/apps/monitor-web/src/pages/ApiDetail/_components/DetailSettings.tsx
new file mode 100644
index 0000000..64d5600
--- /dev/null
+++ b/apps/monitor-web/src/pages/ApiDetail/_components/DetailSettings.tsx
@@ -0,0 +1,77 @@
+import { ReactNode } from "react";
+import { Badge, BasicButton, Icon } from "@/components";
+import { MOCK_SETTINGS } from "@/mock";
+import { cn } from "@/utils";
+
+const DetailSettings = () => {
+ const { requestUrl, httpMethod, checkInterval, isActive, isNotificationEnabled } = MOCK_SETTINGS;
+
+ return (
+
+
+
+ API 설정 정보
+
+
+
+
+ 설정 수정
+
+
+
+
+
+
+
+ {requestUrl}
+
+
+
+
+
+
+
+
+
+ {checkInterval}
+
+
+
+
+ {isActive ? "활성" : "비활성"}
+
+
+
+
+
+ {isNotificationEnabled ? "활성" : "비활성"}
+
+
+
+
+
+ );
+};
+
+export default DetailSettings;
+
+const SettingItem = ({
+ label,
+ children,
+ className,
+}: {
+ label: string;
+ children: ReactNode;
+ className?: string;
+}) => (
+
+
{label}
+ {children}
+
+);
diff --git a/apps/monitor-web/src/pages/ApiDetail/_components/DetailSummaryCards.tsx b/apps/monitor-web/src/pages/ApiDetail/_components/DetailSummaryCards.tsx
new file mode 100644
index 0000000..62d9ab8
--- /dev/null
+++ b/apps/monitor-web/src/pages/ApiDetail/_components/DetailSummaryCards.tsx
@@ -0,0 +1,48 @@
+import { ReactNode } from "react";
+import type { ApiSummaryData } from "../_types";
+
+interface DetailSummaryCardsProps {
+ data: ApiSummaryData;
+}
+
+const DetailSummaryCards = ({ data }: DetailSummaryCardsProps) => {
+ return (
+
+
+
+
+
+ {data.avgResponseTime}
+ ms
+
+
+ 최고 {data.maxResponseTime}ms
+ 최저 {data.minResponseTime}ms
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default DetailSummaryCards;
+
+interface SummaryCardProps {
+ label: string;
+ value?: string;
+ children?: ReactNode;
+}
+
+const SummaryCard = ({ label, value, children }: SummaryCardProps) => (
+
+
{label}
+ {children || {value}}
+
+);
diff --git a/apps/monitor-web/src/pages/ApiDetail/_components/index.ts b/apps/monitor-web/src/pages/ApiDetail/_components/index.ts
index e69de29..1aafabb 100644
--- a/apps/monitor-web/src/pages/ApiDetail/_components/index.ts
+++ b/apps/monitor-web/src/pages/ApiDetail/_components/index.ts
@@ -0,0 +1,7 @@
+export { default as DetailCheckLogs } from "./DetailCheckLogs";
+export { default as DetailHeader } from "./DetailHeader";
+export { default as DetailImpactedFeatures } from "./DetailImpactedFeatures";
+export { default as DetailIncidentHistory } from "./DetailIncidentHistory";
+export { default as DetailResponseChart } from "./DetailResponseChart";
+export { default as DetailSettings } from "./DetailSettings";
+export { default as DetailSummaryCards } from "./DetailSummaryCards";
diff --git a/apps/monitor-web/src/pages/ApiDetail/_types/DetailCheckLogsType.ts b/apps/monitor-web/src/pages/ApiDetail/_types/DetailCheckLogsType.ts
new file mode 100644
index 0000000..1343200
--- /dev/null
+++ b/apps/monitor-web/src/pages/ApiDetail/_types/DetailCheckLogsType.ts
@@ -0,0 +1,11 @@
+import type { ApiStatus } from "@/types";
+
+export interface ApiCheckLog {
+ id: string;
+ status: ApiStatus;
+ time: string;
+ fullDate: string;
+ message: string;
+ statusCode: string;
+ latency: string;
+}
diff --git a/apps/monitor-web/src/pages/ApiDetail/_types/DetailHeader.ts b/apps/monitor-web/src/pages/ApiDetail/_types/DetailHeader.ts
new file mode 100644
index 0000000..dd37004
--- /dev/null
+++ b/apps/monitor-web/src/pages/ApiDetail/_types/DetailHeader.ts
@@ -0,0 +1,11 @@
+import type { ApiStatus } from "@/types";
+
+export interface ApiDetailData {
+ name: string;
+ statusCode: string;
+ status: ApiStatus;
+ category: string;
+ responseTime: string;
+ lastChecked: string;
+ successRate: string;
+}
diff --git a/apps/monitor-web/src/pages/ApiDetail/_types/DetailImpactedFeaturesType.ts b/apps/monitor-web/src/pages/ApiDetail/_types/DetailImpactedFeaturesType.ts
new file mode 100644
index 0000000..505a0c4
--- /dev/null
+++ b/apps/monitor-web/src/pages/ApiDetail/_types/DetailImpactedFeaturesType.ts
@@ -0,0 +1,4 @@
+export interface ImpactedFeature {
+ id: string;
+ name: string;
+}
diff --git a/apps/monitor-web/src/pages/ApiDetail/_types/DetailIncidentHistoryType.ts b/apps/monitor-web/src/pages/ApiDetail/_types/DetailIncidentHistoryType.ts
new file mode 100644
index 0000000..17364a2
--- /dev/null
+++ b/apps/monitor-web/src/pages/ApiDetail/_types/DetailIncidentHistoryType.ts
@@ -0,0 +1,9 @@
+export type IncidentStatus = "Recovered" | "Ongoing" | "Warning";
+
+export interface IncidentHistoryItem {
+ id: string;
+ timestamp: string;
+ duration: string;
+ status: IncidentStatus;
+ message: string;
+}
diff --git a/apps/monitor-web/src/pages/ApiDetail/_types/DetailSettingsType.ts b/apps/monitor-web/src/pages/ApiDetail/_types/DetailSettingsType.ts
new file mode 100644
index 0000000..2a15a1a
--- /dev/null
+++ b/apps/monitor-web/src/pages/ApiDetail/_types/DetailSettingsType.ts
@@ -0,0 +1,7 @@
+export interface DetailSettingsData {
+ requestUrl: string;
+ httpMethod: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
+ checkInterval: string;
+ isActive: boolean;
+ isNotificationEnabled: boolean;
+}
diff --git a/apps/monitor-web/src/pages/ApiDetail/_types/DetailSummaryCardsType.ts b/apps/monitor-web/src/pages/ApiDetail/_types/DetailSummaryCardsType.ts
new file mode 100644
index 0000000..b3b6134
--- /dev/null
+++ b/apps/monitor-web/src/pages/ApiDetail/_types/DetailSummaryCardsType.ts
@@ -0,0 +1,8 @@
+export interface ApiSummaryData {
+ avgResponseTime: number;
+ maxResponseTime: number;
+ minResponseTime: number;
+ successRate: number;
+ errorCount: number;
+ lastErrorAt: string;
+}
diff --git a/apps/monitor-web/src/pages/ApiDetail/_types/index.ts b/apps/monitor-web/src/pages/ApiDetail/_types/index.ts
index e69de29..6a1f7e8 100644
--- a/apps/monitor-web/src/pages/ApiDetail/_types/index.ts
+++ b/apps/monitor-web/src/pages/ApiDetail/_types/index.ts
@@ -0,0 +1,6 @@
+export * from "./DetailCheckLogsType";
+export * from "./DetailHeader";
+export * from "./DetailImpactedFeaturesType";
+export * from "./DetailSummaryCardsType";
+export * from "./DetailSettingsType";
+export * from "./DetailIncidentHistoryType";
diff --git a/apps/monitor-web/src/types/ApiStatusType.ts b/apps/monitor-web/src/types/ApiStatusType.ts
new file mode 100644
index 0000000..67f7f2e
--- /dev/null
+++ b/apps/monitor-web/src/types/ApiStatusType.ts
@@ -0,0 +1,12 @@
+/**
+ * Badge 컴포넌트에서 사용하는 API 상태 타입입니다.
+ *
+ * @remarks
+ * - `healthy`: 정상
+ * - `degraded`: 지연
+ * - `outage`: 장애
+ *
+ * @author junyeol
+ */
+
+export type ApiStatus = "healthy" | "degraded" | "outage";
diff --git a/apps/monitor-web/src/types/index.ts b/apps/monitor-web/src/types/index.ts
index 81dbc19..6e26b2a 100644
--- a/apps/monitor-web/src/types/index.ts
+++ b/apps/monitor-web/src/types/index.ts
@@ -1 +1,2 @@
+export type { ApiStatus } from "./ApiStatusType";
export type { ToastType, Toast } from "./ToastType";