From cfa281a0ac82e858bd07bb53b0e90fa42b67476c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Fri, 29 May 2026 09:32:55 +0900 Subject: [PATCH 01/17] =?UTF-8?q?:sparkles:=20feat:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B2=84=20=EA=B4=91=EA=B3=A0=20=EC=BA=A0=ED=8E=98=EC=9D=B8,?= =?UTF-8?q?=20=EA=B4=91=EA=B3=A0=EA=B7=B8=EB=A3=B9=20=EC=98=88=EC=82=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/naver/client/NaverClient.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/client/NaverClient.java b/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/client/NaverClient.java index c116a6cf..44be2134 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/client/NaverClient.java +++ b/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/client/NaverClient.java @@ -5,6 +5,7 @@ import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.PathVariable; @@ -77,4 +78,22 @@ NaverDTO.StatReportResponse getStatReportStatus( Response downloadStatReport( @RequestHeader Map headers, URI baseUri ); + + // 캠페인 예산 수정 + @PutMapping("/ncc/campaigns/{campaignId}") + NaverDTO.CampaignResponse updateCampaignBudget( + @RequestHeader Map headers, + @PathVariable("campaignId") String campaignId, + @RequestParam("fields") String fields, + @RequestBody NaverDTO.UpdateCampaignBudgetBody body + ); + + // 광고그룹 예산 수정 + @PutMapping("/ncc/adgroups/{adgroupId}") + NaverDTO.AdGroupResponse updateAdGroupBudget( + @RequestHeader Map headers, + @PathVariable("adgroupId") String adgroupId, + @RequestParam("fields") String fields, + @RequestBody NaverDTO.UpdateAdGroupBudgetBody body + ); } From 492ab631ef35372decd597c88d2b3b5c56f1225c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Fri, 29 May 2026 09:34:25 +0900 Subject: [PATCH 02/17] =?UTF-8?q?:sparkles:=20feat:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B2=84=20=EA=B4=91=EA=B3=A0=20=EC=BA=A0=ED=8E=98=EC=9D=B8,?= =?UTF-8?q?=20=EA=B4=91=EA=B3=A0=EA=B7=B8=EB=A3=B9=20=EC=98=88=EC=82=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=9A=94=EC=B2=AD=20=EA=B0=92=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/naver/dto/NaverDTO.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/dto/NaverDTO.java b/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/dto/NaverDTO.java index df0c8e14..dccfeadf 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/dto/NaverDTO.java +++ b/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/dto/NaverDTO.java @@ -133,4 +133,32 @@ public record StatReportRequest( String reportTp, String statDt ) {} + + // 캠페인 예산 수정 요청 (컨트롤러 입력용, ID X) + public record UpdateCampaignBudgetRequest( + Boolean useDailyBudget, + Long dailyBudget + ) {} + + // 캠페인 예산 수정 body (네이버 API 전송용, ID O) + public record UpdateCampaignBudgetBody( + String nccCampaignId, + Boolean useDailyBudget, + Long dailyBudget + ) {} + + // 광고그룹 예산 수정 요청 (컨트롤러 입력용, ID X) + public record UpdateAdGroupBudgetRequest( + Boolean useDailyBudget, + Long dailyBudget, + Long bidAmt + ) {} + + // 광고그룹 예산 수정 body (네이버 API 전송용, ID O) + public record UpdateAdGroupBudgetBody( + String nccAdgroupId, + Boolean useDailyBudget, + Long dailyBudget, + Long bidAmt + ) {} } From 56a263a27fe2469c7c509d7a8d18afac244f2269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Fri, 29 May 2026 09:34:37 +0900 Subject: [PATCH 03/17] =?UTF-8?q?:sparkles:=20feat:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B2=84=20=EA=B4=91=EA=B3=A0=20=EC=BA=A0=ED=8E=98=EC=9D=B8,?= =?UTF-8?q?=20=EA=B4=91=EA=B3=A0=EA=B7=B8=EB=A3=B9=20=EC=98=88=EC=82=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EA=B4=80=EB=A0=A8=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domains/advertisement/exception/code/NaverAdErrorCode.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java index 33a2b4c2..40c7b212 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java @@ -11,6 +11,7 @@ public enum NaverAdErrorCode implements BaseErrorCode { // 400 NAVER_INVALID_DOWNLOAD_URL(HttpStatus.BAD_REQUEST, "NAVER_400_1", "유효하지 않은 다운로드 URL입니다."), + NAVER_INVALID_BUDGET_VALUE(HttpStatus.BAD_REQUEST, "NAVER_400_2", "예산 및 입찰가는 10의 배수여야 합니다."), // 500 NAVER_CAMPAIGN_FETCH_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NAVER_500_1", "네이버 캠페인 목록 조회에 실패했습니다."), @@ -21,6 +22,8 @@ public enum NaverAdErrorCode implements BaseErrorCode { NAVER_REPORT_STATUS_CHECK_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NAVER_500_6", "네이버 성과 보고서 상태 조회에 실패했습니다."), NAVER_REPORT_DOWNLOAD_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NAVER_500_7", "네이버 성과 보고서 원문 다운로드에 실패했습니다."), NAVER_HOURLY_STAT_FETCH_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NAVER_500_8", "네이버 시간대별 통계 조회에 실패했습니다."), + NAVER_CAMPAIGN_BUDGET_UPDATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NAVER_500_9", "네이버 캠페인 예산 수정에 실패했습니다."), + NAVER_AD_GROUP_BUDGET_UPDATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NAVER_500_10", "네이버 광고 그룹 예산 수정에 실패했습니다."), ; private final HttpStatus httpStatus; From 5de43b0b06cdbcc5815ebab0e6c169f2b89bde6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Fri, 29 May 2026 09:34:48 +0900 Subject: [PATCH 04/17] =?UTF-8?q?:sparkles:=20feat:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B2=84=20=EA=B4=91=EA=B3=A0=20=EC=BA=A0=ED=8E=98=EC=9D=B8,?= =?UTF-8?q?=20=EA=B4=91=EA=B3=A0=EA=B7=B8=EB=A3=B9=20=EC=98=88=EC=82=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/service/NaverAdApiService.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java index 0836d870..5e342c5c 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java @@ -166,6 +166,58 @@ public NaverDTO.RawReportResponse downloadReport(Long connectionId, String downl } } + // 캠페인 예산 수정 + @Transactional + public NaverDTO.CampaignResponse updateCampaignBudget(Long connectionId, String campaignId, NaverDTO.UpdateCampaignBudgetRequest request) { + + // 예산이 10의 배수가 아닌 경우 오류(네이버 광고 예산 요청 값 검증) + if (request.dailyBudget() != null && request.dailyBudget() % 10 != 0) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); + } + // API 호출 + try { + // 헤더 제작 + Map headers = adApiAuthUtil.generateAuthHeaders( + connectionId, AdAuthRequest.forMethodAndPath("PUT", "/ncc/campaigns/" + campaignId)); + // 요청 body 제작 + NaverDTO.UpdateCampaignBudgetBody body = + new NaverDTO.UpdateCampaignBudgetBody(campaignId, request.useDailyBudget(), request.dailyBudget()); + // API 호출 + return naverClient.updateCampaignBudget(headers, campaignId, "budget", body); + } catch (Exception e) { + log.error("[NAVER] 캠페인 예산 수정 실패 - connectionId={}, campaignId={}", connectionId, campaignId, e); + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_CAMPAIGN_BUDGET_UPDATE_FAILED); + } + } + + // 광고그룹 예산 수정 + @Transactional + public NaverDTO.AdGroupResponse updateAdGroupBudget(Long connectionId, String adgroupId, NaverDTO.UpdateAdGroupBudgetRequest request) { + + // 예산이 10의 배수가 아닌 경우 오류(네이버 광고 예산 요청 값 검증) + if (request.dailyBudget() != null && request.dailyBudget() % 10 != 0) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); + } + if (request.bidAmt() != null && request.bidAmt() % 10 != 0) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); + } + // API 호출 + try { + // 헤더 제작 + Map headers = adApiAuthUtil.generateAuthHeaders( + connectionId, AdAuthRequest.forMethodAndPath("PUT", "/ncc/adgroups/" + adgroupId)); + String fields = request.bidAmt() != null ? "budget,bidAmt" : "budget"; + // 요청 body 제작 + NaverDTO.UpdateAdGroupBudgetBody body = + new NaverDTO.UpdateAdGroupBudgetBody(adgroupId, request.useDailyBudget(), request.dailyBudget(), request.bidAmt()); + // API 호출 + return naverClient.updateAdGroupBudget(headers, adgroupId, fields, body); + } catch (Exception e) { + log.error("[NAVER] 광고그룹 예산 수정 실패 - connectionId={}, adgroupId={}", connectionId, adgroupId, e); + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_AD_GROUP_BUDGET_UPDATE_FAILED); + } + } + // 일별 기본 지표 조회 (/stats, 기본 일 단위) @Transactional(readOnly = true) public List getDailyStats(Long connectionId, String id, String since, String until) { From 9036cd5fbe1447397dfd8ef21cea78b39b107e6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Fri, 29 May 2026 09:37:01 +0900 Subject: [PATCH 05/17] =?UTF-8?q?:sparkles:=20feat:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B2=84=20=EA=B4=91=EA=B3=A0=20=EC=BA=A0=ED=8E=98=EC=9D=B8,?= =?UTF-8?q?=20=EA=B4=91=EA=B3=A0=EA=B7=B8=EB=A3=B9=20=EC=98=88=EC=82=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20Controller=20=EB=B0=8F=20Docs=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/NaverAdApiController.java | 22 ++++++++++++++++ .../docs/NaverAdApiControllerDocs.java | 26 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java index 0cab27c7..9a3cfc26 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java @@ -139,4 +139,26 @@ public ResponseEntity> ) { return ResponseEntity.ok(DataResponse.from(naverAdSyncService.syncConversionReports(connectionId, statDate))); } + + // 캠페인 예산 수정 + @PutMapping("/campaigns/{campaignId}/budget") + public ResponseEntity> updateCampaignBudget( + @PathVariable Long connectionId, + @PathVariable String campaignId, + @RequestBody NaverDTO.UpdateCampaignBudgetRequest request + ) { + return ResponseEntity.ok(DataResponse.from( + naverAdApiService.updateCampaignBudget(connectionId, campaignId, request))); + } + + // 광고그룹 예산 수정 + @PutMapping("/adgroups/{adgroupId}/budget") + public ResponseEntity> updateAdGroupBudget( + @PathVariable Long connectionId, + @PathVariable String adgroupId, + @RequestBody NaverDTO.UpdateAdGroupBudgetRequest request + ) { + return ResponseEntity.ok(DataResponse.from( + naverAdApiService.updateAdGroupBudget(connectionId, adgroupId, request))); + } } diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java index ff8770ee..3d47e0a2 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java @@ -178,4 +178,30 @@ ResponseEntity> syncCo @Parameter(description = "통계 대상 날짜 (yyyy-MM-dd, 예: 2026-04-08)", required = true) @RequestParam("statDate") String statDate ); + + @Operation(summary = "네이버 캠페인 예산 수정", description = "캠페인의 일일 예산 및 예산 사용 여부를 수정합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "수정된 캠페인 정보 반환"), + @ApiResponse(responseCode = "500", description = "네이버 API 호출 실패") + }) + ResponseEntity> updateCampaignBudget( + @Parameter(description = "네이버 커넥션 ID", example = "1", required = true) + @PathVariable Long connectionId, + @Parameter(description = "수정할 캠페인 ID", required = true) + @PathVariable String campaignId, + @RequestBody NaverDTO.UpdateCampaignBudgetRequest request + ); + + @Operation(summary = "네이버 광고그룹 예산 수정", description = "광고그룹의 일일 예산, 예산 사용 여부, 입찰가를 수정합니다. bidAmt가 null이면 입찰가는 수정하지 않습니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "수정된 광고그룹 정보 반환"), + @ApiResponse(responseCode = "500", description = "네이버 API 호출 실패") + }) + ResponseEntity> updateAdGroupBudget( + @Parameter(description = "네이버 커넥션 ID", example = "1", required = true) + @PathVariable Long connectionId, + @Parameter(description = "수정할 광고그룹 ID", required = true) + @PathVariable String adgroupId, + @RequestBody NaverDTO.UpdateAdGroupBudgetRequest request + ); } From 56b549b94099652606a4995818f107f10e3fc3d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Fri, 29 May 2026 09:40:28 +0900 Subject: [PATCH 06/17] =?UTF-8?q?:memo:=20docs:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B2=84=20=EA=B4=91=EA=B3=A0=20=EC=BA=A0=ED=8E=98=EC=9D=B8,?= =?UTF-8?q?=20=EA=B4=91=EA=B3=A0=EA=B7=B8=EB=A3=B9=20=EC=98=88=EC=82=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20Docs=20=EC=98=88=EC=99=B8=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/docs/NaverAdApiControllerDocs.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java index 3d47e0a2..82832afc 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java @@ -182,6 +182,7 @@ ResponseEntity> syncCo @Operation(summary = "네이버 캠페인 예산 수정", description = "캠페인의 일일 예산 및 예산 사용 여부를 수정합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "수정된 캠페인 정보 반환"), + @ApiResponse(responseCode = "400", description = "예산이 10의 배수가 아님"), @ApiResponse(responseCode = "500", description = "네이버 API 호출 실패") }) ResponseEntity> updateCampaignBudget( @@ -195,6 +196,7 @@ ResponseEntity> updateCampaignBudget( @Operation(summary = "네이버 광고그룹 예산 수정", description = "광고그룹의 일일 예산, 예산 사용 여부, 입찰가를 수정합니다. bidAmt가 null이면 입찰가는 수정하지 않습니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "수정된 광고그룹 정보 반환"), + @ApiResponse(responseCode = "400", description = "예산 또는 입찰가가 10의 배수가 아님"), @ApiResponse(responseCode = "500", description = "네이버 API 호출 실패") }) ResponseEntity> updateAdGroupBudget( From f76e671704b96823cc081586fe960d81619f2f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Fri, 29 May 2026 09:43:22 +0900 Subject: [PATCH 07/17] =?UTF-8?q?:memo:=20docs:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B2=84=20API=20test=EC=9A=A9=20=EC=97=94=EB=93=9C=ED=8F=AC?= =?UTF-8?q?=EC=9D=B8=ED=8A=B8=20Swagger=20Hidden=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/NaverAdApiControllerDocs.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java index 82832afc..f96613c7 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java @@ -4,6 +4,7 @@ import com.whereyouad.WhereYouAd.domains.advertisement.application.dto.response.AdvertisementResponse; import com.whereyouad.WhereYouAd.global.response.DataResponse; import com.whereyouad.WhereYouAd.infrastructure.client.naver.dto.NaverDTO; +import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -17,6 +18,7 @@ public interface NaverAdApiControllerDocs { + @Hidden @Operation(summary = "api 통신 test용: 네이버 광고 캠페인 목록 조회", description = "연동된 네이버 광고 계정의 캠페인 목록을 조회합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "캠페인 목록 반환"), @@ -28,6 +30,7 @@ ResponseEntity>> getCampaigns( @PathVariable Long connectionId ); + @Hidden @Operation(summary = "api 통신 test용: 네이버 광고 그룹 목록 조회", description = "특정 캠페인의 광고 그룹 목록을 조회합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "광고 그룹 목록 반환"), @@ -41,6 +44,7 @@ ResponseEntity>> getAdGroups( @RequestParam("nccCampaignId") String nccCampaignId ); + @Hidden @Operation(summary = "api 통신 test용: 네이버 광고 소재 목록 조회", description = "특정 광고 그룹의 광고 소재 목록을 조회합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "광고 소재 목록 반환"), @@ -54,6 +58,7 @@ ResponseEntity>> getAds( @RequestParam("nccAdgroupId") String nccAdgroupId ); + @Hidden @Operation(summary = "api 통신 test용: 네이버 키워드 원문 조회", description = "광고 그룹에 설정된 키워드 목록을 원문으로 조회합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "키워드 목록 반환"), @@ -66,6 +71,8 @@ ResponseEntity>> getKeywords( @Parameter(description = "네이버 광고 그룹 ID", required = true) @RequestParam("nccAdgroupId") String nccAdgroupId ); + + @Hidden @Operation(summary = "api 통신 test용: 네이버 AD 리포트 생성 요청", description = "특정 일자의 AD 리포트 생성을 네이버에 요청합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "리포트 생성 요청됨(상태 확인 필요)"), @@ -78,6 +85,7 @@ ResponseEntity> requestAdReport( @RequestParam("statDt") String statDt ); + @Hidden @Operation(summary = "api 통신 test용: 네이버 AD_CONVERSION 리포트 생성 요청", description = "특정 일자의 AD_CONVERSION 리포트 생성을 네이버에 요청합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "리포트 생성 요청됨(상태 확인 필요)"), @@ -90,6 +98,7 @@ ResponseEntity> requestAdConversionRep @RequestParam("statDt") String statDt ); + @Hidden @Operation(summary = "api 통신 test용: 대용량 보고서 상태 조회", description = "생성 요청한 보고서의 상태를 확인합니다. (BUILT 상태가 되면 다운로드 가능)") @ApiResponses({ @ApiResponse(responseCode = "200", description = "상태 및 다운로드 URL 반환"), @@ -102,6 +111,7 @@ ResponseEntity> getReportStatus( @PathVariable String reportJobId ); + @Hidden @Operation(summary = "api 통신 test용: 대용량 보고서 다운로드", description = "보고서 다운로드 URL을 통해 원문(TSV 등) 데이터를 가져옵니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "보고서 데이터 원문 반환"), @@ -114,6 +124,7 @@ ResponseEntity> downloadReport( @RequestParam("url") String downloadUrl ); + @Hidden @Operation(summary = "api 통신 test용: 일별 통계 직접 조회", description = "/stats API를 이용하여 특정 대상의 일별 기본 지표를 가져옵니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "일별 통계 반환"), @@ -130,6 +141,7 @@ ResponseEntity>> getDailyStats( @RequestParam("until") String until ); + @Hidden @Operation(summary = "api 통신 test용: 네이버 메타데이터 동기화", description = "연동된 네이버 계정의 캠페인/광고그룹/광고소재를 가져와 DB에 upsert합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "동기화 완료 - 처리된 캠페인/그룹/소재 수 반환"), @@ -153,6 +165,7 @@ ResponseEntity> syncM @RequestBody AdvertisementRequest.ManualSyncRequest request ); + @Hidden @Operation(summary = "api 통신 test용: 네이버 전체 통계 동기화", description = "일별(DAILY) 기본 지표와 전환 리포트를 동기화합니다.") @ApiResponses({ @ApiResponse(responseCode = "200", description = "동기화 완료 - 처리된 광고소재 수 반환"), @@ -166,6 +179,7 @@ ResponseEntity> syncSt @RequestParam("statDate") String statDate ); + @Hidden @Operation(summary = "api 통신 test용: 네이버 전환 리포트만 동기화", description = "전환 데이터만 단독으로 동기화합니다. (기본 Stats 동기화 없이)") @ApiResponses({ @ApiResponse(responseCode = "200", description = "동기화 완료 - 처리된 전환 행 수 반환"), From 3a30ecf8e15c1dd18c6633f14834deb05e4f302d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Fri, 29 May 2026 09:56:28 +0900 Subject: [PATCH 08/17] =?UTF-8?q?:sparkles:=20feat:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B2=84=20=EA=B4=91=EA=B3=A0=20=EC=98=88=EC=82=B0=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20API=20=EC=A1=B0=EC=A7=81=EC=9B=90/ADMIN=20=EA=B6=8C?= =?UTF-8?q?=ED=95=9C=20=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/service/NaverAdApiService.java | 31 +++++++++++++++++-- .../presentation/NaverAdApiController.java | 7 +++-- .../docs/NaverAdApiControllerDocs.java | 4 +++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java index 5e342c5c..39153c41 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java @@ -1,5 +1,11 @@ package com.whereyouad.WhereYouAd.domains.advertisement.domain.service; +import com.whereyouad.WhereYouAd.domains.organization.domain.constant.OrgRole; +import com.whereyouad.WhereYouAd.domains.organization.exception.handler.OrgHandler; +import com.whereyouad.WhereYouAd.domains.organization.exception.code.OrgErrorCode; +import com.whereyouad.WhereYouAd.domains.organization.persistence.entity.OrgMember; +import com.whereyouad.WhereYouAd.domains.organization.persistence.repository.OrgMemberRepository; +import com.whereyouad.WhereYouAd.domains.platform.persistence.entity.PlatformConnection; import com.whereyouad.WhereYouAd.domains.platform.persistence.repository.PlatformConnectionRepository; import com.whereyouad.WhereYouAd.global.utils.AdApiAuthUtil; import com.whereyouad.WhereYouAd.global.adapi.dto.AdAuthRequest; @@ -25,6 +31,7 @@ public class NaverAdApiService { private final PlatformConnectionRepository connectionRepository; + private final OrgMemberRepository orgMemberRepository; private final AdApiAuthUtil adApiAuthUtil; private final NaverClient naverClient; @@ -168,7 +175,17 @@ public NaverDTO.RawReportResponse downloadReport(Long connectionId, String downl // 캠페인 예산 수정 @Transactional - public NaverDTO.CampaignResponse updateCampaignBudget(Long connectionId, String campaignId, NaverDTO.UpdateCampaignBudgetRequest request) { + public NaverDTO.CampaignResponse updateCampaignBudget(Long userId, Long connectionId, String campaignId, NaverDTO.UpdateCampaignBudgetRequest request) { + + // 조직원 및 ADMIN 권한 검증 + PlatformConnection connection = connectionRepository.findWithAccountAndOrgById(connectionId) + .orElseThrow(() -> new AdvertisementHandler(NaverAdErrorCode.NAVER_CAMPAIGN_BUDGET_UPDATE_FAILED)); + 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); + } // 예산이 10의 배수가 아닌 경우 오류(네이버 광고 예산 요청 값 검증) if (request.dailyBudget() != null && request.dailyBudget() % 10 != 0) { @@ -192,7 +209,17 @@ public NaverDTO.CampaignResponse updateCampaignBudget(Long connectionId, String // 광고그룹 예산 수정 @Transactional - public NaverDTO.AdGroupResponse updateAdGroupBudget(Long connectionId, String adgroupId, NaverDTO.UpdateAdGroupBudgetRequest request) { + public NaverDTO.AdGroupResponse updateAdGroupBudget(Long userId, Long connectionId, String adgroupId, NaverDTO.UpdateAdGroupBudgetRequest request) { + + // 조직원 및 ADMIN 권한 검증 + PlatformConnection connection = connectionRepository.findWithAccountAndOrgById(connectionId) + .orElseThrow(() -> new AdvertisementHandler(NaverAdErrorCode.NAVER_AD_GROUP_BUDGET_UPDATE_FAILED)); + 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); + } // 예산이 10의 배수가 아닌 경우 오류(네이버 광고 예산 요청 값 검증) if (request.dailyBudget() != null && request.dailyBudget() % 10 != 0) { diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java index 9a3cfc26..5b96923e 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/NaverAdApiController.java @@ -10,6 +10,7 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -143,22 +144,24 @@ public ResponseEntity> // 캠페인 예산 수정 @PutMapping("/campaigns/{campaignId}/budget") public ResponseEntity> updateCampaignBudget( + @AuthenticationPrincipal(expression = "userId") Long userId, @PathVariable Long connectionId, @PathVariable String campaignId, @RequestBody NaverDTO.UpdateCampaignBudgetRequest request ) { return ResponseEntity.ok(DataResponse.from( - naverAdApiService.updateCampaignBudget(connectionId, campaignId, request))); + naverAdApiService.updateCampaignBudget(userId, connectionId, campaignId, request))); } // 광고그룹 예산 수정 @PutMapping("/adgroups/{adgroupId}/budget") public ResponseEntity> updateAdGroupBudget( + @AuthenticationPrincipal(expression = "userId") Long userId, @PathVariable Long connectionId, @PathVariable String adgroupId, @RequestBody NaverDTO.UpdateAdGroupBudgetRequest request ) { return ResponseEntity.ok(DataResponse.from( - naverAdApiService.updateAdGroupBudget(connectionId, adgroupId, request))); + naverAdApiService.updateAdGroupBudget(userId, connectionId, adgroupId, request))); } } diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java index f96613c7..03ba70f2 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java @@ -197,9 +197,11 @@ ResponseEntity> syncCo @ApiResponses({ @ApiResponse(responseCode = "200", description = "수정된 캠페인 정보 반환"), @ApiResponse(responseCode = "400", description = "예산이 10의 배수가 아님"), + @ApiResponse(responseCode = "403", description = "조직 미가입 또는 ADMIN 권한 없음"), @ApiResponse(responseCode = "500", description = "네이버 API 호출 실패") }) ResponseEntity> updateCampaignBudget( + @Parameter(hidden = true) Long userId, @Parameter(description = "네이버 커넥션 ID", example = "1", required = true) @PathVariable Long connectionId, @Parameter(description = "수정할 캠페인 ID", required = true) @@ -211,9 +213,11 @@ ResponseEntity> updateCampaignBudget( @ApiResponses({ @ApiResponse(responseCode = "200", description = "수정된 광고그룹 정보 반환"), @ApiResponse(responseCode = "400", description = "예산 또는 입찰가가 10의 배수가 아님"), + @ApiResponse(responseCode = "403", description = "조직 미가입 또는 ADMIN 권한 없음"), @ApiResponse(responseCode = "500", description = "네이버 API 호출 실패") }) ResponseEntity> updateAdGroupBudget( + @Parameter(hidden = true) Long userId, @Parameter(description = "네이버 커넥션 ID", example = "1", required = true) @PathVariable Long connectionId, @Parameter(description = "수정할 광고그룹 ID", required = true) From fc5ad9b1b5397f85a09010be9d632b13c9a85cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Fri, 29 May 2026 10:32:54 +0900 Subject: [PATCH 09/17] =?UTF-8?q?:bug:=20fix:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B2=84=20=EA=B4=91=EA=B3=A0=EA=B7=B8=EB=A3=B9=20bidAmt=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=8B=9C=20budget=EA=B3=BC=20=EB=B3=84?= =?UTF-8?q?=EB=8F=84=20API=20=ED=98=B8=EC=B6=9C=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/service/NaverAdApiService.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java index 39153c41..d21db2f9 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java @@ -233,12 +233,17 @@ public NaverDTO.AdGroupResponse updateAdGroupBudget(Long userId, Long connection // 헤더 제작 Map headers = adApiAuthUtil.generateAuthHeaders( connectionId, AdAuthRequest.forMethodAndPath("PUT", "/ncc/adgroups/" + adgroupId)); - String fields = request.bidAmt() != null ? "budget,bidAmt" : "budget"; - // 요청 body 제작 - NaverDTO.UpdateAdGroupBudgetBody body = - new NaverDTO.UpdateAdGroupBudgetBody(adgroupId, request.useDailyBudget(), request.dailyBudget(), request.bidAmt()); - // API 호출 - return naverClient.updateAdGroupBudget(headers, adgroupId, fields, body); + // 네이버 API 제약: budget과 bidAmt는 fields에 함께 넣어도 bidAmt가 무시됨 → 각각 별도 호출 필요 + NaverDTO.AdGroupResponse result = null; + if (request.dailyBudget() != null || request.useDailyBudget() != null) { + result = naverClient.updateAdGroupBudget(headers, adgroupId, "budget", + new NaverDTO.UpdateAdGroupBudgetBody(adgroupId, request.useDailyBudget(), request.dailyBudget(), null)); + } + if (request.bidAmt() != null) { + result = naverClient.updateAdGroupBudget(headers, adgroupId, "bidAmt", + new NaverDTO.UpdateAdGroupBudgetBody(adgroupId, null, null, request.bidAmt())); + } + return result; } catch (Exception e) { log.error("[NAVER] 광고그룹 예산 수정 실패 - connectionId={}, adgroupId={}", connectionId, adgroupId, e); throw new AdvertisementHandler(NaverAdErrorCode.NAVER_AD_GROUP_BUDGET_UPDATE_FAILED); From 935207ebe784cbbb6b99d4a87da39aa94b4fde2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Fri, 29 May 2026 11:36:16 +0900 Subject: [PATCH 10/17] =?UTF-8?q?:recycle:=20refactor:=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B2=84=20=EC=98=88=EC=82=B0=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=ED=8A=B8=EB=9E=9C=EC=9E=AD=EC=85=98=20=EB=B2=94=EC=9C=84=20?= =?UTF-8?q?=EC=B6=95=EC=86=8C=20=EB=B0=8F=20=EC=BB=A4=EB=84=A5=EC=85=98=20?= =?UTF-8?q?=EB=AF=B8=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20404=20=EB=B0=98?= =?UTF-8?q?=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/service/NaverAdApiService.java | 46 ++++++------------- .../exception/code/NaverAdErrorCode.java | 3 ++ .../docs/NaverAdApiControllerDocs.java | 2 + 3 files changed, 19 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java index d21db2f9..14ab5700 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java @@ -174,32 +174,16 @@ public NaverDTO.RawReportResponse downloadReport(Long connectionId, String downl } // 캠페인 예산 수정 - @Transactional public NaverDTO.CampaignResponse updateCampaignBudget(Long userId, Long connectionId, String campaignId, NaverDTO.UpdateCampaignBudgetRequest request) { - - // 조직원 및 ADMIN 권한 검증 - PlatformConnection connection = connectionRepository.findWithAccountAndOrgById(connectionId) - .orElseThrow(() -> new AdvertisementHandler(NaverAdErrorCode.NAVER_CAMPAIGN_BUDGET_UPDATE_FAILED)); - 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); - } - - // 예산이 10의 배수가 아닌 경우 오류(네이버 광고 예산 요청 값 검증) + validateAdminOwnership(userId, connectionId); if (request.dailyBudget() != null && request.dailyBudget() % 10 != 0) { throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); } - // API 호출 try { - // 헤더 제작 Map headers = adApiAuthUtil.generateAuthHeaders( connectionId, AdAuthRequest.forMethodAndPath("PUT", "/ncc/campaigns/" + campaignId)); - // 요청 body 제작 NaverDTO.UpdateCampaignBudgetBody body = new NaverDTO.UpdateCampaignBudgetBody(campaignId, request.useDailyBudget(), request.dailyBudget()); - // API 호출 return naverClient.updateCampaignBudget(headers, campaignId, "budget", body); } catch (Exception e) { log.error("[NAVER] 캠페인 예산 수정 실패 - connectionId={}, campaignId={}", connectionId, campaignId, e); @@ -208,29 +192,15 @@ public NaverDTO.CampaignResponse updateCampaignBudget(Long userId, Long connecti } // 광고그룹 예산 수정 - @Transactional public NaverDTO.AdGroupResponse updateAdGroupBudget(Long userId, Long connectionId, String adgroupId, NaverDTO.UpdateAdGroupBudgetRequest request) { - - // 조직원 및 ADMIN 권한 검증 - PlatformConnection connection = connectionRepository.findWithAccountAndOrgById(connectionId) - .orElseThrow(() -> new AdvertisementHandler(NaverAdErrorCode.NAVER_AD_GROUP_BUDGET_UPDATE_FAILED)); - 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); - } - - // 예산이 10의 배수가 아닌 경우 오류(네이버 광고 예산 요청 값 검증) + validateAdminOwnership(userId, connectionId); if (request.dailyBudget() != null && request.dailyBudget() % 10 != 0) { throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); } if (request.bidAmt() != null && request.bidAmt() % 10 != 0) { throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); } - // API 호출 try { - // 헤더 제작 Map headers = adApiAuthUtil.generateAuthHeaders( connectionId, AdAuthRequest.forMethodAndPath("PUT", "/ncc/adgroups/" + adgroupId)); // 네이버 API 제약: budget과 bidAmt는 fields에 함께 넣어도 bidAmt가 무시됨 → 각각 별도 호출 필요 @@ -250,6 +220,18 @@ public NaverDTO.AdGroupResponse updateAdGroupBudget(Long userId, Long connection } } + @Transactional(readOnly = true) + private void validateAdminOwnership(Long userId, Long connectionId) { + PlatformConnection connection = connectionRepository.findWithAccountAndOrgById(connectionId) + .orElseThrow(() -> new AdvertisementHandler(NaverAdErrorCode.NAVER_CONNECTION_NOT_FOUND)); + 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); + } + } + // 일별 기본 지표 조회 (/stats, 기본 일 단위) @Transactional(readOnly = true) public List getDailyStats(Long connectionId, String id, String since, String until) { diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java index 40c7b212..d41171e4 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java @@ -13,6 +13,9 @@ public enum NaverAdErrorCode implements BaseErrorCode { NAVER_INVALID_DOWNLOAD_URL(HttpStatus.BAD_REQUEST, "NAVER_400_1", "유효하지 않은 다운로드 URL입니다."), NAVER_INVALID_BUDGET_VALUE(HttpStatus.BAD_REQUEST, "NAVER_400_2", "예산 및 입찰가는 10의 배수여야 합니다."), + // 404 + NAVER_CONNECTION_NOT_FOUND(HttpStatus.NOT_FOUND, "NAVER_404_1", "플랫폼 연결 정보를 찾을 수 없습니다."), + // 500 NAVER_CAMPAIGN_FETCH_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NAVER_500_1", "네이버 캠페인 목록 조회에 실패했습니다."), NAVER_AD_GROUP_FETCH_FAILED(HttpStatus.INTERNAL_SERVER_ERROR, "NAVER_500_2", "네이버 광고 그룹 목록 조회에 실패했습니다."), diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java index 03ba70f2..7aa290d5 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/presentation/docs/NaverAdApiControllerDocs.java @@ -198,6 +198,7 @@ ResponseEntity> syncCo @ApiResponse(responseCode = "200", description = "수정된 캠페인 정보 반환"), @ApiResponse(responseCode = "400", description = "예산이 10의 배수가 아님"), @ApiResponse(responseCode = "403", description = "조직 미가입 또는 ADMIN 권한 없음"), + @ApiResponse(responseCode = "404", description = "플랫폼 연결 정보 없음"), @ApiResponse(responseCode = "500", description = "네이버 API 호출 실패") }) ResponseEntity> updateCampaignBudget( @@ -214,6 +215,7 @@ ResponseEntity> updateCampaignBudget( @ApiResponse(responseCode = "200", description = "수정된 광고그룹 정보 반환"), @ApiResponse(responseCode = "400", description = "예산 또는 입찰가가 10의 배수가 아님"), @ApiResponse(responseCode = "403", description = "조직 미가입 또는 ADMIN 권한 없음"), + @ApiResponse(responseCode = "404", description = "플랫폼 연결 정보 없음"), @ApiResponse(responseCode = "500", description = "네이버 API 호출 실패") }) ResponseEntity> updateAdGroupBudget( From 5fddf603d6f049cd226db6e7fe280733df7f65c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Fri, 29 May 2026 11:38:48 +0900 Subject: [PATCH 11/17] =?UTF-8?q?:bug:=20fix:=20=EC=98=88=EC=82=B0=20?= =?UTF-8?q?=EB=B0=8F=20=EC=9E=85=EC=B0=B0=EA=B0=80=20=EC=9D=8C=EC=88=98/0?= =?UTF-8?q?=20=EC=9E=85=EB=A0=A5=20=EA=B0=92=20=EA=B1=B0=EB=B6=80=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../advertisement/domain/service/NaverAdApiService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java index 14ab5700..f177cbba 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java @@ -176,7 +176,7 @@ public NaverDTO.RawReportResponse downloadReport(Long connectionId, String downl // 캠페인 예산 수정 public NaverDTO.CampaignResponse updateCampaignBudget(Long userId, Long connectionId, String campaignId, NaverDTO.UpdateCampaignBudgetRequest request) { validateAdminOwnership(userId, connectionId); - if (request.dailyBudget() != null && request.dailyBudget() % 10 != 0) { + if (request.dailyBudget() != null && (request.dailyBudget() <= 0 || request.dailyBudget() % 10 != 0)) { throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); } try { @@ -194,10 +194,10 @@ public NaverDTO.CampaignResponse updateCampaignBudget(Long userId, Long connecti // 광고그룹 예산 수정 public NaverDTO.AdGroupResponse updateAdGroupBudget(Long userId, Long connectionId, String adgroupId, NaverDTO.UpdateAdGroupBudgetRequest request) { validateAdminOwnership(userId, connectionId); - if (request.dailyBudget() != null && request.dailyBudget() % 10 != 0) { + if (request.dailyBudget() != null && (request.dailyBudget() <= 0 || request.dailyBudget() % 10 != 0)) { throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); } - if (request.bidAmt() != null && request.bidAmt() % 10 != 0) { + if (request.bidAmt() != null && (request.bidAmt() <= 0 || request.bidAmt() % 10 != 0)) { throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); } try { From 3b57c360fb0a9883356631d60a1dc3b02d369a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Fri, 5 Jun 2026 10:49:43 +0900 Subject: [PATCH 12/17] =?UTF-8?q?:sparkles:=20feat:=20AdGroup=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=EC=97=90=20=EC=98=88=EC=82=B0=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domains/advertisement/persistence/entity/AdGroup.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java index a1b2b664..2438e464 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java @@ -43,6 +43,12 @@ public class AdGroup extends BaseEntity { @OneToMany(mappedBy = "adGroup", cascade = CascadeType.ALL) private List adContents = new ArrayList<>(); + @Column(name = "budget") + private Long budget; + + @Column(name = "bid_amount") + private Long bidAmount; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "ad_campaign_id") private AdCampaign adCampaign; From 35ec12e5351b2d245961b30f754d984277d752f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Fri, 5 Jun 2026 10:50:07 +0900 Subject: [PATCH 13/17] =?UTF-8?q?:sparkles:=20feat:=20=EC=98=88=EC=82=B0?= =?UTF-8?q?=20update=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domains/advertisement/persistence/entity/AdCampaign.java | 4 ++++ .../domains/advertisement/persistence/entity/AdGroup.java | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java index abdb4638..61a97104 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java @@ -94,4 +94,8 @@ public void update(String name, Status status, Long budget, Goal goal, this.description = description; } } + + public void updateBudget(Long budget) { + if (budget != null) this.budget = budget; + } } diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java index 2438e464..e2861a83 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdGroup.java @@ -61,4 +61,9 @@ public void update(String name, Status status, String targetingInfo) { this.targetingInfo = targetingInfo; } } + + public void updateBudget(Long budget, Long bidAmount) { + if (budget != null) this.budget = budget; + if (bidAmount != null) this.bidAmount = bidAmount; + } } From 337693858d53c13d98d9f9fd43d5f520cce511f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Mon, 8 Jun 2026 10:48:20 +0900 Subject: [PATCH 14/17] =?UTF-8?q?:sparkles:=20feat:=20campaign,=20adGroup?= =?UTF-8?q?=20=EC=98=88=EC=82=B0=20=EC=88=98=EC=A0=95=20=EC=8B=9C=20db=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/service/NaverAdApiService.java | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java index f177cbba..59437fae 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java @@ -1,5 +1,9 @@ package com.whereyouad.WhereYouAd.domains.advertisement.domain.service; +import com.whereyouad.WhereYouAd.domains.advertisement.persistence.entity.AdCampaign; +import com.whereyouad.WhereYouAd.domains.advertisement.persistence.entity.AdGroup; +import com.whereyouad.WhereYouAd.domains.advertisement.persistence.repository.AdCampaignRepository; +import com.whereyouad.WhereYouAd.domains.advertisement.persistence.repository.AdGroupRepository; import com.whereyouad.WhereYouAd.domains.organization.domain.constant.OrgRole; import com.whereyouad.WhereYouAd.domains.organization.exception.handler.OrgHandler; import com.whereyouad.WhereYouAd.domains.organization.exception.code.OrgErrorCode; @@ -34,6 +38,8 @@ public class NaverAdApiService { private final OrgMemberRepository orgMemberRepository; private final AdApiAuthUtil adApiAuthUtil; private final NaverClient naverClient; + private final AdGroupRepository adGroupRepository; + private final AdCampaignRepository adCampaignRepository; // 캠페인 목록 조회 @Transactional(readOnly = true) @@ -174,8 +180,9 @@ public NaverDTO.RawReportResponse downloadReport(Long connectionId, String downl } // 캠페인 예산 수정 + @Transactional public NaverDTO.CampaignResponse updateCampaignBudget(Long userId, Long connectionId, String campaignId, NaverDTO.UpdateCampaignBudgetRequest request) { - validateAdminOwnership(userId, connectionId); + PlatformConnection connection = validateAdminOwnership(userId, connectionId); if (request.dailyBudget() != null && (request.dailyBudget() <= 0 || request.dailyBudget() % 10 != 0)) { throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); } @@ -184,7 +191,13 @@ public NaverDTO.CampaignResponse updateCampaignBudget(Long userId, Long connecti connectionId, AdAuthRequest.forMethodAndPath("PUT", "/ncc/campaigns/" + campaignId)); NaverDTO.UpdateCampaignBudgetBody body = new NaverDTO.UpdateCampaignBudgetBody(campaignId, request.useDailyBudget(), request.dailyBudget()); - return naverClient.updateCampaignBudget(headers, campaignId, "budget", body); + NaverDTO.CampaignResponse result = naverClient.updateCampaignBudget(headers, campaignId, "budget", body); + + adCampaignRepository + .findByPlatformAccountAndExternalCampaignId(connection.getPlatformAccount(), campaignId) + .ifPresent(campaign -> campaign.updateBudget(request.dailyBudget())); + + return result; } catch (Exception e) { log.error("[NAVER] 캠페인 예산 수정 실패 - connectionId={}, campaignId={}", connectionId, campaignId, e); throw new AdvertisementHandler(NaverAdErrorCode.NAVER_CAMPAIGN_BUDGET_UPDATE_FAILED); @@ -192,8 +205,9 @@ public NaverDTO.CampaignResponse updateCampaignBudget(Long userId, Long connecti } // 광고그룹 예산 수정 + @Transactional public NaverDTO.AdGroupResponse updateAdGroupBudget(Long userId, Long connectionId, String adgroupId, NaverDTO.UpdateAdGroupBudgetRequest request) { - validateAdminOwnership(userId, connectionId); + PlatformConnection connection = validateAdminOwnership(userId, connectionId); if (request.dailyBudget() != null && (request.dailyBudget() <= 0 || request.dailyBudget() % 10 != 0)) { throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); } @@ -213,6 +227,11 @@ public NaverDTO.AdGroupResponse updateAdGroupBudget(Long userId, Long connection result = naverClient.updateAdGroupBudget(headers, adgroupId, "bidAmt", new NaverDTO.UpdateAdGroupBudgetBody(adgroupId, null, null, request.bidAmt())); } + + adGroupRepository + .findByAdCampaign_PlatformAccountAndExternalGroupId(connection.getPlatformAccount(), adgroupId) + .ifPresent(adGroup -> adGroup.updateBudget(request.dailyBudget(), request.bidAmt())); + return result; } catch (Exception e) { log.error("[NAVER] 광고그룹 예산 수정 실패 - connectionId={}, adgroupId={}", connectionId, adgroupId, e); @@ -221,7 +240,7 @@ public NaverDTO.AdGroupResponse updateAdGroupBudget(Long userId, Long connection } @Transactional(readOnly = true) - private void validateAdminOwnership(Long userId, Long connectionId) { + private PlatformConnection validateAdminOwnership(Long userId, Long connectionId) { PlatformConnection connection = connectionRepository.findWithAccountAndOrgById(connectionId) .orElseThrow(() -> new AdvertisementHandler(NaverAdErrorCode.NAVER_CONNECTION_NOT_FOUND)); Long orgId = connection.getPlatformAccount().getOrganization().getId(); @@ -230,6 +249,7 @@ private void validateAdminOwnership(Long userId, Long connectionId) { if (orgMember.getRole() != OrgRole.ADMIN) { throw new OrgHandler(OrgErrorCode.ORG_MEMBER_FORBIDDEN); } + return connection; } // 일별 기본 지표 조회 (/stats, 기본 일 단위) From a2e19dfe72c1f19ebd2b7d1c6ed21d12a6dadce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Mon, 8 Jun 2026 11:06:57 +0900 Subject: [PATCH 15/17] =?UTF-8?q?:sparkles:=20feat:=20=EC=98=88=EC=82=B0,?= =?UTF-8?q?=20=EC=9E=85=EC=B0=B0=EA=B0=80=20=EC=9E=85=EB=A0=A5=EA=B0=92=20?= =?UTF-8?q?=EB=B2=94=EC=9C=84=20=EB=B0=8F=20=EB=8B=A8=EC=9C=84=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EA=B0=95=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/service/NaverAdApiService.java | 27 ++++++++++++++----- .../exception/code/NaverAdErrorCode.java | 4 ++- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java index 59437fae..9525a41d 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/domain/service/NaverAdApiService.java @@ -183,8 +183,13 @@ public NaverDTO.RawReportResponse downloadReport(Long connectionId, String downl @Transactional public NaverDTO.CampaignResponse updateCampaignBudget(Long userId, Long connectionId, String campaignId, NaverDTO.UpdateCampaignBudgetRequest request) { PlatformConnection connection = validateAdminOwnership(userId, connectionId); - if (request.dailyBudget() != null && (request.dailyBudget() <= 0 || request.dailyBudget() % 10 != 0)) { - throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); + if (request.dailyBudget() != null) { + if (request.dailyBudget() % 10 != 0) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); + } + if (request.dailyBudget() < 50 || request.dailyBudget() > 1_000_000_000) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_RANGE); + } } try { Map headers = adApiAuthUtil.generateAuthHeaders( @@ -208,11 +213,21 @@ public NaverDTO.CampaignResponse updateCampaignBudget(Long userId, Long connecti @Transactional public NaverDTO.AdGroupResponse updateAdGroupBudget(Long userId, Long connectionId, String adgroupId, NaverDTO.UpdateAdGroupBudgetRequest request) { PlatformConnection connection = validateAdminOwnership(userId, connectionId); - if (request.dailyBudget() != null && (request.dailyBudget() <= 0 || request.dailyBudget() % 10 != 0)) { - throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); + if (request.dailyBudget() != null) { + if (request.dailyBudget() % 10 != 0) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); + } + if (request.dailyBudget() < 50 || request.dailyBudget() > 1_000_000_000) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_RANGE); + } } - if (request.bidAmt() != null && (request.bidAmt() <= 0 || request.bidAmt() % 10 != 0)) { - throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); + if (request.bidAmt() != null) { + if (request.bidAmt() % 10 != 0) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BUDGET_VALUE); + } + if (request.bidAmt() < 70 || request.bidAmt() > 100_000) { + throw new AdvertisementHandler(NaverAdErrorCode.NAVER_INVALID_BID_AMOUNT_RANGE); + } } try { Map headers = adApiAuthUtil.generateAuthHeaders( diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java index d41171e4..fe633ee2 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/exception/code/NaverAdErrorCode.java @@ -11,7 +11,9 @@ public enum NaverAdErrorCode implements BaseErrorCode { // 400 NAVER_INVALID_DOWNLOAD_URL(HttpStatus.BAD_REQUEST, "NAVER_400_1", "유효하지 않은 다운로드 URL입니다."), - NAVER_INVALID_BUDGET_VALUE(HttpStatus.BAD_REQUEST, "NAVER_400_2", "예산 및 입찰가는 10의 배수여야 합니다."), + NAVER_INVALID_BUDGET_VALUE(HttpStatus.BAD_REQUEST, "NAVER_400_2", "예산 및 입찰가는 10원 단위로 입력해야 합니다."), + NAVER_INVALID_BUDGET_RANGE(HttpStatus.BAD_REQUEST, "NAVER_400_3", "예산은 50원 이상 1,000,000,000원 이하로 입력해야 합니다."), + NAVER_INVALID_BID_AMOUNT_RANGE(HttpStatus.BAD_REQUEST, "NAVER_400_4", "입찰가는 70원 이상 100,000원 이하로 입력해야 합니다."), // 404 NAVER_CONNECTION_NOT_FOUND(HttpStatus.NOT_FOUND, "NAVER_404_1", "플랫폼 연결 정보를 찾을 수 없습니다."), From 86177c373c4ffca273af36d8715b2d7eff451883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Mon, 8 Jun 2026 11:12:32 +0900 Subject: [PATCH 16/17] =?UTF-8?q?:sparkles:=20feat:=20AdGroup=20=EB=8F=99?= =?UTF-8?q?=EA=B8=B0=ED=99=94=20=EC=8B=9C=20=EC=98=88=EC=82=B0=20=EB=B0=8F?= =?UTF-8?q?=20=EC=9E=85=EC=B0=B0=EA=B0=80=20=ED=95=84=EB=93=9C=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/naver/converter/NaverConverter.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/converter/NaverConverter.java b/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/converter/NaverConverter.java index 665fd615..b4723801 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/converter/NaverConverter.java +++ b/src/main/java/com/whereyouad/WhereYouAd/infrastructure/client/naver/converter/NaverConverter.java @@ -60,6 +60,8 @@ public static AdGroup toAdGroupEntity(NaverDTO.AdGroupResponse dto, AdCampaign c .targetingInfo(extractTargetingInfo(keywords)) .status(mapToDomainStatus(dto.status())) .adCampaign(campaign) + .budget(dto.useDailyBudget() != null && dto.useDailyBudget() ? dto.dailyBudget() : null) + .bidAmount(dto.bidAmt()) .build(); } @@ -70,6 +72,10 @@ public static void updateAdGroup(AdGroup entity, NaverDTO.AdGroupResponse dto, j mapToDomainStatus(dto.status()), extractTargetingInfo(keywords) ); + entity.updateBudget( + dto.useDailyBudget() != null && dto.useDailyBudget() ? dto.dailyBudget() : null, + dto.bidAmt() + ); } // 광고 소재 생성용 From f9e7a55bc292dbe9e5308b921619388cacca545d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EA=B7=9C?= Date: Mon, 8 Jun 2026 11:37:33 +0900 Subject: [PATCH 17/17] =?UTF-8?q?:bug:=20fix:=20=EC=BA=A0=ED=8E=98?= =?UTF-8?q?=EC=9D=B8=20=EC=98=88=EC=82=B0=20=EB=B9=84=ED=99=9C=EC=84=B1?= =?UTF-8?q?=ED=99=94=20=EC=8B=9C=20DB=20budget=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EB=AF=B8=EB=B0=98=EC=98=81=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domains/advertisement/persistence/entity/AdCampaign.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java index 61a97104..885c5f03 100644 --- a/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java +++ b/src/main/java/com/whereyouad/WhereYouAd/domains/advertisement/persistence/entity/AdCampaign.java @@ -96,6 +96,6 @@ public void update(String name, Status status, Long budget, Goal goal, } public void updateBudget(Long budget) { - if (budget != null) this.budget = budget; + this.budget = budget; } }