Skip to content

Commit 5f77afb

Browse files
authored
Merge pull request #240 from prgrms-web-devcourse-final-project/feat/239-law
Feat/239 law
2 parents c777318 + 1c4f211 commit 5f77afb

File tree

5 files changed

+142
-12
lines changed

5 files changed

+142
-12
lines changed

backend/src/main/java/com/ai/lawyer/domain/law/service/LawService.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.springframework.transaction.annotation.Transactional;
1616
import org.springframework.util.StringUtils;
1717
import org.springframework.web.client.RestTemplate;
18+
import org.springframework.web.reactive.function.client.WebClient;
1819
import org.springframework.web.util.UriComponentsBuilder;
1920

2021
import java.io.IOException;
@@ -36,8 +37,8 @@ public class LawService {
3637
private final JoRepository joRepository;
3738
private final HangRepository hangRepository;
3839
private final HoRepository hoRepository;
39-
private final RestTemplate restTemplate = new RestTemplate();
4040
private final ObjectMapper objectMapper = new ObjectMapper();
41+
private final WebClient webClient = WebClient.builder().build();
4142

4243
// 상수 정의
4344
private static final String BASE_URL = "http://www.law.go.kr/DRF";
@@ -149,7 +150,11 @@ private String getLawSearchResponse(String query) {
149150
.build()
150151
.toUriString();
151152

152-
return restTemplate.getForObject(url, String.class);
153+
return webClient.get()
154+
.uri(url)
155+
.retrieve()
156+
.bodyToMono(String.class)
157+
.block();
153158
}
154159

155160
/**
@@ -167,7 +172,11 @@ private String getLawDetailResponse(String lawId) {
167172
.build()
168173
.toUriString();
169174

170-
return restTemplate.getForObject(url, String.class);
175+
return webClient.get()
176+
.uri(url)
177+
.retrieve()
178+
.bodyToMono(String.class)
179+
.block();
171180
}
172181

173182
/**

backend/src/main/java/com/ai/lawyer/domain/lawWord/controller/LawWordController.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,25 @@ public class LawWordController {
1919

2020
private final LawWordService lawWordService;
2121

22-
@GetMapping("/{word}")
23-
@Operation(summary = "법령 용어 검색", description = "법령 용어에 대한 정의를 반환합니다. \n" +
24-
"예시: /api/law-word/선박")
25-
public ResponseEntity<?> getPrecedent(@PathVariable String word) {
22+
@GetMapping("/v1/{word}")
23+
@Operation(summary = "법령 용어 검색 version 1", description = "법령 용어에 대한 정의를 반환합니다. \n" +
24+
"예시: /api/law-word/승소")
25+
public ResponseEntity<?> getPrecedentV1(@PathVariable String word) {
2626
try {
2727
return ResponseEntity.ok(lawWordService.findDefinition(word));
2828
}catch (Exception e){
2929
return ResponseEntity.badRequest().body(e.getMessage());
3030
}
31+
}
3132

33+
@GetMapping("/v2/{word}")
34+
@Operation(summary = "법령 용어 검색 version 2", description = "법령 용어에 대한 정의를 반환합니다. \n" +
35+
"예시: /api/law-word/승소")
36+
public ResponseEntity<?> getPrecedentV2(@PathVariable String word) {
37+
try {
38+
return ResponseEntity.ok(lawWordService.findDefinitionV2(word));
39+
}catch (Exception e){
40+
return ResponseEntity.badRequest().body(e.getMessage());
41+
}
3242
}
3343
}

backend/src/main/java/com/ai/lawyer/domain/lawWord/service/LawWordService.java

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,51 @@
1010
import org.springframework.stereotype.Service;
1111
import org.springframework.web.client.HttpClientErrorException;
1212
import org.springframework.web.client.RestTemplate;
13+
import org.springframework.web.reactive.function.client.WebClient;
14+
import org.springframework.web.util.UriComponentsBuilder;
15+
16+
import java.util.ArrayList;
17+
import java.util.List;
1318

1419
@Service
1520
@Slf4j
1621
@AllArgsConstructor
1722
public class LawWordService {
1823

1924
private final LawWordRepository lawWordRepository;
20-
private final RestTemplate restTemplate = new RestTemplate();
25+
private final WebClient webClient = WebClient.builder().build();
2126
private final ObjectMapper objectMapper = new ObjectMapper();
2227

2328
private static final String API_BASE_URL = "https://www.law.go.kr/DRF/lawService.do";
2429
private static final String API_OC = "noheechul";
2530

31+
// 우리말샘 API 설정
32+
private static final String KOREAN_DICT_API_BASE_URL = "https://opendict.korean.go.kr/api/search";
33+
private static final String API_KEY = "2A4D1A844C8BAB682B38E5F192D3D42A";
34+
2635
public String findDefinition(String word) {
2736
// 1) DB에서 먼저 조회
2837
return lawWordRepository.findByWord(word)
2938
.map(LawWord::getDefinition)
3039
.orElseGet(() -> fetchAndSaveDefinition(word));
3140
}
3241

42+
public String findDefinitionV2(String word) {
43+
// 1) DB에서 먼저 조회
44+
return lawWordRepository.findByWord(word)
45+
.map(LawWord::getDefinition)
46+
.orElseGet(() -> fetchAndSaveDefinitionV2(word));
47+
}
48+
3349
private String fetchAndSaveDefinition(String word) {
3450
try {
3551
String url = buildApiUrl(word);
36-
String json = restTemplate.getForObject(url, String.class);
52+
// WebClient 호출 (동기 방식)
53+
String json = webClient.get()
54+
.uri(url)
55+
.retrieve()
56+
.bodyToMono(String.class)
57+
.block();
3758

3859
String definition = extractDefinitionFromJson(json);
3960
saveDefinition(word, definition);
@@ -52,10 +73,54 @@ private String fetchAndSaveDefinition(String word) {
5273
}
5374
}
5475

76+
private String fetchAndSaveDefinitionV2(String word) {
77+
try {
78+
String url = buildKoreanDictApiUrl(word);
79+
80+
// WebClient 호출 (동기 방식)
81+
String json = webClient.get()
82+
.uri(url)
83+
.retrieve()
84+
.bodyToMono(String.class)
85+
.block();
86+
87+
String combinedDefinitions = extractTop3DefinitionsFromJson(json);
88+
saveDefinition(word, combinedDefinitions);
89+
90+
return combinedDefinitions;
91+
92+
} catch (HttpClientErrorException e) {
93+
log.error("한국어사전 API 호출 중 클라이언트 오류 발생: {}", e.getMessage());
94+
throw new RuntimeException("한국어사전 API 호출 중 오류가 발생했습니다.");
95+
} catch (JsonProcessingException e) {
96+
log.error("JSON 파싱 중 오류 발생: {}", e.getMessage());
97+
throw new RuntimeException("한국어사전 API 응답 처리 중 파싱 오류가 발생했습니다.");
98+
} catch (Exception e) {
99+
log.error("정의 조회 실패: ", e);
100+
throw new RuntimeException("한국어사전 정의 조회 중 알 수 없는 오류가 발생했습니다.");
101+
}
102+
}
103+
55104
private String buildApiUrl(String word) {
56105
return API_BASE_URL + "?OC=" + API_OC + "&target=lstrm&type=JSON&query=" + word;
57106
}
58107

108+
private String buildKoreanDictApiUrl(String word) {
109+
return UriComponentsBuilder.fromHttpUrl(KOREAN_DICT_API_BASE_URL)
110+
.queryParam("key", API_KEY)
111+
.queryParam("req_type", "json")
112+
.queryParam("part", "word")
113+
.queryParam("q", word)
114+
.queryParam("sort", "dict")
115+
.queryParam("start", "1")
116+
.queryParam("num", "10")
117+
.queryParam("advanced", "y")
118+
.queryParam("type4", "all")
119+
.queryParam("cat", "23")
120+
.build()
121+
.toUriString();
122+
}
123+
59124
private String extractDefinitionFromJson(String json) throws JsonProcessingException {
60125
JsonNode rootNode = objectMapper.readTree(json);
61126
if (rootNode.has("Law")) {
@@ -71,6 +136,41 @@ private String extractDefinitionFromJson(String json) throws JsonProcessingExcep
71136
}
72137
}
73138

139+
private String extractTop3DefinitionsFromJson(String json) throws JsonProcessingException {
140+
JsonNode rootNode = objectMapper.readTree(json);
141+
142+
// channel > item 배열에서 아이템들 추출
143+
JsonNode itemsNode = rootNode.path("channel").path("item");
144+
145+
if (!itemsNode.isArray() || itemsNode.size() == 0) {
146+
throw new RuntimeException("검색 결과가 없습니다.");
147+
}
148+
149+
List<String> definitions = new ArrayList<>();
150+
151+
// 최대 3개의 definition 추출
152+
for (int i = 0; i < Math.min(itemsNode.size(), 3); i++) {
153+
JsonNode item = itemsNode.get(i);
154+
JsonNode senseNode = item.path("sense");
155+
156+
if (senseNode.isArray() && senseNode.size() > 0) {
157+
JsonNode firstSense = senseNode.get(0);
158+
String definition = firstSense.path("definition").asText();
159+
160+
if (definition != null && !definition.trim().isEmpty()) {
161+
definitions.add(definition.trim());
162+
}
163+
}
164+
}
165+
166+
if (definitions.isEmpty()) {
167+
throw new RuntimeException("검색 결과에서 정의를 찾을 수 없습니다.");
168+
}
169+
170+
// 줄바꿈으로 연결하여 하나의 문자열로 만들기
171+
return String.join("\n", definitions);
172+
}
173+
74174
private void saveDefinition(String word, String definition) {
75175
LawWord entity = LawWord.builder()
76176
.word(word)

backend/src/main/java/com/ai/lawyer/domain/precedent/dto/PrecedentSummaryListDto.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
import lombok.AllArgsConstructor;
44
import lombok.Data;
5+
import lombok.NoArgsConstructor;
56

67
import java.time.LocalDate;
78

89
@Data
910
@AllArgsConstructor
11+
@NoArgsConstructor
1012
public class PrecedentSummaryListDto {
1113
private Long id;
1214
private String caseName; // 사건명

backend/src/main/java/com/ai/lawyer/domain/precedent/service/PrecedentService.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.springframework.transaction.annotation.Transactional;
1515
import org.springframework.util.StringUtils;
1616
import org.springframework.web.client.RestTemplate;
17+
import org.springframework.web.reactive.function.client.WebClient;
1718
import org.springframework.web.util.UriComponentsBuilder;
1819

1920
import java.time.LocalDate;
@@ -31,7 +32,7 @@ public class PrecedentService {
3132

3233
private final PrecedentRepository precedentRepository;
3334
private final EntityManager entityManager;
34-
private final RestTemplate restTemplate = new RestTemplate();
35+
private final WebClient webClient = WebClient.builder().build();
3536
private final ObjectMapper objectMapper = new ObjectMapper();
3637

3738
// 상수 정의
@@ -78,7 +79,11 @@ public List<String> getPrecedentNumbers(String query) {
7879

7980
do {
8081
String url = buildSearchUrl(query, page);
81-
String json = restTemplate.getForObject(url, String.class);
82+
String json = webClient.get()
83+
.uri(url)
84+
.retrieve()
85+
.bodyToMono(String.class)
86+
.block();
8287
JsonNode root = objectMapper.readTree(json);
8388
JsonNode precSearch = root.path("PrecSearch");
8489

@@ -114,7 +119,11 @@ public List<Precedent> getPrecedentDetails(List<String> precedentIds) {
114119
for (String precedentId : precedentIds) {
115120
try {
116121
String url = buildDetailUrl(precedentId);
117-
String json = restTemplate.getForObject(url, String.class);
122+
String json = webClient.get()
123+
.uri(url)
124+
.retrieve()
125+
.bodyToMono(String.class)
126+
.block();
118127

119128
Precedent precedent = parseJsonToPrecedent(json);
120129
if (precedent != null) {

0 commit comments

Comments
 (0)