Skip to content

Commit 71e4594

Browse files
authored
Merge pull request #28 from CarToi/dev
배포 추가 - 지오코딩 로직 + 요청 / 응답 환산 처리 변경
2 parents 1f77840 + 7ef5aad commit 71e4594

File tree

13 files changed

+281
-23
lines changed

13 files changed

+281
-23
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//package org.jun.saemangeum.consume.config;
2+
//
3+
//import org.springframework.beans.factory.annotation.Value;
4+
//import org.springframework.context.annotation.Bean;
5+
//import org.springframework.context.annotation.Configuration;
6+
//import org.springframework.http.client.SimpleClientHttpRequestFactory;
7+
//import org.springframework.web.client.RestTemplate;
8+
//
9+
//@Configuration
10+
//public class CoordinateConfig {
11+
//
12+
// @Value("${kakao.apiKey}")
13+
// private String apiKey;
14+
//
15+
// @Bean(name = "coordinateTemplate")
16+
// public RestTemplate coordinateTemplate() {
17+
// SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
18+
// requestFactory.setConnectTimeout(5000);
19+
// requestFactory.setReadTimeout(5000);
20+
//
21+
// RestTemplate restTemplate = new RestTemplate(requestFactory);
22+
//
23+
// // Authorization 헤더 자동 추가용 인터셉터 설정
24+
// restTemplate.getInterceptors().add((request, body, execution) -> {
25+
// request.getHeaders().add("Authorization", "KakaoAK " + apiKey);
26+
// request.getHeaders().add("User-Agent", "Mozilla/5.0");
27+
// return execution.execute(request, body);
28+
// });
29+
//
30+
// return restTemplate;
31+
// }
32+
//}
33+
34+
// 카카오 인코딩 정책을 먼저 파악하고 빈 등록하기
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.jun.saemangeum.consume.domain.dto;
2+
3+
public record Coordinate(
4+
double longitude, // 경도(longitude)
5+
double latitude // 위도(latitude)
6+
) {
7+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.jun.saemangeum.consume.domain.dto;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
import java.util.List;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
import lombok.Setter;
8+
9+
@Getter
10+
@Setter
11+
@NoArgsConstructor
12+
@JsonIgnoreProperties(ignoreUnknown = true)
13+
public class KakaoResponse {
14+
private List<Document> documents;
15+
16+
@Getter
17+
@Setter
18+
@NoArgsConstructor
19+
@JsonIgnoreProperties(ignoreUnknown = true)
20+
public static class Document {
21+
private String x;
22+
private String y;
23+
private Address address;
24+
private RoadAddress road_address;
25+
26+
@Getter
27+
@Setter
28+
@NoArgsConstructor
29+
@JsonIgnoreProperties(ignoreUnknown = true)
30+
public static class Address {
31+
private String x;
32+
private String y;
33+
}
34+
35+
@Getter
36+
@Setter
37+
@NoArgsConstructor
38+
@JsonIgnoreProperties(ignoreUnknown = true)
39+
public static class RoadAddress {
40+
private String x;
41+
private String y;
42+
}
43+
}
44+
}
Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
package org.jun.saemangeum.consume.domain.dto;
22

3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
35
import org.jun.saemangeum.global.domain.Category;
46

5-
public record RecommendationResponse(
6-
String title, // 추천 컨텐츠명
7-
String position, // 위치 (주소 혹은 추상적인 지리적 위치)
8-
Category category, // 컨텐츠 분류 (FESTIVAL, EVENT, TOUR, CULTURE)
9-
String image, // 이미지 소스 url (null 가능성 존재)
10-
String url // 컨텐츠 데이터 출처
11-
) {
7+
@Getter
8+
@AllArgsConstructor
9+
public class RecommendationResponse {
10+
private String title; // 추천 컨텐츠명
11+
private String position; // 위치 (주소 혹은 추상적인 지리적 위치)
12+
private Category category; // 컨텐츠 분류 (FESTIVAL, EVENT, TOUR, CULTURE)
13+
private String image; // 이미지 소스 url (null 가능성 존재)
14+
private String url; // 컨텐츠 데이터 출처
15+
private Coordinate coordinate; // 위경도 좌표 내부 JSON
16+
17+
public void updateCoordinate(Coordinate coordinate) {
18+
this.coordinate = coordinate;
19+
}
1220
}

src/main/java/org/jun/saemangeum/consume/domain/swap/ContentView.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public class ContentView implements IContent {
4242

4343
@Override
4444
public RecommendationResponse to() {
45-
return new RecommendationResponse(title, position, category, image, url);
45+
return new RecommendationResponse(title, position, category, image, url, null);
4646
}
4747

4848
@Override

src/main/java/org/jun/saemangeum/consume/service/application/SurveyRecommendationService.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.jun.saemangeum.consume.service.application;
22

33
import lombok.RequiredArgsConstructor;
4+
import org.jun.saemangeum.consume.domain.dto.Coordinate;
45
import org.jun.saemangeum.consume.domain.dto.RecommendationResponse;
56
import org.jun.saemangeum.consume.domain.dto.SurveyCreateRequest;
67
import org.jun.saemangeum.consume.domain.dto.SurveyUpdateRequest;
@@ -9,6 +10,7 @@
910
import org.jun.saemangeum.consume.service.domain.RecommendationLogService;
1011
import org.jun.saemangeum.consume.service.domain.SurveyService;
1112
import org.jun.saemangeum.consume.service.strategy.StrategyContextHolder;
13+
import org.jun.saemangeum.consume.util.CoordinateCalculator;
1214
import org.jun.saemangeum.global.domain.IContent;
1315
import org.jun.saemangeum.global.exception.SatisfactionsException;
1416
import org.springframework.stereotype.Service;
@@ -19,6 +21,7 @@
1921
@Service
2022
@RequiredArgsConstructor
2123
public class SurveyRecommendationService {
24+
private final CoordinateCalculator coordinateCalculator;
2225
private final SurveyService surveyService;
2326
private final RecommendationLogService recommendationLogService;
2427

@@ -27,7 +30,8 @@ public class SurveyRecommendationService {
2730
*/
2831
public List<RecommendationResponse> createRecommendationsBySurvey(SurveyCreateRequest request) {
2932
String age = request.age() > 30 ? "늙은" : "젊은";
30-
String text = request.gender() + " " + age + " "
33+
String awareness = ""; // request.resident()
34+
String text = request.gender() + " " + age + " " + awareness
3135
+ request.city() + " " + request.mood() + " " + request.want();
3236

3337
// 전략 패턴 적용
@@ -38,7 +42,12 @@ public List<RecommendationResponse> createRecommendationsBySurvey(SurveyCreateRe
3842
.map(e -> new RecommendationLog(e, survey)).toList();
3943
recommendationLogService.saveALl(recommendationLogs);
4044

41-
return contents.stream().map(IContent::to).toList();
45+
Coordinate coordinate = new Coordinate(0.1, 0.1);
46+
47+
return contents.stream()
48+
.map(IContent::to)
49+
.peek(e -> e.updateCoordinate(coordinateCalculator.getCoordinate(e.getPosition())))
50+
.toList();
4251
}
4352

4453
/**

src/main/java/org/jun/saemangeum/consume/service/strategy/TableEmbeddingVectorStrategy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public List<? extends IContent> calculateSimilarity(String text) {
3737

3838
TableEmbeddingVectorStrategy.ContentSimilarity cs =
3939
new TableEmbeddingVectorStrategy.ContentSimilarity(vec.getContent(), similarity);
40-
if (pq.size() < 18) {
40+
if (pq.size() < 10) {
4141
pq.offer(cs);
4242
} else if (similarity > pq.peek().similarity) {
4343
pq.poll();

src/main/java/org/jun/saemangeum/consume/service/strategy/ViewEmbeddingVectorStrategy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public List<? extends IContent> calculateSimilarity(String text) {
3737

3838
ViewEmbeddingVectorStrategy.ContentSimilarity cs =
3939
new ViewEmbeddingVectorStrategy.ContentSimilarity(vec.getContentView(), similarity);
40-
if (pq.size() < 18) {
40+
if (pq.size() < 10) {
4141
pq.offer(cs);
4242
} else if (similarity > pq.peek().similarity) {
4343
pq.poll();
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package org.jun.saemangeum.consume.util;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import java.io.IOException;
6+
import java.net.URI;
7+
import java.net.URLEncoder;
8+
import java.net.http.HttpClient;
9+
import java.net.http.HttpRequest;
10+
import java.net.http.HttpResponse;
11+
import java.nio.charset.StandardCharsets;
12+
import lombok.extern.slf4j.Slf4j;
13+
import org.jun.saemangeum.consume.domain.dto.Coordinate;
14+
import org.jun.saemangeum.consume.domain.dto.KakaoResponse;
15+
import org.springframework.beans.factory.annotation.Value;
16+
import org.springframework.stereotype.Component;
17+
18+
@Slf4j
19+
@Component
20+
public class CoordinateCalculator {
21+
22+
@Value("${kakao.apiKey}")
23+
private String apiKey;
24+
25+
@Value("${kakao.centralX}")
26+
private double centralX;
27+
28+
@Value("${kakao.centralY}")
29+
private double centralY;
30+
31+
@Value("${kakao.distance}")
32+
private double distance;
33+
34+
private final ObjectMapper objectMapper = new ObjectMapper();
35+
private final HttpClient httpClient = HttpClient.newHttpClient();
36+
37+
public Coordinate getCoordinate(String position) {
38+
String responseJson = getResponseJson(position);
39+
KakaoResponse body = null;
40+
41+
try {
42+
body = objectMapper.readValue(responseJson, KakaoResponse.class);
43+
} catch (JsonProcessingException ex) {
44+
log.error(ex.getMessage());
45+
}
46+
47+
if (body == null) return null;
48+
if (body.getDocuments() == null || body.getDocuments().isEmpty()) return null;
49+
50+
var doc = body.getDocuments().getFirst();
51+
52+
// 최상위 x/y
53+
if (doc.getX() != null && doc.getY() != null) return toCoordinate(doc.getX(), doc.getY());
54+
55+
// road_address
56+
if (doc.getRoad_address() != null && doc.getRoad_address().getX() != null) {
57+
return toCoordinate(doc.getRoad_address().getX(), doc.getRoad_address().getY());
58+
}
59+
60+
// address
61+
if (doc.getAddress() != null && doc.getAddress().getX() != null) {
62+
return toCoordinate(doc.getAddress().getX(), doc.getAddress().getY());
63+
}
64+
65+
// 최종 폴백
66+
return null;
67+
}
68+
69+
private String getResponseJson(String position) {
70+
String apiUrl = "https://dapi.kakao.com/v2/local/search/address.json";
71+
72+
try {
73+
String encodedPosition = URLEncoder.encode(position, StandardCharsets.UTF_8);
74+
String urlWithParams = apiUrl + "?query=" + encodedPosition;
75+
76+
HttpRequest request = HttpRequest.newBuilder()
77+
.uri(URI.create(urlWithParams))
78+
.header("Authorization", "KakaoAK " + apiKey)
79+
.GET()
80+
.build();
81+
82+
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
83+
return response.body();
84+
85+
} catch (IOException | InterruptedException e) {
86+
log.error(e.getMessage());
87+
}
88+
89+
return null;
90+
}
91+
92+
private Coordinate toCoordinate(String x, String y) {
93+
double dx = Double.parseDouble(x);
94+
double dy = Double.parseDouble(y);
95+
return calculate(dx, dy) ? new Coordinate(dx, dy) : null;
96+
}
97+
98+
private boolean calculate(double x, double y) {
99+
double dx = x - centralX;
100+
double dy = y - centralY;
101+
102+
return distance >= Math.sqrt(dx * dx + dy * dy);
103+
}
104+
}

src/main/java/org/jun/saemangeum/global/domain/Content.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public void setVector(Vector vector) {
6767

6868
@Override
6969
public RecommendationResponse to() {
70-
return new RecommendationResponse(title, position, category, image, url);
70+
return new RecommendationResponse(title, position, category, image, url, null);
7171
}
7272

7373
@Override

0 commit comments

Comments
 (0)