Skip to content

Feat/#29 조직 삭제 API 추가#30

Merged
ojy0903 merged 13 commits intodevelopfrom
feat/#29
Feb 9, 2026
Merged

Feat/#29 조직 삭제 API 추가#30
ojy0903 merged 13 commits intodevelopfrom
feat/#29

Conversation

@ojy0903
Copy link
Collaborator

@ojy0903 ojy0903 commented Feb 5, 2026

📌 관련 이슈

🚀 개요

이번 PR에서 변경된 핵심 내용을 요약해주세요.

조직 삭제 API (Soft & Hard) 추가, Soft Delete 복구 API (PATCH) 추가

📄 작업 내용

구체적인 작업 내용을 설명해주세요.

  • 조직 삭제 API 추가 (하나의 API 에서 내부적으로 Soft / Hard Delete 구분)
  • 삭제 로직 내부에 요청한 회원이 ADMIN 인지 확인하는 로직 포함
  • Soft Delete 복구 API 추가

📸 스크린샷 / 테스트 결과 (선택)

결과물 확인을 위한 사진이나 테스트 로그를 첨부해주세요.

  1. DB 에서 org_id = 4 인 조직 Soft & Hard Delete 진행

1-1. 해당 조직의 생성자인 id = 2 인 사용자로 로그인, 토큰 획득
image

1-2 . 해당 토큰으로 조직 Soft delete 요청, DB 정상 반영(status => DELETED)
image

image

1-3. Soft Delete 조직 복구, DB 정상 반영(status => ACTIVE)
image

image

1-4. org_id = 4 조직에 대해 Hard Delete 요청, DB 정상 반영
image

image

===예외 처리===

  1. id = 1 인 회원이 id = 2 인 회원이 만든 조직에 삭제 요청 보낼 시 오류 처리 (403 Forbidden)
image image
  1. 잘못된 org_id 로 접근 시 오류 (404 Not Found)
image
  1. 이미 상태가 ACTIVE 인 조직에 대해 Soft Delete 복구 API 요청 시 오류 (409 Conflict)
image

✅ 체크리스트

  • [✅] 브랜치 전략(GitHub Flow)을 준수했나요?
  • [✅] 메서드 단위로 코드가 잘 쪼개져 있나요?
  • [✅] 테스트 통과 확인
  • [✅] 서버 실행 확인
  • [✅] API 동작 확인

🔍 리뷰 포인트 (Review Points)

리뷰어가 중점적으로 확인했으면 하는 부분을 적어주세요. (P1~P4 적용 가이드)

  • 피그마에서는 조직 삭제를 기본적으로 Hard Delete 로 다루는것으로 보이긴 하는데, 저희 Organization 에서 status 필드가 있어서 Soft Delete 와 Hard Delete 를 @RequestParam 으로 구분해서 접근하도록 해봤는데 어떤가요?
  • 이전에 조직 정보 수정 API 에서 "조직 생성자가 아닌 회원이 수정 API 접근 시 오류" 처리하던 코드를 수정 뿐만이 아닌 삭제에서도 사용할 수 있도록 수정해봤는데 괜찮을까요?

💬 리뷰어 가이드 (P-Rules)
P1: 필수 반영 (Critical) - 버그 가능성, 컨벤션 위반. 해결 전 머지 불가.
P2: 적극 권장 (Recommended) - 더 나은 대안 제시. 가급적 반영 권장.
P3: 제안 (Suggestion) - 아이디어 공유. 반영 여부는 드라이버 자율.
P4: 단순 확인/칭찬 (Nit) - 사소한 오타, 칭찬 등 피드백.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 조직 소프트 삭제(비활성화), 복구 기능 추가
    • 삭제 시 선택 가능한 완전 삭제(하드 삭제) 옵션 추가
    • 삭제/복구 결과를 반환하는 응답 페이로드 추가 (복구 시 식별자 및 메시지 포함)
  • 버그 수정

    • 조직 권한 검사 메시지 및 권한 검증 동작 개선
    • 이미 활성화된 조직에 대한 충돌 처리 추가
  • 문서

    • API 문서에 복구 및 삭제(하드/소프트) 엔드포인트와 응답 내용 반영

@ojy0903 ojy0903 self-assigned this Feb 5, 2026
@ojy0903 ojy0903 added the ✨ Feature 새로운 기능 추가 label Feb 5, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 5, 2026

Walkthrough

조직 삭제 기능에 Soft/Hard 삭제와 소프트 삭제 복구(PATCH)가 추가되었습니다. OrgRequest.Delete DTO는 제거되고 OrgResponse.Delete가 추가되었으며, 서비스·컨트롤러·엔티티·리포지토리·에러코드가 관련 로직으로 확장되었습니다. (사실만 보고)

Changes

Cohort / File(s) Summary
DTOs
src/main/java/com/whereyouad/WhereYouAd/domains/organization/application/dto/request/OrgRequest.java, src/main/java/com/whereyouad/WhereYouAd/domains/organization/application/dto/response/OrgResponse.java
요청 DTO에서 Delete 레코드 제거(OrgRequest). 응답 DTO에 Delete(Long orgId, String message) 추가(복구 응답).
서비스 인터페이스/구현
src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgService.java, src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgServiceImpl.java
removeOrganizationSoft, removeOrganization(hard delete), restoreOrganization 메서드 추가. modifyOrganization의 에러코드 사용 변경(ORG_FORBIDDEN). 복구/삭제 로직과 상태 검증 추가.
엔티티
src/main/java/com/whereyouad/WhereYouAd/domains/organization/persistence/entity/Organization.java
엔티티에 softDelete()restoreDelete() 메서드 추가(상태 DELETED/ACTIVE 토글).
리포지토리 & 매퍼
src/main/java/com/whereyouad/WhereYouAd/domains/organization/persistence/repository/OrgMemberRepository.java, src/main/java/com/whereyouad/WhereYouAd/domains/organization/application/mapper/OrgConverter.java
OrgMemberRepository에 findOrgMemberByOrg(Organization) 쿼리 추가. OrgConverter에 toRestoredResponse(Organization) 추가.
컨트롤러 & 문서
src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/OrgController.java, src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/docs/OrgControllerDocs.java
PATCH /api/org/{orgId}/restore 엔드포인트 추가. DELETE /api/org/{orgId}isHard 쿼리 파라미터 추가(기본 false)로 soft/hard 분기. 응답 래핑(DataResponse) 및 문서화 추가/수정.
에러 코드
src/main/java/com/whereyouad/WhereYouAd/domains/organization/exception/code/OrgErrorCode.java
ORG_UPDATE_FORBIDDENORG_FORBIDDEN 이름/메시지 변경. ORG_ALREADY_ACTIVE(409) 신규 추가.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Controller
    participant Service
    participant Repository
    participant Entity

    rect rgba(100,200,100,0.5)
    Note over Client,Entity: Soft Delete & Restore Flow
    Client->>Controller: DELETE /api/org/{orgId}?isHard=false
    Controller->>Service: removeOrganizationSoft(userId, orgId)
    Service->>Repository: findById(orgId)
    Repository-->>Service: Organization(status=ACTIVE)
    Service->>Service: 권한 검증 (creator)
    Service->>Entity: softDelete() -> status=DELETED
    Service->>Repository: save(Organization)
    Repository-->>Service: saved
    Controller-->>Client: 200/204 (DataResponse<String>)
    Client->>Controller: PATCH /api/org/{orgId}/restore
    Controller->>Service: restoreOrganization(userId, orgId)
    Service->>Repository: findById(orgId)
    Repository-->>Service: Organization(status=DELETED)
    Service->>Service: 권한 및 상태 검증
    Service->>Entity: restoreDelete() -> status=ACTIVE
    Service->>Repository: save(Organization)
    Service-->>Controller: OrgResponse.Delete(orgId, message)
    Controller-->>Client: 200 DataResponse<OrgResponse.Delete>
    end
Loading
sequenceDiagram
    participant Client
    participant Controller
    participant Service
    participant Repository

    rect rgba(200,100,100,0.5)
    Note over Client,Repository: Hard Delete Flow
    Client->>Controller: DELETE /api/org/{orgId}?isHard=true
    Controller->>Service: removeOrganization(userId, orgId)
    Service->>Repository: findById(orgId)
    Repository-->>Service: Organization
    Service->>Service: 권한 검증
    Service->>Repository: findOrgMemberByOrg(Organization)
    Repository-->>Service: List<OrgMember>
    Service->>Repository: deleteAll(orgMembers)
    Service->>Repository: delete(Organization)
    Controller-->>Client: 200/204 (DataResponse<String>)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Feat/#19 #20: Organization·OrgMember·OrgStatus 도입 PR — 엔티티 상태와 연관 쿼리/메서드 사용이 중복됨.
  • Feat/#23 #24: 조직 CRUD 초기화 PR — 이전에 도입된 DTO·컨버터·서비스 인터페이스 변경과 직접 연관됨.
  • Feat/#26 #28: 조직 도메인 및 에러 코드 변경 PR — ORG_* 에러 코드 네이밍/메시지 충돌 가능성 있음.

Suggested reviewers

  • jinnieusLab
  • kingmingyu

시니어 관점 짧은 코멘트:

  • 잘한 점: 권한 검증과 하드 삭제 시 연관 OrgMember 선삭제 흐름이 적절합니다. isHard 기본값 false로 안전하게 설계한 것도 좋습니다.
  • 확인할 점: 하드 삭제와 OrgMember 삭제가 단일 트랜잭션으로 묶여 있는지 확인하세요(중간 실패 시 고아 레코드 위험). 복구 시 이미 ACTIVE인 경우 ORG_ALREADY_ACTIVE 처리 위치도 점검하세요.
🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive 모든 변경사항이 이슈 #29의 조직 삭제 및 복구 기능 범위 내에 있습니다. 다만 OrgErrorCode.java에서 ORG_UPDATE_FORBIDDEN을 ORG_FORBIDDEN으로 변경한 부분은 기존 기능에 영향을 미칠 수 있어 검토가 필요합니다. ORG_UPDATE_FORBIDDEN에서 ORG_FORBIDDEN으로의 변경이 기존 조직 수정 API 오류 처리에 미치는 영향을 확인하고, 필요시 해당 변경사항을 별도 PR로 분리하거나 정당성을 명확히 설명해주세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목 'Feat/#29 조직 삭제 API 추가'는 PR의 주요 변경사항인 조직 삭제 API 추가를 명확하게 설명하고 있습니다.
Description check ✅ Passed PR 설명이 제공된 템플릿 구조를 잘 따르고 있으며, 이슈 연결, 개요, 작업 내용, 테스트 결과, 체크리스트, 리뷰 포인트가 모두 포함되어 있습니다.
Linked Issues check ✅ Passed PR에서 구현한 모든 기능이 이슈 #29의 요구사항을 충족합니다: 조직 삭제 API (Soft/Hard 구분), 삭제 로직 내 생성자 확인, Soft Delete 복구 API 추가.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#29

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/docs/OrgControllerDocs.java (1)

51-54: restoreOrganization의 반환 타입이 OrgResponse.Delete인 점이 의미적으로 혼란스럽습니다.

복구(restore) 작업의 응답으로 "Delete"라는 이름의 DTO를 사용하면, API를 사용하는 클라이언트나 다른 개발자가 코드를 읽을 때 혼란을 느낄 수 있어요. 예를 들어:

// 읽는 사람 입장에서 "복구했는데 Delete 응답?"이라고 생각할 수 있음
OrgResponse.Delete response = orgService.restoreOrganization(userId, orgId);

OrgResponse.Restore 또는 OrgResponse.StatusChange처럼 의미가 명확한 이름으로 변경하는 것을 권장드립니다. 물론 현재 PR 범위에서는 동작에 문제가 없으니, 추후 리팩토링 때 고려해 주세요!

src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/OrgController.java (2)

78-86: 삭제 타입 분기 로직을 서비스 레이어로 이동하는 것을 고려해 보세요.

현재 Controller에서 isHard 값에 따라 분기 처리를 하고 있는데, 이 로직을 Service 레이어로 옮기면 Controller가 더 얇아지고 단일 책임 원칙(SRP)에 부합합니다.

// 현재 (Controller에서 분기)
if (isHard) {
    orgService.removeOrganization(userId, orgId);
} else {
    orgService.removeOrganizationSoft(userId, orgId);
}

// 권장 (Service에서 분기)
orgService.removeOrganization(userId, orgId, isHard);

또한 removeOrganizationSoft()OrgResponse.Delete를 반환하는데 현재 무시되고 있어요. Soft Delete와 Hard Delete의 응답 메시지를 다르게 하거나, 반환값을 활용하면 더 좋을 것 같습니다!

♻️ 리팩토링 제안

Option 1: 서비스 메서드 통합

-    if (isHard) {
-        orgService.removeOrganization(userId, orgId);
-    } else {
-        orgService.removeOrganizationSoft(userId, orgId);
-    }
+    String message = orgService.removeOrganization(userId, orgId, isHard);
     return ResponseEntity.ok(
-            DataResponse.from("조직이 정상적으로 삭제 처리 되었습니다.")
+            DataResponse.from(message)
     );

Option 2: 삭제 타입별 다른 메시지 반환

+    String message = isHard 
+            ? "조직이 영구적으로 삭제되었습니다."
+            : "조직이 임시 삭제되었습니다. 복구 API를 통해 복원할 수 있습니다.";
+
     if (isHard) {
         orgService.removeOrganization(userId, orgId);
     } else {
         orgService.removeOrganizationSoft(userId, orgId);
     }
     return ResponseEntity.ok(
-            DataResponse.from("조직이 정상적으로 삭제 처리 되었습니다.")
+            DataResponse.from(message)
     );

85-85: 하드코딩된 한글 문자열을 상수나 메시지 소스로 관리하면 유지보수가 쉬워집니다.

현재 "조직이 정상적으로 삭제 처리 되었습니다."가 직접 작성되어 있는데, 나중에 다국어 지원이나 메시지 변경 시 여러 곳을 수정해야 할 수 있어요. 아직 프로젝트 초기라면 크게 문제되지 않지만, 메시지를 상수로 분리하거나 MessageSource를 활용하는 것도 좋은 습관입니다.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgServiceImpl.java`:
- Around line 113-119: The current hard-delete loop uses
orgMemberRepository.deleteAll(orgMembers) which issues individual DELETEs per
OrgMember; replace that with a batch delete to improve performance by calling
orgMemberRepository.deleteAllInBatch(orgMembers) (or the repository's
deleteAllInBatch variant) before orgRepository.delete(organization), ensuring
OrgMember has no JPA lifecycle callbacks like `@PreRemove` that you rely on;
update the call at the site where orgMemberRepository.deleteAll(orgMembers) is
invoked in OrgServiceImpl.java and verify behavior.

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/docs/OrgControllerDocs.java`:
- Around line 51-55: Remove mapping annotations from the OrgControllerDocs
interface: delete the `@PatchMapping` on restoreOrganization and the
`@DeleteMapping` on the other documented method(s) so only OrgController
implementation declares request mappings; keep only documentation annotations
like `@Operation` and `@ApiResponses` in OrgControllerDocs for consistency with
createOrganization and modifyOrganization. While editing, confirm the
restoreOrganization return type: if restore should not return
OrgResponse.Delete, change the signature in OrgControllerDocs (and align
OrgController) to the correct response DTO (e.g., OrgResponse.Restore or
OrgResponse.Detail) to match intended semantics. Ensure method names
restoreOrganization, createOrganization, modifyOrganization and the
OrgResponse.* types are updated consistently between interface and
implementation.
🧹 Nitpick comments (7)
src/main/java/com/whereyouad/WhereYouAd/domains/organization/application/dto/response/OrgResponse.java (1)

24-28: Record 이름이 용도와 맞지 않아요.

Delete record가 실제로는 Soft Delete 복구 응답에 사용되고 있어서, 이름만 보면 "삭제 응답"으로 오해할 수 있어요. 예를 들어 RestoreRestoreResponse 같은 이름이 더 직관적일 것 같습니다.

// 현재: Delete -> 삭제 응답처럼 보임
// 권장: Restore -> 복구 응답임을 명확히 표현
♻️ 이름 변경 제안
-    //Soft Delete 복구 시 응답값
-    public record Delete (
+    //Soft Delete 복구 시 응답값
+    public record Restore (
             Long orgId,
             String message
     ) {}
src/main/java/com/whereyouad/WhereYouAd/domains/organization/persistence/repository/OrgMemberRepository.java (1)

17-19: Spring Data JPA의 쿼리 메서드 파생 기능을 활용하면 더 간단해져요.

현재 @Query 어노테이션을 사용하고 있는데, Spring Data JPA는 메서드 이름만으로 쿼리를 자동 생성해줍니다. 더 간결하고 유지보수하기 쉬워요.

♻️ 파생 쿼리 메서드 사용 제안
     //특정 Organization 에 속한 OrgMember 모두 추출하는 메서드
-    `@Query`("select om from OrgMember om where om.organization = :organization")
-    List<OrgMember> findOrgMemberByOrg(`@Param`(value = "organization") Organization organization);
+    List<OrgMember> findByOrganization(Organization organization);

이렇게 하면 Spring Data JPA가 organization 필드를 기준으로 자동으로 쿼리를 생성해줍니다. findOrgMemberByUser와 네이밍 패턴도 일관되게 findByOrganization으로 맞출 수 있어요.

src/main/java/com/whereyouad/WhereYouAd/domains/organization/application/mapper/OrgConverter.java (1)

23-26: 메서드 구현은 좋은데, 메시지 관리 방식을 고려해보세요.

toRestoredResponse 메서드명은 명확하고 좋아요! 다만 두 가지 개선 포인트가 있습니다:

  1. 반환 타입 OrgResponse.Delete: 앞서 언급했듯이 OrgResponse.Restore로 이름을 바꾸면 이 메서드와도 더 잘 어울립니다.

  2. 하드코딩된 메시지: 나중에 다국어 지원이나 메시지 일괄 관리가 필요할 때를 대비해서, 상수나 MessageSource로 분리하는 것도 좋아요.

// 예시: 상수로 분리
private static final String RESTORE_SUCCESS_MESSAGE = "해당 조직이 활성화 되었습니다";

현재 규모에서는 큰 문제가 아니니 참고만 해주세요!

src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgServiceImpl.java (2)

68-75: 권한 검증 로직이 반복되고 있어요.

findById + 존재 여부 확인 + ownerUserId 검증 패턴이 4개 메서드에서 동일하게 반복됩니다. Private 헬퍼 메서드로 추출하면 코드 중복을 줄이고 유지보수성을 높일 수 있어요.

♻️ 공통 로직 추출 제안
/**
 * 조직을 조회하고, 요청자가 조직 생성자인지 검증합니다.
 * `@throws` OrgHandler ORG_NOT_FOUND - 조직이 존재하지 않을 때
 * `@throws` OrgHandler ORG_FORBIDDEN - 요청자가 조직 생성자가 아닐 때
 */
private Organization findOrganizationAndValidateOwner(Long userId, Long orgId) {
    Organization organization = orgRepository.findById(orgId)
            .orElseThrow(() -> new OrgHandler(OrgErrorCode.ORG_NOT_FOUND));
    
    if (!organization.getOwnerUserId().equals(userId)) {
        throw new OrgHandler(OrgErrorCode.ORG_FORBIDDEN);
    }
    
    return organization;
}

사용 예시:

public OrgResponse.Update modifyOrganization(Long userId, Long orgId, OrgRequest.Update request) {
    Organization organization = findOrganizationAndValidateOwner(userId, orgId);
    organization.modifyInfo(request);
    return OrgConverter.toUpdatedResponse(organization);
}

public void removeOrganizationSoft(Long userId, Long orgId) {
    Organization organization = findOrganizationAndValidateOwner(userId, orgId);
    organization.softDelete();
}

이렇게 하면 검증 로직 변경 시 한 곳만 수정하면 되고, 테스트도 더 쉬워집니다!

Also applies to: 84-91, 103-111, 122-130


122-134: Soft Delete 시 이미 삭제된 조직에 대한 검증이 없어요.

restoreOrganization에서는 이미 ACTIVE인 경우를 체크하는데, removeOrganizationSoft에서는 이미 DELETED인 경우를 체크하지 않아요.

동작에 문제가 생기진 않지만(같은 값으로 다시 설정하는 것이니까), 일관성 있는 API 동작을 위해 검증을 추가하는 것이 좋을 것 같아요. 클라이언트 입장에서 "이미 삭제된 조직입니다"라는 피드백을 받는 게 더 명확하거든요.

💡 상태 검증 추가 제안
     public void removeOrganizationSoft(Long userId, Long orgId) {
         Organization organization = orgRepository.findById(orgId)
                 .orElseThrow(() -> new OrgHandler(OrgErrorCode.ORG_NOT_FOUND));

         if (!organization.getOwnerUserId().equals(userId)) {
             throw new OrgHandler(OrgErrorCode.ORG_FORBIDDEN);
         }

+        if (organization.getStatus() == OrgStatus.DELETED) {
+            throw new OrgHandler(OrgErrorCode.ORG_ALREADY_DELETED); // 새 에러코드 필요
+        }
+
         organization.softDelete();
     }

OrgErrorCodeORG_ALREADY_DELETED 추가가 필요합니다.

src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/docs/OrgControllerDocs.java (1)

52-52: Restore API의 응답 타입 이름이 혼란스러울 수 있습니다.

restoreOrganization 메서드가 OrgResponse.Delete 타입을 반환하는데, "Delete"라는 이름이 복구(restore) 기능과 의미적으로 맞지 않습니다.

예를 들어, 나중에 코드를 읽는 개발자가 "왜 복구 API가 Delete 응답을 반환하지?"라고 혼란스러워 할 수 있어요. OrgResponse.Restore 또는 OrgResponse.StatusChange와 같이 의도가 명확한 이름을 고려해 보세요.

src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/OrgController.java (1)

78-82: 컨트롤러의 if-else 분기를 서비스 레이어로 이동하는 것을 고려해 보세요.

현재 isHard 값에 따라 컨트롤러에서 직접 분기하고 있는데, 이 로직을 서비스 레이어에서 처리하면 컨트롤러가 더 단순해지고 단일 책임 원칙(SRP)에 부합합니다.

// 현재 방식
if (isHard) {
    orgService.removeOrganization(userId, orgId);
} else {
    orgService.removeOrganizationSoft(userId, orgId);
}

// 대안: 서비스에 위임
orgService.removeOrganization(userId, orgId, isHard);

서비스 메서드 하나로 통합하면, 향후 삭제 방식이 추가되거나 정책이 변경될 때 컨트롤러 수정 없이 서비스만 변경하면 됩니다. 다만 현재 구조도 충분히 명확하므로, 이 부분은 선택적 리팩토링입니다.

Comment on lines +113 to +119
//해당 조직에 가입된 모든 회원들의 가입 정보 삭제
List<OrgMember> orgMembers = orgMemberRepository.findOrgMemberByOrg(organization);

orgMemberRepository.deleteAll(orgMembers);

//조직 실제 삭제
orgRepository.delete(organization);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Hard Delete 시 성능 개선 포인트가 있어요.

deleteAll(orgMembers)는 내부적으로 각 엔티티마다 개별 DELETE 쿼리를 실행해요. 만약 조직에 멤버가 100명이면 DELETE 쿼리가 100번 나가게 됩니다.

deleteAllInBatch()를 사용하면 단일 쿼리로 처리할 수 있어요:

-- deleteAll: DELETE FROM org_member WHERE id = 1; DELETE FROM org_member WHERE id = 2; ...
-- deleteAllInBatch: DELETE FROM org_member WHERE id IN (1, 2, 3, ...)
⚡ 성능 개선 제안
         //해당 조직에 가입된 모든 회원들의 가입 정보 삭제
         List<OrgMember> orgMembers = orgMemberRepository.findOrgMemberByOrg(organization);

-        orgMemberRepository.deleteAll(orgMembers);
+        orgMemberRepository.deleteAllInBatch(orgMembers);

         //조직 실제 삭제
         orgRepository.delete(organization);

단, deleteAllInBatch는 영속성 컨텍스트를 거치지 않으므로 @PreRemove 같은 JPA 라이프사이클 콜백이 호출되지 않아요. OrgMember에 그런 콜백이 없다면 deleteAllInBatch 사용을 권장합니다.

🤖 Prompt for AI Agents
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgServiceImpl.java`
around lines 113 - 119, The current hard-delete loop uses
orgMemberRepository.deleteAll(orgMembers) which issues individual DELETEs per
OrgMember; replace that with a batch delete to improve performance by calling
orgMemberRepository.deleteAllInBatch(orgMembers) (or the repository's
deleteAllInBatch variant) before orgRepository.delete(organization), ensuring
OrgMember has no JPA lifecycle callbacks like `@PreRemove` that you rely on;
update the call at the site where orgMemberRepository.deleteAll(orgMembers) is
invoked in OrgServiceImpl.java and verify behavior.

Copy link
Collaborator

@jinnieusLab jinnieusLab left a comment

Choose a reason for hiding this comment

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

P4: 고생하셨습니다! soft와 hard로 삭제를 나누니 확실히 백엔드 쪽에서도 덜 헷갈릴 것 같고 좋네요!
조직 생성자 외에 요청 시 나오는 에러코드로 통일한 것도 깔끔해서 좋습니다!

Copy link
Collaborator

@kingmingyu kingmingyu left a comment

Choose a reason for hiding this comment

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

P4: 고생하셨습니다! 응답 값이나 예외 처리도 꼼꼼하게 하신 것 같아요!

@ojy0903 ojy0903 merged commit e2adbae into develop Feb 9, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 조직 삭제 API - Organization CRUD 중 삭제(Delete API) 추가

3 participants