-
Notifications
You must be signed in to change notification settings - Fork 0
[FEAT]: 시나리오 AI 이미지 생성 기능 구현 #96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
| @Table(name = "scenarios", | ||
| indexes = { | ||
| @Index(name = "idx_scenario_user_status", columnList = "user_id, status, created_date"), | ||
| @Index(name = "idx_scenario_decision_line", columnList = "decision_line_id"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uniqueConstraints 생성 시에 유니크 속성으로 인해서 인덱스가 만들어지게 돼요.
이 부분 @Index(name = "idx_scenario_decision_line", columnList = "decision_line_id")은 지워도 무방할 것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
반영했습니다! 리뷰 감사드립니다 :)
| String s3Url = String.format( | ||
| "https://%s.s3.%s.amazonaws.com/%s", | ||
| imageAiConfig.getS3BucketName(), | ||
| imageAiConfig.getS3Region(), | ||
| fileName | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
너무 좋습니다! 👍
url의 경우에는 영건님께서 구상하고 계신 게 거의 맞습니다.
다만, 지금 이 url은 s3를 통해 직접 접근하는 방식이고, 현재 우리 팀의 버킷 설정 상 s3로의 직접적인 퍼블릭 접근이 막혀있어요.
그래서 제가 cloudfront -> s3 를 통해 리소스 접근을 하게 한 것인데요. 왜 이렇게 했는지 알려드릴게요!
-
리소스 낭비 줄이기
S3의 경우 요청(Get), 저장(Put), 삭제(Delete), 복사(Copy) 등 다양한 요청 방식으로부터 요금이 청구되는 방식이예요.
그렇다 보니, 우리가 만약 이미지를 요청하는 시나리오 창에서 영건님의 구현 방향 (s3로 직접 Get)으로 가게 되면, 그만큼의 Get 요청이 날아게 되고, 높은 요금 청구로 이어지게 될 가능성이 생겨요. 그래서 이미지, 정적 웹사이트 등과 같은 정적 데이터의 경우에는 CloudFront와 같은 CDN 서비스를 통해 호출하는 방식을 권장하고 있어요. -
CloudFront의 동작 방식
위에서 말씀드린 것처럼 CloudFront -> S3를 통해 요청을 보내는 방향으로 제가 설정해놓았는데요.
최초로 이미지를 불러온다고 하면 CloudFront가 S3로부터 경로에 맞는 사진을 달라고 요청합니다. 이 때 처음 불러오는 거라면 S3에 Get 요청이 온 것이므로 Get 비용이 발생됩니다. 그리고 CloudFront 서버에서는 이 이미지에 대해 캐싱 작업을 하게 돼요. 여기가 CloudFront의 장점이 드러나는 부분인데요. 사용자가 새로고침 등으로 인해 이미지를 다시 요청한다고 하면, CloudFront -> S3로 가는 게 아니라, CloudFront에서 캐싱된 이미지를 바로 리턴해줍니다. 그래서 S3 쪽에 Get 요청이 가지 않게 돼요.
다만, 제가 ttl 이라고 하는 "최초 요청 이후부터 몇 초 동안은 캐싱하게 해주세요" 라고 하는 부분이 있어서, 일정 시간이 지나고 나면 그 때 Get 요청이 S3에 다시 가게 돼요. -
보안
S3에 직접적인 퍼블릭 접근이 웬만하면 막혀있기 때문에, 권한이 없는 외부 사람들이 악의적으로 이미지를 삭제하는 등의 행위를 일체 방어하는 게 가능해지죠. -
CloudFront의 비용적 메리트
S3에 비해 장점을 가져요. 프리티어를 사용하고 있지 않고, 서비스 규모가 작은 현재 상황에서는 이 차이가 더욱 커지게 되구요.- CloudFront
- 매월 요청(http / https) 수 1천만 건 무료 => 이후 요금 부과 시작
- 매월 대역폭 1TB 무료 => 이후 요금 부과 시작
- 상시 CloudFront가 S3에서 데이터를 가져올 때 전송 비용 무료
- 캐싱된 요청 덕에 response 사이즈가 작음
- S3
- 요청마다 비용 부과
- 대역폭 무료 없음. 따라서 이미지 캐시가 되지 않으므로 매 요청마다 이미지 크기에 해당하는 요금 발생
- jpeg로 고민해주신 거 너무 좋아요! 👍👍
- CloudFront
-
그러면 어떻게 이미지 url을 줘야 할까요?
저장해주실 때, (https:// - 선택) + AWS_CLOUD_FRONT_DOMAIN (.env.production 참고) + "/" + 파일명 조합으로 해주시는 게 좋아요. => https://AWS_CLOUD_FRONT_DOMAIN/파일명
그러면 이제 프론트 측에서 이미지 요청을 보낼 때는 우리 cdn 링크로 요청을 보내게 될 거예요!
한편 만약에 cdn 도메인이 바뀌면 나중에 어떻게 처리해야할까에 대한 고민이 남긴 합니다
lcs9317
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋습니다
simount3
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다
제목 규칙 자세히 보기
[TYPE]: 제목FEATFIXREFACTORCOMMENTSTYLETESTCHOREINIT무엇을 / 왜
ImageAiClient인터페이스 및StableDiffusionImageClient구현체 추가ImageAiProperties설정 클래스 추가 (API 키, URL, 타임아웃, 재시도 설정)AiService.generateImage()메서드 구현application.yml에 이미지 AI 설정 추가 (Stable Diffusion API 키, 타임아웃, 재시도)StorageService인터페이스 추가 (Local/S3 추상화)LocalStorageService구현체 추가 (개발 환경용)application.yml에 스토리지 타입 설정 추가 (storage-type: local,local-storage-path,local-base-url)AsyncConfig클래스에aiTaskExecutorBean 추가application.yml에 스레드풀 설정 추가@EnableAsync활성화AiServiceImpl에AiParsingExceptionimport 및 사용AiServiceException유지AiParsingException으로 변경exceptionally()핸들러에서AiParsingException을 그대로 전파하도록 수정 (cause 체크 로직 추가)AiParsingException→AiServiceException으로 변경AiParsingExceptionexpect 유지ObjectMapper주입 (setField()메서드 사용)handleImageGeneration()메서드에서aiService.generateImage()호출Scenario.img필드에 저장WebMvcConfigurer구현하여/images/**경로를 로컬 파일 시스템에 매핑로컬 환경에서만 활성화 (
storage-type: local조건)1시간 브라우저 캐싱 설정
왜(Why):
exceptionally()핸들러가 모든 예외를AiServiceException으로 감싸는 문제 해결AiServiceImpl)의 예외 타입과 테스트 기대값을 일치시켜 테스트 정확성 확보SituationPrompt.extractSituation())는 Mockito로 mock 불가하므로 실제 ObjectMapper 사용/images/**URL로 저장된 이미지 파일 접근 가능<img>태그로 이미지 직접 로드 가능어떻게(요약) — 3줄 이내
영향 범위
체크리스트
ToDo (선택)
스크린샷/증빙(선택)
이슈 연결 (자동)
Closes #97