Skip to content

Feat/#23#24

Merged
ojy0903 merged 14 commits intodevelopfrom
feat/#23
Feb 3, 2026
Merged

Feat/#23#24
ojy0903 merged 14 commits intodevelopfrom
feat/#23

Conversation

@ojy0903
Copy link
Collaborator

@ojy0903 ojy0903 commented Feb 3, 2026

📌 관련 이슈

🚀 개요

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

조직(워크스페이스) 생성 API 추가 및 조회, 수정, 삭제 에 대한 스켈레톤 코드 추가

📄 작업 내용

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

  • 조직 생성 API 추가 (조직 이름, 설명, 로고 이미지 URL 입력받아 생성)
  • 조직 조회, 수정, 삭제에 대한 스켈레톤 코드 추가 (TODO 로 남겨뒀습니다 -> 추후 진행)
  • 조직 관련 예외, DTO 추가

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

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

==조직 정상 생성 로직==

  1. test, test2, test3 라는 이름을 가진 3명의 유저 존재 (각각 test, test2, test3 @ example.com 으로 회원가입)
image
  1. test 유저로 로그인
image
  1. test 유저가 org1 이라는 이름의 조직(워크스페이스 생성) (이전 테스트로 인해 id 값이 3으로 올라가 있습니다.)
image
  1. DB 에 organization 과 org_member 가 정상적으로 추가된다.
image image
  1. test2 유저로 로그인하여 같은 조직이름(org1) 으로 생성해도 정상 동작한다.
image image image

===예외 처리===
DB 에 test 유저가 org1 이라는 이름의 조직을 만든 상태에서, 똑같은 이름(org1)의 조직 생성 시도 시 오류
image

AccessToken 없이 접근 시 오류
image

✅ 체크리스트

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

🔍 리뷰 포인트 (Review Points)

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

  • 조직을 생성할 때 만약 한 유저(test@example.com) 가 같은 이름의 조직(org1) 을 중복 생성하려 할 경우 이를 예외 처리하고 막는 것으로 진행했는데 이런 상황을 막지 않는 것이 나을까요?
  • 생성을 제외한 나머지 API 나 메서드는 TODO 로 남겨놓고 추후 진행할 예정입니다.

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

Summary by CodeRabbit

  • 새로운 기능
    • 조직 생성 기능이 추가되었습니다. 이름, 설명, 로고를 포함하여 새 조직을 등록할 수 있습니다.
    • 동일한 이름의 조직 등록 시도 시 유효성 검사가 수행됩니다.
    • 조직 관리를 위한 기본 API 구조가 마련되었습니다.

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

coderabbitai bot commented Feb 3, 2026

Walkthrough

조직 생성(Create) API를 구현합니다. 요청/응답 DTO, 데이터 변환 로직, 예외 처리, REST 컨트롤러, 서비스 인터페이스 및 구현을 추가하며, 사용자별 조직명 중복 검증 기능을 포함합니다. Read/Update/Delete 메서드는 스켈레톤 코드로 구성됩니다.

Changes

Cohort / File(s) Summary
DTO 레이어
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
Create 요청/응답 DTO 정의. OrgRequest.Create는 name, description, logoUrl을 포함하고 name에 @NotBlank 제약 적용. OrgResponse.Create는 orgId, createdAt 반환. Read/Update/Delete는 TODO 플레이스홀더로 구성.
예외 처리 레이어
src/main/java/com/whereyouad/WhereYouAd/domains/organization/exception/code/OrgErrorCode.java, src/main/java/com/whereyouad/WhereYouAd/domains/organization/exception/handler/OrgHandler.java
ORG_NAME_DUPLICATE 에러코드 정의 (HTTP 400). AppException을 상속한 OrgHandler 예외 클래스 추가.
비즈니스 로직 레이어
src/main/java/com/whereyouad/WhereYouAd/domains/organization/application/mapper/OrgConverter.java, src/main/java/com/whereyouad/WhereYouAd/domains/organization/application/mapper/OrgMemberConverter.java, src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgService.java, src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgServiceImpl.java
OrgConverter: OrgRequest → Organization, Organization → OrgResponse.Create 매핑. OrgMemberConverter: User+Organization → OrgMember(ADMIN role) 매핑. OrgService 인터페이스 정의. OrgServiceImpl에서 사용자 검증, 중복명 검증(쿼리 호출), Organization/OrgMember 생성 및 저장 로직 구현.
데이터 접근 레이어
src/main/java/com/whereyouad/WhereYouAd/domains/organization/persistence/repository/OrgMemberRepository.java
findOrgMemberByUser(User user) 메서드 추가. 사용자의 모든 조직 멤버십 조회.
프레젠테이션 레이어
src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/OrgController.java, src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/docs/OrgControllerDocs.java
OrgController: POST /api/org/create 엔드포인트 구현. 인증된 사용자 ID 추출, 요청 검증, 서비스 호출, DataResponse로 래핑 후 반환. OrgControllerDocs: API 문서화 인터페이스 정의 (Swagger 주석 포함). Read/Update/Delete 엔드포인트는 @Hidden으로 숨김.

Sequence Diagram

sequenceDiagram
    participant Client
    participant OrgController
    participant OrgServiceImpl
    participant OrgMemberRepository
    participant OrgRepository
    participant Database

    Client->>OrgController: POST /api/org/create<br/>(name, description, logoUrl)
    OrgController->>OrgServiceImpl: createOrganization(userId, request)
    
    OrgServiceImpl->>Database: UserRepository.findById(userId)
    Database-->>OrgServiceImpl: User object
    
    OrgServiceImpl->>OrgMemberRepository: findOrgMemberByUser(user)
    OrgMemberRepository->>Database: Query OrgMembers by user
    Database-->>OrgMemberRepository: List<OrgMember>
    
    OrgServiceImpl->>OrgServiceImpl: validateNoDuplicateOrgName<br/>(orgMembers, requestName)
    alt Duplicate Found
        OrgServiceImpl->>OrgServiceImpl: throw OrgHandler<br/>(ORG_NAME_DUPLICATE)
        OrgServiceImpl-->>OrgController: OrgHandler exception
        OrgController-->>Client: 400 Bad Request
    else No Duplicate
        OrgServiceImpl->>OrgRepository: save(organization)
        OrgRepository->>Database: INSERT Organization
        Database-->>OrgRepository: orgId
        
        OrgServiceImpl->>OrgRepository: save(orgMember)
        OrgRepository->>Database: INSERT OrgMember
        Database-->>OrgRepository: Success
        
        OrgServiceImpl->>OrgServiceImpl: toCreatedResponse(org)
        OrgServiceImpl-->>OrgController: OrgResponse.Create
        OrgController-->>Client: 200 OK + {orgId, createdAt}
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45분

주요 검토 포인트:

  • 중복 검증 로직: findOrgMemberByUser() 쿼리 결과를 메모리에서 필터링하는데, 사용자가 많은 조직에 속해있으면 N+1 문제 가능성. 추후 데이터베이스 레벨에서 직접 쿼리하는 것을 고려해볼 가치 있습니다.
  • @transactional 범위: createOrganization에서 User, OrgMember 조회 후 Organization 저장 → OrgMember 저장까지 일관성 있게 처리되고 있어 좋습니다.
  • 예외 처리: 신규 에러코드 및 핸들러 구현이 기존 패턴을 잘 따르고 있습니다.
  • 다양한 파일/레이어 변경으로 전체 흐름 검토 필요.

Possibly related PRs

  • Feat/#19 #20: 이 PR에서 추가한 OrgRequest, OrgResponse, OrgConverter, OrgServiceImpl, OrgController 등이 직접적으로 Organization, OrgMember, OrgRole, OrgStatus 타입과 OrgRepository, OrgMemberRepository 인터페이스를 활용하고 있어 코드 레벨에서 밀접하게 연관됨.

Suggested reviewers

  • kingmingyu
  • jinnieusLab

리뷰 시 특히 살펴볼 점:

1️⃣ DB 쿼리 최적화 (중요)

List<OrgMember> orgMembers = orgMemberRepository.findOrgMemberByUser(user);
// 이후 메모리에서 스트림 필터링으로 중복 검사

현재는 사용자의 모든 조직멤버십을 메모리로 끌어와서 체크하는데, 사용자가 10개 이상 조직에 속해있으면 불필요한 데이터 로드가 발생합니다. 추천: OrgRepositoryexistsByNameAndOwnerUserId(String name, Long userId) 같은 쿼리 메서드를 추가하면 한 줄로 해결됩니다. 👍

2️⃣ 입력 검증

  • OrgRequest.Create의 name에 @NotBlank 제약이 있어 좋습니다.
  • description과 logoUrl도 길이/형식 검증이 필요할 수 있습니다. (URL 형식, 문자열 길이 등)

3️⃣ 응답 일관성

  • DataResponse.created()DataResponse.from() 메서드가 일관되게 사용되고 있는지 확인 필요.
  • HTTP 상태 코드가 적절한지 (201 Created vs 200 OK) 확인.
🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning PR 제목 'Feat/#23'은 너무 모호하며 구체적인 변경 사항을 설명하지 않습니다. 이슈 번호만으로는 내용을 알 수 없습니다. 예: 'Feat: 조직 생성 API 구현' 또는 'Feat: Organization creation API 추가' 같이 구체적인 주제를 포함하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 22.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed PR 설명이 템플릿을 충실히 따르고 있으며, 개요, 작업 내용, 테스트 결과, 체크리스트, 리뷰 포인트가 모두 포함되어 있습니다.
Linked Issues check ✅ Passed PR이 이슈 #23의 요구사항을 만족합니다: 조직 생성 API 구현, CRUD 스켈레톤 코드 추가, 예외 처리 구현 모두 완료되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 조직 생성 API와 관련 인프라(DTO, Exception, Service, Controller)에 집중되어 있으며, 이슈 범위 내에 있습니다.

✏️ 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/#23

Important

Action Needed: IP Allowlist Update

If your organization protects your Git platform with IP whitelisting, please add the new CodeRabbit IP address to your allowlist:

  • 136.113.208.247/32 (new)
  • 34.170.211.100/32
  • 35.222.179.152/32

Failure to add the new IP will result in interrupted reviews.


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

@ojy0903
Copy link
Collaborator Author

ojy0903 commented Feb 3, 2026

Swagger 를 보니까 조직 조회, 수정, 삭제에 대한 API 스켈레톤 코드들도 자동 인식이 되서 나타나는것 같네요ㅠㅠ 혼동을 막기위해 Controller 쪽 스켈레톤 코드는 주석처리 하겠습니다.

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: 5

🤖 Fix all issues with AI agents
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/application/dto/request/OrgRequest.java`:
- Around line 5-9: The Create record in OrgRequest lacks validation annotations
so empty/null values pass through; add `@NotBlank` to the name component of the
OrgRequest.Create record (and import javax.validation.constraints.NotBlank) so
Spring's `@Valid` triggers MethodArgumentNotValidException consistently;
optionally mirror patterns used in SignUpRequest/LoginRequest if you want
additional constraints (e.g., `@Size` or `@NotNull`) for description/logoUrl.

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/application/mapper/OrgConverter.java`:
- Around line 10-11: The toCreatedResponse method in OrgConverter should return
the entity's persisted created timestamp instead of LocalDateTime.now(); change
the returned createdAt to use organization.getCreatedAt() (or
organization.getCreatedAt().orElse(LocalDateTime.now()) / null-check fallback if
the field can be null) when constructing OrgResponse.Create so the response
reflects the DB-stored creation time; update OrgConverter.toCreatedResponse and
ensure OrgResponse.Create still accepts that timestamp type.

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgCRUDService.java`:
- Around line 41-49: The loop in OrgCRUDService that fetches
orgMemberRepository.findOrgMemberByUser(user) and inspects
orgMember.getOrganization().getName() causes N+1 queries and has a race
condition; replace that logic by adding a repository method boolean
existsByUserAndOrganization_Name(User user, String orgName) in
OrgMemberRepository and use
orgMemberRepository.existsByUserAndOrganization_Name(user, request.name()) to
perform a single query for the duplicate check; to prevent concurrent duplicate
creations also either apply a pessimistic lock on the enclosing service method
(e.g., `@Lock`(LockModeType.PESSIMISTIC_WRITE) on the transaction) or enforce a
DB-level unique constraint on (user_id, name).
- Around line 74-87: The three unimplemented methods getOrganization(Long
userId), modifyOrganization(Long userId, Long orgId, OrgRequest.Update request),
and removeOrganization(Long userId, Long orgId) currently return null/are empty
and cause misleading 200/204 responses; update them to throw a clear
not-implemented exception by throwing new
AppException(ErrorCode.NOT_IMPLEMENTED). If ErrorCode lacks NOT_IMPLEMENTED, add
NOT_IMPLEMENTED with HTTP 501 semantics to the ErrorCode enum and ensure
GlobalExceptionHandler maps AppException(ErrorCode.NOT_IMPLEMENTED) to an HTTP
501 response so clients receive a proper 501 Not Implemented for these methods.

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/docs/OrgControllerDocs.java`:
- Around line 19-24: The `@ApiResponses` on OrgControllerDocs.createOrganization
uses a non-standard responseCode "400_1"; change it to a valid HTTP status code
(e.g., "400") and add a "401" ApiResponse to document authentication failure;
keep any fine-grained error identifiers in the response body (not in
responseCode). Update the `@ApiResponse` entries for createOrganization (and other
controllers that follow the same pattern) to use standard numeric HTTP status
strings and ensure the response schema/body contains the project-specific error
code/details instead of using "400_1" style codes.
🧹 Nitpick comments (3)
src/main/java/com/whereyouad/WhereYouAd/domains/organization/persistence/repository/OrgMemberRepository.java (1)

11-12: 중복 이름 검증은 exists 쿼리로 단일 SQL 처리하는 편이 안전합니다.

서비스에서 OrgMember 목록을 모두 조회한 뒤 조직명을 비교하면, OrgMember -> Organization이 LAZY일 때 N+1이 발생할 수 있어요. 중복 여부만 필요하다면 DB에서 바로 체크하는 메서드를 추가하는 편이 비용이 적습니다.

🛠️ 제안 변경
+    boolean existsByUserAndOrganization_Name(User user, String name);

필드명이 다르면 Organization 경로는 실제 엔티티명에 맞게 조정해주세요.

As per coding guidelines, JPA 사용 시 N+1 문제나 불필요한 쿼리가 발생하지 않는지, FetchType.LAZY가 잘 적용되었는지 체크하라.

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

21-30: 생성 응답은 201을 고려해 주세요

현재 200 OK인데, 생성 성공을 더 명확히 하려면 201 Created가 REST 관점에서 직관적입니다(필요하면 Location 헤더도 추가 가능).

🔧 제안 변경
-        return ResponseEntity.ok(
-                DataResponse.created(response)
-        );
+        return ResponseEntity.status(HttpStatus.CREATED)
+                .body(DataResponse.created(response));
+import org.springframework.http.HttpStatus;

42-47: Update DTO에 제약 추가 시 @Valid도 함께 붙여주세요

Update 필드에 Bean Validation을 붙일 예정이라면 여기에도 @Valid가 있어야 검증이 동작합니다.

🔧 제안 변경
     public ResponseEntity<Void> modifyOrganization(
             `@AuthenticationPrincipal`(expression = "userId") Long userId,
             `@PathVariable` Long orgId,
-            `@RequestBody` OrgRequest.Update request
+            `@RequestBody` `@Valid` OrgRequest.Update request
     )

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

find . -name "OrgCRUDService.java" -o -name "OrgMemberRepository.java" -o -name "OrgMember.java" | head -20

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 378


🏁 Script executed:

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

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 4292


🏁 Script executed:

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

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 656


🏁 Script executed:

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

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 1342


N+1 쿼리와 동시성 경합 문제가 확인되었습니다

현재 코드는 사용자의 모든 OrgMember를 먼저 조회한 후 루프에서 orgMember.getOrganization().getName()을 호출합니다. organization 필드가 FetchType.LAZY로 설정되어 있으므로, 사용자가 10개 조직에 속해 있으면 초기 쿼리 1건 + 루프에서 10건 = 총 11번의 DB 조회가 발생합니다. 이는 성능 가이드라인에 위배됩니다.

또한 동일 사용자가 두 개의 탭에서 동시에 같은 이름으로 조직 생성을 요청하면, 두 요청이 모두 중복 체크를 통과해 중복 생성될 가능성이 있습니다. 현재는 메서드 수준의 명시적 락이 없기 때문입니다.

개선 방법:

  1. OrgMemberRepository에 다음 메서드를 추가하세요:

    boolean existsByUserAndOrganization_Name(User user, String orgName);

    이렇게 하면 단 1개의 쿼리로 중복 확인이 가능합니다.

  2. 동시성 문제 방지를 위해 메서드에 @Lock(LockModeType.PESSIMISTIC_WRITE) 추가를 검토하거나, DB 레벨에서 (user_id, name) 복합 유니크 제약을 정의하세요.

🤖 Prompt for AI Agents
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgCRUDService.java`
around lines 41 - 49, The loop in OrgCRUDService that fetches
orgMemberRepository.findOrgMemberByUser(user) and inspects
orgMember.getOrganization().getName() causes N+1 queries and has a race
condition; replace that logic by adding a repository method boolean
existsByUserAndOrganization_Name(User user, String orgName) in
OrgMemberRepository and use
orgMemberRepository.existsByUserAndOrganization_Name(user, request.name()) to
perform a single query for the duplicate check; to prevent concurrent duplicate
creations also either apply a pessimistic lock on the enclosing service method
(e.g., `@Lock`(LockModeType.PESSIMISTIC_WRITE) on the transaction) or enforce a
DB-level unique constraint on (user_id, name).

Comment on lines +74 to +87
public OrgResponse.Read getOrganization(Long userId) {
//TODO
return null;
}

public void modifyOrganization(Long userId, Long orgId, OrgRequest.Update request) {
//TODO

}

public void removeOrganization(Long userId, Long orgId) {
//TODO

}
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:

# 먼저 파일 크기 확인
wc -l src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgCRUDService.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 171


🏁 Script executed:

# 특정 라인 74-87 확인
sed -n '60,100p' src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgCRUDService.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 800


🏁 Script executed:

# 전체 파일 구조 확인 (메서드 시그니처)
grep -n "public.*Organization" src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgCRUDService.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 385


🏁 Script executed:

# 관련 컨트롤러 찾기
find src/main/java/com/whereyouad/WhereYouAd/domains/organization -name "*Controller*" -type f

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 267


🏁 Script executed:

# OrgController 확인
cat -n src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/OrgController.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 2895


🏁 Script executed:

# GlobalExceptionHandler 찾기
find src/main/java -name "*GlobalExceptionHandler*" -o -name "*ExceptionHandler*" | grep -i global

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 157


🏁 Script executed:

# 예외 처리 관련 코드 확인
find src/main/java -path "*/global/*" -o -path "*/common/*" -path "*Exception*" | head -20

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 1499


🏁 Script executed:

# GlobalExceptionHandler 확인
cat -n src/main/java/com/whereyouad/WhereYouAd/global/exception/GlobalExceptionHandler.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 5191


🏁 Script executed:

# ErrorCode 확인
cat -n src/main/java/com/whereyouad/WhereYouAd/global/exception/ErrorCode.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 931


🏁 Script executed:

# AppException 확인
cat -n src/main/java/com/whereyouad/WhereYouAd/global/exception/AppException.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 486


🏁 Script executed:

# 다른 TODO/미구현 메서드 패턴 찾기
grep -r "//TODO\|throw new UnsupportedOperationException\|NOT_IMPLEMENTED" src/main/java --include="*.java" | head -20

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 871


🏁 Script executed:

# 501 상태 코드 혹은 NOT_IMPLEMENTED 패턴이 있는지 확인
grep -r "501\|NOT_IMPLEMENTED\|not.implemented\|미구현" src/main/java --include="*.java"

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 55


🏁 Script executed:

# DataResponse 구조 확인 (null 반환시 어떻게 처리되는지)
cat -n src/main/java/com/whereyouad/WhereYouAd/global/response/DataResponse.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 1092


미구현 메서드는 명시적으로 실패 처리해 주세요

현재 상태를 확인해보니, 정확히 문제가 있습니다:

  • getOrganization()null을 반환하면서 ResponseEntity.ok()로 200을 응답 (클라이언트가 성공으로 인식)
  • modifyOrganization()removeOrganization()은 비어있으면서 ResponseEntity.noContent()로 204를 응답 (아무것도 실행되지 않았는데 성공 표시)

이렇게 하면 API 클라이언트가 실제로 처리가 안 됐는데 성공한 줄로 알게 됩니다.

빠른 해결책: UnsupportedOperationException 던지면 됩니다. 다만 현재 설정상 500 에러가 반환되는데, 의미상 더 정확하려면 501 Not Implemented 상태를 쓰는 게 좋습니다.

추천 접근법:

  1. ErrorCode enum에 501 상태의 NOT_IMPLEMENTED 추가
  2. GlobalExceptionHandler에 해당 예외 핸들러 등록
  3. 메서드에서 throw new AppException(ErrorCode.NOT_IMPLEMENTED) 던지기

예시:

public OrgResponse.Read getOrganization(Long userId) {
    throw new AppException(ErrorCode.NOT_IMPLEMENTED);
}

이렇게 하면 클라이언트가 501로 받게 되어 "아직 안 만들어진 기능이구나"를 명확히 알 수 있습니다.

🤖 Prompt for AI Agents
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/domain/service/OrgCRUDService.java`
around lines 74 - 87, The three unimplemented methods getOrganization(Long
userId), modifyOrganization(Long userId, Long orgId, OrgRequest.Update request),
and removeOrganization(Long userId, Long orgId) currently return null/are empty
and cause misleading 200/204 responses; update them to throw a clear
not-implemented exception by throwing new
AppException(ErrorCode.NOT_IMPLEMENTED). If ErrorCode lacks NOT_IMPLEMENTED, add
NOT_IMPLEMENTED with HTTP 501 semantics to the ErrorCode enum and ensure
GlobalExceptionHandler maps AppException(ErrorCode.NOT_IMPLEMENTED) to an HTTP
501 response so clients receive a proper 501 Not Implemented for these methods.

Comment on lines +19 to +24
@ApiResponses({
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400_1", description = "조직 이름 중복")
})
public ResponseEntity<DataResponse<OrgResponse.Create>> createOrganization(@AuthenticationPrincipal(expression = "userId") Long userId,
@RequestBody @Valid OrgRequest.Create request);
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:

# 파일 위치 확인 및 전체 내용 보기
find . -name "OrgControllerDocs.java" -type f

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 176


🏁 Script executed:

# 해당 파일의 전체 내용 확인
cat -n src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/docs/OrgControllerDocs.java

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 1563


🏁 Script executed:

# 실제 Controller 구현 파일 찾기
find . -name "*OrgController.java" -type f | grep -v Docs

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 167


🏁 Script executed:

# 프로젝트 내 다른 `@ApiResponse` 패턴 찾기 (responseCode 형식 확인)
rg "@ApiResponse.*responseCode" --type java -A 1 | head -50

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 5797


🏁 Script executed:

# 다른 인증 필요 엔드포인트 확인
rg "@AuthenticationPrincipal" --type java -B 5 | grep -A 5 "ApiResponse" | head -60

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 3827


🏁 Script executed:

# 프로젝트 전체 ApiResponse 형식 통계 (400_X 패턴이 정말 광범위한지 확인)
rg "responseCode.*=.*\"[0-9]" --type java | wc -l

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 75


🏁 Script executed:

# 표준 HTTP 상태코드만 사용하는 경우가 있는지 확인
rg "responseCode.*=.*\"[0-9]{3}\"" --type java | head -20

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 3197


🏁 Script executed:

# OrgController 구현 확인 (실제 에러 처리 방식)
cat -n src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/OrgController.java | head -60

Repository: WhereYouAd/WhereYouAd-Backend

Length of output: 2873


OpenAPI 스펙 준수: responseCode는 HTTP 상태 코드만 사용해야 합니다.

responseCode = "400_1"는 OpenAPI 3.0 스펙 위반입니다. Swagger UI 렌더링이나 클라이언트 코드 생성 도구에서 문제가 발생할 수 있어요. 현재 프로젝트 전역에서 이 패턴(400_1, 401_1 등)을 사용 중인데, 이를 기회로 체계적으로 정규 HTTP 상태 코드(400, 401, 404 등)로 통일하고 세부 에러 코드는 response body에 담는 구조로 리팩토링하길 권장합니다.

또한 이 엔드포인트는 @AuthenticationPrincipal로 인증이 필수인데, 인증 실패 시나리오(401)가 문서화되지 않았습니다. 다른 인증 필요 엔드포인트들도 마찬가지로 401을 빠뜨리고 있으니 함께 보완해주세요.

🛠️ 예시 변경 (프로젝트 전역 통일 권장)
     `@ApiResponses`({
             `@ApiResponse`(responseCode = "200", description = "성공"),
-            `@ApiResponse`(responseCode = "400_1", description = "조직 이름 중복")
+            `@ApiResponse`(responseCode = "400", description = "조직 이름 중복 (ORG_400_1)"),
+            `@ApiResponse`(responseCode = "401", description = "인증 필요")
     })
🤖 Prompt for AI Agents
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/docs/OrgControllerDocs.java`
around lines 19 - 24, The `@ApiResponses` on OrgControllerDocs.createOrganization
uses a non-standard responseCode "400_1"; change it to a valid HTTP status code
(e.g., "400") and add a "401" ApiResponse to document authentication failure;
keep any fine-grained error identifiers in the response body (not in
responseCode). Update the `@ApiResponse` entries for createOrganization (and other
controllers that follow the same pattern) to use standard numeric HTTP status
strings and ensure the response schema/body contains the project-specific error
code/details instead of using "400_1" style codes.

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.

고생하셨습니다! 항상 되게 빠르시네요! 저도 동일한 사람이 동일한 조직을 만드는건 불필요하다고 생각해서 예외처리도 괜찮은 것 같습니다!

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: 고생하셨습니다! 조직 생성한 유저가 ADMIN 권한을 갖도록 로직이 잘 구현되어 있는 듯 해 좋습니다!

@ojy0903
Copy link
Collaborator Author

ojy0903 commented Feb 3, 2026

Swagger @hidden 어노테이션으로 미완성 API 숨김처리 하였고, Builder 패턴 Converter 로 리팩터링 진행하는 거랑
Service 클래스 인터페이스 & 구현 클래스로 분리하는 리팩터링 진행했습니다!

@ojy0903 ojy0903 merged commit b144a5b into develop Feb 3, 2026
2 checks passed
This was referenced Feb 4, 2026
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 중 생성(Create API) 추가

3 participants