From b239bc29f4b1964dc4c62dd5c9af89f59eef1809 Mon Sep 17 00:00:00 2001 From: SeoHyeon Date: Tue, 2 Jun 2026 00:23:26 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20SSE=20=ED=98=B8=EC=B6=9C=EB=B6=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=9D=B4=EC=A0=84=EC=97=90=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=ED=95=9C=20=EB=A0=88=ED=8F=AC=EB=A5=BC=20?= =?UTF-8?q?=EC=9E=AC=EB=93=B1=EB=A1=9D=20=ED=96=88=EC=9D=84=20=EB=95=8C=20?= =?UTF-8?q?unique=20=EC=A0=9C=EC=95=BD=20=EC=9C=84=EB=B0=98=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/GithubRepositoryService.java | 28 +++++++++++++------ .../github/domain/GithubRepository.java | 7 +++++ .../domain/GithubRepositoryRepository.java | 2 ++ frontend/src/features/analysis/model/types.ts | 2 +- .../model/useWorkspaceAnalysisStream.ts | 3 +- 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/backend/src/main/java/com/stackup/stackup/github/application/GithubRepositoryService.java b/backend/src/main/java/com/stackup/stackup/github/application/GithubRepositoryService.java index 39ba37e3..6312a1b9 100644 --- a/backend/src/main/java/com/stackup/stackup/github/application/GithubRepositoryService.java +++ b/backend/src/main/java/com/stackup/stackup/github/application/GithubRepositoryService.java @@ -42,7 +42,11 @@ public GithubRepositoryResult register(Long userId, RegisterRepositoryCommand co User user = userRepository.findByIdAndDeletedFalse(userId) .orElseThrow(() -> new DomainException(ApiErrorCode.USER_NOT_FOUND)); - if (repositoryRepository.findByUser_IdAndGithubRepoIdAndDeletedFalse(userId, command.githubRepoId()).isPresent()) { + // soft delete 된 행이 있으면 새 INSERT 대신 그 행을 복구한다 + GithubRepository existing = repositoryRepository + .findByUser_IdAndGithubRepoId(userId, command.githubRepoId()) + .orElse(null); + if (existing != null && !existing.isDeleted()) { throw new DomainException(ApiErrorCode.REPO_ALREADY_REGISTERED); } @@ -55,14 +59,20 @@ public GithubRepositoryResult register(Long userId, RegisterRepositoryCommand co throw new DomainException(ApiErrorCode.VALIDATION_ERROR); } - GithubRepository repo = repositoryRepository.save(GithubRepository.create( - user, - meta.id(), - meta.name(), - meta.fullName(), - meta.htmlUrl(), - meta.defaultBranch() - )); + GithubRepository repo; + if (existing != null) { + existing.reactivate(meta.name(), meta.fullName(), meta.htmlUrl(), meta.defaultBranch()); + repo = existing; + } else { + repo = repositoryRepository.save(GithubRepository.create( + user, + meta.id(), + meta.name(), + meta.fullName(), + meta.htmlUrl(), + meta.defaultBranch() + )); + } events.publishEvent(new RepositoryRegisteredEvent(userId, repo.getId())); return GithubRepositoryResult.of(repo); diff --git a/backend/src/main/java/com/stackup/stackup/github/domain/GithubRepository.java b/backend/src/main/java/com/stackup/stackup/github/domain/GithubRepository.java index 09f967b6..eee1bf87 100644 --- a/backend/src/main/java/com/stackup/stackup/github/domain/GithubRepository.java +++ b/backend/src/main/java/com/stackup/stackup/github/domain/GithubRepository.java @@ -117,4 +117,11 @@ public void markFailed() { public void markDeleted() { this.deleted = true; } + + // 이전에 soft delete 된 레포를 다시 등록할 때 같은 행을 복구한다. + public void reactivate(String repoName, String repoFullName, String repoUrl, String defaultBranch) { + this.deleted = false; + this.status = RepositoryStatus.PENDING; + updateMetadata(repoName, repoFullName, repoUrl, defaultBranch); + } } diff --git a/backend/src/main/java/com/stackup/stackup/github/domain/GithubRepositoryRepository.java b/backend/src/main/java/com/stackup/stackup/github/domain/GithubRepositoryRepository.java index b7c2514a..560a1c4a 100644 --- a/backend/src/main/java/com/stackup/stackup/github/domain/GithubRepositoryRepository.java +++ b/backend/src/main/java/com/stackup/stackup/github/domain/GithubRepositoryRepository.java @@ -13,5 +13,7 @@ public interface GithubRepositoryRepository extends JpaRepository findByUser_IdAndGithubRepoIdAndDeletedFalse(Long userId, Long githubRepoId); + Optional findByUser_IdAndGithubRepoId(Long userId, Long githubRepoId); + List findByUser_IdAndGithubRepoIdInAndDeletedFalse(Long userId, Set githubRepoIds); } diff --git a/frontend/src/features/analysis/model/types.ts b/frontend/src/features/analysis/model/types.ts index 71b7c1d3..d5bad745 100644 --- a/frontend/src/features/analysis/model/types.ts +++ b/frontend/src/features/analysis/model/types.ts @@ -20,7 +20,7 @@ export type AnalyzedDocument = { updatedAt: string } -// /api/stream/me 의 DOC_STATE / REPO_STATE 이벤트 data 의 payload 필드. +// /realtime/stream/me 의 DOC_STATE / REPO_STATE 이벤트 data 의 payload 필드. export type AnalysisEventPayload = { targetType: AnalysisSourceType targetId: number diff --git a/frontend/src/pages/Workspace/model/useWorkspaceAnalysisStream.ts b/frontend/src/pages/Workspace/model/useWorkspaceAnalysisStream.ts index 53a43e4c..77d65b1e 100644 --- a/frontend/src/pages/Workspace/model/useWorkspaceAnalysisStream.ts +++ b/frontend/src/pages/Workspace/model/useWorkspaceAnalysisStream.ts @@ -6,7 +6,6 @@ import { repoKeys } from '@/features/repo' import { documentKeys } from '@/features/analysis' import { useEventStream } from '@/shared/hooks' -// /api/stream/me 를 구독해 분석 상태(DOC_STATE/REPO_STATE) 가 오면 // 이력서·레포·문서 쿼리를 무효화 → 화면이 자동으로 최신 상태로 갱신된다. export function useWorkspaceAnalysisStream() { const queryClient = useQueryClient() @@ -20,7 +19,7 @@ export function useWorkspaceAnalysisStream() { }, [queryClient]) useEventStream({ - path: '/api/stream/me', + path: '/realtime/stream/me', getToken, handlers: { DOC_STATE: invalidateAll,