Skip to content

Feat/#141 예산 수정 - 네이버 광고 예산 수정 API 구현#142

Merged
kingmingyu merged 19 commits into
developfrom
feat/#141
Jun 22, 2026
Merged

Feat/#141 예산 수정 - 네이버 광고 예산 수정 API 구현#142
kingmingyu merged 19 commits into
developfrom
feat/#141

Conversation

@kingmingyu

@kingmingyu kingmingyu commented May 29, 2026

Copy link
Copy Markdown
Collaborator

📌 관련 이슈

🚀 개요

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

네이버 광고 예산 수정 API를 이용하여 캠페인 or 광고 그룹에 할당된 예산을 수정합니다.

📄 작업 내용

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

  • NaverClient 네이버 광고 예산 수정 엔드포인트 추가
  • 요청 DTO 추가 및 입력값 검증(예산은 10의 배수)
  • 서비스 로직 구현(조직원 및 권한 검증 -> 예산 값 검증 -> 헤더 제작 -> body 제작 -> 네이버 광고 API 호출 및 결과 반환)

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

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

  • 수정 전 예산 상태(캠페인, 하루 예산: 60원)
image image
  • 캠페인 예산 수정 요청 (하루 예산: 80원으로 수정 요청)
image - 응답 결과 image image
  • 광고 그룹 예산 상태(기본 입찰가: 70원, 하루 예산 50원)
image
  • 광고 그룹 예산 수정 요청(기본 입찰가: 90원, 하루 예산 80원으로 수정 요청)
image
  • 응답 결과
image image
  • 하루 예산 (dailyBudget)
    하루 동안 광고에 쓸 수 있는 최대 금액. 이 금액을 다 쓰면 그날 광고가 자동으로 멈춤.

  • 기본 입찰가 (bidAmt)
    광고 클릭 1번에 최대 얼마까지 지불할 의향이 있는지 설정하는 금액. 경쟁 광고주보다 입찰가가 높을수록 더 좋은 위치에 광고가 노출됨.

✅ 체크리스트

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

🔍 리뷰 포인트 (Review Points)

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

  • (예: 이 로직이 최선일까요? P2)

  • (예: 예외 처리 누락 여부 확인 부탁드립니다. P1)

  • Ad-Group쪽에는 광고 예산을 저장하는 필드가 없는 걸로 아는데 추가하는 편이 괜찮을까요? 그리고 추가한다면 하루 예산이랑 기본 입찰가 2개다 추가하는 것이 괜찮을까요..? 제 생각에는 둘 다 수정가능한 값이라 둘 다 저장하는 것이 괜찮을 것 같습니다..! 그리고 저희 서비스 내부에서 수정한 값에 대해서 budget_update_at같은 필드를 또 추가해서 예산 수정 시점을 반환하는 것도 괜찮을 것 같습니다..!

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

Summary by CodeRabbit

릴리스 노트

  • New Features
    • 네이버 캠페인 일일 예산 수정 기능 추가
    • 네이버 광고그룹 일일 예산 및 입찰가 수정 기능 추가
    • 캠페인/광고그룹 예산·입찰가 입력값 유효성 검증(10배수 및 허용 범위) 강화
  • Security / Access
    • 연결된 조직에 대해 관리자 권한 검증을 적용해 요청을 제어
  • Documentation
    • 관련 API 문서(캠페인/광고그룹 예산 수정) 추가 및 노출 방식 조정

@kingmingyu kingmingyu requested review from jinnieusLab and ojy0903 May 29, 2026 01:52
@kingmingyu kingmingyu self-assigned this May 29, 2026
@kingmingyu kingmingyu added the ✨ Feature 새로운 기능 추가 label May 29, 2026
@kingmingyu kingmingyu linked an issue May 29, 2026 that may be closed by this pull request
3 tasks
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6db2b847-fbea-4d7a-be3a-4a0ab2c49b5a

📥 Commits

Reviewing files that changed from the base of the PR and between 86177c3 and cb28198.

📒 Files selected for processing (1)
  • src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java

Walkthrough

캠페인·광고그룹 예산(및 광고그룹 입찰가) 수정 기능을 추가했습니다: DTO·에러코드 → Feign 클라이언트 → 서비스 권한·검증·PUT 호출 → 컨트롤러 엔드포인트 → Swagger 문서 → 엔티티/컨버터 매핑 순으로 구현됨.

Changes

네이버 광고 예산 수정 기능

Layer / File(s) Summary
DTO 계약 및 에러 코드 정의
src/main/java/.../dto/NaverDTO.java, src/main/java/.../exception/code/NaverAdErrorCode.java
캠페인/광고그룹 예산 수정용 Request/Body DTO 4개와 예산/입찰 검증(10배수, 범위) 및 업데이트 실패용 오류 코드 상수 5개를 추가함.
Naver API 클라이언트 통합
src/main/java/.../client/naver/client/NaverClient.java
Feign 기반 NaverClient에 캠페인·광고그룹 예산 수정용 PUT 메서드 2개와 관련 import를 추가함(헤더, 경로 변수, fields 쿼리 파라미터, 요청 바디, 응답 DTO).
서비스 계층 - 권한 검증 및 예산 수정 로직
src/main/java/.../domain/service/NaverAdApiService.java
OrgMemberRepository 주입으로 조직 ADMIN 여부 검증(private validateAdminOwnership 메서드), dailyBudget/bidAmt의 10배수·범위 검증, Naver PUT 호출(광고그룹은 제약사항에 맞춰 예산과 입찰가를 분리 호출) 및 로컬 엔티티 업데이트와 전용 오류 코드 기반 예외 처리를 구현함.
HTTP API 엔드포인트
src/main/java/.../presentation/NaverAdApiController.java
@AuthenticationPrincipal으로 userId를 주입받아 PUT /campaigns/{campaignId}/budgetPUT /adgroups/{adgroupId}/budget 엔드포인트를 추가하고 서비스 결과를 DataResponse로 반환함.
Swagger API 문서화
src/main/java/.../presentation/docs/NaverAdApiControllerDocs.java
기존 Naver 연동 API들(캠페인 조회, 광고그룹 조회, 소재, 키워드, 리포트 등 11개)에 @Hidden을 적용하여 문서 노출을 숨기고, 새로 추가된 예산 수정 엔드포인트 2개에 대한 Operation/ApiResponses/파라미터 설명을 추가함.
엔티티·컨버터 갱신
src/main/java/.../persistence/entity/AdCampaign.java, .../AdGroup.java, src/main/java/.../converter/NaverConverter.java
AdCampaign.updateBudget(Long) 메서드 추가, AdGroupbudget·bidAmount 컬럼 추가 및 updateBudget(Long, Long) 메서드 추가, NaverConverter에서 AdGroup 빌드 시 예산/입찰가 매핑과 갱신 시 updateBudget 호출로 변경됨.

Sequence Diagram

sequenceDiagram
  participant Client
  participant NaverAdApiController
  participant NaverAdApiService
  participant OrgMemberRepository
  participant NaverClient
  Client->>NaverAdApiController: PUT /campaigns/{campaignId}/budget
  NaverAdApiController->>NaverAdApiService: updateCampaignBudget(userId, connectionId, campaignId, request)
  NaverAdApiService->>OrgMemberRepository: findOrgMemberByConnectionOrg(...)
  OrgMemberRepository-->>NaverAdApiService: OrgMember (role)
  NaverAdApiService->>NaverAdApiService: validate ADMIN, validate budget/bid rules
  NaverAdApiService->>NaverClient: updateCampaignBudget(headers, campaignId, fields, body)
  NaverClient-->>NaverAdApiService: CampaignResponse
  NaverAdApiService-->>NaverAdApiController: CampaignResponse
  NaverAdApiController-->>Client: DataResponse<CampaignResponse>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • WhereYouAd/WhereYouAd-Backend#50: AdCampaign 관련 예산 변경을 다루는 PR로, 새로 추가된 AdCampaign.updateBudget(...)AdGroup의 budget/bidAmount 필드와 도메인 모델 레벨에서 상호작용함.

Suggested reviewers

  • ojy0903
  • jinnieusLab

📌 리뷰 포인트

이 PR은 전반적으로 잘 구성되어 있습니다. 시니어 관점에서 몇 가지 확인할 포인트를 남깁니다:

✅ 좋은 점:

  1. 권한 검증이 명확함: validateAdminOwnership private 메서드로 조직 권한을 먼저 확인하는 패턴이 깔끔합니다. OrgMemberRepository 주입으로 조직 멤버 정보를 조회하는 구조도 좋습니다.
  2. 분리된 호출 전략: 광고그룹의 예산/입찰가를 별도 PUT으로 호출하는 부분이 Naver API 제약사항을 정확히 반영했습니다.
  3. DTO 분리: 컨트롤러 입력용 Request와 API 전송용 Body를 구분한 점이 계층 간 책임 분리를 잘 보여줍니다.

⚠️ 확인 권장:

  1. N+1 쿼리 가능성: NaverAdApiService.validateAdminOwnership에서 OrgMemberRepository.findOrgMemberByConnectionOrg(...)를 호출할 때, 만약 대량 요청이 들어올 경우 DB 부하를 모니터링하세요.
  2. 트랜잭션 경계: 서비스 메서드(updateCampaignBudget, updateAdGroupBudget)에 @Transactional 어노테이션이 있는지 확인해주세요. Naver API 호출 성공 후 로컬 엔티티 업데이트 시 일관성을 보장해야 합니다.
  3. 범위 검증 테스트: dailyBudget의 허용 범위(501,000,000,000)와 bidAmt의 허용 범위(70100,000)가 실제 Naver API 명세와 일치하는지 한 번 더 확인하세요.
  4. 에러 응답: API 실패 시(NAVER_CAMPAIGN_BUDGET_UPDATE_FAILED, NAVER_AD_GROUP_BUDGET_UPDATE_FAILED) 클라이언트에 충분한 오류 정보(예: 네이버 API 응답 코드/메시지)를 전달하는지 확인하면 좋습니다.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 구현한 주요 기능(네이버 광고 예산 수정 API)을 명확하게 반영하고 있으며, 핵심 변경사항을 정확히 설명하고 있습니다.
Description check ✅ Passed PR 설명이 템플릿을 대부분 준수하며, 관련 이슈, 개요, 작업 내용, 스크린샷, 체크리스트, 리뷰 포인트가 모두 포함되어 있고 완전합니다.
Linked Issues check ✅ Passed PR이 #141의 모든 주요 요구사항을 충족합니다: 캠페인 별 예산 수정 API 구현 [#141], 광고그룹 별 예산 수정 API 구현 [#141], 네이버 광고 API 명세 준수 [#141].
Out of Scope Changes check ✅ Passed 모든 변경사항이 #141의 네이버 광고 예산 수정 API 구현 범위 내에 있습니다. AdGroup 엔티티의 budget/bidAmount 필드 추가는 리뷰 포인트에서 논의된 미래 개선사항이며, PR 설명의 검토 포인트에서 명시적으로 언급되어 있습니다.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#141

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java (1)

144-166: 💤 Low value

엔드포인트 구조는 기존 패턴과 잘 맞습니다 👍

@AuthenticationPrincipal(expression = "userId")로 인증 사용자 추출 → 서비스 위임 → DataResponse 래핑까지 컨트롤러는 얇게 유지되어 좋습니다. 한 가지만, 예산/입찰가의 "10의 배수" 검증이 지금은 서비스 안의 수동 if문으로 흩어져 있는데, 향후 DTO에 Bean Validation(예: 커스텀 제약 또는 @Positive)을 붙이고 컨트롤러에서 @Valid로 받으면 검증 위치가 한곳으로 모여 더 선언적이고 일관됩니다. 지금 당장 막는 이슈는 아니라 참고만 해주세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java`
around lines 144 - 166, Move the "10의 배수" and other budget validation into
declarative Bean Validation: add appropriate validation annotations to
NaverDTO.UpdateCampaignBudgetRequest and NaverDTO.UpdateAdGroupBudgetRequest
(e.g., `@Positive` for >0 and a custom constraint annotation like
`@MultipleOf`(value=10) implemented via ConstraintValidator to enforce multiples
of 10), then annotate the controller parameters in updateCampaignBudget and
updateAdGroupBudget with `@Valid` so Spring validates before calling
naverAdApiService; keep the service logic focused on business rules only and
remove the duplicated manual if-checks from the service methods.
src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java (1)

180-188: ⚡ Quick win

조직원/ADMIN 권한 검증 로직이 두 메서드에 그대로 복붙되어 있어요 (DRY)

180-188 블록과 214-222 블록이 connectionId 조회 → orgId 추출 → ADMIN 검증까지 사실상 동일합니다. 한쪽 정책이 바뀌면 양쪽을 모두 고쳐야 하므로, 공통 private 헬퍼로 추출하는 걸 권장합니다.

♻️ 권한 검증 헬퍼 추출 예시
private void validateAdmin(Long userId, Long connectionId, NaverAdErrorCode notFoundCode) {
    PlatformConnection connection = connectionRepository.findWithAccountAndOrgById(connectionId)
            .orElseThrow(() -> new AdvertisementHandler(notFoundCode));
    Long orgId = connection.getPlatformAccount().getOrganization().getId();
    OrgMember orgMember = orgMemberRepository.findByUserIdAndOrgId(userId, orgId)
            .orElseThrow(() -> new OrgHandler(OrgErrorCode.ORG_MEMBER_NOT_FOUND));
    if (orgMember.getRole() != OrgRole.ADMIN) {
        throw new OrgHandler(OrgErrorCode.ORG_MEMBER_FORBIDDEN);
    }
}

As per coding guidelines, "SOLID 원칙, 의존성 주입(DI)" 점검 항목에 따라 중복 제거를 제안합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java`
around lines 180 - 188, Extract the duplicated ADMIN permission checks into a
single private helper (e.g., validateAdmin) and replace both inline blocks with
calls to it; the helper should use
connectionRepository.findWithAccountAndOrgById(connectionId) and throw new
AdvertisementHandler(...) when not found, derive orgId from
connection.getPlatformAccount().getOrganization().getId(), check
orgMemberRepository.findByUserIdAndOrgId(userId, orgId) (throwing new
OrgHandler(OrgErrorCode.ORG_MEMBER_NOT_FOUND) if absent) and finally verify
orgMember.getRole() == OrgRole.ADMIN (throw new
OrgHandler(OrgErrorCode.ORG_MEMBER_FORBIDDEN) otherwise), so callers simply call
validateAdmin(userId, connectionId,
NaverAdErrorCode.NAVER_CAMPAIGN_BUDGET_UPDATE_FAILED) or appropriate
notFoundCode.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java`:
- Around line 176-178: Both updateCampaignBudget and updateAdGroupBudget
currently keep a `@Transactional` open while making slow external Naver PUT calls;
split the short DB/permission checks into dedicated methods annotated
`@Transactional`(readOnly = true) (e.g., validateCampaignOwnership,
validateAdGroupOwnership) that perform only the necessary JPA reads, remove
`@Transactional` from updateCampaignBudget and updateAdGroupBudget so the external
HTTP calls run outside any transaction/DB connection, and ensure any required
entities/DTOs from the validation methods are returned for use by the
non-transactional methods.
- Around line 181-182: The repository call
connectionRepository.findWithAccountAndOrgById currently throws an
AdvertisementHandler with NaverAdErrorCode.NAVER_CAMPAIGN_BUDGET_UPDATE_FAILED
(500) when empty; change this to throw a new/not-found-specific
AdvertisementHandler error (add or use a NaverAdErrorCode like
NAVER_CONNECTION_NOT_FOUND mapped to 404) so missing PlatformConnection returns
404, and make the identical change where the same pattern is used in the
ad-group block that calls findWithAccountAndOrgById (around the other
occurrence). Also update the Swagger response documentation sections that
describe these endpoints (the docs around the current 196-202 and 213-217
regions) to include the 404 response and reference the new error code.
- Around line 191-193: The current validation in NaverAdApiService only checks
request.dailyBudget() % 10 == 0 which lets negative multiples pass; update the
validation to require a positive 10-multiple (e.g., if (request.dailyBudget() ==
null || request.dailyBudget() <= 0 || request.dailyBudget() % 10 != 0) throw new
AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE)); apply the
same positive 10-multiple check to the bidAmt validation referenced around the
bidAmt handling (lines ~225-230) so negative values are rejected before calling
the Naver API.

---

Nitpick comments:
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java`:
- Around line 180-188: Extract the duplicated ADMIN permission checks into a
single private helper (e.g., validateAdmin) and replace both inline blocks with
calls to it; the helper should use
connectionRepository.findWithAccountAndOrgById(connectionId) and throw new
AdvertisementHandler(...) when not found, derive orgId from
connection.getPlatformAccount().getOrganization().getId(), check
orgMemberRepository.findByUserIdAndOrgId(userId, orgId) (throwing new
OrgHandler(OrgErrorCode.ORG_MEMBER_NOT_FOUND) if absent) and finally verify
orgMember.getRole() == OrgRole.ADMIN (throw new
OrgHandler(OrgErrorCode.ORG_MEMBER_FORBIDDEN) otherwise), so callers simply call
validateAdmin(userId, connectionId,
NaverAdErrorCode.NAVER_CAMPAIGN_BUDGET_UPDATE_FAILED) or appropriate
notFoundCode.

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java`:
- Around line 144-166: Move the "10의 배수" and other budget validation into
declarative Bean Validation: add appropriate validation annotations to
NaverDTO.UpdateCampaignBudgetRequest and NaverDTO.UpdateAdGroupBudgetRequest
(e.g., `@Positive` for >0 and a custom constraint annotation like
`@MultipleOf`(value=10) implemented via ConstraintValidator to enforce multiples
of 10), then annotate the controller parameters in updateCampaignBudget and
updateAdGroupBudget with `@Valid` so Spring validates before calling
naverAdApiService; keep the service logic focused on business rules only and
remove the duplicated manual if-checks from the service methods.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 552ea33e-8f31-46ac-a28a-6977c0c8fac5

📥 Commits

Reviewing files that changed from the base of the PR and between 6a59971 and 3a30ecf.

📒 Files selected for processing (6)
  • src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java
  • src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java
  • src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java
  • src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java
  • src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/client/NaverClient.java
  • src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/dto/NaverDTO.java

@ojy0903 ojy0903 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

P4: 고생하셨습니다! SQLD 시험 때문에 좀 늦게 확인했네요...ㅠㅠ
AdGroup 쪽에 입찰가 필드 추가하는건 구글은 광고 그룹에도 예산이 있는지 잘 모르겠지만... 일단 Meta 에서는 활용 가능할 거 같아서 추가해도 문제되진 않을 것 같습니다.
그리고 추가한다면 AdGroup 쪽에 Long bidAmount, Long dailyBudget 이런 식으로 추가하고, 플랫폼 API 에서 저희 서비스 AdGroup 엔티티로 받아올 때 단위만 잘 변환시켜주면 될 것 같습니다...!!

@kingmingyu

Copy link
Copy Markdown
Collaborator Author

P4: 고생하셨습니다! SQLD 시험 때문에 좀 늦게 확인했네요...ㅠㅠ AdGroup 쪽에 입찰가 필드 추가하는건 구글은 광고 그룹에도 예산이 있는지 잘 모르겠지만... 일단 Meta 에서는 활용 가능할 거 같아서 추가해도 문제되진 않을 것 같습니다. 그리고 추가한다면 AdGroup 쪽에 Long bidAmount, Long dailyBudget 이런 식으로 추가하고, 플랫폼 API 에서 저희 서비스 AdGroup 엔티티로 받아올 때 단위만 잘 변환시켜주면 될 것 같습니다...!!

좋습니다! 수정하려면 기존 값이 얼마인지 보여주는 것이 괜찮을 것 같아서 저장하는 것이 좋을 것 같긴한데 그러면 스케줄러 쪽에도 저 필드를 동기화 시켜주는 로직을 추가해야 해서 지민님 의견도 듣고 추가해보겠습니다!

@jinnieusLab jinnieusLab left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

P4: 고생하셨습니다! 리뷰가 늦었네요 ㅠㅠ 죄송합니다.. API로 예산 수정하는 로직을 깔끔하게 구현해주셔서 좋네요!
다만 구글은 광고 그룹 단위로 예산을 수정할 수 없고(예산 수정은 캠페인 단위로만 수정 가능) 입찰가만 조정이 가능해서, 만약 AdGroup에 예산과 입찰가 필드를 추가한다면 구글은 광고그룹 예산을 null로 두면 될 것 같네요! (만약 구글만 null로 두는 게 별로라면 세 플랫폼 모두 광고 그룹에서는 입찰가만 수정할 수 있게 입찰가만 필드로 추가하는 방법도 있을 것 같네요...)
예산 수정 시점도 타임라인 그래프에서 보여줘야하니 기록할 수 있는 필드를 추가하는 아이디어 좋다고 생각합니다!

@kingmingyu

Copy link
Copy Markdown
Collaborator Author

P4: 고생하셨습니다! 리뷰가 늦었네요 ㅠㅠ 죄송합니다.. API로 예산 수정하는 로직을 깔끔하게 구현해주셔서 좋네요! 다만 구글은 광고 그룹 단위로 예산을 수정할 수 없고(예산 수정은 캠페인 단위로만 수정 가능) 입찰가만 조정이 가능해서, 만약 AdGroup에 예산과 입찰가 필드를 추가한다면 구글은 광고그룹 예산을 null로 두면 될 것 같네요! (만약 구글만 null로 두는 게 별로라면 세 플랫폼 모두 광고 그룹에서는 입찰가만 수정할 수 있게 입찰가만 필드로 추가하는 방법도 있을 것 같네요...) 예산 수정 시점도 타임라인 그래프에서 보여줘야하니 기록할 수 있는 필드를 추가하는 아이디어 좋다고 생각합니다!

좋습니다! 그러면 필드는 예산, 입찰가 2개로 추가하고, 예산 수정 시점은 입찰가 수정 시점으로 하겠습니다!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java`:
- Around line 97-100: The updateBudget method in AdCampaign currently ignores
null inputs (if (budget != null) this.budget = budget;), so calls from
NaverAdApiService.updateCampaignBudget passing dailyBudget = null won't clear
the stored budget; change AdCampaign.updateBudget to always assign the incoming
value (this.budget = budget) so a null value clears the local field (and ensure
any DB mapping allows null if necessary).

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java`:
- Around line 65-68: The updateBudget method currently treats budget==null as
"no change", but callers sometimes mean "clear the budget"
(useDailyBudget=false); change the signature of AdGroup.updateBudget to include
a boolean flag (e.g., boolean useDailyBudget) and implement logic: if
useDailyBudget is false set this.budget = null (clearing saved budget) and set
this.bidAmount only if bidAmount != null; if useDailyBudget is true then apply
the existing behavior (set this.budget = budget when budget != null and
this.bidAmount = bidAmount when bidAmount != null). Update call sites such as
NaverAdApiService.updateAdGroupBudget and NaverConverter.updateAdGroup to pass
the new useDailyBudget flag accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 561d6efc-9c03-4b5c-8bf0-311efd125e60

📥 Commits

Reviewing files that changed from the base of the PR and between 3a30ecf and 86177c3.

📒 Files selected for processing (6)
  • src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java
  • src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java
  • src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java
  • src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java
  • src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java
  • src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/converter/NaverConverter.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java
  • src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java

Comment on lines +65 to +68
public void updateBudget(Long budget, Long bidAmount) {
if (budget != null) this.budget = budget;
if (bidAmount != null) this.bidAmount = bidAmount;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

budget은 null을 “미수정”이 아니라 “해제”로 해석해야 하는 케이스가 있습니다.

Line 66 조건식 때문에 useDailyBudget=false 흐름에서 전달되는 budget=null이 반영되지 않아, 광고그룹 예산이 DB에 잔존할 수 있습니다. NaverAdApiService.updateAdGroupBudgetNaverConverter.updateAdGroup 모두 해당 경로를 타고 들어옵니다.

🔧 제안 수정(의도 전달을 위해 플래그 포함)
-    public void updateBudget(Long budget, Long bidAmount) {
-        if (budget != null) this.budget = budget;
-        if (bidAmount != null) this.bidAmount = bidAmount;
-    }
+    public void updateBudget(Boolean useDailyBudget, Long budget, Long bidAmount) {
+        if (Boolean.FALSE.equals(useDailyBudget)) {
+            this.budget = null;
+        } else if (budget != null) {
+            this.budget = budget;
+        }
+        if (bidAmount != null) {
+            this.bidAmount = bidAmount;
+        }
+    }

(호출부에서 useDailyBudget 전달도 함께 맞춰주세요.)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java`
around lines 65 - 68, The updateBudget method currently treats budget==null as
"no change", but callers sometimes mean "clear the budget"
(useDailyBudget=false); change the signature of AdGroup.updateBudget to include
a boolean flag (e.g., boolean useDailyBudget) and implement logic: if
useDailyBudget is false set this.budget = null (clearing saved budget) and set
this.bidAmount only if bidAmount != null; if useDailyBudget is true then apply
the existing behavior (set this.budget = budget when budget != null and
this.bidAmount = bidAmount when bidAmount != null). Update call sites such as
NaverAdApiService.updateAdGroupBudget and NaverConverter.updateAdGroup to pass
the new useDailyBudget flag accordingly.

@kingmingyu kingmingyu merged commit ead5624 into develop Jun 22, 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

3 participants