Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.IOException;
Expand All @@ -36,8 +37,8 @@ public class LawService {
private final JoRepository joRepository;
private final HangRepository hangRepository;
private final HoRepository hoRepository;
private final RestTemplate restTemplate = new RestTemplate();
private final ObjectMapper objectMapper = new ObjectMapper();
private final WebClient webClient = WebClient.builder().build();

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

return restTemplate.getForObject(url, String.class);
return webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.block();
}

/**
Expand All @@ -167,7 +172,11 @@ private String getLawDetailResponse(String lawId) {
.build()
.toUriString();

return restTemplate.getForObject(url, String.class);
return webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.block();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,25 @@ public class LawWordController {

private final LawWordService lawWordService;

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

@GetMapping("/v2/{word}")
@Operation(summary = "법령 용어 검색 version 2", description = "법령 용어에 대한 정의를 반환합니다. \n" +
"예시: /api/law-word/승소")
public ResponseEntity<?> getPrecedentV2(@PathVariable String word) {
try {
return ResponseEntity.ok(lawWordService.findDefinitionV2(word));
}catch (Exception e){
return ResponseEntity.badRequest().body(e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,51 @@
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
@AllArgsConstructor
public class LawWordService {

private final LawWordRepository lawWordRepository;
private final RestTemplate restTemplate = new RestTemplate();
private final WebClient webClient = WebClient.builder().build();
private final ObjectMapper objectMapper = new ObjectMapper();

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

// 우리말샘 API 설정
private static final String KOREAN_DICT_API_BASE_URL = "https://opendict.korean.go.kr/api/search";
private static final String API_KEY = "2A4D1A844C8BAB682B38E5F192D3D42A";

public String findDefinition(String word) {
// 1) DB에서 먼저 조회
return lawWordRepository.findByWord(word)
.map(LawWord::getDefinition)
.orElseGet(() -> fetchAndSaveDefinition(word));
}

public String findDefinitionV2(String word) {
// 1) DB에서 먼저 조회
return lawWordRepository.findByWord(word)
.map(LawWord::getDefinition)
.orElseGet(() -> fetchAndSaveDefinitionV2(word));
}

private String fetchAndSaveDefinition(String word) {
try {
String url = buildApiUrl(word);
String json = restTemplate.getForObject(url, String.class);
// WebClient 호출 (동기 방식)
String json = webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.block();

String definition = extractDefinitionFromJson(json);
saveDefinition(word, definition);
Expand All @@ -52,10 +73,54 @@ private String fetchAndSaveDefinition(String word) {
}
}

private String fetchAndSaveDefinitionV2(String word) {
try {
String url = buildKoreanDictApiUrl(word);

// WebClient 호출 (동기 방식)
String json = webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.block();

String combinedDefinitions = extractTop3DefinitionsFromJson(json);
saveDefinition(word, combinedDefinitions);

return combinedDefinitions;

} catch (HttpClientErrorException e) {
log.error("한국어사전 API 호출 중 클라이언트 오류 발생: {}", e.getMessage());
throw new RuntimeException("한국어사전 API 호출 중 오류가 발생했습니다.");
} catch (JsonProcessingException e) {
log.error("JSON 파싱 중 오류 발생: {}", e.getMessage());
throw new RuntimeException("한국어사전 API 응답 처리 중 파싱 오류가 발생했습니다.");
} catch (Exception e) {
log.error("정의 조회 실패: ", e);
throw new RuntimeException("한국어사전 정의 조회 중 알 수 없는 오류가 발생했습니다.");
}
}

private String buildApiUrl(String word) {
return API_BASE_URL + "?OC=" + API_OC + "&target=lstrm&type=JSON&query=" + word;
}

private String buildKoreanDictApiUrl(String word) {
return UriComponentsBuilder.fromHttpUrl(KOREAN_DICT_API_BASE_URL)
.queryParam("key", API_KEY)
.queryParam("req_type", "json")
.queryParam("part", "word")
.queryParam("q", word)
.queryParam("sort", "dict")
.queryParam("start", "1")
.queryParam("num", "10")
.queryParam("advanced", "y")
.queryParam("type4", "all")
.queryParam("cat", "23")
.build()
.toUriString();
}

private String extractDefinitionFromJson(String json) throws JsonProcessingException {
JsonNode rootNode = objectMapper.readTree(json);
if (rootNode.has("Law")) {
Expand All @@ -71,6 +136,41 @@ private String extractDefinitionFromJson(String json) throws JsonProcessingExcep
}
}

private String extractTop3DefinitionsFromJson(String json) throws JsonProcessingException {
JsonNode rootNode = objectMapper.readTree(json);

// channel > item 배열에서 아이템들 추출
JsonNode itemsNode = rootNode.path("channel").path("item");

if (!itemsNode.isArray() || itemsNode.size() == 0) {
throw new RuntimeException("검색 결과가 없습니다.");
}

List<String> definitions = new ArrayList<>();

// 최대 3개의 definition 추출
for (int i = 0; i < Math.min(itemsNode.size(), 3); i++) {
JsonNode item = itemsNode.get(i);
JsonNode senseNode = item.path("sense");

if (senseNode.isArray() && senseNode.size() > 0) {
JsonNode firstSense = senseNode.get(0);
String definition = firstSense.path("definition").asText();

if (definition != null && !definition.trim().isEmpty()) {
definitions.add(definition.trim());
}
}
}

if (definitions.isEmpty()) {
throw new RuntimeException("검색 결과에서 정의를 찾을 수 없습니다.");
}

// 줄바꿈으로 연결하여 하나의 문자열로 만들기
return String.join("\n", definitions);
}

private void saveDefinition(String word, String definition) {
LawWord entity = LawWord.builder()
.word(word)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDate;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PrecedentSummaryListDto {
private Long id;
private String caseName; // 사건명
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;

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

private final PrecedentRepository precedentRepository;
private final EntityManager entityManager;
private final RestTemplate restTemplate = new RestTemplate();
private final WebClient webClient = WebClient.builder().build();
private final ObjectMapper objectMapper = new ObjectMapper();

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

do {
String url = buildSearchUrl(query, page);
String json = restTemplate.getForObject(url, String.class);
String json = webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.block();
JsonNode root = objectMapper.readTree(json);
JsonNode precSearch = root.path("PrecSearch");

Expand Down Expand Up @@ -114,7 +119,11 @@ public List<Precedent> getPrecedentDetails(List<String> precedentIds) {
for (String precedentId : precedentIds) {
try {
String url = buildDetailUrl(precedentId);
String json = restTemplate.getForObject(url, String.class);
String json = webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.block();

Precedent precedent = parseJsonToPrecedent(json);
if (precedent != null) {
Expand Down