Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ public interface OrgService {
void removeOrganizationSoft(Long userId, Long orgId);

OrgResponse.Delete restoreOrganization(Long userId, Long orgId);

// orgId 조직에서 memberId에 해당하는 맴버 제거
void removeMemberFromOrg(Long userId, Long orgId, Long memberId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.whereyouad.WhereYouAd.domains.organization.application.dto.response.OrgResponse;
import com.whereyouad.WhereYouAd.domains.organization.application.mapper.OrgConverter;
import com.whereyouad.WhereYouAd.domains.organization.application.mapper.OrgMemberConverter;
import com.whereyouad.WhereYouAd.domains.organization.domain.constant.OrgRole;
import com.whereyouad.WhereYouAd.domains.organization.domain.constant.OrgStatus;
import com.whereyouad.WhereYouAd.domains.organization.exception.code.OrgErrorCode;
import com.whereyouad.WhereYouAd.domains.organization.exception.handler.OrgHandler;
Expand All @@ -20,37 +21,38 @@
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;

@Service
@Transactional
@RequiredArgsConstructor
public class OrgServiceImpl implements OrgService{
public class OrgServiceImpl implements OrgService {

private final OrgRepository orgRepository;
private final OrgMemberRepository orgMemberRepository;
private final UserRepository userRepository;

//조직(워크스페이스) 생성 메서드
// 조직(워크스페이스) 생성 메서드
public OrgResponse.Create createOrganization(Long userId, OrgRequest.Create request) {

//유저 정보 추출
// 유저 정보 추출
User user = userRepository.findById(userId)
.orElseThrow(() -> new UserHandler(UserErrorCode.USER_NOT_FOUND));

//만약 해당 User 가 이미 같은 name 을 가진 Organization 에 속해있으면 예외처리
//해당 User 의 OrgMember 를 모두 추출해서,
// 만약 해당 User 가 이미 같은 name 을 가진 Organization 에 속해있으면 예외처리
// 해당 User 의 OrgMember 를 모두 추출해서,
List<OrgMember> orgMemberByUser = orgMemberRepository.findOrgMemberByUser(user);
for (OrgMember orgMember : orgMemberByUser) {
//OrgMember 내부 Organization 의 name 이 생성하려는 request 의 name 과 같으면
// OrgMember 내부 Organization 의 name 이 생성하려는 request 의 name 과 같으면
if (orgMember.getOrganization().getName().equals(request.name())) {
throw new OrgHandler(OrgErrorCode.ORG_NAME_DUPLICATE); //예외처리
throw new OrgHandler(OrgErrorCode.ORG_NAME_DUPLICATE); // 예외처리
}
Comment on lines 44 to 49
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# OrgMember 엔티티 파일 위치 찾기
fd OrgMember.java --type f

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 167


🏁 Script executed:

# OrgServiceImpl 파일 위치 찾기 및 해당 메서드 확인
fd OrgServiceImpl.java --type f

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 168


🏁 Script executed:

# OrgMemberRepository 파일 위치
fd OrgMemberRepository.java --type f

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 181


🏁 Script executed:

cat -n src/main/java/com/whereyouad/WhereYouAd/domains/organization/persistence/entity/OrgMember.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 1342


🏁 Script executed:

cat -n src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgServiceImpl.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 8207


🏁 Script executed:

cat -n src/main/java/com/whereyouad/WhereYouAd/domains/organization/persistence/repository/OrgMemberRepository.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 2743


createOrganization에서 N+1 쿼리 문제가 실제로 발생합니다.

OrgMemberorganization 필드가 FetchType.LAZY로 설정되어 있고, findOrgMemberByUser(user) 메서드가 JOIN FETCH 없이 단순 쿼리로 작동하기 때문입니다.

현재 코드 흐름:

  • 1번 쿼리: findOrgMemberByUser로 OrgMember 리스트 조회
  • N번 쿼리: 루프 내 orgMember.getOrganization().getName() 호출 시 각각 Organization 조회
  • 총 1 + N개 쿼리 발생 (N = 사용자가 속한 조직 수)

해결 방법:

  1. Repository 메서드에 JOIN FETCH 추가 (권장)

    `@Query`("SELECT om FROM OrgMember om " +
           "JOIN FETCH om.organization o " +
           "WHERE om.user = :user")
    List<OrgMember> findOrgMemberByUser(`@Param`("user") User user);
  2. 이름 중복 검사 전용 쿼리 추가

    `@Query`("SELECT CASE WHEN COUNT(o) > 0 THEN true ELSE false END " +
           "FROM OrgMember om " +
           "JOIN om.organization o " +
           "WHERE om.user = :user AND o.name = :name")
    boolean existsByUserAndOrganizationName(`@Param`("user") User user, `@Param`("name") String name);

중복 검사만 필요하다면 두 번째 방식이 더 효율적입니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgServiceImpl.java`
around lines 44 - 49, The createOrganization method in OrgServiceImpl triggers
an N+1 query because orgMemberRepository.findOrgMemberByUser(user) returns
OrgMember entities whose organization is LAZY and you call
orgMember.getOrganization().getName() in a loop; fix this by updating the
repository: either change findOrgMemberByUser to fetch organizations with a JOIN
FETCH so Organization is loaded with OrgMember, or (preferred for simple
duplicate checks) add a repository method existsByUserAndOrganizationName(user,
name) that runs a single query to test name existence and call that from
createOrganization instead of iterating orgMemberRepository.findOrgMemberByUser;
update OrgServiceImpl.createOrganization to use the new repository method (or
the JOIN FETCH variant) and remove the loop-based name checks to eliminate the
N+1 queries.

}

//조직 생성
// 조직 생성
Organization organization = OrgConverter.toOrganization(userId, request);

//OrgMember 생성
// OrgMember 생성
OrgMember orgMember = OrgMemberConverter.toOrgMemberADMIN(user, organization);

orgRepository.save(organization);
Expand All @@ -60,76 +62,108 @@ public OrgResponse.Create createOrganization(Long userId, OrgRequest.Create requ
}

public OrgResponse.Read getOrganization(Long userId) {
//TODO
// TODO
return null;
}

//조직 정보 수정 메서드
// 조직 정보 수정 메서드
public OrgResponse.Update modifyOrganization(Long userId, Long orgId, OrgRequest.Update request) {
Organization organization = orgRepository.findById(orgId)
.orElseThrow(() -> new OrgHandler(OrgErrorCode.ORG_NOT_FOUND));

//만약 조직 정보 수정을 요청한 회원이 해당 조직을 생성한 회원이 아니라면,
// 만약 조직 정보 수정을 요청한 회원이 해당 조직을 생성한 회원이 아니라면,
if (!organization.getOwnerUserId().equals(userId)) {
throw new OrgHandler(OrgErrorCode.ORG_FORBIDDEN); //예외처리
throw new OrgHandler(OrgErrorCode.ORG_FORBIDDEN); // 예외처리
}

//조직 정보 수정
// 조직 정보 수정
organization.modifyInfo(request);

//변환 된 필드값과 해당 조직의 Id, updatedAt 가 포함된 DTO 로 반환
// 변환 된 필드값과 해당 조직의 Id, updatedAt 가 포함된 DTO 로 반환
return OrgConverter.toUpdatedResponse(organization);
}

public OrgResponse.Delete restoreOrganization(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); //예외처리
throw new OrgHandler(OrgErrorCode.ORG_FORBIDDEN); // 예외처리
}

//조직이 이미 활성화 상태라면,
// 조직이 이미 활성화 상태라면,
if (organization.getStatus() == OrgStatus.ACTIVE) {
throw new OrgHandler(OrgErrorCode.ORG_ALREADY_ACTIVE); //예외처리
throw new OrgHandler(OrgErrorCode.ORG_ALREADY_ACTIVE); // 예외처리
}

organization.restoreDelete(); //조직 Soft Delete 복구
organization.restoreDelete(); // 조직 Soft Delete 복구

return OrgConverter.toRestoredResponse(organization);
}

//조직 삭제 메서드 -> Hard Delete (DB 에서 완전히 제거)
// 조직 삭제 메서드 -> Hard Delete (DB 에서 완전히 제거)
public void removeOrganization(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); //예외처리
throw new OrgHandler(OrgErrorCode.ORG_FORBIDDEN); // 예외처리
}

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

orgMemberRepository.deleteAll(orgMembers);

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

//조직 삭제 메서드 -> Soft Delete (status 만 DELETED 로 변경)
// 조직 삭제 메서드 -> Soft Delete (status 만 DELETED 로 변경)
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);
}

//조직 status 만 DELETED 로 변경 후 종료
// 조직 status 만 DELETED 로 변경 후 종료
organization.softDelete();
}

public void removeMemberFromOrg(Long userId, Long orgId, Long memberId) {

// 0. 본인은 삭제 불가
if(Objects.equals(userId, memberId)){
throw new OrgHandler(OrgErrorCode.ORG_CANNOT_KICK_SELF);
}

// 1. 조직 존재 여부 확인
orgRepository.findById(orgId)
.orElseThrow(() -> new OrgHandler(OrgErrorCode.ORG_NOT_FOUND));

// 2. 요청자가 해당 조직의 ADMIN인지 확인
OrgMember requester = orgMemberRepository.findByUserIdAndOrgId(userId, orgId)
.orElseThrow(() -> new OrgHandler(OrgErrorCode.ORG_MEMBER_NOT_FOUND));

if (requester.getRole() != OrgRole.ADMIN) {
throw new OrgHandler(OrgErrorCode.ORG_MEMBER_FORBIDDEN);
}
Comment on lines +174 to +179
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

요청자가 조직에 없을 때 ORG_MEMBER_NOT_FOUND(404)를 반환하면 HTTP 의미론(semantics)과 맞지 않습니다.

현재 로직은:

  • 요청자가 조직에 없으면 → ORG_MEMBER_NOT_FOUND (404)
  • 요청자가 있지만 ADMIN이 아니면 → ORG_MEMBER_FORBIDDEN (403)

두 경우 모두 "권한 없음" 상황인데, 전자는 404, 후자는 403이 반환됩니다. 조직에 없는 외부인이 멤버 추방을 시도할 때 "해당 멤버가 조직에 존재하지 않습니다"(404)를 받으면 조직 멤버십 여부가 노출되는 정보 노출 문제도 있습니다. 두 경우 모두 ORG_MEMBER_FORBIDDEN(403)으로 통일하는 것이 더 안전합니다.

🛠️ 수정 제안
-        OrgMember requester = orgMemberRepository.findByUserIdAndOrgId(userId, orgId)
-                .orElseThrow(() -> new OrgHandler(OrgErrorCode.ORG_MEMBER_NOT_FOUND));
+        OrgMember requester = orgMemberRepository.findByUserIdAndOrgId(userId, orgId)
+                .orElseThrow(() -> new OrgHandler(OrgErrorCode.ORG_MEMBER_FORBIDDEN));

         if (requester.getRole() != OrgRole.ADMIN) {
             throw new OrgHandler(OrgErrorCode.ORG_MEMBER_FORBIDDEN);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgServiceImpl.java`
around lines 150 - 155, Change the behavior in OrgServiceImpl where you fetch
the requester via orgMemberRepository.findByUserIdAndOrgId(...) so that both
"not a member" and "not ADMIN" cases throw OrgHandler with
OrgErrorCode.ORG_MEMBER_FORBIDDEN instead of returning ORG_MEMBER_NOT_FOUND;
specifically replace the orElseThrow(...) that currently uses
ORG_MEMBER_NOT_FOUND to use ORG_MEMBER_FORBIDDEN, leaving the subsequent role
check throwing ORG_MEMBER_FORBIDDEN intact so both branches consistently return
403.


// 3. 삭제 대상 멤버가 해당 조직에 존재하는지 확인
OrgMember targetMember = orgMemberRepository.findByUserIdAndOrgId(memberId, orgId)
.orElseThrow(() -> new OrgHandler(OrgErrorCode.ORG_MEMBER_NOT_FOUND));

// 4. 대상 맴버가 ADMIN이라면 추방 불가
if (targetMember.getRole() == OrgRole.ADMIN) {
throw new OrgHandler(OrgErrorCode.ORG_CANNOT_KICK_ADMIN);
}

// 5. 중간 테이블에서 해당 멤버 삭제
orgMemberRepository.delete(targetMember);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,23 @@
@Getter
@AllArgsConstructor
public enum OrgErrorCode implements BaseErrorCode {
//400
// 400
ORG_NAME_DUPLICATE(HttpStatus.BAD_REQUEST, "ORG_400_1", "사용자가 이미 속해있는 조직의 이름입니다."),
ORG_CANNOT_KICK_SELF(HttpStatus.BAD_REQUEST, "ORG_400_2", "자기 자신을 추방할 수 없습니다."),
ORG_CANNOT_KICK_ADMIN(HttpStatus.BAD_REQUEST, "ORG_400_3", "ADMIN은 추방할 수 없습니다."),

//403
// 403
ORG_FORBIDDEN(HttpStatus.FORBIDDEN, "ORG_403_1", "해당 요청은 조직 생성자만 요청 가능합니다."),
ORG_MEMBER_FORBIDDEN(HttpStatus.FORBIDDEN, "ORG_403_2", "해당 요청은 ADMIN 권한을 가진 멤버만 요청 가능합니다."),

//404
// 404
ORG_NOT_FOUND(HttpStatus.NOT_FOUND, "ORG_404_1", "해당 id 의 조직이 존재하지 않습니다."),
ORG_MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "ORG_404_2", "해당 멤버가 조직에 존재하지 않습니다."),

//409
ORG_ALREADY_ACTIVE(HttpStatus.CONFLICT, "ORG_409_1", "해당 조직은 이미 활성화 상태 입니다.")
// 409
ORG_ALREADY_ACTIVE(HttpStatus.CONFLICT, "ORG_409_1", "해당 조직은 이미 활성화 상태 입니다."),
;

private final HttpStatus httpStatus;
private final String code;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,20 @@
import org.springframework.data.repository.query.Param;

import java.util.List;
import java.util.Optional;

public interface OrgMemberRepository extends JpaRepository<OrgMember, Long> {

//User 가 가진 OrgMember 모두 추출하는 메서드
List<OrgMember> findOrgMemberByUser(User user);

// userId 와 orgId 로 특정 OrgMember 조회
@Query("SELECT om FROM OrgMember om " +
"WHERE om.user.id = :userId " +
"AND om.organization.id = :orgId " +
"AND om.user.status = 'ACTIVE'")
Optional<OrgMember> findByUserIdAndOrgId(@Param("userId") Long userId, @Param("orgId") Long orgId);
Comment on lines +22 to +26
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

🧩 Analysis chain

🏁 Script executed:

# OrgMemberRepository 전체 내용 조회
find . -type f -name "OrgMemberRepository.java" | head -1 | xargs cat -n

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 2743


🏁 Script executed:

# User 엔티티 찾기 및 status 필드 확인
find . -type f -name "User.java" | grep -E "domains|entity" | head -3

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 156


🏁 Script executed:

# UserStatus enum 찾기
find . -type f -name "UserStatus.java" | head -1

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 159


🏁 Script executed:

# User.java에서 status 필드 및 `@Enumerated` 설정 확인
cat -n ./src/main/java/com/whereyouad/WhereYouAd/domains/user/persistence/entity/User.java | grep -A 5 -B 5 "status"

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 610


🏁 Script executed:

# UserStatus enum 전체 확인
cat -n ./src/main/java/com/whereyouad/WhereYouAd/domains/user/domain/constant/UserStatus.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 230


🏁 Script executed:

# removeMemberFromOrg 메서드 찾기
rg "removeMemberFromOrg|findByUserIdAndOrgId" -A 5 -B 2

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 5882


일관성 있게 매개변수로 UserStatus를 전달하세요.

현재 User 엔티티의 status는 @Enumerated(EnumType.STRING) 으로 설정되어 있어서, 'ACTIVE' 문자열 리터럴도 기술적으로는 정상 작동합니다. 다만 같은 파일의 findByOrganizationIdWithCursor(라인 36), countByOrganizationIdAndUserStatus(라인 50) 메서드들은 모두 @Param("status") UserStatus status 로 매개변수화하여 타입 안전하게 처리하고 있습니다.

이 메서드도 동일한 패턴을 따르면:

  • 쿼리 문자열에 하드코딩된 상태값을 제거해 유지보수성 향상
  • 향후 SUSPENDED나 DELETED 상태를 조회해야 할 필요가 생길 때 유연하게 대응
  • 코드 일관성 개선

호출부(OrgServiceImpl)에서는 UserStatus.ACTIVE를 명시적으로 전달하면 됩니다.

-    `@Query`("SELECT om FROM OrgMember om " +
-            "WHERE om.user.id = :userId " +
-            "AND om.organization.id = :orgId " +
-            "AND om.user.status = 'ACTIVE'")
-    Optional<OrgMember> findByUserIdAndOrgId(`@Param`("userId") Long userId, `@Param`("orgId") Long orgId);
+    `@Query`("SELECT om FROM OrgMember om " +
+            "WHERE om.user.id = :userId " +
+            "AND om.organization.id = :orgId " +
+            "AND om.user.status = :status")
+    Optional<OrgMember> findByUserIdAndOrgId(
+            `@Param`("userId") Long userId,
+            `@Param`("orgId") Long orgId,
+            `@Param`("status") UserStatus status);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/persistence/repository/OrgMemberRepository.java`
around lines 22 - 26, The findByUserIdAndOrgId method in OrgMemberRepository
currently hardcodes 'ACTIVE' in the JPQL; change the query to use a status
parameter (e.g. "AND om.user.status = :status"), update the method signature to
accept `@Param`("status") UserStatus status, and ensure callers (like
OrgServiceImpl) pass UserStatus.ACTIVE; this makes the method type-safe and
consistent with findByOrganizationIdWithCursor and
countByOrganizationIdAndUserStatus.


//특정 Organization 에 속한 OrgMember 모두 추출하는 메서드
@Query("select om from OrgMember om where om.organization = :organization")
List<OrgMember> findOrgMemberByOrg(@Param(value = "organization") Organization organization);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,14 @@ public ResponseEntity<DataResponse<OrgResponse.OrgMemberCountDTO>> getOrgMembers
OrgResponse.OrgMemberCountDTO response = orgQueryService.getOrgMembersCount(orgId);
return ResponseEntity.ok(DataResponse.from(response));
}

@DeleteMapping("{orgId}/members/{memberId}")
public ResponseEntity<DataResponse<String>> removeMember(
@AuthenticationPrincipal(expression = "userId") Long userId, // 관리자 ID
@PathVariable Long orgId,
@PathVariable Long memberId
) {
orgService.removeMemberFromOrg(userId, orgId, memberId);
return ResponseEntity.ok(DataResponse.from("해당 맴버가 조직에서 제외되었습니다."));
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

사용자 응답 메시지에 오타가 있습니다: "맴버" → "멤버"

"해당 맴버가 조직에서 제외되었습니다." 에서 "맴버"는 잘못된 표기입니다. PR의 다른 파일들(OrgControllerDocs.java)과 일관된 "멤버"로 수정해야 합니다.

✏️ 수정 제안
-        return ResponseEntity.ok(DataResponse.from("해당 맴버가 조직에서 제외되었습니다."));
+        return ResponseEntity.ok(DataResponse.from("해당 멤버가 조직에서 제외되었습니다."));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return ResponseEntity.ok(DataResponse.from("해당 맴버가 조직에서 제외되었습니다."));
return ResponseEntity.ok(DataResponse.from("해당 멤버가 조직에서 제외되었습니다."));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/OrgController.java`
at line 116, In OrgController (method returning
ResponseEntity.ok(DataResponse.from(...))) fix the Korean typo in the response
string by changing "해당 맴버가 조직에서 제외되었습니다." to "해당 멤버가 조직에서 제외되었습니다." so it
matches other docs (e.g., OrgControllerDocs) and maintains consistent messaging;
locate the DataResponse.from(...) call in OrgController and update the literal
accordingly.

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,19 @@ ResponseEntity<DataResponse<OrgResponse.OrgMemberSliceDTO>> getOrgMembers(
ResponseEntity<DataResponse<OrgResponse.OrgMemberCountDTO>> getOrgMembersCount(
@PathVariable Long orgId
);

@Operation(
summary = "조직 맴버 삭제 API",
description = "맴버 삭제를 요청한 유저의 권한이 ADMIN인 경우 실행이 가능합니다. memberId에 해당하는 맴버를 조직에서 제외시킵니다."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "403", description = "권한이 부족한 경우(요청을 보낸 유저의 권한이 ADMIN이 아닌 경우)"),
@ApiResponse(responseCode = "404", description = "해당 id의 데이터 존재 X")
})
Comment on lines +127 to +131
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

Swagger 문서에 400 오류 케이스가 누락되었습니다.

OrgServiceImpl.removeMemberFromOrg에서 실제로 400(BAD_REQUEST) 상태코드를 반환하는 케이스가 2가지(ORG_400_2: 자기 자신 추방 불가, ORG_400_3: ADMIN 추방 불가) 있지만, @ApiResponses에 문서화되어 있지 않습니다. Swagger UI를 사용하는 클라이언트 개발자가 이 오류를 인지할 수 없습니다.

📄 수정 제안
     `@ApiResponses`({
             `@ApiResponse`(responseCode = "200", description = "성공"),
+            `@ApiResponse`(responseCode = "400_2", description = "자기 자신을 추방하려는 경우"),
+            `@ApiResponse`(responseCode = "400_3", description = "ADMIN 멤버를 추방하려는 경우"),
             `@ApiResponse`(responseCode = "403", description = "권한이 부족한 경우(요청을 보낸 유저의 권한이 ADMIN이 아닌 경우)"),
             `@ApiResponse`(responseCode = "404", description = "해당 id의 데이터 존재 X")
     })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "403", description = "권한이 부족한 경우(요청을 보낸 유저의 권한이 ADMIN이 아닌 경우)"),
@ApiResponse(responseCode = "404", description = "해당 id의 데이터 존재 X")
})
`@ApiResponses`({
`@ApiResponse`(responseCode = "200", description = "성공"),
`@ApiResponse`(responseCode = "400", description = "요청이 잘못된 경우(자기 자신 추방 불가, ADMIN 멤버 추방 불가)"),
`@ApiResponse`(responseCode = "403", description = "권한이 부족한 경우(요청을 보낸 유저의 권한이 ADMIN이 아닌 경우)"),
`@ApiResponse`(responseCode = "404", description = "해당 id의 데이터 존재 X")
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/docs/OrgControllerDocs.java`
around lines 104 - 108, The Swagger docs for OrgController are missing 400
responses for cases thrown by OrgServiceImpl.removeMemberFromOrg; update the
`@ApiResponses` array in OrgControllerDocs (where `@ApiResponses/`@ApiResponse are
declared) to include two `@ApiResponse` entries with responseCode = "400" and
descriptions for ORG_400_2 ("cannot remove yourself") and ORG_400_3 ("cannot
remove ADMIN"), so clients see the BAD_REQUEST cases that removeMemberFromOrg
can return.

public ResponseEntity<DataResponse<String>> removeMember(
@AuthenticationPrincipal(expression = "userId") Long userId,
@PathVariable Long orgId,
@PathVariable Long memberId
);
}