From 4e239c5afd258fad48c9e04c1c137b3c7cdcaee4 Mon Sep 17 00:00:00 2001 From: lsh9672 Date: Fri, 13 Jun 2025 11:26:43 +0900 Subject: [PATCH 01/12] =?UTF-8?q?feat=20:=20member=20git=20action=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/member-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/member-ci.yml b/.github/workflows/member-ci.yml index 78339de..e313066 100644 --- a/.github/workflows/member-ci.yml +++ b/.github/workflows/member-ci.yml @@ -23,6 +23,7 @@ env: MANIFEST_REPO: dg04-hi/hi-manifest MANIFEST_FILE_PATH: member/deployment.yml + jobs: build-and-push: runs-on: ubuntu-latest From 1d4a40357ead554a738927719747cbc4079b6999 Mon Sep 17 00:00:00 2001 From: lsh9672 Date: Fri, 13 Jun 2025 13:54:45 +0900 Subject: [PATCH 02/12] =?UTF-8?q?fix=20:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/ktds/hi/member/config/SecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/member/src/main/java/com/ktds/hi/member/config/SecurityConfig.java b/member/src/main/java/com/ktds/hi/member/config/SecurityConfig.java index 8a0e6b0..ea30d1c 100644 --- a/member/src/main/java/com/ktds/hi/member/config/SecurityConfig.java +++ b/member/src/main/java/com/ktds/hi/member/config/SecurityConfig.java @@ -38,7 +38,7 @@ public class SecurityConfig { .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(authz -> authz - .requestMatchers("/api/auth/**", "/api/members/register").permitAll() + .requestMatchers("/api/auth/**", "/api/members/register", "/api/auth/login").permitAll() .requestMatchers("/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**").permitAll() .requestMatchers("/swagger-resources/**", "/webjars/**").permitAll() .requestMatchers("/actuator/**").permitAll() From fb830ac16bbe3b89bfad7a14ec1f9c221bc30b88 Mon Sep 17 00:00:00 2001 From: lsh9672 Date: Fri, 13 Jun 2025 14:14:19 +0900 Subject: [PATCH 03/12] =?UTF-8?q?fix=20:=20=EB=B6=84=EC=84=9D=20api=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../biz/service/AnalyticsService.java | 276 +++++++++++++++++- 1 file changed, 264 insertions(+), 12 deletions(-) diff --git a/analytics/src/main/java/com/ktds/hi/analytics/biz/service/AnalyticsService.java b/analytics/src/main/java/com/ktds/hi/analytics/biz/service/AnalyticsService.java index 18c40af..2a36644 100644 --- a/analytics/src/main/java/com/ktds/hi/analytics/biz/service/AnalyticsService.java +++ b/analytics/src/main/java/com/ktds/hi/analytics/biz/service/AnalyticsService.java @@ -12,6 +12,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -119,32 +120,283 @@ public class AnalyticsService implements AnalyticsUseCase { @Override public StoreStatisticsResponse getStoreStatistics(Long storeId, LocalDate startDate, LocalDate endDate) { - // 이전 구현과 동일 - return null; // 구현 생략 + log.info("매장 통계 조회 시작: storeId={}, startDate={}, endDate={}", storeId, startDate, endDate); + + try { + // 1. 캐시 키 생성 + String cacheKey = String.format("statistics:store:%d:%s:%s", storeId, startDate, endDate); + var cachedResult = cachePort.getAnalyticsCache(cacheKey); + if (cachedResult.isPresent()) { + log.info("캐시에서 통계 데이터 반환: storeId={}", storeId); + return (StoreStatisticsResponse) cachedResult.get(); + } + + // 2. 주문 통계 데이터 조회 (실제 OrderStatistics 도메인 필드 사용) + var orderStatistics = orderDataPort.getOrderStatistics(storeId, startDate, endDate); + + // 3. 응답 생성 + StoreStatisticsResponse response = StoreStatisticsResponse.builder() + .storeId(storeId) + .startDate(startDate) + .endDate(endDate) + .totalOrders(orderStatistics.getTotalOrders()) + .totalRevenue(orderStatistics.getTotalRevenue()) + .averageOrderValue(orderStatistics.getAverageOrderValue()) + .peakHour(orderStatistics.getPeakHour()) + .popularMenus(orderStatistics.getPopularMenus()) + .customerAgeDistribution(orderStatistics.getCustomerAgeDistribution()) + .build(); + + // 4. 캐시에 저장 + cachePort.putAnalyticsCache(cacheKey, response, java.time.Duration.ofMinutes(30)); + + log.info("매장 통계 조회 완료: storeId={}", storeId); + return response; + + } catch (Exception e) { + log.error("매장 통계 조회 중 오류 발생: storeId={}", storeId, e); + throw new RuntimeException("매장 통계 조회에 실패했습니다.", e); + } } @Override public AiFeedbackSummaryResponse getAIFeedbackSummary(Long storeId) { - // 이전 구현과 동일 - return null; // 구현 생략 + log.info("AI 피드백 요약 조회 시작: storeId={}", storeId); + + try { + // 1. 캐시에서 확인 + String cacheKey = "ai_feedback_summary:store:" + storeId; + var cachedResult = cachePort.getAnalyticsCache(cacheKey); + if (cachedResult.isPresent()) { + return (AiFeedbackSummaryResponse) cachedResult.get(); + } + + // 2. AI 피드백 조회 + var aiFeedback = analyticsPort.findAIFeedbackByStoreId(storeId); + + if (aiFeedback.isEmpty()) { + // 3. 피드백이 없으면 기본 응답 생성 + AiFeedbackSummaryResponse emptyResponse = AiFeedbackSummaryResponse.builder() + .storeId(storeId) + .hasData(false) + .message("분석할 데이터가 부족합니다.") + .lastUpdated(LocalDateTime.now()) + .build(); + + cachePort.putAnalyticsCache(cacheKey, emptyResponse, java.time.Duration.ofHours(1)); + return emptyResponse; + } + + // 4. 응답 생성 + AiFeedbackSummaryResponse response = AiFeedbackSummaryResponse.builder() + .storeId(storeId) + .hasData(true) + .message("AI 분석이 완료되었습니다.") + .overallScore(aiFeedback.get().getConfidenceScore()) + .keyInsight(aiFeedback.get().getSummary()) + .priorityRecommendation(getFirstRecommendation(aiFeedback.get())) + .lastUpdated(aiFeedback.get().getUpdatedAt()) + .build(); + + // 5. 캐시에 저장 + cachePort.putAnalyticsCache(cacheKey, response, java.time.Duration.ofHours(2)); + + log.info("AI 피드백 요약 조회 완료: storeId={}", storeId); + return response; + + } catch (Exception e) { + log.error("AI 피드백 요약 조회 중 오류 발생: storeId={}", storeId, e); + throw new RuntimeException("AI 피드백 요약 조회에 실패했습니다.", e); + } } @Override public ReviewAnalysisResponse getReviewAnalysis(Long storeId) { - // 이전 구현과 동일 - return null; // 구현 생략 + log.info("리뷰 분석 조회 시작: storeId={}", storeId); + + try { + // 1. 캐시에서 확인 + String cacheKey = "review_analysis:store:" + storeId; + var cachedResult = cachePort.getAnalyticsCache(cacheKey); + if (cachedResult.isPresent()) { + return (ReviewAnalysisResponse) cachedResult.get(); + } + + // 2. 최근 리뷰 데이터 조회 (30일) + List recentReviews = externalReviewPort.getRecentReviews(storeId, 30); + + if (recentReviews.isEmpty()) { + ReviewAnalysisResponse emptyResponse = ReviewAnalysisResponse.builder() + .storeId(storeId) + .totalReviews(0) + .positiveReviewCount(0) + .negativeReviewCount(0) + .positiveRate(0.0) + .negativeRate(0.0) + .analysisDate(LocalDate.now()) + .build(); + + cachePort.putAnalyticsCache(cacheKey, emptyResponse, java.time.Duration.ofHours(1)); + return emptyResponse; + } + + // 3. 응답 생성 + int positiveCount = countPositiveReviews(recentReviews); + int negativeCount = countNegativeReviews(recentReviews); + int totalCount = recentReviews.size(); + + ReviewAnalysisResponse response = ReviewAnalysisResponse.builder() + .storeId(storeId) + .totalReviews(totalCount) + .positiveReviewCount(positiveCount) + .negativeReviewCount(negativeCount) + .positiveRate((double) positiveCount / totalCount * 100) + .negativeRate((double) negativeCount / totalCount * 100) + .analysisDate(LocalDate.now()) + .build(); + + // 4. 캐시에 저장 + cachePort.putAnalyticsCache(cacheKey, response, java.time.Duration.ofHours(4)); + + log.info("리뷰 분석 조회 완료: storeId={}", storeId); + return response; + + } catch (Exception e) { + log.error("리뷰 분석 중 오류 발생: storeId={}", storeId, e); + throw new RuntimeException("리뷰 분석에 실패했습니다.", e); + } } // private 메서드들 @Transactional - private Analytics generateNewAnalytics(Long storeId) { - // 이전 구현과 동일 - return null; // 구현 생략 + public Analytics generateNewAnalytics(Long storeId) { + log.info("새로운 분석 데이터 생성 시작: storeId={}", storeId); + + try { + // 1. 리뷰 데이터 수집 + List reviewData = externalReviewPort.getReviewData(storeId); + int totalReviews = reviewData.size(); + + if (totalReviews == 0) { + log.warn("리뷰 데이터가 없어 기본값으로 분석 데이터 생성: storeId={}", storeId); + return createDefaultAnalytics(storeId); + } + + // 2. 기본 통계 계산 + double averageRating = 4.0; // 기본값 + double sentimentScore = 0.5; // 중립 + double positiveRate = 60.0; + double negativeRate = 20.0; + + // 3. Analytics 도메인 객체 생성 + Analytics analytics = Analytics.builder() + .storeId(storeId) + .totalReviews(totalReviews) + .averageRating(averageRating) + .sentimentScore(sentimentScore) + .positiveReviewRate(positiveRate) + .negativeReviewRate(negativeRate) + .lastAnalysisDate(LocalDateTime.now()) + .createdAt(LocalDateTime.now()) + .updatedAt(LocalDateTime.now()) + .build(); + + // 4. 데이터베이스에 저장 + Analytics saved = analyticsPort.saveAnalytics(analytics); + + log.info("새로운 분석 데이터 생성 완료: storeId={}", storeId); + return saved; + + } catch (Exception e) { + log.error("분석 데이터 생성 중 오류 발생: storeId={}", storeId, e); + return createDefaultAnalytics(storeId); + } } @Transactional - private AiFeedback generateAIFeedback(Long storeId) { - // 이전 구현과 동일 - return null; // 구현 생략 + public AiFeedback generateAIFeedback(Long storeId) { + log.info("AI 피드백 생성 시작: storeId={}", storeId); + + try { + // 1. 최근 30일 리뷰 데이터 수집 + List reviewData = externalReviewPort.getRecentReviews(storeId, 30); + + if (reviewData.isEmpty()) { + log.warn("AI 피드백 생성을 위한 리뷰 데이터가 없습니다: storeId={}", storeId); + return createDefaultAIFeedback(storeId); + } + + // 2. AI 피드백 생성 (실제로는 AI 서비스 호출) + AiFeedback aiFeedback = AiFeedback.builder() + .storeId(storeId) + .summary("고객들의 전반적인 만족도가 높습니다.") + .positivePoints(List.of("맛이 좋다", "서비스가 친절하다", "분위기가 좋다")) + .improvementPoints(List.of("대기시간 단축", "가격 경쟁력", "메뉴 다양성")) + .recommendations(List.of("특별 메뉴 개발", "예약 시스템 도입", "고객 서비스 교육")) + .sentimentAnalysis("POSITIVE") + .confidenceScore(0.85) + .generatedAt(LocalDateTime.now()) + .createdAt(LocalDateTime.now()) + .updatedAt(LocalDateTime.now()) + .build(); + + // 3. 데이터베이스에 저장 + AiFeedback saved = analyticsPort.saveAIFeedback(aiFeedback); + + log.info("AI 피드백 생성 완료: storeId={}", storeId); + return saved; + + } catch (Exception e) { + log.error("AI 피드백 생성 중 오류 발생: storeId={}", storeId, e); + return createDefaultAIFeedback(storeId); + } + + + } + + private Analytics createDefaultAnalytics(Long storeId) { + return Analytics.builder() + .storeId(storeId) + .totalReviews(0) + .averageRating(0.0) + .sentimentScore(0.0) + .positiveReviewRate(0.0) + .negativeReviewRate(0.0) + .lastAnalysisDate(LocalDateTime.now()) + .createdAt(LocalDateTime.now()) + .updatedAt(LocalDateTime.now()) + .build(); + } + + private AiFeedback createDefaultAIFeedback(Long storeId) { + return AiFeedback.builder() + .storeId(storeId) + .summary("분석할 리뷰 데이터가 부족합니다.") + .positivePoints(List.of("데이터 부족으로 분석 불가")) + .improvementPoints(List.of("리뷰 데이터 수집 필요")) + .recommendations(List.of("고객들의 리뷰 작성을 유도해보세요")) + .sentimentAnalysis("NEUTRAL") + .confidenceScore(0.0) + .generatedAt(LocalDateTime.now()) + .createdAt(LocalDateTime.now()) + .updatedAt(LocalDateTime.now()) + .build(); + } + + private String getFirstRecommendation(AiFeedback feedback) { + if (feedback.getRecommendations() != null && !feedback.getRecommendations().isEmpty()) { + return feedback.getRecommendations().get(0); + } + return "추천사항이 없습니다."; + } + + private int countPositiveReviews(List reviews) { + // 실제로는 AI 서비스를 통한 감정 분석 필요 + return (int) (reviews.size() * 0.6); // 60% 가정 + } + + private int countNegativeReviews(List reviews) { + // 실제로는 AI 서비스를 통한 감정 분석 필요 + return (int) (reviews.size() * 0.2); // 20% 가정 } } From 4e19516eac0e8eaa2cd5bc6d7b99cc42527ebb70 Mon Sep 17 00:00:00 2001 From: UNGGU0704 Date: Fri, 13 Jun 2025 14:21:38 +0900 Subject: [PATCH 04/12] =?UTF-8?q?update:=20ddl=5Fauto=20update=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- analytics/src/main/resources/application.yml | 2 +- common/src/main/resources/application-common.yml | 2 +- member/src/main/resources/application.yml | 2 +- recommend/src/main/resources/application.yml | 2 +- review/src/main/resources/application.yml | 2 +- store/src/main/resources/application.yml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/analytics/src/main/resources/application.yml b/analytics/src/main/resources/application.yml index 751ad1c..c18bf57 100644 --- a/analytics/src/main/resources/application.yml +++ b/analytics/src/main/resources/application.yml @@ -18,7 +18,7 @@ spring: jpa: hibernate: - ddl-auto: ${JPA_DDL_AUTO:create} + ddl-auto: ${JPA_DDL_AUTO:update} show-sql: ${JPA_SHOW_SQL:false} properties: hibernate: diff --git a/common/src/main/resources/application-common.yml b/common/src/main/resources/application-common.yml index 06c3902..32ccc51 100644 --- a/common/src/main/resources/application-common.yml +++ b/common/src/main/resources/application-common.yml @@ -2,7 +2,7 @@ spring: # JPA 설정 jpa: hibernate: - ddl-auto: ${JPA_DDL_AUTO:validate} + ddl-auto: ${JPA_DDL_AUTO:update} naming: physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl diff --git a/member/src/main/resources/application.yml b/member/src/main/resources/application.yml index 6851aa9..202da20 100644 --- a/member/src/main/resources/application.yml +++ b/member/src/main/resources/application.yml @@ -13,7 +13,7 @@ spring: jpa: hibernate: - ddl-auto: ${JPA_DDL_AUTO:create} + ddl-auto: ${JPA_DDL_AUTO:update} show-sql: ${JPA_SHOW_SQL:false} properties: hibernate: diff --git a/recommend/src/main/resources/application.yml b/recommend/src/main/resources/application.yml index 5a8062c..eff4854 100644 --- a/recommend/src/main/resources/application.yml +++ b/recommend/src/main/resources/application.yml @@ -30,7 +30,7 @@ spring: # JPA 설정 jpa: hibernate: - ddl-auto: ${JPA_DDL_AUTO:create} + ddl-auto: ${JPA_DDL_AUTO:update} show-sql: ${JPA_SHOW_SQL:false} properties: hibernate: diff --git a/review/src/main/resources/application.yml b/review/src/main/resources/application.yml index c84841a..f20d8a6 100644 --- a/review/src/main/resources/application.yml +++ b/review/src/main/resources/application.yml @@ -13,7 +13,7 @@ spring: jpa: hibernate: - ddl-auto: ${JPA_DDL_AUTO:create} + ddl-auto: ${JPA_DDL_AUTO:update} show-sql: ${JPA_SHOW_SQL:false} properties: hibernate: diff --git a/store/src/main/resources/application.yml b/store/src/main/resources/application.yml index fedfdbe..cb7b868 100644 --- a/store/src/main/resources/application.yml +++ b/store/src/main/resources/application.yml @@ -13,7 +13,7 @@ spring: jpa: hibernate: - ddl-auto: ${JPA_DDL_AUTO:create} + ddl-auto: ${JPA_DDL_AUTO:update} show-sql: ${JPA_SHOW_SQL:false} properties: hibernate: From da118db233bee8216281f5d15488a0011b59d72c Mon Sep 17 00:00:00 2001 From: UNGGU0704 Date: Fri, 13 Jun 2025 14:39:31 +0900 Subject: [PATCH 05/12] =?UTF-8?q?Add:=20member-swagger=20token=20scheme=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 --- .../com/ktds/hi/member/config/SwaggerConfig.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/member/src/main/java/com/ktds/hi/member/config/SwaggerConfig.java b/member/src/main/java/com/ktds/hi/member/config/SwaggerConfig.java index fbd659f..993c465 100644 --- a/member/src/main/java/com/ktds/hi/member/config/SwaggerConfig.java +++ b/member/src/main/java/com/ktds/hi/member/config/SwaggerConfig.java @@ -2,6 +2,7 @@ package com.ktds.hi.member.config; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.servers.Server; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -22,4 +23,17 @@ public class SwaggerConfig { .description("회원 가입, 로그인, 취향 관리 등 회원 관련 기능을 제공하는 API") .version("1.0.0")); } + + /** + * JWT Bearer 토큰을 위한 Security Scheme 생성 + */ + private SecurityScheme createAPIKeyScheme() { + return new SecurityScheme() + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT") + .in(SecurityScheme.In.HEADER) + .name("Authorization") + .description("JWT 토큰을 입력하세요 (Bearer 접두사 제외)"); + } } From a863e9e73478189ead7bd796cb9564f0fd17fa18 Mon Sep 17 00:00:00 2001 From: UNGGU0704 Date: Fri, 13 Jun 2025 14:42:21 +0900 Subject: [PATCH 06/12] =?UTF-8?q?Add:=20=EB=82=98=EB=A8=B8=EC=A7=80=20swag?= =?UTF-8?q?ger=20token=20scheme=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analytics/infra/config/SwaggerConfig.java | 20 +++++++++++++++++- .../recommend/infra/config/SwaggerConfig.java | 21 +++++++++++++++++++ .../hi/review/infra/config/SwaggerConfig.java | 21 +++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/analytics/src/main/java/com/ktds/hi/analytics/infra/config/SwaggerConfig.java b/analytics/src/main/java/com/ktds/hi/analytics/infra/config/SwaggerConfig.java index 3f96b18..e894a51 100644 --- a/analytics/src/main/java/com/ktds/hi/analytics/infra/config/SwaggerConfig.java +++ b/analytics/src/main/java/com/ktds/hi/analytics/infra/config/SwaggerConfig.java @@ -23,5 +23,23 @@ public class SwaggerConfig { .description("하이오더 분석 서비스 API 문서") .version("1.0.0")); } - + /** + * JWT Bearer 토큰을 위한 Security Scheme 생성 + */ + private SecurityScheme createAPIKeyScheme() { + return new SecurityScheme() + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT") + .in(SecurityScheme.In.HEADER) + .name("Authorization") + .description(""" + JWT 토큰을 입력하세요 + + 사용법: + 1. 로그인 API로 토큰 발급 + 2. Bearer 접두사 없이 토큰만 입력 + 3. 예: eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOi... + """); + } } diff --git a/recommend/src/main/java/com/ktds/hi/recommend/infra/config/SwaggerConfig.java b/recommend/src/main/java/com/ktds/hi/recommend/infra/config/SwaggerConfig.java index 425511b..f0e4f1d 100644 --- a/recommend/src/main/java/com/ktds/hi/recommend/infra/config/SwaggerConfig.java +++ b/recommend/src/main/java/com/ktds/hi/recommend/infra/config/SwaggerConfig.java @@ -2,6 +2,7 @@ package com.ktds.hi.recommend.infra.config; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.servers.Server; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -23,4 +24,24 @@ public class SwaggerConfig { .description("사용자 취향 기반 매장 추천 및 취향 분석 관련 기능을 제공하는 API") .version("1.0.0")); } + + /** + * JWT Bearer 토큰을 위한 Security Scheme 생성 + */ + private SecurityScheme createAPIKeyScheme() { + return new SecurityScheme() + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT") + .in(SecurityScheme.In.HEADER) + .name("Authorization") + .description(""" + JWT 토큰을 입력하세요 + + 사용법: + 1. 로그인 API로 토큰 발급 + 2. Bearer 접두사 없이 토큰만 입력 + 3. 예: eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOi... + """); + } } diff --git a/review/src/main/java/com/ktds/hi/review/infra/config/SwaggerConfig.java b/review/src/main/java/com/ktds/hi/review/infra/config/SwaggerConfig.java index 051b36d..628e277 100644 --- a/review/src/main/java/com/ktds/hi/review/infra/config/SwaggerConfig.java +++ b/review/src/main/java/com/ktds/hi/review/infra/config/SwaggerConfig.java @@ -2,6 +2,7 @@ package com.ktds.hi.review.infra.config; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.servers.Server; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -22,5 +23,25 @@ public class SwaggerConfig { .description("리뷰 작성, 조회, 삭제, 반응, 댓글 등 리뷰 관련 기능을 제공하는 API") .version("1.0.0")); } + + /** + * JWT Bearer 토큰을 위한 Security Scheme 생성 + */ + private SecurityScheme createAPIKeyScheme() { + return new SecurityScheme() + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT") + .in(SecurityScheme.In.HEADER) + .name("Authorization") + .description(""" + JWT 토큰을 입력하세요 + + 사용법: + 1. 로그인 API로 토큰 발급 + 2. Bearer 접두사 없이 토큰만 입력 + 3. 예: eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOi... + """); + } } From 6b7e70857e5503ce1d416b2eb8e8422836f6cd59 Mon Sep 17 00:00:00 2001 From: lsh9672 Date: Fri, 13 Jun 2025 14:50:47 +0900 Subject: [PATCH 07/12] =?UTF-8?q?fix=20:=20cors=20=EC=84=A4=EC=A0=95=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 --- .../infra/config/SecurityConfig.java | 7 ++ analytics/src/main/resources/application.yml | 2 +- .../com/ktds/hi/common/config/CorsConfig.java | 102 ++++++++++++++++++ .../src/main/resources/application-common.yml | 5 +- .../ktds/hi/member/config/SecurityConfig.java | 7 +- .../infra/config/SecurityConfig.java | 52 +++++++++ recommend/src/main/resources/application.yml | 1 - .../review/infra/config/SecurityConfig.java | 7 ++ .../ktds/hi/store/config/SecurityConfig.java | 7 ++ 9 files changed, 184 insertions(+), 6 deletions(-) create mode 100644 common/src/main/java/com/ktds/hi/common/config/CorsConfig.java create mode 100644 recommend/src/main/java/com/ktds/hi/recommend/infra/config/SecurityConfig.java diff --git a/analytics/src/main/java/com/ktds/hi/analytics/infra/config/SecurityConfig.java b/analytics/src/main/java/com/ktds/hi/analytics/infra/config/SecurityConfig.java index 1a43aa3..1c7aca7 100644 --- a/analytics/src/main/java/com/ktds/hi/analytics/infra/config/SecurityConfig.java +++ b/analytics/src/main/java/com/ktds/hi/analytics/infra/config/SecurityConfig.java @@ -7,6 +7,9 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.cors.CorsConfigurationSource; + +import lombok.RequiredArgsConstructor; /** * Analytics 서비스 보안 설정 클래스 @@ -14,12 +17,16 @@ import org.springframework.security.web.SecurityFilterChain; */ @Configuration @EnableWebSecurity +@RequiredArgsConstructor public class SecurityConfig { + private final CorsConfigurationSource corsConfigurationSource; + @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) + .cors(cors -> cors.configurationSource(corsConfigurationSource)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth // Swagger 관련 경로 모두 허용 diff --git a/analytics/src/main/resources/application.yml b/analytics/src/main/resources/application.yml index c18bf57..f9b1049 100644 --- a/analytics/src/main/resources/application.yml +++ b/analytics/src/main/resources/application.yml @@ -92,4 +92,4 @@ azure: ai-analysis-events: ${AZURE_EVENTHUB_AI_ANALYSIS_EVENTS:ai-analysis-events} storage: connection-string: ${AZURE_STORAGE_CONNECTION_STRING:DefaultEndpointsProtocol=https;AccountName=yourstorageaccount;AccountKey=your-storage-key;EndpointSuffix=core.windows.net} - container-name: ${AZURE_STORAGE_CONTAINER_NAME:eventhub-checkpoints} \ No newline at end of file + container-name: ${AZURE_STORAGE_CONTAINER_NAME:eventhub-checkpoints} diff --git a/common/src/main/java/com/ktds/hi/common/config/CorsConfig.java b/common/src/main/java/com/ktds/hi/common/config/CorsConfig.java new file mode 100644 index 0000000..3f14ba3 --- /dev/null +++ b/common/src/main/java/com/ktds/hi/common/config/CorsConfig.java @@ -0,0 +1,102 @@ +package com.ktds.hi.common.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.Arrays; +import java.util.List; + +/** + * 전체 서비스 통합 CORS 설정 클래스 + * 모든 마이크로서비스에서 공통으로 사용되는 CORS 정책을 정의 + */ +@Configuration +public class CorsConfig implements WebMvcConfigurer { + + @Value("${app.cors.allowed-origins:http://localhost:3000,http://localhost:8080,http://localhost:3001}") + private String allowedOrigins; + + @Value("${app.cors.allowed-methods:GET,POST,PUT,DELETE,PATCH,OPTIONS}") + private String allowedMethods; + + @Value("${app.cors.allowed-headers:*}") + private String allowedHeaders; + + @Value("${app.cors.exposed-headers:Authorization,X-Total-Count}") + private String exposedHeaders; + + @Value("${app.cors.allow-credentials:true}") + private boolean allowCredentials; + + @Value("${app.cors.max-age:3600}") + private long maxAge; + + /** + * WebMvcConfigurer를 통한 CORS 설정 + */ + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOriginPatterns(allowedOrigins.split(",")) + .allowedMethods(allowedMethods.split(",")) + .allowedHeaders(allowedHeaders.split(",")) + .exposedHeaders(exposedHeaders.split(",")) + .allowCredentials(allowCredentials) + .maxAge(maxAge); + } + + /** + * CorsConfigurationSource Bean 생성 + * Spring Security와 함께 사용되는 CORS 설정 + */ + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + + // Origin 설정 + List origins = Arrays.asList(allowedOrigins.split(",")); + configuration.setAllowedOriginPatterns(origins); + + // Method 설정 + List methods = Arrays.asList(allowedMethods.split(",")); + configuration.setAllowedMethods(methods); + + // Header 설정 + if ("*".equals(allowedHeaders)) { + configuration.addAllowedHeader("*"); + } else { + List headers = Arrays.asList(allowedHeaders.split(",")); + configuration.setAllowedHeaders(headers); + } + + // Exposed Headers 설정 + List exposed = Arrays.asList(exposedHeaders.split(",")); + configuration.setExposedHeaders(exposed); + + // Credentials 설정 + configuration.setAllowCredentials(allowCredentials); + + // Max Age 설정 + configuration.setMaxAge(maxAge); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } + + /** + * CorsFilter Bean 생성 + * 글로벌 CORS 필터로 사용 + */ + @Bean + public CorsFilter corsFilter() { + return new CorsFilter(corsConfigurationSource()); + } +} \ No newline at end of file diff --git a/common/src/main/resources/application-common.yml b/common/src/main/resources/application-common.yml index 32ccc51..04c052f 100644 --- a/common/src/main/resources/application-common.yml +++ b/common/src/main/resources/application-common.yml @@ -51,13 +51,12 @@ app: secret-key: ${JWT_SECRET_KEY:hiorder-secret-key-for-jwt-token-generation-2024-very-long-secret-key} access-token-validity: ${JWT_ACCESS_TOKEN_VALIDITY:3600000} # 1시간 refresh-token-validity: ${JWT_REFRESH_TOKEN_VALIDITY:604800000} # 7일 - # CORS 설정 cors: - allowed-origins: ${CORS_ALLOWED_ORIGINS:http://localhost:3000,http://localhost:8080} + allowed-origins: ${CORS_ALLOWED_ORIGINS:http://20.214.126.84:80,http://localhost:8080} allowed-methods: ${CORS_ALLOWED_METHODS:GET,POST,PUT,DELETE,OPTIONS} allowed-headers: ${CORS_ALLOWED_HEADERS:*} - exposed-headers: ${CORS_EXPOSED_HEADERS:Authorization} + exposed-headers: ${CORS_EXPOSED_HEADERS:Authorization, X-Total-Count} allow-credentials: ${CORS_ALLOW_CREDENTIALS:true} max-age: ${CORS_MAX_AGE:3600} diff --git a/member/src/main/java/com/ktds/hi/member/config/SecurityConfig.java b/member/src/main/java/com/ktds/hi/member/config/SecurityConfig.java index ea30d1c..47fcf95 100644 --- a/member/src/main/java/com/ktds/hi/member/config/SecurityConfig.java +++ b/member/src/main/java/com/ktds/hi/member/config/SecurityConfig.java @@ -5,6 +5,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.ktds.hi.common.security.JwtTokenProvider; import com.ktds.hi.common.security.JwtAuthenticationFilter; import lombok.RequiredArgsConstructor; + +import org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpoint; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; @@ -16,6 +18,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfigurationSource; /** * Spring Security 설정 클래스 @@ -27,15 +30,17 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic public class SecurityConfig { private final JwtTokenProvider jwtTokenProvider; + private final CorsConfigurationSource corsConfigurationSource; /** * 보안 필터 체인 설정 * JWT 인증 방식을 사용하고 세션은 무상태로 관리 */ @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + public SecurityFilterChain filterChain(HttpSecurity http, ConditionsReportEndpoint conditionsReportEndpoint) throws Exception { http .csrf(csrf -> csrf.disable()) + .cors(cors -> cors.configurationSource(corsConfigurationSource)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(authz -> authz .requestMatchers("/api/auth/**", "/api/members/register", "/api/auth/login").permitAll() diff --git a/recommend/src/main/java/com/ktds/hi/recommend/infra/config/SecurityConfig.java b/recommend/src/main/java/com/ktds/hi/recommend/infra/config/SecurityConfig.java new file mode 100644 index 0000000..f1b3a0c --- /dev/null +++ b/recommend/src/main/java/com/ktds/hi/recommend/infra/config/SecurityConfig.java @@ -0,0 +1,52 @@ +package com.ktds.hi.recommend.infra.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.cors.CorsConfigurationSource; + +import lombok.RequiredArgsConstructor; + +/** + * Analytics 서비스 보안 설정 클래스 + * 테스트를 위해 모든 엔드포인트를 인증 없이 접근 가능하도록 설정 + */ +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + private final CorsConfigurationSource corsConfigurationSource; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + + + http + .csrf(AbstractHttpConfigurer::disable) + .cors(cors -> cors.configurationSource(corsConfigurationSource)) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(auth -> auth + // Swagger 관련 경로 모두 허용 + .requestMatchers("/swagger-ui.html","/swagger-ui/**", "/swagger-ui.html").permitAll() + .requestMatchers("/api-docs/**", "/v3/api-docs/**").permitAll() + .requestMatchers("/swagger-resources/**", "/webjars/**").permitAll() + + // Analytics API 모두 허용 (테스트용) + .requestMatchers("/api/analytics/**").permitAll() + .requestMatchers("/api/action-plans/**").permitAll() + + // Actuator 엔드포인트 허용 + .requestMatchers("/actuator/**").permitAll() + + // 기타 모든 요청 허용 (테스트용) + .anyRequest().permitAll() + ); + + return http.build(); + } +} diff --git a/recommend/src/main/resources/application.yml b/recommend/src/main/resources/application.yml index eff4854..6f8573a 100644 --- a/recommend/src/main/resources/application.yml +++ b/recommend/src/main/resources/application.yml @@ -132,7 +132,6 @@ management: springdoc: api-docs: path: /api-docs - enabled: true swagger-ui: path: /swagger-ui.html tags-sorter: alpha diff --git a/review/src/main/java/com/ktds/hi/review/infra/config/SecurityConfig.java b/review/src/main/java/com/ktds/hi/review/infra/config/SecurityConfig.java index 92a1a7f..6c66b1a 100644 --- a/review/src/main/java/com/ktds/hi/review/infra/config/SecurityConfig.java +++ b/review/src/main/java/com/ktds/hi/review/infra/config/SecurityConfig.java @@ -7,6 +7,9 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.cors.CorsConfigurationSource; + +import lombok.RequiredArgsConstructor; /** * Analytics 서비스 보안 설정 클래스 @@ -14,12 +17,16 @@ import org.springframework.security.web.SecurityFilterChain; */ @Configuration @EnableWebSecurity +@RequiredArgsConstructor public class SecurityConfig { + private final CorsConfigurationSource corsConfigurationSource; + @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) + .cors(cors -> cors.configurationSource(corsConfigurationSource)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth // Swagger 관련 경로 모두 허용 diff --git a/store/src/main/java/com/ktds/hi/store/config/SecurityConfig.java b/store/src/main/java/com/ktds/hi/store/config/SecurityConfig.java index 1beeacd..1c1d436 100644 --- a/store/src/main/java/com/ktds/hi/store/config/SecurityConfig.java +++ b/store/src/main/java/com/ktds/hi/store/config/SecurityConfig.java @@ -7,6 +7,9 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.web.cors.CorsConfigurationSource; + +import lombok.RequiredArgsConstructor; /** * Analytics 서비스 보안 설정 클래스 @@ -14,12 +17,16 @@ import org.springframework.security.web.SecurityFilterChain; */ @Configuration @EnableWebSecurity +@RequiredArgsConstructor public class SecurityConfig { + private final CorsConfigurationSource corsConfigurationSource; + @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) + .cors(cors -> cors.configurationSource(corsConfigurationSource)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth // Swagger 관련 경로 모두 허용 From 1963d21cc8c80fb1ae0e6e48087392487715464d Mon Sep 17 00:00:00 2001 From: UNGGU0704 Date: Fri, 13 Jun 2025 14:57:57 +0900 Subject: [PATCH 08/12] =?UTF-8?q?Update:=20review-swagger=20token=20input?= =?UTF-8?q?=20=EA=B0=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ktds/hi/review/infra/config/SwaggerConfig.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/review/src/main/java/com/ktds/hi/review/infra/config/SwaggerConfig.java b/review/src/main/java/com/ktds/hi/review/infra/config/SwaggerConfig.java index 628e277..b778a9f 100644 --- a/review/src/main/java/com/ktds/hi/review/infra/config/SwaggerConfig.java +++ b/review/src/main/java/com/ktds/hi/review/infra/config/SwaggerConfig.java @@ -1,5 +1,7 @@ package com.ktds.hi.review.infra.config; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.security.SecurityScheme; @@ -13,7 +15,8 @@ import org.springframework.context.annotation.Configuration; */ @Configuration public class SwaggerConfig { - + + private static final String SECURITY_SCHEME_NAME = "bearerAuth"; @Bean public OpenAPI openAPI() { return new OpenAPI() @@ -21,7 +24,10 @@ public class SwaggerConfig { .info(new Info() .title("하이오더 리뷰 관리 서비스 API") .description("리뷰 작성, 조회, 삭제, 반응, 댓글 등 리뷰 관련 기능을 제공하는 API") - .version("1.0.0")); + .version("1.0.0")) + .components(new Components() // 이 줄 추가! + .addSecuritySchemes(SECURITY_SCHEME_NAME, createAPIKeyScheme())) // 이 줄 추가! + .addSecurityItem(new SecurityRequirement().addList(SECURITY_SCHEME_NAME)); // 이 줄 추가! } /** From ff52122ea2e78b14ddfd3b2fe3a4122ec213231b Mon Sep 17 00:00:00 2001 From: UNGGU0704 Date: Fri, 13 Jun 2025 15:06:46 +0900 Subject: [PATCH 09/12] =?UTF-8?q?Update:=20swgger=20auth=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hi/review/infra/config/SwaggerConfig.java | 43 ++++++------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/review/src/main/java/com/ktds/hi/review/infra/config/SwaggerConfig.java b/review/src/main/java/com/ktds/hi/review/infra/config/SwaggerConfig.java index b778a9f..ff1f042 100644 --- a/review/src/main/java/com/ktds/hi/review/infra/config/SwaggerConfig.java +++ b/review/src/main/java/com/ktds/hi/review/infra/config/SwaggerConfig.java @@ -1,53 +1,34 @@ package com.ktds.hi.review.infra.config; import io.swagger.v3.oas.models.Components; -import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.servers.Server; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -/** - * Swagger 설정 클래스 - * API 문서화를 위한 OpenAPI 설정 - */ @Configuration public class SwaggerConfig { - private static final String SECURITY_SCHEME_NAME = "bearerAuth"; @Bean public OpenAPI openAPI() { + final String securitySchemeName = "Bearer Authentication"; + return new OpenAPI() .addServersItem(new Server().url("/")) .info(new Info() .title("하이오더 리뷰 관리 서비스 API") .description("리뷰 작성, 조회, 삭제, 반응, 댓글 등 리뷰 관련 기능을 제공하는 API") .version("1.0.0")) - .components(new Components() // 이 줄 추가! - .addSecuritySchemes(SECURITY_SCHEME_NAME, createAPIKeyScheme())) // 이 줄 추가! - .addSecurityItem(new SecurityRequirement().addList(SECURITY_SCHEME_NAME)); // 이 줄 추가! + .addSecurityItem(new SecurityRequirement() + .addList(securitySchemeName)) + .components(new Components() + .addSecuritySchemes(securitySchemeName, new SecurityScheme() + .name(securitySchemeName) + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT"))); } - - /** - * JWT Bearer 토큰을 위한 Security Scheme 생성 - */ - private SecurityScheme createAPIKeyScheme() { - return new SecurityScheme() - .type(SecurityScheme.Type.HTTP) - .scheme("bearer") - .bearerFormat("JWT") - .in(SecurityScheme.In.HEADER) - .name("Authorization") - .description(""" - JWT 토큰을 입력하세요 - - 사용법: - 1. 로그인 API로 토큰 발급 - 2. Bearer 접두사 없이 토큰만 입력 - 3. 예: eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOi... - """); - } -} - +} \ No newline at end of file From ddcb46dae96a70a73b02f1ba059d72021b9d3457 Mon Sep 17 00:00:00 2001 From: lsh9672 Date: Fri, 13 Jun 2025 15:16:43 +0900 Subject: [PATCH 10/12] =?UTF-8?q?fix=20:=20=EC=8B=9C=ED=81=90=EB=A6=AC?= =?UTF-8?q?=ED=8B=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/ktds/hi/member/config/SecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/member/src/main/java/com/ktds/hi/member/config/SecurityConfig.java b/member/src/main/java/com/ktds/hi/member/config/SecurityConfig.java index 47fcf95..20dbd85 100644 --- a/member/src/main/java/com/ktds/hi/member/config/SecurityConfig.java +++ b/member/src/main/java/com/ktds/hi/member/config/SecurityConfig.java @@ -37,7 +37,7 @@ public class SecurityConfig { * JWT 인증 방식을 사용하고 세션은 무상태로 관리 */ @Bean - public SecurityFilterChain filterChain(HttpSecurity http, ConditionsReportEndpoint conditionsReportEndpoint) throws Exception { + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .cors(cors -> cors.configurationSource(corsConfigurationSource)) From 94107fbcd22465d0f91057940d5232137bbac36f Mon Sep 17 00:00:00 2001 From: UNGGU0704 Date: Fri, 13 Jun 2025 15:28:15 +0900 Subject: [PATCH 11/12] =?UTF-8?q?Fix:=20review=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EC=95=88=EB=90=98=EB=8A=94=20=ED=98=84=EC=83=81=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 --- .../com/ktds/hi/review/biz/service/ReviewInteractor.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/review/src/main/java/com/ktds/hi/review/biz/service/ReviewInteractor.java b/review/src/main/java/com/ktds/hi/review/biz/service/ReviewInteractor.java index d0ca3b3..b69a463 100644 --- a/review/src/main/java/com/ktds/hi/review/biz/service/ReviewInteractor.java +++ b/review/src/main/java/com/ktds/hi/review/biz/service/ReviewInteractor.java @@ -65,9 +65,8 @@ public class ReviewInteractor implements CreateReviewUseCase, DeleteReviewUseCas public ReviewDeleteResponse deleteReview(Long reviewId, Long memberId) { Review review = reviewRepository.findReviewByIdAndMemberId(reviewId, memberId) .orElseThrow(() -> new BusinessException("리뷰를 찾을 수 없거나 권한이 없습니다")); - - Review deletedReview = review.updateStatus(ReviewStatus.DELETED); - reviewRepository.saveReview(deletedReview); + + reviewRepository.deleteReview(reviewId); log.info("리뷰 삭제 완료: reviewId={}, memberId={}", reviewId, memberId); From d1364c1402723714e2e141cfeabf7f0fbef75c1f Mon Sep 17 00:00:00 2001 From: lsh9672 Date: Fri, 13 Jun 2025 15:47:12 +0900 Subject: [PATCH 12/12] =?UTF-8?q?fix=20:=20=EC=8B=9C=ED=81=90=EB=A6=AC?= =?UTF-8?q?=ED=8B=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ktds/hi/analytics/infra/config/SecurityConfig.java | 1 + .../src/main/java/com/ktds/hi/common/config/CorsConfig.java | 4 ++-- common/src/main/resources/application-common.yml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/analytics/src/main/java/com/ktds/hi/analytics/infra/config/SecurityConfig.java b/analytics/src/main/java/com/ktds/hi/analytics/infra/config/SecurityConfig.java index 1c7aca7..722388e 100644 --- a/analytics/src/main/java/com/ktds/hi/analytics/infra/config/SecurityConfig.java +++ b/analytics/src/main/java/com/ktds/hi/analytics/infra/config/SecurityConfig.java @@ -20,6 +20,7 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public class SecurityConfig { + private final CorsConfigurationSource corsConfigurationSource; @Bean diff --git a/common/src/main/java/com/ktds/hi/common/config/CorsConfig.java b/common/src/main/java/com/ktds/hi/common/config/CorsConfig.java index 3f14ba3..013a726 100644 --- a/common/src/main/java/com/ktds/hi/common/config/CorsConfig.java +++ b/common/src/main/java/com/ktds/hi/common/config/CorsConfig.java @@ -20,10 +20,10 @@ import java.util.List; @Configuration public class CorsConfig implements WebMvcConfigurer { - @Value("${app.cors.allowed-origins:http://localhost:3000,http://localhost:8080,http://localhost:3001}") + @Value("${app.cors.allowed-origins:http://20.214.126.84,http://localhost:3000}") private String allowedOrigins; - @Value("${app.cors.allowed-methods:GET,POST,PUT,DELETE,PATCH,OPTIONS}") + @Value("${app.cors.allowed-methods:GET,POST,PUT,DELETE,OPTIONS}") private String allowedMethods; @Value("${app.cors.allowed-headers:*}") diff --git a/common/src/main/resources/application-common.yml b/common/src/main/resources/application-common.yml index 04c052f..0d276bd 100644 --- a/common/src/main/resources/application-common.yml +++ b/common/src/main/resources/application-common.yml @@ -53,7 +53,7 @@ app: refresh-token-validity: ${JWT_REFRESH_TOKEN_VALIDITY:604800000} # 7일 # CORS 설정 cors: - allowed-origins: ${CORS_ALLOWED_ORIGINS:http://20.214.126.84:80,http://localhost:8080} + allowed-origins: ${CORS_ALLOWED_ORIGINS:http://20.214.126.84,http://localhost:8080} allowed-methods: ${CORS_ALLOWED_METHODS:GET,POST,PUT,DELETE,OPTIONS} allowed-headers: ${CORS_ALLOWED_HEADERS:*} exposed-headers: ${CORS_EXPOSED_HEADERS:Authorization, X-Total-Count}