Skip to content

Feat/#26#28

Open
ojy0903 wants to merge 5 commits intodevelopfrom
feat/#26
Open

Feat/#26#28
ojy0903 wants to merge 5 commits intodevelopfrom
feat/#26

Conversation

@ojy0903
Copy link
Collaborator

@ojy0903 ojy0903 commented Feb 4, 2026

📌 관련 이슈

🚀 개요

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

조직 정보 수정 API 추가

📄 작업 내용

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

  • 조직 정보 수정 API 추가
  • 조직 정보 수정 관련 오류 코드 추가
  • 조직 정보 수정 관련 DTO 추가

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

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

===이름 test 인 유저(id = 1) 가 생성한 org1 조직의 이름, 설명, 로고 이미지 URL 변경하기===

  1. 변경 전 users 테이블 & organization 테이블
image image
  1. 조직 생성자만 정보 수정 가능하므로, id = 1 인 test 유저로 로그인 하여 토큰 획득
image
  1. 조직 정보 새롭게 변경
image

DB organization 테이블 변경 내역 확인 가능
image

===예외 처리===

  1. 조직 생성자(ADMIN) 가 아닌 회원으로 조직 정보 변경 요청 시

1-1. id = 2 인 test2 사용자로 로그인, 토큰 획득
image

1-2. test2 사용자의 AccessToken 으로 org1 조직 정보 변경 요청 시 예외처리 (403 Forbidden)
image

  1. 잘못된 조직 id 로 접근한 경우 -> DB 내 존재하지 않는 orgId = 6 에 대해 정보 수정 요청시 예외 (404 Not Found)
image
  1. 정보 수정 요청 시 필수 값인 조직 이름이 공백으로 넘어오는 경우 -> @Valid 검사 예외 처리 (400 Bad Request)
image

✅ 체크리스트

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

🔍 리뷰 포인트 (Review Points)

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

  • 조직 조회 API 의 경우 한 회원의 모든 조직 리스트 / 각 조직의 세부 내용 조회 / 조직에 속한 회원들 조회 등 여러가지 관점이 있을 것 같아 비교적 작업이 빠르게 끝날 것 같은 수정, 삭제 API 부터 진행하려 합니다.
  • 제가 기존에 알기로는 CRUD 에서 Update 와 Delete 는 요청이 성공적으로 처리되었을 때 204 noContent() 로 응답값을 따로 반환하지 않는 걸로 알고 있었는데 조사를 해보니 최근에는 Update 의 경우 200 OK 로 처리해서 Body 에 변경된 내용과 해당 조직 id, updatedAt 를 함께 반환하는 것도 권장된다고 하더라구요. 어떤 방법이 나을까요?
  • 정보 수정 Request DTO 에서 조직 이름에만 @notblank 를 적용하고 나머지 필드에는 적용하지 않았는데, 나머지 필드에도 적용하는게 좋을까요?

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

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • 조직 정보 수정 기능 추가 - 조직 이름, 설명, 로고 URL을 업데이트할 수 있습니다.
    • 권한 검증 추가 - 조직 생성자만 정보를 수정할 수 있습니다.
    • 조직 존재 여부 확인 기능 추가로 안정성 향상.

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

coderabbitai bot commented Feb 4, 2026

Walkthrough

조직 정보 수정 API를 구현했습니다. 요청 DTO에 필드를 추가하고, 서비스 계층에서 조직 존재 여부 및 소유권을 검증한 후 엔티티의 수정 메서드를 호출하는 플로우를 완성했습니다.

Changes

Cohort / File(s) Summary
DTO & 도메인 모델
src/main/java/.../dto/request/OrgRequest.java, src/main/java/.../persistence/entity/Organization.java
Update 레코드에 name, description, logoUrl 필드 추가 및 @NotBlank 검증 적용. Organization에 modifyInfo(OrgRequest.Update) 메서드 추가하여 필드 업데이트 로직 구현.
서비스 계층
src/main/java/.../domain/service/OrgServiceImpl.java
modifyOrganization 메서드 구현: 조직 존재 여부 확인, 호출자와 소유자의 userId 비교를 통한 권한 검증, 조건 만족 시 modifyInfo 호출.
에러 처리
src/main/java/.../exception/code/OrgErrorCode.java
두 개의 에러코드 추가: ORG_NOT_FOUND (404), ORG_UPDATE_FORBIDDEN (403). 메시지는 한국어로 명확하게 작성.
API 레이어
src/main/java/.../presentation/OrgController.java, src/main/java/.../presentation/docs/OrgControllerDocs.java
컨트롤러에 @Valid 추가로 요청 본문 검증 활성화, @Hidden 제거로 API 문서에 노출. OrgControllerDocs에 수정 엔드포인트 문서화 추가.

Sequence Diagram

sequenceDiagram
    participant Client
    participant OrgController
    participant OrgServiceImpl
    participant OrgRepository
    participant Organization
    participant OrgErrorCode

    Client->>OrgController: PATCH /organizations/{orgId}<br/>(userId, OrgRequest.Update)
    OrgController->>OrgController: `@Valid로` 요청 검증
    OrgController->>OrgServiceImpl: modifyOrganization(userId, orgId, request)
    
    OrgServiceImpl->>OrgRepository: findById(orgId)
    OrgRepository-->>OrgServiceImpl: Optional<Organization>
    
    alt 조직 미존재
        OrgServiceImpl->>OrgErrorCode: ORG_NOT_FOUND
        OrgErrorCode-->>OrgServiceImpl: 404 예외
        OrgServiceImpl-->>Client: 404 응답
    else 조직 존재
        OrgServiceImpl->>OrgServiceImpl: owner.userId == userId 검증
        alt 소유자 불일치
            OrgServiceImpl->>OrgErrorCode: ORG_UPDATE_FORBIDDEN
            OrgErrorCode-->>OrgServiceImpl: 403 예외
            OrgServiceImpl-->>Client: 403 응답
        else 소유자 일치
            OrgServiceImpl->>Organization: modifyInfo(request)
            Organization->>Organization: name, description, logoUrl 업데이트
            OrgServiceImpl->>OrgRepository: save(organization)
            OrgRepository-->>OrgServiceImpl: 저장 완료
            OrgServiceImpl-->>Client: 204 No Content
        end
    end
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly Related PRs

  • Feat/#19 #20 — Organization 엔티티에 modifyInfo 메서드를 추가한 PR로, 이번 PR의 핵심 도메인 로직 구현의 기반이 됩니다.
  • Feat/#23 #24 — OrgRequest.Update 레코드와 OrgServiceImpl.modifyOrganization 메서드를 구현한 PR로, 동일 기능의 직접적인 선행 작업입니다.

Suggested Reviewers

  • kingmingyu
  • jinnieusLab

💡 시니어 개발자의 의견

✅ 잘 구현된 부분

  1. 명확한 권한 검증 로직
    owner.userId == userId 비교로 소유자만 수정 가능하도록 제한한 것이 깔끔합니다. 향후 역할 기반 접근제어(RBAC)로 확장할 때도 이 지점에서 처리하면 좋을 것 같습니다.

  2. 일관된 에러 코드 관리
    ORG_NOT_FOUND, ORG_UPDATE_FORBIDDEN을 명확하게 정의하고, HTTP 상태 코드와 에러 코드를 매핑한 것이 REST API 표준을 잘 따릅니다.

  3. 요청 본문 검증
    @Valid 추가로 OrgRequest.Update의 @NotBlank 제약이 제대로 작동하게 했습니다. 이렇게 하면 비즈니스 로직 이전에 데이터 무결성을 보장합니다.

⚠️ 확인해볼 점

  1. 조직 조회 성능 (N+1 쿼리 가능성)

    Organization organization = orgRepository.findById(orgId).orElseThrow(...);

    현재 코드에서 조직을 조회할 때 owner 엔티티가 Lazy 로딩으로 설정되어 있다면, owner.userId 접근 시 추가 쿼리가 발생할 수 있습니다.

    개선 제안: @EntityGraph 또는 fetch join으로 owner를 함께 조회하는 것을 검토하세요. 특히 대규모 조직 시스템에서는 눈에 띄는 성능 차이가 날 수 있습니다.

  2. 트랜잭션 범위 확인
    OrgServiceImpl.modifyOrganization 메서드에 @Transactional 어노테이션이 있는지 확인하세요. 없다면 save 이후의 flush 시점이 불명확할 수 있습니다.

  3. 부분 업데이트 정책
    현재 Update 요청에서 null인 필드도 함께 전달되면, 이전 값이 null로 덮어씌워질 가능성이 있습니다.
    검토 사항: 클라이언트에서 수정하지 않은 필드는 null을 보내지 않도록 가이드하거나, modifyInfo에서 null check를 추가하는 것을 고려하세요.

    public void modifyInfo(OrgRequest.Update request) {
        if (request.name() != null) this.name = request.name();
        if (request.description() != null) this.description = request.description();
        if (request.logoUrl() != null) this.logoUrl = request.logoUrl();
    }

📌 주의사항

  • 컨트롤러와 서비스 사이의 userId 전달 과정이 명확한지 다시 한번 확인하세요. 토큰에서 추출한 userId가 정확히 전달되고 있는지 테스트를 통해 검증하면 좋습니다.
🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning PR 제목 'Feat/#26'은 이슈 번호만 포함하고 있어 실제 변경 내용을 전혀 설명하지 못합니다. 제목을 '조직 정보 수정 API 추가' 또는 'feat: Add organization update API endpoint' 처럼 변경하여 핵심 변경 내용을 명확하게 전달해주세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% 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의 모든 코드 변경이 연결된 이슈 #26의 '조직 정보 수정(Update) API 추가' 요구사항을 충족합니다.
Out of Scope Changes check ✅ Passed 모든 코드 변경이 조직 수정 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/#26

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

Reviews will stop working after February 8, 2026 if the new IP is not added to your allowlist.


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/persistence/entity/Organization.java`:
- Around line 40-43: modifyInfo currently overwrites existing fields with null
when description or logoUrl are omitted; change
Organization.modifyInfo(OrgRequest.Update) to only assign
name/description/logoUrl if the incoming value is non-null (e.g., if
(request.description() != null) this.description = request.description(); and
similarly for logoUrl) or alternatively use a distinct partial-update DTO,
ensuring OrgRequest.Update is not blindly applied and existing values are
preserved when nulls are passed.

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/docs/OrgControllerDocs.java`:
- Around line 27-40: The API documentation in OrgControllerDocs.java for
modifyOrganization uses incorrect responseCode strings ("403_1", "404_1");
update the `@ApiResponse` entries to match the actual OrgErrorCode constants (use
"ORG_403_1" and "ORG_404_1") so the documented response codes align with
OrgErrorCode; search for and fix any other `@ApiResponse` entries in
OrgControllerDocs that reference the shorthand codes to use the full constant
names.
🧹 Nitpick comments (1)
src/main/java/com/whereyouad/WhereYouAd/domains/organization/persistence/entity/Organization.java (1)

3-41: 엔티티가 요청 DTO에 직접 의존

Organization이 OrgRequest.Update에 직접 의존하면 계층 방향이 뒤집혀 유지보수성이 떨어집니다. 도메인 메서드는 primitive 인자(또는 도메인 커맨드)로 받고, 매핑은 서비스/컨버터로 이동하는 편이 좋습니다.

♻️ 리팩터링 예시
- public void modifyInfo(OrgRequest.Update request) {
-     this.name = request.name();
-     this.description = request.description();
-     this.logoUrl = request.logoUrl();
- }
+ public void modifyInfo(String name, String description, String logoUrl) {
+     this.name = name;
+     this.description = description;
+     this.logoUrl = logoUrl;
+ }

Comment on lines +40 to +43
public void modifyInfo(OrgRequest.Update request) {
this.name = request.name();
this.description = request.description();
this.logoUrl = request.logoUrl();
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

PATCH에서 null 덮어쓰기로 데이터 유실 가능

description/logoUrl을 생략하면 null이 저장되어 기존 값이 사라질 수 있어요. 부분 업데이트 의도라면 null은 유지하도록 가드하거나 별도 DTO로 분리하는 게 안전합니다.

🛠️ 수정 예시 (null 유지)
 public void modifyInfo(OrgRequest.Update request) {
-    this.name = request.name();
-    this.description = request.description();
-    this.logoUrl = request.logoUrl();
+    if (request.name() != null) {
+        this.name = request.name();
+    }
+    if (request.description() != null) {
+        this.description = request.description();
+    }
+    if (request.logoUrl() != null) {
+        this.logoUrl = request.logoUrl();
+    }
 }
🤖 Prompt for AI Agents
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/persistence/entity/Organization.java`
around lines 40 - 43, modifyInfo currently overwrites existing fields with null
when description or logoUrl are omitted; change
Organization.modifyInfo(OrgRequest.Update) to only assign
name/description/logoUrl if the incoming value is non-null (e.g., if
(request.description() != null) this.description = request.description(); and
similarly for logoUrl) or alternatively use a distinct partial-update DTO,
ensuring OrgRequest.Update is not blindly applied and existing values are
preserved when nulls are passed.

Comment on lines +27 to +40
@Operation(
summary = "조직 정보 수정 API",
description = "새로운 조직 이름, 설명, 로고 이미지 URL 을 받아 저장(해당 조직을 생성한 회원만 정보 변경 가능)"
)
@ApiResponses({
@ApiResponse(responseCode = "204", description = "성공(응답X)"),
@ApiResponse(responseCode = "403_1", description = "허가되지 않은 회원의 요청(조직 생성 회원 X)"),
@ApiResponse(responseCode = "404_1", description = "해당 id 조직 존재 X")
})
public ResponseEntity<Void> modifyOrganization(
@AuthenticationPrincipal(expression = "userId") Long userId,
@PathVariable Long orgId,
@RequestBody @Valid OrgRequest.Update 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 | 🟡 Minor

문서의 에러 코드 값 일치 여부 확인

OrgErrorCode는 ORG_403_1, ORG_404_1인데 문서에는 403_1, 404_1로 표기되어 있습니다. 실제 응답 코드와 동일하게 맞추는 게 좋아요.

📝 문서 수정 예시
- `@ApiResponse`(responseCode = "403_1", description = "허가되지 않은 회원의 요청(조직 생성 회원 X)"),
- `@ApiResponse`(responseCode = "404_1", description = "해당 id 조직 존재 X")
+ `@ApiResponse`(responseCode = "ORG_403_1", description = "허가되지 않은 회원의 요청(조직 생성 회원 X)"),
+ `@ApiResponse`(responseCode = "ORG_404_1", 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
@Operation(
summary = "조직 정보 수정 API",
description = "새로운 조직 이름, 설명, 로고 이미지 URL 을 받아 저장(해당 조직을 생성한 회원만 정보 변경 가능)"
)
@ApiResponses({
@ApiResponse(responseCode = "204", description = "성공(응답X)"),
@ApiResponse(responseCode = "403_1", description = "허가되지 않은 회원의 요청(조직 생성 회원 X)"),
@ApiResponse(responseCode = "404_1", description = "해당 id 조직 존재 X")
})
public ResponseEntity<Void> modifyOrganization(
@AuthenticationPrincipal(expression = "userId") Long userId,
@PathVariable Long orgId,
@RequestBody @Valid OrgRequest.Update request
);
`@Operation`(
summary = "조직 정보 수정 API",
description = "새로운 조직 이름, 설명, 로고 이미지 URL 을 받아 저장(해당 조직을 생성한 회원만 정보 변경 가능)"
)
`@ApiResponses`({
`@ApiResponse`(responseCode = "ORG_403_1", description = "허가되지 않은 회원의 요청(조직 생성 회원 X)"),
`@ApiResponse`(responseCode = "ORG_404_1", description = "해당 id 조직 존재 X")
})
public ResponseEntity<Void> modifyOrganization(
`@AuthenticationPrincipal`(expression = "userId") Long userId,
`@PathVariable` Long orgId,
`@RequestBody` `@Valid` OrgRequest.Update request
);
🤖 Prompt for AI Agents
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/organization/presentation/docs/OrgControllerDocs.java`
around lines 27 - 40, The API documentation in OrgControllerDocs.java for
modifyOrganization uses incorrect responseCode strings ("403_1", "404_1");
update the `@ApiResponse` entries to match the actual OrgErrorCode constants (use
"ORG_403_1" and "ORG_404_1") so the documented response codes align with
OrgErrorCode; search for and fix any other `@ApiResponse` entries in
OrgControllerDocs that reference the shorthand codes to use the full constant
names.

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: 고생하셨습니다! 제 생각에는 수정 요청 이후 프론트가 별도 GET api 요청을 하는 불편함을 줄이기 위해서, 수정 응답을 그대로 쓸 수 있게 변경 내용을 함께 포함하는 것도 괜찮을 것 같습니다! 그리고 @notblank 제약도 지금처럼 두어도 될 듯 합니다!

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 중 수정(Update API) 추가

2 participants