Skip to content

v20260609.0#179

Merged
ZaMan0806 merged 6 commits into
mainfrom
develop
Jun 9, 2026
Merged

v20260609.0#179
ZaMan0806 merged 6 commits into
mainfrom
develop

Conversation

@ZaMan0806

Copy link
Copy Markdown
Collaborator

개요 💡

#169 ~ #178 의 내용을 상용환경에 적용합니다.

ZaMan0806 added 6 commits June 5, 2026 23:16
서버에서 Webhook CRUD가 OpenAPI(API Key)에서 웹 로그인(JWT) 기반으로
이동(datagsm-server#353)하여, API Key로 호출하는 공개 Webhook 관리 API가
더 이상 존재하지 않으므로 관련 문서를 모두 제거합니다.

- /api/webhook 문서 7개 페이지 삭제 (개요·등록·목록·수정·삭제·이벤트 명세·서명 검증)
- 네비게이션(constants.ts)에서 Webhook 블록 제거
- /api/http 엔드포인트 표의 Webhook 행 및 안내 링크 제거
- /api 권한 범위 표의 WEBHOOK_WRITE 행 제거
Webhook CRUD가 웹 로그인(JWT) 기반으로 전환됨에 따라(datagsm-server#353),
client 앱에 로그인 사용자가 본인 Webhook을 등록·조회·수정·삭제하는 UI를 추가합니다.
인증은 기존 axios 인터셉터의 JWT 자동 부착을 그대로 재사용하며, 기존 OAuth Client
관리 기능과 동일한 FSD 구조를 따릅니다.

- shared: webhookUrl / webhookQueryKeys 추가 (/v1/webhooks)
- shared: 네비게이션에 Webhook 탭 추가 (/webhooks)
- entities/webhooks: 타입(서버 snake_case 응답), 이벤트 9종 상수, zod 폼 스키마
- views/webhooks: useGetWebhooks, WebhooksPage (10개 제한 시 추가 버튼 비활성)
- widgets/webhooks: 생성/수정/삭제 mutation 훅, 이벤트 선택 훅,
  폼 다이얼로그, 목록, secret 1회 노출 성공 다이얼로그
isInitialized ref로 다이얼로그가 열리는 시점에만 폼을 reset하도록 가드를 추가.
React Query 자동 refetch로 webhook 참조가 바뀌어도 수정 중인 입력 값이
유지됩니다.
onSubmit이 edit 모드도 처리하도록 통합하고 저장 버튼을 type="submit"으로
변경. 입력 필드에서 엔터를 눌러도 수정 확인 다이얼로그가 정상적으로 열립니다.
중복되던 onSaveClick 핸들러를 제거했습니다.
[feat] 웹에서 Webhook 관리 기능 추가 및 OpenAPI 문서 제거
@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
datagsm-front-admin Ready Ready Preview, Comment Jun 9, 2026 1:27pm
datagsm-front-client Ready Ready Preview, Comment Jun 9, 2026 1:27pm
datagsm-front-docs Ready Ready Preview, Comment Jun 9, 2026 1:27pm
datagsm-front-oauth Ready Ready Preview, Comment Jun 9, 2026 1:27pm
datagsm-front-status Ready Ready Preview, Comment Jun 9, 2026 1:27pm

Request Review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a Webhook management interface in the client application, including pages, forms, lists, dialogs, and React Query hooks, while removing the corresponding public API documentation. The review feedback highlights several key improvements: optimizing the useWebhookEventSelection hook by hoisting the watch call, safely accessing the webhook list with optional chaining to prevent runtime errors, adding suppressHydrationWarning to avoid SSR hydration mismatches on date rendering, and replacing hardcoded query keys with centralized query key helpers for better maintainability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +12 to +37
export const useWebhookEventSelection = ({ watch, setValue }: UseWebhookEventSelectionParams) => {
const fieldName = 'events';

const handleEventToggle = (event: string) => {
const currentEvents = watch(fieldName) || [];

setValue(
fieldName,
currentEvents.includes(event)
? currentEvents.filter((id) => id !== event)
: [...currentEvents, event],
{ shouldValidate: true, shouldDirty: true },
);
};

const isEventChecked = (event: string) => {
const currentEvents = watch(fieldName) || [];

return currentEvents.includes(event);
};

return {
handleEventToggle,
isEventChecked,
};
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

useWebhookEventSelection 훅 내부에서 watch('events')handleEventToggleisEventChecked가 호출될 때마다 반복적으로 실행되고 있습니다. 특히 isEventChecked는 렌더링 중에 모든 이벤트 옵션(현재 9개)에 대해 각각 호출되므로, 매번 watch를 호출하는 것은 비효율적입니다.

watch('events')를 훅의 최상위 레벨에서 한 번만 호출하여 변수에 할당하고, 이를 재사용하도록 개선하면 성능과 가독성을 모두 높일 수 있습니다.

export const useWebhookEventSelection = ({ watch, setValue }: UseWebhookEventSelectionParams) => {
  const fieldName = 'events';
  const currentEvents = watch(fieldName) || [];

  const handleEventToggle = (event: string) => {
    setValue(
      fieldName,
      currentEvents.includes(event)
        ? currentEvents.filter((id) => id !== event)
        : [...currentEvents, event],
      { shouldValidate: true, shouldDirty: true },
    );
  };

  const isEventChecked = (event: string) => {
    return currentEvents.includes(event);
  };

  return {
    handleEventToggle,
    isEventChecked,
  };
};

setIsSuccessDialogOpen(true);
};

const webhooks = webhooksData?.data.webhooks || [];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

API 응답 에러나 예기치 않은 응답 구조로 인해 webhooksData는 존재하지만 data 필드가 없을 수 있습니다. 런타임 에러를 방지하기 위해 옵셔널 체이닝을 한 단계 더 적용하여 webhooksData?.data?.webhooks 형태로 안전하게 접근하는 것을 권장합니다.

Suggested change
const webhooks = webhooksData?.data.webhooks || [];
const webhooks = webhooksData?.data?.webhooks || [];

Comment on lines +78 to +80
<TableCell className={cn('text-muted-foreground font-mono text-xs')}>
{formatDate(webhook.created_at)}
</TableCell>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Next.js의 SSR(Server-Side Rendering) 환경에서 new Date().toLocaleString()을 사용하면 서버와 클라이언트의 시간대(Timezone) 차이로 인해 Hydration Mismatch 경고가 발생할 수 있습니다. 이를 방지하기 위해 해당 TableCellsuppressHydrationWarning 속성을 추가하는 것을 권장합니다.

Suggested change
<TableCell className={cn('text-muted-foreground font-mono text-xs')}>
{formatDate(webhook.created_at)}
</TableCell>
<TableCell className={cn('text-muted-foreground font-mono text-xs')} suppressHydrationWarning>
{formatDate(webhook.created_at)}
</TableCell>

Comment on lines +70 to +75
onSuccess: (data) => {
setOpen(false);
queryClient.invalidateQueries({ queryKey: ['webhooks'] });
toast.success('웹훅이 생성되었습니다.');
onCreateSuccess?.(data.data);
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

쿼리 키를 무효화(invalidate)할 때 하드코딩된 문자열 배열 ['webhooks'] 대신, 공통으로 정의된 webhookQueryKeys.getWebhooks()를 사용하는 것이 안전하고 유지보수에 좋습니다. 이렇게 하면 추후 쿼리 키 구조가 변경되더라도 일관성을 유지할 수 있습니다.

참고: @repo/shared/api에서 webhookQueryKeys를 임포트하여 사용해야 합니다.

Suggested change
onSuccess: (data) => {
setOpen(false);
queryClient.invalidateQueries({ queryKey: ['webhooks'] });
toast.success('웹훅이 생성되었습니다.');
onCreateSuccess?.(data.data);
},
onSuccess: (data) => {
setOpen(false);
queryClient.invalidateQueries({ queryKey: webhookQueryKeys.getWebhooks() });
toast.success('웹훅이 생성되었습니다.');
onCreateSuccess?.(data.data);
},

Comment on lines +82 to +86
onSuccess: () => {
setOpen(false);
queryClient.invalidateQueries({ queryKey: ['webhooks'] });
toast.success('웹훅이 수정되었습니다.');
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

쿼리 키를 무효화할 때 하드코딩된 ['webhooks'] 대신 공통 정의된 webhookQueryKeys.getWebhooks()를 사용하여 일관성을 유지하는 것을 권장합니다.

참고: @repo/shared/api에서 webhookQueryKeys를 임포트하여 사용해야 합니다.

Suggested change
onSuccess: () => {
setOpen(false);
queryClient.invalidateQueries({ queryKey: ['webhooks'] });
toast.success('웹훅이 수정되었습니다.');
},
onSuccess: () => {
setOpen(false);
queryClient.invalidateQueries({ queryKey: webhookQueryKeys.getWebhooks() });
toast.success('웹훅이 수정되었습니다.');
},

Comment on lines +126 to +129
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['webhooks'] });
toast.success('웹훅이 삭제되었습니다.');
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

쿼리 키를 무효화할 때 하드코딩된 ['webhooks'] 대신 공통 정의된 webhookQueryKeys.getWebhooks()를 사용하여 일관성을 유지하는 것을 권장합니다.

참고: @repo/shared/api에서 webhookQueryKeys를 임포트하여 사용해야 합니다.

Suggested change
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['webhooks'] });
toast.success('웹훅이 삭제되었습니다.');
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: webhookQueryKeys.getWebhooks() });
toast.success('웹훅이 삭제되었습니다.');
},

@ZaMan0806 ZaMan0806 merged commit 1b74eb1 into main Jun 9, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants