현대자동차 소프티어 부트캠프의 프로젝트 요구사항에 욕설 및 부정적인 문장을 필터링하라는 정책이 있어, 네이버 클라우드 플랫폼에서 제공하는 감정분석 API를 활용하여 간단하게 이를 구현해보았다.
먼저 네이버 클라우드 플랫폼의 client-id와 client-secret를 발급받고 이를 application-yml과 같은 환경변수에 등록해둔다.
naver:
client-id: {발급받은 client-id}
client-secret: {발급받은 clinet-secret}
url: https://naveropenapi.apigw.ntruss.com/sentiment-analysis/v1/analyze
환경변수가 많으면, @Value가 아닌 @Configuration을 통해 변수를 등록할 수 있다.
@Data
@Configuration
@ConfigurationProperties(prefix = "naver")
public class NaverApiConfig {
private String clientId;
private String clientSecret;
private String url;
}
@RequiredArgsConstructor
@Service
public class ApiService {
private final NaverApiConfig naverApiConfig;
// 비즈니스 로직..
}
참고로 API 요청의 응답은 아래와 같은 형태로 나타난다.
sentiment는 감정의 결과 (positive/negative/neutral)이며, confidence는 구체적인 판정 확률이다.
{
"document": {
"sentiment": "positive",
"confidence": {
"negative": 0.037957642,
"positive": 99.957375,
"neutral": 0.004668334
}
},
"sentences": [
{
"content": "현대자동차 만세!",
"offset": 0,
"length": 9,
"sentiment": "positive",
"confidence": {
"negative": 0.0022774586,
"positive": 0.9974425,
"neutral": 2.8010004E-4
},
"highlights": [
{
"offset": 0,
"length": 8
}
]
}
]
}
감정분석 API를 활용하는 현재 프로젝트에서는 "부정" 평가의 경우 "부정" 비율이 99.5% 이상이라면 욕설 및 비속어가 포함된 상태로 간주하여 예외를 발생시켜야 한다.
이를 반영하여 ApiService에 코드를 작성하였고, 실행 결과 문제가 없음을 확인하였다.
@RequiredArgsConstructor
@Service
public class ApiService {
private final NaverApiConfig naverApiConfig;
public boolean analyzeComment(String content) {
// 요청 Header 및 Body 설정
HttpHeaders headers = new HttpHeaders();
headers.set(ConstantUtil.CLIENT_ID, naverApiConfig.getClientId());
headers.set(ConstantUtil.CLIENT_SECRET, naverApiConfig.getClientSecret());
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> requestEntity = new HttpEntity<>(content, headers);
// RestTemplate을 이용하여 POST로 요청 후 응답 받기
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> responseEntity = restTemplate.postForEntity(naverApiConfig.getUrl(), requestEntity, String.class);
String responseBody = responseEntity.getBody();
boolean isPositive = true;
// JSON 형식의 API 응답을 분석하여 긍정/부정 여부 확인
ObjectMapper objectMapper = new ObjectMapper();
try {
JsonNode rootNode = objectMapper.readTree(responseBody);
String sentiment = rootNode.path("document").path("sentiment").asText();
if (sentiment.equals("negative")) {
isPositive = false;
double documentNegativeConfidence = rootNode.path("document").path("confidence").path("negative").asDouble();
if (documentNegativeConfidence >= ConstantUtil.LIMIT_NEGATIVE_CONFIDENCE) { // 부정이며 확률이 99.5% 이상일 경우 재작성 요청
throw new CommentException(ErrorCode.INVALID_COMMENT);
}
}
} catch (JsonProcessingException e) {
throw new CommentException(ErrorCode.INVALID_JSON);
}
return isPositive;
}
}
크게 요청 Header 및 Body 설정, POST로 요청 후 응답 수신, 응답 분석으로 단계를 나눌 수 있다.
Naver Cloud API 명세서에서 요구하는 요청 헤더 및 content를 설정한 뒤 RestTemplate를 활용해 전송하고 JSON 분석하고 예외처리해주었다.
Springboot에서 외부로 API를 요청할 때 대표적으로 이 두 가지를 사용한다.
실제로 Webflux의 경우 1000명 이상의 동시 사용자가 존재할 때 높은 효율을 보여준다.
다만 현재 요구사항 상 1000명이 동시에 Comment를 작성하는 케이스가 발생할 확률은 매우 낮고, 요청 대상이 안정적인 네이버 클라우드의 API이다.
이를 감안하여 나는 전통적인 동기식인 RestTemplate를 활용하여 신속하게 요구사항을 구현하고자 했다.
물론 feign client과 같은 다른 방법도 존재한다.