refactor: webClientConfig 변경

This commit is contained in:
yuhalog 2025-06-18 11:34:54 +09:00
parent 9108232c5a
commit b2a40bcee8
7 changed files with 29 additions and 38 deletions

View File

@ -48,33 +48,19 @@ public class PosterContentService implements PosterContentUseCase {
@Override @Override
@Transactional @Transactional
public PosterContentCreateResponse generatePosterContent(List<MultipartFile> images, PosterContentCreateRequest request) { public PosterContentCreateResponse generatePosterContent(List<MultipartFile> images, PosterContentCreateRequest request) {
log.info("지점1-1");
// 1. 이미지 blob storage에 저장하고 request 저장 // 1. 이미지 blob storage에 저장하고 request 저장
List<String> imageUrls = blobStorageService.uploadImage(images, posterImageContainer); List<String> imageUrls = blobStorageService.uploadImage(images, posterImageContainer);
request.setImages(imageUrls); request.setImages(imageUrls);
log.info("지점2-1");
// 2. AI 요청 // 2. AI 요청
String generatedPoster = aiPosterGenerator.generatePoster(request); String generatedPoster = aiPosterGenerator.generatePoster(request);
// 3. 저장
Content savedContent = savePosterContent(request, generatedPoster);
// 생성 조건 정보 구성
CreationConditions conditions = CreationConditions.builder()
.category(request.getCategory())
.requirement(request.getRequirement())
.eventName(request.getEventName())
.startDate(request.getStartDate())
.endDate(request.getEndDate())
.photoStyle(request.getPhotoStyle())
.build();
return PosterContentCreateResponse.builder() return PosterContentCreateResponse.builder()
.contentId(null) // 임시 생성이므로 ID 없음 .contentId(null) // 임시 생성이므로 ID 없음
.contentType(ContentType.POSTER.name()) .contentType(ContentType.POSTER.name())
.title(request.getTitle()) .title(request.getTitle())
.posterImage(generatedPoster) .content(generatedPoster)
.posterSizes(new HashMap<>()) // 반환 (사이즈 변환 안함)
.status(ContentStatus.DRAFT.name()) .status(ContentStatus.DRAFT.name())
.build(); .build();
} }
@ -85,7 +71,7 @@ public class PosterContentService implements PosterContentUseCase {
* @param request 포스터 콘텐츠 저장 요청 * @param request 포스터 콘텐츠 저장 요청
*/ */
@Transactional @Transactional
public Content savePosterContent(PosterContentCreateRequest request, String generatedPoster) { public Content savePosterContent(PosterContentSaveRequest request) {
// 생성 조건 구성 // 생성 조건 구성
CreationConditions conditions = CreationConditions.builder() CreationConditions conditions = CreationConditions.builder()
.category(request.getCategory()) .category(request.getCategory())
@ -101,7 +87,7 @@ public class PosterContentService implements PosterContentUseCase {
.contentType(ContentType.POSTER) .contentType(ContentType.POSTER)
.platform(Platform.GENERAL) .platform(Platform.GENERAL)
.title(request.getTitle()) .title(request.getTitle())
.content(generatedPoster) // .content(request.gen)
.images(request.getImages()) .images(request.getImages())
.status(ContentStatus.PUBLISHED) .status(ContentStatus.PUBLISHED)
.creationConditions(conditions) .creationConditions(conditions)
@ -109,8 +95,6 @@ public class PosterContentService implements PosterContentUseCase {
.build(); .build();
// 저장 // 저장
Content savedContent = contentRepository.save(content); return contentRepository.save(content);
return savedContent;
} }
} }

View File

@ -1,6 +1,7 @@
// marketing-content/src/main/java/com/won/smarketing/content/application/usecase/PosterContentUseCase.java // marketing-content/src/main/java/com/won/smarketing/content/application/usecase/PosterContentUseCase.java
package com.won.smarketing.content.application.usecase; package com.won.smarketing.content.application.usecase;
import com.won.smarketing.content.domain.model.Content;
import com.won.smarketing.content.presentation.dto.PosterContentCreateRequest; import com.won.smarketing.content.presentation.dto.PosterContentCreateRequest;
import com.won.smarketing.content.presentation.dto.PosterContentCreateResponse; import com.won.smarketing.content.presentation.dto.PosterContentCreateResponse;
import com.won.smarketing.content.presentation.dto.PosterContentSaveRequest; import com.won.smarketing.content.presentation.dto.PosterContentSaveRequest;
@ -15,10 +16,15 @@ import java.util.List;
public interface PosterContentUseCase { public interface PosterContentUseCase {
/** /**
* 포스터 콘텐츠 생성 저장 * 포스터 콘텐츠 생성
* @param request 포스터 콘텐츠 생성 요청 * @param request 포스터 콘텐츠 생성 요청
* @return 포스터 콘텐츠 생성 응답 * @return 포스터 콘텐츠 생성 응답
*/ */
PosterContentCreateResponse generatePosterContent(List<MultipartFile> images, PosterContentCreateRequest request); PosterContentCreateResponse generatePosterContent(List<MultipartFile> images, PosterContentCreateRequest request);
/**
* 포스터 콘텐츠 저장
* @param request 포스터 콘텐츠 저장 요청
*/
Content savePosterContent(PosterContentSaveRequest request);
} }

View File

@ -1,4 +1,3 @@
// marketing-content/src/main/java/com/won/smarketing/content/config/WebClientConfig.java
package com.won.smarketing.content.config; package com.won.smarketing.content.config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -20,8 +19,8 @@ public class WebClientConfig {
@Bean @Bean
public WebClient webClient() { public WebClient webClient() {
HttpClient httpClient = HttpClient.create() HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 15000) // 연결 타임아웃: 15초
.responseTimeout(Duration.ofMillis(30000)); .responseTimeout(Duration.ofMinutes(5)); // 응답 타임아웃: 5분
return WebClient.builder() return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient)) .clientConnector(new ReactorClientHttpConnector(httpClient))

View File

@ -51,7 +51,7 @@ public class PythonAiPosterGenerator implements AiPosterGenerator {
.bodyValue(requestBody) .bodyValue(requestBody)
.retrieve() .retrieve()
.bodyToMono(Map.class) .bodyToMono(Map.class)
.timeout(Duration.ofSeconds(60)) // 포스터 생성은 시간이 오래 걸릴 있음 .timeout(Duration.ofSeconds(90)) // 포스터 생성은 시간이 오래 걸릴 있음
.block(); .block();
// 응답에서 content(이미지 URL) 추출 // 응답에서 content(이미지 URL) 추출

View File

@ -85,6 +85,19 @@ public class ContentController {
return ResponseEntity.ok(ApiResponse.success(response, "포스터 콘텐츠가 성공적으로 생성되었습니다.")); return ResponseEntity.ok(ApiResponse.success(response, "포스터 콘텐츠가 성공적으로 생성되었습니다."));
} }
/**
* 홍보 포스터 저장
*
* @param request 포스터 콘텐츠 저장 요청
* @return 저장 성공 응답
*/
@Operation(summary = "홍보 포스터 저장", description = "생성된 홍보 포스터를 저장합니다.")
@PostMapping("/poster/save")
public ResponseEntity<ApiResponse<Void>> savePosterContent(@Valid @RequestBody PosterContentSaveRequest request) {
posterContentUseCase.savePosterContent(request);
return ResponseEntity.ok(ApiResponse.success(null, "포스터 콘텐츠가 성공적으로 저장되었습니다."));
}
/** /**
* 콘텐츠 수정 * 콘텐츠 수정
* *

View File

@ -31,19 +31,9 @@ public class PosterContentCreateResponse {
@Schema(description = "생성된 포스터 타입") @Schema(description = "생성된 포스터 타입")
private String contentType; private String contentType;
@Schema(description = "포스터 이미지 URL")
private String posterImage;
@Schema(description = "원본 이미지 URL 목록")
private List<String> originalImages;
@Schema(description = "이미지 스타일", example = "모던") @Schema(description = "이미지 스타일", example = "모던")
private String imageStyle; private String imageStyle;
@Schema(description = "생성 상태", example = "DRAFT") @Schema(description = "생성 상태", example = "DRAFT")
private String status; private String status;
@Schema(description = "포스터사이즈", example = "800x600")
private Map<String, String> posterSizes;
} }

View File

@ -32,7 +32,6 @@ public class PosterContentSaveRequest {
@Schema(description = "발행 상태", example = "PUBLISHED") @Schema(description = "발행 상태", example = "PUBLISHED")
private String status; private String status;
// CreationConditions에 필요한 필드들
@Schema(description = "콘텐츠 카테고리", example = "이벤트") @Schema(description = "콘텐츠 카테고리", example = "이벤트")
private String category; private String category;