From a5639185c8c1f5157df0d8b6d3dee2eb5de9fd49 Mon Sep 17 00:00:00 2001 From: Seojun Lee Date: Mon, 9 Feb 2026 20:16:06 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/GeocodingService.java | 46 +++++++++++++++++++ .../domain/member/service/ProfileService.java | 11 +++++ .../security/oauth/dto/KakaoMapDto.java | 42 +++++++++++++++++ .../security/oauth/kakao/KakaoMapClient.java | 23 ++++++++++ 4 files changed, 122 insertions(+) create mode 100644 src/main/java/com/meetkey/server/domain/member/service/GeocodingService.java create mode 100644 src/main/java/com/meetkey/server/global/security/oauth/dto/KakaoMapDto.java create mode 100644 src/main/java/com/meetkey/server/global/security/oauth/kakao/KakaoMapClient.java diff --git a/src/main/java/com/meetkey/server/domain/member/service/GeocodingService.java b/src/main/java/com/meetkey/server/domain/member/service/GeocodingService.java new file mode 100644 index 0000000..8192749 --- /dev/null +++ b/src/main/java/com/meetkey/server/domain/member/service/GeocodingService.java @@ -0,0 +1,46 @@ +package com.meetkey.server.domain.member.service; + +import com.meetkey.server.global.security.oauth.dto.KakaoMapDto; +import com.meetkey.server.global.security.oauth.kakao.KakaoMapClient; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class GeocodingService { + + private final KakaoMapClient kakaoMapClient; + + @Value("${kakao.map-api-key}") + private String kakaoMapApiKey; + + public String getAddress(Double latitude, Double longitude) { + if (latitude == null || longitude == null) { + return null; + } + + try { + String authorization = "KakaoAK " + kakaoMapApiKey; + + KakaoMapDto response = kakaoMapClient.getRegionCode(authorization, longitude, latitude); + + if (response != null && response.getDocuments() != null && !response.getDocuments().isEmpty()) { + KakaoMapDto.Document doc = response.getDocuments().stream() + .filter(d -> "H".equals(d.getRegionType())) + .findFirst() + .orElse(response.getDocuments().get(0)); + + String city = doc.getRegion1depthName(); + String district = doc.getRegion2depthName(); + + return "대한민국, " + city; + } + } catch (Exception e) { + log.error("Geocoding failed for lat: {}, lon: {}", latitude, longitude, e); + } + return null; + } +} diff --git a/src/main/java/com/meetkey/server/domain/member/service/ProfileService.java b/src/main/java/com/meetkey/server/domain/member/service/ProfileService.java index 8825ab3..0ddf9a7 100644 --- a/src/main/java/com/meetkey/server/domain/member/service/ProfileService.java +++ b/src/main/java/com/meetkey/server/domain/member/service/ProfileService.java @@ -38,6 +38,7 @@ public class ProfileService { private final MemberLocationRepository memberLocationRepository; private final EvaluationRepository evaluationRepository; private final BadgeService badgeService; + private final GeocodingService geocodingService; public ProfileUpdateResponse updateProfile(Long memberId, ProfileUpdateRequest request) { Member member = getMember(memberId); @@ -54,6 +55,11 @@ public ProfileUpdateResponse updateProfile(Long memberId, ProfileUpdateRequest r } else { memberLocation.update(request.latitude(), request.longitude()); } + + String address = geocodingService.getAddress(request.latitude(), request.longitude()); + if (address != null) { + member.updateProfileInfo(address, request.bio(), request.first(), request.target(), request.level()); + } } badgeService.checkProfileCompletion(memberId); @@ -73,6 +79,11 @@ public void updateLocation(Long memberId, LocationUpdateRequest request) { } else { memberLocation.update(request.latitude(), request.longitude()); } + + String address = geocodingService.getAddress(request.latitude(), request.longitude()); + if (address != null) { + member.updateProfileInfo(address, member.getBio(), member.getFirstLanguage(), member.getTargetLanguage(), member.getTargetLanguageLevel()); + } } } diff --git a/src/main/java/com/meetkey/server/global/security/oauth/dto/KakaoMapDto.java b/src/main/java/com/meetkey/server/global/security/oauth/dto/KakaoMapDto.java new file mode 100644 index 0000000..2f86d79 --- /dev/null +++ b/src/main/java/com/meetkey/server/global/security/oauth/dto/KakaoMapDto.java @@ -0,0 +1,42 @@ +package com.meetkey.server.global.security.oauth.dto; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@NoArgsConstructor +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class KakaoMapDto { + private Meta meta; + private List documents; + + @Getter + @NoArgsConstructor + @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) + public static class Meta { + private int totalCount; + } + + @Getter + @NoArgsConstructor + @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) + public static class Document { + private String regionType; + private String addressName; + @com.fasterxml.jackson.annotation.JsonProperty("region_1depth_name") + private String region1depthName; // 시/도 + @com.fasterxml.jackson.annotation.JsonProperty("region_2depth_name") + private String region2depthName; // 시/군/구 + @com.fasterxml.jackson.annotation.JsonProperty("region_3depth_name") + private String region3depthName; // 읍/면/동 + @com.fasterxml.jackson.annotation.JsonProperty("region_4depth_name") + private String region4depthName; + private String code; + private double x; + private double y; + } +} diff --git a/src/main/java/com/meetkey/server/global/security/oauth/kakao/KakaoMapClient.java b/src/main/java/com/meetkey/server/global/security/oauth/kakao/KakaoMapClient.java new file mode 100644 index 0000000..45601e1 --- /dev/null +++ b/src/main/java/com/meetkey/server/global/security/oauth/kakao/KakaoMapClient.java @@ -0,0 +1,23 @@ +package com.meetkey.server.global.security.oauth.kakao; + +import com.meetkey.server.global.config.FeignConfig; +import com.meetkey.server.global.config.OauthConfig; +import com.meetkey.server.global.security.oauth.dto.KakaoMapDto; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient( + name = "KakaoMapClient", + url = "https://dapi.kakao.com", + configuration = {OauthConfig.class, FeignConfig.class} +) +public interface KakaoMapClient { + @GetMapping("/v2/local/geo/coord2regioncode.json") + KakaoMapDto getRegionCode( + @RequestHeader("Authorization") String authorization, + @RequestParam("x") Double longitude, + @RequestParam("y") Double latitude + ); +} From 3cf01ecd9aff261c04d386624f35ad3d003280d8 Mon Sep 17 00:00:00 2001 From: Seojun Lee Date: Mon, 9 Feb 2026 20:17:22 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20=ED=99=88(=EC=B6=94=EC=B2=9C)?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=88=84=EB=9D=BD=EB=90=9C=20=EB=B1=83?= =?UTF-8?q?=EC=A7=80,=20=EC=9C=84=EC=B9=98=20=EC=A0=95=EB=B3=B4=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 --- .../server/domain/match/dto/RecommendationResDTO.java | 4 +++- .../server/domain/match/service/MatchServiceImpl.java | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/meetkey/server/domain/match/dto/RecommendationResDTO.java b/src/main/java/com/meetkey/server/domain/match/dto/RecommendationResDTO.java index ddf78a5..8aacf3f 100644 --- a/src/main/java/com/meetkey/server/domain/match/dto/RecommendationResDTO.java +++ b/src/main/java/com/meetkey/server/domain/match/dto/RecommendationResDTO.java @@ -18,7 +18,9 @@ public record RecommendationResDTO( List interests, PersonalityDTO personality, List photoUrls, - String introduction + String introduction, + String badgeLevel, + String location ) { @Builder public record LanguageDTO( diff --git a/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java b/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java index fa2b41f..3c67fdd 100644 --- a/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java +++ b/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java @@ -1,5 +1,6 @@ package com.meetkey.server.domain.match.service; +import com.meetkey.server.domain.badge.enums.BadgeLevel; import com.meetkey.server.domain.chat.repository.ChatRoomMemberRepository; import com.meetkey.server.domain.match.dto.*; import com.meetkey.server.domain.match.entity.RecommendationQueue; @@ -43,6 +44,7 @@ public class MatchServiceImpl implements MatchService { private final PreferenceRepository preferenceRepository; private final RecommendationQueueRepository recommendationQueueRepository; private final ChatRoomMemberRepository chatRoomMemberRepository; + private final com.meetkey.server.domain.badge.respository.PointHistoryRepository pointHistoryRepository; @Transactional @Override @@ -324,6 +326,11 @@ private RecommendationResDTO convertToDTO(Member member) { private RecommendationResDTO convertToDTO(Member member, Preference pref, double distance) { int age = member.getBirthday() != null ? LocalDate.now().getYear() - member.getBirthday().getYear() + 1 : 0; + int totalScore = pointHistoryRepository.calculateTotalScore(member); + String badgeLevel = BadgeLevel.fromScore(totalScore).name(); + + String location = member.getLocation(); + RecommendationResDTO.PersonalityDTO personalityDTO = null; if (pref != null) { personalityDTO = RecommendationResDTO.PersonalityDTO.builder() @@ -359,6 +366,8 @@ private RecommendationResDTO convertToDTO(Member member, Preference pref, double .personality(personalityDTO) .photoUrls(Collections.emptyList()) // 플레이스홀더 .introduction(member.getBio()) + .badgeLevel(badgeLevel) + .location(location) .build(); } From 63a0094875caa43b387574a575fdab83c05a43b9 Mon Sep 17 00:00:00 2001 From: Seojun Lee Date: Mon, 9 Feb 2026 20:17:00 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20[CI/CD]=20KAKAO=20MAP=20API=20?= =?UTF-8?q?=ED=99=98=EA=B2=BD=EB=B3=80=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd.yml | 2 ++ docker-compose.yml | 1 + src/main/resources/application-local.yaml | 1 + src/main/resources/application-prod.yaml | 1 + src/main/resources/application-test.yaml | 1 + 5 files changed, 6 insertions(+) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 85fa530..2ba7bd5 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -65,6 +65,8 @@ jobs: export FCM_KEY_PATH=/app/firebase/${{ secrets.FCM_FILE_NAME }} + export KAKAO_MAP_API_KEY='${{ secrets.KAKAO_MAP_API_KEY }}' + # Spring Boot 앱만 pull docker compose -f docker-compose.yml pull spring-boot-app diff --git a/docker-compose.yml b/docker-compose.yml index 7df5846..06e8525 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,7 @@ services: COOLSMS_API_KEY: ${COOLSMS_API_KEY} COOLSMS_API_SECRET: ${COOLSMS_API_SECRET} COOLSMS_SENDER: ${COOLSMS_SENDER} + KAKAO_MAP_API_KEY: ${KAKAO_MAP_API_KEY} volumes: - /home/ec2-user/app/firebase:/app/firebase:ro depends_on: diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml index b2e2106..e8d6094 100644 --- a/src/main/resources/application-local.yaml +++ b/src/main/resources/application-local.yaml @@ -33,6 +33,7 @@ admin: kakao: app-key: ${KAKAO_APP_KEY:} + map-api-key: ${KAKAO_MAP_API_KEY:} apple: app-key: ${APPLE_APP_KEY:} diff --git a/src/main/resources/application-prod.yaml b/src/main/resources/application-prod.yaml index e825d0c..07e0654 100644 --- a/src/main/resources/application-prod.yaml +++ b/src/main/resources/application-prod.yaml @@ -33,6 +33,7 @@ admin: kakao: app-key: ${KAKAO_APP_KEY:} + map-api-key: ${KAKAO_MAP_API_KEY:} apple: app-key: ${APPLE_APP_KEY:} \ No newline at end of file diff --git a/src/main/resources/application-test.yaml b/src/main/resources/application-test.yaml index 0872f09..6d107dd 100644 --- a/src/main/resources/application-test.yaml +++ b/src/main/resources/application-test.yaml @@ -37,6 +37,7 @@ admin: kakao: app-key: ${KAKAO_APP_KEY:} + map-api-key: ${KAKAO_MAP_API_KEY:} apple: app-key: ${APPLE_APP_KEY:} From 44e56108c10fc4c78225e7c938bb36ae22c7a354 Mon Sep 17 00:00:00 2001 From: Seojun Lee Date: Mon, 9 Feb 2026 21:03:43 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20=EB=B1=83=EC=A7=80=EC=97=90=20?= =?UTF-8?q?=EB=88=84=EB=9D=BD=EB=90=9C=20=EC=A0=90=EC=88=98=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/domain/match/dto/RecommendationResDTO.java | 9 ++++++++- .../server/domain/match/service/MatchServiceImpl.java | 5 ++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/meetkey/server/domain/match/dto/RecommendationResDTO.java b/src/main/java/com/meetkey/server/domain/match/dto/RecommendationResDTO.java index 8aacf3f..f3dbf47 100644 --- a/src/main/java/com/meetkey/server/domain/match/dto/RecommendationResDTO.java +++ b/src/main/java/com/meetkey/server/domain/match/dto/RecommendationResDTO.java @@ -19,7 +19,7 @@ public record RecommendationResDTO( PersonalityDTO personality, List photoUrls, String introduction, - String badgeLevel, + BadgeInfoDTO badge, String location ) { @Builder @@ -38,4 +38,11 @@ public record PersonalityDTO( RelationType relationType ) { } + + @Builder + public record BadgeInfoDTO( + String level, + int score + ) { + } } diff --git a/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java b/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java index 3c67fdd..2dc734c 100644 --- a/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java +++ b/src/main/java/com/meetkey/server/domain/match/service/MatchServiceImpl.java @@ -366,7 +366,10 @@ private RecommendationResDTO convertToDTO(Member member, Preference pref, double .personality(personalityDTO) .photoUrls(Collections.emptyList()) // 플레이스홀더 .introduction(member.getBio()) - .badgeLevel(badgeLevel) + .badge(RecommendationResDTO.BadgeInfoDTO.builder() + .level(badgeLevel) + .score(totalScore) + .build()) .location(location) .build(); }