From 435142f5d30b8b6064f99d4b961834f3aa073dd3 Mon Sep 17 00:00:00 2001 From: eedo_y Date: Sun, 18 Jan 2026 16:34:56 +0900 Subject: [PATCH 01/12] =?UTF-8?q?=E2=9C=A8feat=20:=20=EC=9D=B8=ED=94=84?= =?UTF-8?q?=EB=9D=BC=20DTO=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/NoticeFacilityListResponse.java | 5 +- .../application/service/FacilityService.java | 3 +- .../service/FacilityStatService.java | 40 +++++++---- .../facility/domain/entity/FacilityType.java | 70 ++++++++++++++++--- .../FacilityStatDocumentRepositoryImpl.java | 20 +++++- .../presentation/FacilityTypeConverter.java | 14 +--- .../dto/NoticeDetailFilterRequest.java | 2 +- .../dto/UnitTypeCompareResponse.java | 2 +- .../application/dto/FastSearchRequest.java | 2 +- .../dto/UpdateFacilityTypesRequest.java | 2 +- .../user/application/dto/UserRequest.java | 2 +- 11 files changed, 116 insertions(+), 46 deletions(-) diff --git a/src/main/java/com/pinHouse/server/platform/housing/facility/application/dto/NoticeFacilityListResponse.java b/src/main/java/com/pinHouse/server/platform/housing/facility/application/dto/NoticeFacilityListResponse.java index d318d56c..c42a3547 100644 --- a/src/main/java/com/pinHouse/server/platform/housing/facility/application/dto/NoticeFacilityListResponse.java +++ b/src/main/java/com/pinHouse/server/platform/housing/facility/application/dto/NoticeFacilityListResponse.java @@ -12,7 +12,7 @@ @Builder @Schema(name = "[응답][시설] 주변 인프라 시설 목록", description = "단지 주변 1KM 이내의 주요 생활편의 시설 정보") public record NoticeFacilityListResponse( - @Schema(description = "주변 인프라 시설 타입 목록 (3개 이상 있는 시설만 포함)", example = "[\"PARK\", \"LIBRARY\", \"HOSPITAL\"]") + @Schema(description = "주변 인프라 시설 타입 목록 (3개 이상 있는 시설만 포함)", example = "[\"문화센터\", \"병원-약국\"]") List infra ) { /// 정적 팩토리 메서드 @@ -23,7 +23,8 @@ public static NoticeFacilityListResponse from(Map src) { List infraList = src.entrySet().stream() .filter(e -> e.getValue() != null && e.getValue() >= 3) - .map(Map.Entry::getKey) + .map(entry -> entry.getKey().displayType()) + .distinct() .toList(); return NoticeFacilityListResponse.builder() diff --git a/src/main/java/com/pinHouse/server/platform/housing/facility/application/service/FacilityService.java b/src/main/java/com/pinHouse/server/platform/housing/facility/application/service/FacilityService.java index ef117439..d773cbc9 100644 --- a/src/main/java/com/pinHouse/server/platform/housing/facility/application/service/FacilityService.java +++ b/src/main/java/com/pinHouse/server/platform/housing/facility/application/service/FacilityService.java @@ -111,7 +111,8 @@ public List getFacilities(String complexId) { /// 3개 이상인 FacilityType return response.entrySet().stream() .filter(entry -> entry.getValue() != null && entry.getValue() >= 3) - .map(Map.Entry::getKey) + .map(entry -> entry.getKey().displayType()) + .distinct() .toList(); } diff --git a/src/main/java/com/pinHouse/server/platform/housing/facility/application/service/FacilityStatService.java b/src/main/java/com/pinHouse/server/platform/housing/facility/application/service/FacilityStatService.java index 4baccf53..e67e0102 100644 --- a/src/main/java/com/pinHouse/server/platform/housing/facility/application/service/FacilityStatService.java +++ b/src/main/java/com/pinHouse/server/platform/housing/facility/application/service/FacilityStatService.java @@ -5,19 +5,14 @@ import com.pinHouse.server.platform.housing.facility.domain.repository.FacilityStatDocumentRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.bson.Document; -import org.springframework.data.geo.Metrics; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.aggregation.*; -import org.springframework.data.mongodb.core.geo.GeoJsonPoint; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.NearQuery; -import org.springframework.data.mongodb.core.query.Query; import org.springframework.stereotype.Service; import java.time.Duration; import java.time.Instant; -import java.util.*; +import java.util.Collection; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; @Service @RequiredArgsConstructor @@ -42,20 +37,20 @@ public Map getCountsOrRecompute(String complexId, double // 문서가 아예 없을 경우 → 새로 계산 if (existing == null) { - Map fresh = countsRepo.aggregateCounts(lng, lat, RADIUS_M); + Map fresh = withDerivedCounts(countsRepo.aggregateCounts(lng, lat, RADIUS_M)); upsertCounts(complexId, fresh); return fresh; } // TTL 지나면 재계산 if (isExpired(existing.getUpdatedAt())) { - Map fresh = countsRepo.aggregateCounts(lng, lat, RADIUS_M); + Map fresh = withDerivedCounts(countsRepo.aggregateCounts(lng, lat, RADIUS_M)); upsertCounts(complexId, fresh); return fresh; } // 유효한 캐시면 그대로 사용 - return existing.getCounts(); + return withDerivedCounts(existing.getCounts()); } @@ -63,7 +58,7 @@ private void upsertCounts(String complexId, Map counts) { FacilityStatDocument doc = FacilityStatDocument.builder() .id(complexId) .radiusKm(RADIUS_KM) - .counts(counts) + .counts(withDerivedCounts(counts)) .updatedAt(Instant.now()) .build(); countsRepo.save(doc); @@ -72,4 +67,23 @@ private void upsertCounts(String complexId, Map counts) { private boolean isExpired(Instant t) { return t == null || t.isBefore(Instant.now().minus(TTL)); } + + private Map withDerivedCounts(Map counts) { + if (counts == null) { + return Map.of(); + } + + Map enriched = new EnumMap<>(FacilityType.class); + for (FacilityType type : FacilityType.values()) { + enriched.put(type, counts.getOrDefault(type, 0)); + } + enriched.put(FacilityType.CULTURE_CENTER, computeCultureCenterCount(enriched)); + return enriched; + } + + private int computeCultureCenterCount(Map map) { + return FacilityType.cultureCenterMembers().stream() + .mapToInt(t -> map.getOrDefault(t, 0)) + .sum(); + } } diff --git a/src/main/java/com/pinHouse/server/platform/housing/facility/domain/entity/FacilityType.java b/src/main/java/com/pinHouse/server/platform/housing/facility/domain/entity/FacilityType.java index 242132ba..9983a534 100644 --- a/src/main/java/com/pinHouse/server/platform/housing/facility/domain/entity/FacilityType.java +++ b/src/main/java/com/pinHouse/server/platform/housing/facility/domain/entity/FacilityType.java @@ -4,22 +4,29 @@ import com.fasterxml.jackson.annotation.JsonValue; import com.pinHouse.server.core.exception.code.FacilityErrorCode; import com.pinHouse.server.core.response.response.CustomException; -import com.pinHouse.server.core.response.response.ErrorCode; import lombok.RequiredArgsConstructor; +import java.util.EnumSet; +import java.util.Locale; +import java.util.Set; + @RequiredArgsConstructor public enum FacilityType { - LIBRARY("도서관"), // 도서관 - PARK("공원"), // 공원 - ANIMAL("동물 관련시설"), // 동물 관련 시설 - WALKING("산책로"), // 산책로 - SPORT("스포츠 시설"), // 스포츠 시설 - STORE("대형점포"), // 대형점포 - HOSPITAL("병원"), // 병원 - EXHIBITION("전시회"), // 전시회 - LAUNDRY("빨래방"); // 빨래방 + LIBRARY("도서관", Set.of("도서관", "문화센터")), + PARK("공원", Set.of("공원", "문화센터")), + ANIMAL("동물 관련시설", Set.of("동물 관련시설", "문화센터")), + EXHIBITION("전시회", Set.of("전시회", "문화센터")), + + WALKING("산책길", Set.of("산책길", "산책로")), + SPORT("실내 액티비티", Set.of("실내 액티비티", "스포츠 시설")), + STORE("대형마트-백화점", Set.of("대형마트-백화점", "대형점포")), + HOSPITAL("병원-약국", Set.of("병원-약국", "병원")), + LAUNDRY("세탁소", Set.of("세탁소", "빨래방")), + CULTURE_CENTER("문화센터", Set.of("문화센터")); + private final String value; + private final Set aliases; @JsonValue public String getValue() { @@ -28,12 +35,53 @@ public String getValue() { @JsonCreator public static FacilityType fromValue(String value) { + if (value == null) { + throw new CustomException(FacilityErrorCode.BAD_REQUEST_INPUT_FACILITY); + } + + String normalized = normalize(value); + + if (CULTURE_CENTER.matches(normalized) || + FacilityType.cultureCenterMembers().stream().anyMatch(t -> t.matches(normalized))) { + return CULTURE_CENTER; + } + for (FacilityType facilityType : FacilityType.values()) { - if (facilityType.getValue().equals(value)) { + if (facilityType == CULTURE_CENTER) continue; + if (facilityType.matches(normalized)) { return facilityType; } } + + try { + return FacilityType.valueOf(normalized.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException ignore) { } + throw new CustomException(FacilityErrorCode.BAD_REQUEST_INPUT_FACILITY); } + public FacilityType displayType() { + return isCultureCenterMember(this) ? CULTURE_CENTER : this; + } + + private boolean matches(String source) { + String normalized = normalize(source); + return aliases.stream().anyMatch(alias -> normalize(alias).equals(normalized)) + || normalize(getValue()).equals(normalized); + } + + private static String normalize(String input) { + return input == null ? "" : input.trim().replaceAll("\\s+", ""); + } + + public static boolean isCultureCenterMember(FacilityType type) { + return CULTURE_TYPES.contains(type); + } + + public static Set cultureCenterMembers() { + return CULTURE_TYPES; + } + + private static final Set CULTURE_TYPES = EnumSet.of(LIBRARY, PARK, ANIMAL, EXHIBITION); + } diff --git a/src/main/java/com/pinHouse/server/platform/housing/facility/domain/repository/FacilityStatDocumentRepositoryImpl.java b/src/main/java/com/pinHouse/server/platform/housing/facility/domain/repository/FacilityStatDocumentRepositoryImpl.java index 3adff23d..46cd125f 100644 --- a/src/main/java/com/pinHouse/server/platform/housing/facility/domain/repository/FacilityStatDocumentRepositoryImpl.java +++ b/src/main/java/com/pinHouse/server/platform/housing/facility/domain/repository/FacilityStatDocumentRepositoryImpl.java @@ -32,7 +32,17 @@ public List findByAllTypesOver(Collection ty } List ands = types.stream() - .map(t -> Criteria.where("counts." + t.name()).gte(min)) + .map(t -> { + if (t == FacilityType.CULTURE_CENTER) { + List ors = new ArrayList<>(); + ors.add(Criteria.where("counts." + FacilityType.CULTURE_CENTER.name()).gte(min)); + FacilityType.cultureCenterMembers().forEach(member -> + ors.add(Criteria.where("counts." + member.name()).gte(min)) + ); + return new Criteria().orOperator(ors.toArray(new Criteria[0])); + } + return Criteria.where("counts." + t.name()).gte(min); + }) .toList(); Query q = new Query(new Criteria().andOperator(ands.toArray(new Criteria[0]))); @@ -87,6 +97,7 @@ public Map aggregateCounts(double lng, double lat, double for (FacilityType t : FacilityType.values()) { map.putIfAbsent(t, 0); } + map.put(FacilityType.CULTURE_CENTER, computeCultureCenterCount(map)); return map; } @@ -95,7 +106,14 @@ private Map initEmptyMap() { for (FacilityType t : FacilityType.values()) { empty.put(t, 0); } + empty.put(FacilityType.CULTURE_CENTER, 0); return empty; } + private int computeCultureCenterCount(Map map) { + return FacilityType.cultureCenterMembers().stream() + .mapToInt(t -> map.getOrDefault(t, 0)) + .sum(); + } + } diff --git a/src/main/java/com/pinHouse/server/platform/housing/facility/presentation/FacilityTypeConverter.java b/src/main/java/com/pinHouse/server/platform/housing/facility/presentation/FacilityTypeConverter.java index b6bbcb4d..116b223d 100644 --- a/src/main/java/com/pinHouse/server/platform/housing/facility/presentation/FacilityTypeConverter.java +++ b/src/main/java/com/pinHouse/server/platform/housing/facility/presentation/FacilityTypeConverter.java @@ -16,18 +16,6 @@ public FacilityType convert(String source) { if (source == null) { throw new CustomException(FacilityErrorCode.BAD_REQUEST_INPUT_FACILITY); } - String s = source.trim(); - - /// 한글 라벨 매칭 - for (FacilityType t : FacilityType.values()) { - /// getValue()는 "공원", "도서관" 등 - if (t.getValue().equals(s)) return t; - } - /// 영문 enum 이름 매칭도 허용하고 싶으면: - try { - return FacilityType.valueOf(s.toUpperCase(Locale.ROOT)); - } catch (IllegalArgumentException ignore) {} - - throw new CustomException(FacilityErrorCode.BAD_REQUEST_INPUT_FACILITY); + return FacilityType.fromValue(source.trim()); } } diff --git a/src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilterRequest.java b/src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilterRequest.java index b0233989..aa453470 100644 --- a/src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilterRequest.java +++ b/src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/NoticeDetailFilterRequest.java @@ -36,7 +36,7 @@ public record NoticeDetailFilterRequest( @Schema(description = "주택형 코드 필터", example = "[\"26A\"]") List typeCode, - @Schema(description = "원하는 인프라, 최대 3개까지 가능", example = "[\"공원\"]") + @Schema(description = "원하는 인프라, 최대 3개까지 가능", example = "[\"문화센터\"]") @Size(max = 3) List facilities diff --git a/src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/UnitTypeCompareResponse.java b/src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/UnitTypeCompareResponse.java index 863fed6d..76e2722a 100644 --- a/src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/UnitTypeCompareResponse.java +++ b/src/main/java/com/pinHouse/server/platform/housing/notice/application/dto/UnitTypeCompareResponse.java @@ -49,7 +49,7 @@ public record UnitTypeComparisonItem( @Schema(description = "비용 정보") CostInfo cost, - @Schema(description = "단지 기반 주변 인프라 태그 (Complex에 속한 시설 정보)", example = "[\"공원\", \"도서관\", \"병원\"]") + @Schema(description = "단지 기반 주변 인프라 태그 (Complex에 속한 시설 정보)", example = "[\"문화센터\", \"병원-약국\"]") List nearbyFacilities, @Schema(description = "핀포인트 기준 대중교통 총 소요 시간", example = "1시간 30분") diff --git a/src/main/java/com/pinHouse/server/platform/search/application/dto/FastSearchRequest.java b/src/main/java/com/pinHouse/server/platform/search/application/dto/FastSearchRequest.java index 05bc8487..4d07865b 100644 --- a/src/main/java/com/pinHouse/server/platform/search/application/dto/FastSearchRequest.java +++ b/src/main/java/com/pinHouse/server/platform/search/application/dto/FastSearchRequest.java @@ -36,7 +36,7 @@ public record FastSearchRequest( @Schema(description = "월 임대료 최대값", example = "300000") Integer maxMonthPay, - @Schema(description = "원하는 인프라, 최대 3개까지 가능", example = "[\"도서관\"]") + @Schema(description = "원하는 인프라, 최대 3개까지 가능", example = "[\"문화센터\"]") @Size(max = 3) List facilities, diff --git a/src/main/java/com/pinHouse/server/platform/user/application/dto/UpdateFacilityTypesRequest.java b/src/main/java/com/pinHouse/server/platform/user/application/dto/UpdateFacilityTypesRequest.java index bb70dce3..b6d7a4ea 100644 --- a/src/main/java/com/pinHouse/server/platform/user/application/dto/UpdateFacilityTypesRequest.java +++ b/src/main/java/com/pinHouse/server/platform/user/application/dto/UpdateFacilityTypesRequest.java @@ -9,7 +9,7 @@ @Schema(description = "관심 시설 타입 수정 요청") public record UpdateFacilityTypesRequest( - @Schema(description = "관심 시설 타입 목록", example = "[\"도서관\", \"공원\", \"병원\"]") + @Schema(description = "관심 시설 타입 목록", example = "[\"문화센터\", \"병원-약국\", \"실내 액티비티\"]") @NotNull(message = "관심 시설 타입 목록은 필수입니다") List facilityTypes ) { diff --git a/src/main/java/com/pinHouse/server/platform/user/application/dto/UserRequest.java b/src/main/java/com/pinHouse/server/platform/user/application/dto/UserRequest.java index edee7375..1d002770 100644 --- a/src/main/java/com/pinHouse/server/platform/user/application/dto/UserRequest.java +++ b/src/main/java/com/pinHouse/server/platform/user/application/dto/UserRequest.java @@ -7,7 +7,7 @@ @Schema(name = "[요청][사용자] 사용자 요청", description = "사용자 요청을 위한 DTO입니다.") public record UserRequest( - @Schema(description = "사용자가 선택한 시설 유형 목록", example = "[\"도서관\", \"산책로\"]") + @Schema(description = "사용자가 선택한 시설 유형 목록", example = "[\"문화센터\", \"산책길\"]") List facilityTypes ) { From 15808b0c67676ce9be8c09e42b93c3b2d7f5b49e Mon Sep 17 00:00:00 2001 From: eedo_y Date: Sun, 18 Jan 2026 16:35:18 +0900 Subject: [PATCH 02/12] =?UTF-8?q?=F0=9F=94=A8chore=20:=20=EA=B0=9C?= =?UTF-8?q?=EB=B0=9C=EC=9A=A9=20docker-compose.yml=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..4d0f6591 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,31 @@ +services: + + spring: + image: pinhouse/server:latest + container_name: pinhouse-app + ports: + - "8080:8080" + networks: + - backend-bridge + + node-exporter: + image: prom/node-exporter:latest + container_name: node-exporter + restart: unless-stopped + volumes: + - /proc:/host/proc:ro + - /sys:/host/sys:ro + - /:/rootfs:ro + command: + - '--path.procfs=/host/proc' + - '--path.rootfs=/rootfs' + - '--path.sysfs=/host/sys' + - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)' + ports: + - "9100:9100" + networks: + - backend-bridge + +networks: + backend-bridge: + driver: bridge From 70792b7c3ae1d12dc2da995da0126ccb13ba09bf Mon Sep 17 00:00:00 2001 From: eedo_y Date: Sun, 18 Jan 2026 16:57:51 +0900 Subject: [PATCH 03/12] =?UTF-8?q?=F0=9F=94=A8chore=20:=20prod=EC=9A=A9=20C?= =?UTF-8?q?ICD=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci-test.yml | 115 ----------------- .github/workflows/dev-ci-cd.yml | 2 +- .github/workflows/dev-ci-test.yml | 88 +++++++++++++ .github/workflows/main-ci-cd.yml | 201 ++++++++++++++++++++++++++++++ Dockerfile => Dockerfile-dev | 0 Dockerfile-prod | 19 +++ docker-compose.yml | 2 +- 7 files changed, 310 insertions(+), 117 deletions(-) delete mode 100644 .github/workflows/ci-test.yml create mode 100644 .github/workflows/dev-ci-test.yml create mode 100644 .github/workflows/main-ci-cd.yml rename Dockerfile => Dockerfile-dev (100%) create mode 100644 Dockerfile-prod diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml deleted file mode 100644 index 96bf0534..00000000 --- a/.github/workflows/ci-test.yml +++ /dev/null @@ -1,115 +0,0 @@ -name: 핀하우스 테스트 CI 파이프라인 - -### develop으로 PR 올릴 때, 테스트 코드를 실행하여, 빌드 여부 및 테스트를 체크하는 로직을 수행한다. - -on: - pull_request: - branches: [ "develop"] - -jobs: - #1. 통합 테스트 용 - test: - - runs-on: ubuntu-22.04 - services: - - mysql: - image: mysql:8.0 - ports: - - '3306:3306' - env: - MYSQL_DATABASE: pinhouse_test - MYSQL_USER: testuser - MYSQL_PASSWORD: testpass - MYSQL_ROOT_PASSWORD: root - - redis: - image: redis:7.2.5 - ports: - - '6379:6379' - - mongo: - image: mongo:6.0 - ports: - - 27017:27017 - - permissions: - contents: write - checks: write - pull-requests: write - - steps: - # 1-1. repository checkout - - uses: actions/checkout@v4 - - # 1-2. jdk 환경 설치 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - - # 1-3. '*.yml' 파일 세팅 - - name: application.yml 파일 설정 - run: | - mkdir -p src/main/resources - echo "${{ secrets.APPLICATION_YML }}" > ./src/main/resources/application.yml - - - name: application-dev.yml 설정 - run: echo "${{ secrets.APPLICATION_DEV_YML }}" > ./src/main/resources/application-dev.yml - - - name: application-oauth2.yml 설정 - run: echo "${{ secrets.APPLICATION_OAUTH2_YML }}" > ./src/main/resources/application-oauth2.yml - - - name: application-test.yml 설정 - run: | - mkdir -p src/test/resources - echo "${{ secrets.APPLICATION_TEST_YML }}" > ./src/test/resources/application-test.yml - - - # 1-4. 성능 향상을 위한 Gradle 캐싱 - - name: Gradle Caching - uses: actions/cache@v3 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - gradle-${{ runner.os }} - - # 1-5. 테스트 컨테이너들 작동여부 체크 - - name: MySQL 체크 - run: | - until nc -z localhost 3306; do - echo "Waiting for MySQL..." - sleep 3 - done - - - - name: Redis 체크 - run: | - until nc -z localhost 6379; do - echo "Waiting for Redis..." - sleep 3 - done - - - name: MongoDB 체크 - run: | - until nc -z localhost 27017; do - echo "Waiting for MongoDB..." - sleep 3 - done - - # 2. 빌드 - - name: Build with Gradle Wrapper - run: ./gradlew clean build - - # 3. JUnit 테스트 결과 게시 - - name: Test 결과 출력 - uses: EnricoMi/publish-unit-test-result-action@v2 - if: always() - with: - junit_files: '**/build/test-results/test/TEST-*.xml' - github_token: ${{ secrets.GITHUB_TOKEN }} - diff --git a/.github/workflows/dev-ci-cd.yml b/.github/workflows/dev-ci-cd.yml index aa9e808c..150c3c96 100644 --- a/.github/workflows/dev-ci-cd.yml +++ b/.github/workflows/dev-ci-cd.yml @@ -119,7 +119,7 @@ jobs: uses: docker/build-push-action@v5 with: context: . - dockerfile: Dockerfile + dockerfile: Dockerfile-dev push: true tags: ${{secrets.DOCKER_USERNAME}}/server:latest diff --git a/.github/workflows/dev-ci-test.yml b/.github/workflows/dev-ci-test.yml new file mode 100644 index 00000000..a5ce06be --- /dev/null +++ b/.github/workflows/dev-ci-test.yml @@ -0,0 +1,88 @@ +name: 핀하우스 테스트 CI 파이프라인 + +on: + pull_request: + branches: [ "develop", "main" ] + +jobs: + test: + runs-on: ubuntu-22.04 + services: + mysql: + image: mysql:8.0 + ports: [ '3306:3306' ] + env: + MYSQL_DATABASE: pinhouse_test + MYSQL_USER: testuser + MYSQL_PASSWORD: testpass + MYSQL_ROOT_PASSWORD: root + redis: + image: redis:7.2.5 + ports: [ '6379:6379' ] + mongo: + image: mongo:6.0 + ports: [ 27017:27017 ] + + permissions: + contents: write + checks: write + pull-requests: write + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + # 1-3. 환경 설정 파일 세팅 + - name: Resources 디렉토리 생성 + run: | + mkdir -p src/main/resources + mkdir -p src/test/resources + + - name: 설정 파일 생성 (Secrets 활용) + run: | + echo "${{ secrets.APPLICATION_DEV_YML }}" > ./src/main/resources/application-dev.yml + echo "${{ secrets.APPLICATION_PROD_YML }}" > ./src/main/resources/application-prod.yml + echo "${{ secrets.APPLICATION_OAUTH2_YML }}" > ./src/main/resources/application-oauth2.yml + echo "${{ secrets.APPLICATION_TEST_YML }}" > ./src/test/resources/application-test.yml + + - name: Gradle Caching + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: gradle-${{ runner.os }} + + - name: 서비스 대기 (Health Check) + run: | + for port in 3306 6379 27017; do + until nc -z localhost $port; do + echo "Waiting for port $port..." + sleep 3 + done + done + + # 2. 브랜치에 따른 조건부 빌드 + # PR 대상이 main이면 prod 프로파일, 그 외(develop)는 dev 프로파일 사용 + - name: Build with Gradle Wrapper + run: | + if [ "${{ github.base_ref }}" == "main" ]; then + echo "Running Build for Main Branch (PROD profile)" + ./gradlew clean build -Dspring.profiles.active=prod + else + echo "Running Build for Develop Branch (DEV profile)" + ./gradlew clean build -Dspring.profiles.active=dev + fi + + - name: Test 결과 출력 + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + junit_files: '**/build/test-results/test/TEST-*.xml' + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/main-ci-cd.yml b/.github/workflows/main-ci-cd.yml new file mode 100644 index 00000000..5f8dee19 --- /dev/null +++ b/.github/workflows/main-ci-cd.yml @@ -0,0 +1,201 @@ +name: 핀하우스 prod CI-CD 파이프라인 + +### main으로 Merge 되었을 때, 테스트 코드를 실행하여, 빌드 및 배포하는 로직을 수행한다. + +on: + push: + branches: [ "main"] + +jobs: + #1. 운영 서버 CI, Build 용 + CI: + runs-on: ubuntu-22.04 + services: + mysql: + image: mysql:8.0 + ports: + - '3306:3306' + env: + MYSQL_DATABASE: pinhouse_test + MYSQL_USER: testuser + MYSQL_PASSWORD: testpass + MYSQL_ROOT_PASSWORD: root + + redis: + image: redis:7.2.5 + ports: + - '6379:6379' + + mongo: + image: mongo:6.0 + ports: + - 27017:27017 + + permissions: + contents: write + checks: write + pull-requests: write + + steps: + # 1. repository checkout + - uses: actions/checkout@v4 + + # 2. jdk 환경 설치 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + # 3. '*.yml' 파일 세팅 + - name: application.yml 파일 설정 + run: | + mkdir -p src/main/resources + + cat < ./src/main/resources/application.yml + spring: + profiles: + active: prod + include: + - oauth2-dev + EOF + + echo "${{ secrets.APPLICATION_PROD_YML }}" > ./src/main/resources/application-prod.yml + echo "${{ secrets.APPLICATION_OAUTH2_DEV_YML }}" > ./src/main/resources/application-oauth2-dev.yml + + mkdir -p src/test/resources + echo "${{ secrets.APPLICATION_TEST_YML }}" > ./src/test/resources/application-test.yml + + # 체크 + - name: MySQL 체크 + run: | + until nc -z localhost 3306; do + echo "Waiting for MySQL..." + sleep 3 + done + + + - name: Redis 체크 + run: | + until nc -z localhost 6379; do + echo "Waiting for Redis..." + sleep 3 + done + + - name: MongoDB 체크 + run: | + until nc -z localhost 27017; do + echo "Waiting for MongoDB..." + sleep 3 + done + + # 4. gradle 환경 설치 + - name: Gradle Wrapper 권한 부여 + run: chmod +x gradlew + + # 4-1. 캐싱 + - name: Gradle Caching + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + gradle-${{ runner.os }} + + - name: Gradle 빌드 + run: ./gradlew clean -Dspring.profiles.active=prod build + + # 5. JUnit 테스트 결과 게시 + - name: Test 결과 출력 + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + junit_files: '**/build/test-results/test/TEST-*.xml' + github_token: ${{ secrets.GITHUB_TOKEN }} + + #6. 도커 허브 로그인 + - name: Docker 로그인 + uses: docker/login-action@v3 + with: + username: ${{secrets.DOCKER_USERNAME}} + password: ${{secrets.DOCKER_ACCESS_TOKEN}} + + #7. 도커 이미지 Push + - name: Docker 이미지 Push + uses: docker/build-push-action@v5 + with: + context: . + dockerfile: Dockerfile-prod + push: true + tags: ${{secrets.DOCKER_USERNAME}}/prod-server:latest + + CD: + needs: CI + runs-on: ubuntu-22.04 + + steps: + - name: 1. Checkout source code + uses: actions/checkout@v3 + + - name: env 생성 + run: echo "${{ secrets.ENV }}" > .env + + - name: 3. docker-compose.yml 전달 + uses: appleboy/scp-action@master + with: + host: ${{ secrets.EC2_PROD_PUBLIC_IP }} + username: ${{ secrets.SSH_USER }} + key: ${{ secrets.EC2_PROD_PRIVATE_KEY }} + source: ".env,docker-compose.yml" + target: "/home/ubuntu/app/" + + - name: 4. EC2에서 docker-compose 실행 + uses: appleboy/ssh-action@master # SSH를 사용하여 EC2에서 명령 실행 + with: + host: ${{ secrets.EC2_PROD_PUBLIC_IP }} # EC2 퍼블릭 IP + username: ${{ secrets.SSH_USER }} + key: ${{ secrets.EC2_PROD_PRIVATE_KEY }} + script: | + # docker-compose 명령어 실행 + cd /home/ubuntu/app/ + docker compose pull spring + docker compose up -d spring + + notify-discord: + needs: + - CD + runs-on: ubuntu-latest + steps: + - name: 체크아웃 + uses: actions/checkout@v2 + + - name: 최신 커밋 추출하기 + id: commit-info + run: | + COMMIT_MSG=$(git log -1 --pretty=format:'%s') + COMMIT_AUTHOR=$(git log -1 --pretty=format:'%an') + COMMIT_HASH=$(git log -1 --pretty=format:'%h') + + echo "message=$COMMIT_MSG" >> $GITHUB_OUTPUT + echo "author=$COMMIT_AUTHOR" >> $GITHUB_OUTPUT + echo "hash=$COMMIT_HASH" >> $GITHUB_OUTPUT + + - name: 디스코드 알림 전송하기 + uses: sarisia/actions-status-discord@v1 + with: + webhook: ${{secrets.DISCORD_WEBHOOK_URL}} + title: "✅ 운영서버 배포" + description: | + **✅ 공지**: 운영 환경에 새로운 변경사항이 배포되었습니다. + + **배포 정보**: + - 커밋: `${{ github.sha }}` + - 배포자: ${{ github.actor }} + - 시간: ${{ github.event.head_commit.timestamp }} + + [커밋 보기](${{ github.event.head_commit.url }}) + color: 0x4287f5 + username: 핀하우스 GitHub Dev Bot + avatar_url: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png diff --git a/Dockerfile b/Dockerfile-dev similarity index 100% rename from Dockerfile rename to Dockerfile-dev diff --git a/Dockerfile-prod b/Dockerfile-prod new file mode 100644 index 00000000..8deb5006 --- /dev/null +++ b/Dockerfile-prod @@ -0,0 +1,19 @@ +# ====== Build Stage ====== +FROM eclipse-temurin:21-jdk-alpine AS builder +# 작업 디렉토리를 /build로 설정합니다. +WORKDIR /build +# 파일을 이동시킵니다. +COPY . . +# 실행 가능한 Jar를 만듭니다. +RUN ./gradlew bootJar + +# ====== Runtime Stage ====== +FROM eclipse-temurin:21-jre-alpine +# 실행 파일 이동 +WORKDIR /app +# 빌드한 jar 파일 가져오기 +COPY --from=builder /build/build/libs/server-0.0.1-SNAPSHOT.jar app.jar +# 노출할 포트 +EXPOSE 8080 +# 실행하기 +CMD ["java", "-Dspring.profiles.active=prod", "-jar", "app.jar"] diff --git a/docker-compose.yml b/docker-compose.yml index 4d0f6591..e73c729d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: spring: - image: pinhouse/server:latest + image: pinhouse/prod-server:latest container_name: pinhouse-app ports: - "8080:8080" From b6658daaede7c0d87b46f2dfdacf2124bdabc9cf Mon Sep 17 00:00:00 2001 From: eedo_y Date: Sun, 18 Jan 2026 17:01:24 +0900 Subject: [PATCH 04/12] =?UTF-8?q?=F0=9F=94=A8chore=20:=20PROD=5FENV=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 --- .github/workflows/main-ci-cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main-ci-cd.yml b/.github/workflows/main-ci-cd.yml index 5f8dee19..68dc320c 100644 --- a/.github/workflows/main-ci-cd.yml +++ b/.github/workflows/main-ci-cd.yml @@ -140,7 +140,7 @@ jobs: uses: actions/checkout@v3 - name: env 생성 - run: echo "${{ secrets.ENV }}" > .env + run: echo "${{ secrets.PROD_ENV }}" > .env - name: 3. docker-compose.yml 전달 uses: appleboy/scp-action@master From 2a4ea9b5d5512f7bfe89f9d36b964fa19a5886ad Mon Sep 17 00:00:00 2001 From: eedo_y Date: Sun, 18 Jan 2026 17:03:36 +0900 Subject: [PATCH 05/12] =?UTF-8?q?=F0=9F=94=A8chore=20:=20docker-compose.ym?= =?UTF-8?q?l=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index e73c729d..cb70ce91 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,10 @@ services: container_name: pinhouse-app ports: - "8080:8080" + env_file: + - .env # 여기서 .env 파일을 읽어 환경 변수로 주입 + environment: + - SPRING_PROFILES_ACTIVE=prod networks: - backend-bridge From 4e3e31fd6a05e32989a134684b312e2b12dc166c Mon Sep 17 00:00:00 2001 From: eedo_y Date: Sun, 18 Jan 2026 17:10:55 +0900 Subject: [PATCH 06/12] =?UTF-8?q?=F0=9F=94=A8chore=20:=20dockerfile=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 --- .github/workflows/dev-ci-cd.yml | 2 +- .github/workflows/main-ci-cd.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dev-ci-cd.yml b/.github/workflows/dev-ci-cd.yml index 150c3c96..559cbab6 100644 --- a/.github/workflows/dev-ci-cd.yml +++ b/.github/workflows/dev-ci-cd.yml @@ -119,7 +119,7 @@ jobs: uses: docker/build-push-action@v5 with: context: . - dockerfile: Dockerfile-dev + file: ./Dockerfile-dev push: true tags: ${{secrets.DOCKER_USERNAME}}/server:latest diff --git a/.github/workflows/main-ci-cd.yml b/.github/workflows/main-ci-cd.yml index 68dc320c..5da5b591 100644 --- a/.github/workflows/main-ci-cd.yml +++ b/.github/workflows/main-ci-cd.yml @@ -127,7 +127,7 @@ jobs: uses: docker/build-push-action@v5 with: context: . - dockerfile: Dockerfile-prod + file: ./Dockerfile-prod push: true tags: ${{secrets.DOCKER_USERNAME}}/prod-server:latest From d67274139ede22af2e2291e17b82a6251acb26fe Mon Sep 17 00:00:00 2001 From: eedo_y Date: Sun, 18 Jan 2026 21:44:11 +0900 Subject: [PATCH 07/12] =?UTF-8?q?=F0=9F=94=A8chore=20:=20=EC=9A=B4?= =?UTF-8?q?=EC=98=81=EC=84=9C=EB=B2=84=EC=97=90=20=EB=A7=9E=EA=B2=8C=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 --- .github/workflows/main-ci-cd.yml | 2 +- docker-compose.yml | 8 +-- .../config/swaagger/ProdSwaggerConfig.java | 49 +++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/pinHouse/server/core/config/swaagger/ProdSwaggerConfig.java diff --git a/.github/workflows/main-ci-cd.yml b/.github/workflows/main-ci-cd.yml index 5da5b591..a8666db9 100644 --- a/.github/workflows/main-ci-cd.yml +++ b/.github/workflows/main-ci-cd.yml @@ -61,7 +61,7 @@ jobs: EOF echo "${{ secrets.APPLICATION_PROD_YML }}" > ./src/main/resources/application-prod.yml - echo "${{ secrets.APPLICATION_OAUTH2_DEV_YML }}" > ./src/main/resources/application-oauth2-dev.yml + echo "${{ secrets.APPLICATION_OAUTH2_PROD_YML }}" > ./src/main/resources/application-oauth2-prod.yml mkdir -p src/test/resources echo "${{ secrets.APPLICATION_TEST_YML }}" > ./src/test/resources/application-test.yml diff --git a/docker-compose.yml b/docker-compose.yml index cb70ce91..afea1abb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,10 +5,12 @@ services: container_name: pinhouse-app ports: - "8080:8080" - env_file: - - .env # 여기서 .env 파일을 읽어 환경 변수로 주입 + volumes: + # 서버의 설정파일 폴더를 컨테이너 내부로 연결 + - /home/ubuntu/app/config:/app/config environment: - - SPRING_PROFILES_ACTIVE=prod + # Spring에게 외부 파일을 읽으라고 명령 (이게 핵심입니다) + - SPRING_CONFIG_LOCATION=optional:classpath:/,file:/app/config/application-prod.yml,file:/app/config/application-oauth2-prod.yml networks: - backend-bridge diff --git a/src/main/java/com/pinHouse/server/core/config/swaagger/ProdSwaggerConfig.java b/src/main/java/com/pinHouse/server/core/config/swaagger/ProdSwaggerConfig.java new file mode 100644 index 00000000..229e8d85 --- /dev/null +++ b/src/main/java/com/pinHouse/server/core/config/swaagger/ProdSwaggerConfig.java @@ -0,0 +1,49 @@ +package com.pinHouse.server.core.config.swaagger; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.servers.Server; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.media.Schema; +import org.springdoc.core.customizers.OpenApiCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +import java.util.Map; +import java.util.TreeMap; + +/** + * Swagger / OpenAPI 설정 클래스 + * - HTTPS 설정으로 인해 @OpenAPIDefinition 추가 + * - 스웨거 내부 JWT 설정 추가 + */ + +@Configuration +@Profile("prod") +@OpenAPIDefinition(servers = {@Server(url = "https://api.pinhouse.co.kr", description = "PinHouse 운영 서버")}) +public class ProdSwaggerConfig { + + @Bean + public OpenAPI openAPI() { + return new OpenAPI() + .info(apiInfo()); + } + + + private Info apiInfo() { + return new Info() + .title("PinHouse Swagger") + .description("핀하우스 스웨거입니다.") + .version("1.0.0"); + } + + /// 스키마 이름 기준 오름차순 + @Bean + public OpenApiCustomizer sortSchemasAlphabetically() { + return openApi -> { + Map schemas = openApi.getComponents().getSchemas(); + openApi.getComponents().setSchemas(new TreeMap<>(schemas)); + }; + } +} From 366dcffdfe1b9e318589a915f95ce28e05abf17f Mon Sep 17 00:00:00 2001 From: eedo_y Date: Sun, 18 Jan 2026 21:53:02 +0900 Subject: [PATCH 08/12] =?UTF-8?q?=F0=9F=94=A8chore=20:=20=EC=A0=91?= =?UTF-8?q?=EC=86=8D=ED=95=9C=20=ED=99=88=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=EC=8A=A4=EC=9B=A8=EA=B1=B0=20=EB=85=B8=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/static/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 349d8e03..8da7e0a6 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -14,7 +14,7 @@

핀하우스 백엔드 서버입니다

이곳은 핀하우스 프로젝트의 개발 서버입니다.
아래 버튼을 눌러 API 문서를 확인하세요.

- + Swagger API From 6fac12a507683a189c5d778ccd1b770491d1d72c Mon Sep 17 00:00:00 2001 From: eedo_y Date: Mon, 19 Jan 2026 00:25:07 +0900 Subject: [PATCH 09/12] =?UTF-8?q?=E2=9C=A8feat:=20=EC=83=88=EB=A1=9C?= =?UTF-8?q?=EC=9A=B4=20=EC=A3=BC=EB=B3=80=ED=99=98=EA=B2=BD=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 --- .../application/dto/NoticeFacilityListResponse.java | 2 +- .../facility/application/service/FacilityService.java | 2 +- .../housing/facility/domain/entity/FacilityType.java | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/pinHouse/server/platform/housing/facility/application/dto/NoticeFacilityListResponse.java b/src/main/java/com/pinHouse/server/platform/housing/facility/application/dto/NoticeFacilityListResponse.java index c42a3547..7a44f36f 100644 --- a/src/main/java/com/pinHouse/server/platform/housing/facility/application/dto/NoticeFacilityListResponse.java +++ b/src/main/java/com/pinHouse/server/platform/housing/facility/application/dto/NoticeFacilityListResponse.java @@ -23,7 +23,7 @@ public static NoticeFacilityListResponse from(Map src) { List infraList = src.entrySet().stream() .filter(e -> e.getValue() != null && e.getValue() >= 3) - .map(entry -> entry.getKey().displayType()) + .map(Map.Entry::getKey) .distinct() .toList(); diff --git a/src/main/java/com/pinHouse/server/platform/housing/facility/application/service/FacilityService.java b/src/main/java/com/pinHouse/server/platform/housing/facility/application/service/FacilityService.java index d773cbc9..dd6f3adf 100644 --- a/src/main/java/com/pinHouse/server/platform/housing/facility/application/service/FacilityService.java +++ b/src/main/java/com/pinHouse/server/platform/housing/facility/application/service/FacilityService.java @@ -111,7 +111,7 @@ public List getFacilities(String complexId) { /// 3개 이상인 FacilityType return response.entrySet().stream() .filter(entry -> entry.getValue() != null && entry.getValue() >= 3) - .map(entry -> entry.getKey().displayType()) + .map(Map.Entry::getKey) .distinct() .toList(); } diff --git a/src/main/java/com/pinHouse/server/platform/housing/facility/domain/entity/FacilityType.java b/src/main/java/com/pinHouse/server/platform/housing/facility/domain/entity/FacilityType.java index 9983a534..d212c548 100644 --- a/src/main/java/com/pinHouse/server/platform/housing/facility/domain/entity/FacilityType.java +++ b/src/main/java/com/pinHouse/server/platform/housing/facility/domain/entity/FacilityType.java @@ -23,6 +23,14 @@ public enum FacilityType { STORE("대형마트-백화점", Set.of("대형마트-백화점", "대형점포")), HOSPITAL("병원-약국", Set.of("병원-약국", "병원")), LAUNDRY("세탁소", Set.of("세탁소", "빨래방")), + + ELDER("노인 시설", Set.of("노인 시설")), + CHILD("아동 시설", Set.of("아동 시설")), + BICYCLE("자전거길", Set.of("자전거길")), + DISABLED("장애인 시설", Set.of("장애인 시설")), + YOUTH("청소년 시설", Set.of("청소년 시설")), + POLICE("파출소", Set.of("파출소")), + CULTURE_CENTER("문화센터", Set.of("문화센터")); private final String value; From 2c7ec1463fc12b9107e27a6240bc3c7cfa77ba66 Mon Sep 17 00:00:00 2001 From: eedo_y Date: Tue, 20 Jan 2026 21:40:16 +0900 Subject: [PATCH 10/12] =?UTF-8?q?=E2=9C=A8feat:=20=EB=A6=AC=EB=8B=A4?= =?UTF-8?q?=EC=9D=B4=EB=A0=89=ED=8A=B8=20=EC=A3=BC=EC=86=8C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/security/oauth2/handler/OAuth2FailureHandler.java | 2 +- .../server/security/oauth2/handler/OAuth2SuccessHandler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/pinHouse/server/security/oauth2/handler/OAuth2FailureHandler.java b/src/main/java/com/pinHouse/server/security/oauth2/handler/OAuth2FailureHandler.java index 19f65bee..43e3642a 100644 --- a/src/main/java/com/pinHouse/server/security/oauth2/handler/OAuth2FailureHandler.java +++ b/src/main/java/com/pinHouse/server/security/oauth2/handler/OAuth2FailureHandler.java @@ -25,7 +25,7 @@ public class OAuth2FailureHandler extends SimpleUrlAuthenticationFailureHandler private final RedisTemplate redisTemplate; private final KeyUtil keyUtil; - @Value("${cors.front.local}") + @Value("${cors.front.redirect}") public String REDIRECT_PATH; /** diff --git a/src/main/java/com/pinHouse/server/security/oauth2/handler/OAuth2SuccessHandler.java b/src/main/java/com/pinHouse/server/security/oauth2/handler/OAuth2SuccessHandler.java index 79c5c50e..bc893d31 100644 --- a/src/main/java/com/pinHouse/server/security/oauth2/handler/OAuth2SuccessHandler.java +++ b/src/main/java/com/pinHouse/server/security/oauth2/handler/OAuth2SuccessHandler.java @@ -26,7 +26,7 @@ public class OAuth2SuccessHandler extends SimpleUrlAuthenticationSuccessHandler private final JwtProvider jwtProvider; private final HttpUtil httpUtil; - @Value("${cors.front.local}") + @Value("${cors.front.redirect}") private String REDIRECT_PATH; /* From 36a9a9239356e63339265e34f5628248df2f2f34 Mon Sep 17 00:00:00 2001 From: eedo_y Date: Tue, 20 Jan 2026 22:01:15 +0900 Subject: [PATCH 11/12] =?UTF-8?q?=E2=9C=A8feat:=20CORS=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 --- .../java/com/pinHouse/server/security/config/CorsConfig.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/pinHouse/server/security/config/CorsConfig.java b/src/main/java/com/pinHouse/server/security/config/CorsConfig.java index d5f04029..6ca01ead 100644 --- a/src/main/java/com/pinHouse/server/security/config/CorsConfig.java +++ b/src/main/java/com/pinHouse/server/security/config/CorsConfig.java @@ -16,6 +16,9 @@ public class CorsConfig { @Value("${cors.front.dev}") private String front_dev; + @Value("${cors.front.prod}") + private String front_prod; + @Value("${cors.back.dev}") private String back_dev; @@ -29,6 +32,7 @@ public CorsConfigurationSource corsConfigurationSource() { /// CORS 추가 configuration.addAllowedOriginPattern(front_local); configuration.addAllowedOriginPattern(front_dev); + configuration.addAllowedOriginPattern(front_prod); configuration.addAllowedOriginPattern(back_dev); configuration.addAllowedHeader("*"); From 75a9d8675b54d651f0f0e9580a77eefaadad4d65 Mon Sep 17 00:00:00 2001 From: eedo_y Date: Tue, 20 Jan 2026 22:24:56 +0900 Subject: [PATCH 12/12] =?UTF-8?q?=E2=9C=A8feat:=20RedisConfig=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pinHouse/server/core/config/RedisConfig.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/pinHouse/server/core/config/RedisConfig.java b/src/main/java/com/pinHouse/server/core/config/RedisConfig.java index ee6b1da6..66511845 100644 --- a/src/main/java/com/pinHouse/server/core/config/RedisConfig.java +++ b/src/main/java/com/pinHouse/server/core/config/RedisConfig.java @@ -5,13 +5,14 @@ import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration -public class RedisConfig{ +public class RedisConfig { @Value("${spring.data.redis.host}") private String host; @@ -19,6 +20,9 @@ public class RedisConfig{ @Value("${spring.data.redis.port}") private int port; + @Value("${spring.data.redis.ssl.enabled:false}") + private boolean sslEnabled; + /** * 레디스 커넥트 팩토리 설정 */ @@ -27,7 +31,13 @@ public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration(); redisConfiguration.setHostName(host); redisConfiguration.setPort(port); - return new LettuceConnectionFactory(redisConfiguration); + + LettuceClientConfiguration.LettuceClientConfigurationBuilder clientConfigBuilder = LettuceClientConfiguration.builder(); + if (sslEnabled) { + clientConfigBuilder.useSsl(); + } + + return new LettuceConnectionFactory(redisConfiguration, clientConfigBuilder.build()); } /**