1. build.gradle
// webflux
implementation 'org.springframework.boot:spring-boot-starter-webflux'
  1. WebClient 사용할 class
import lombok.extern.slf4j.Slf4j;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import reactor.core.publisher.Mono;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;

@Slf4j
@Component
public class CitationItemProcessor  implements ItemProcessor<Paper, Paper> {

    private final WebClient webClient;

    @Value("${scopus.api-key}")
    private String openAiToken;

    @Autowired
    public CitationItemProcessor(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.build();
    }

    @Override
    public Paper process(Paper paper) {
        try {
            log.info(" ************ Processing paper with DOI: {}  ************ ", paper.getDoi());
            String doi = paper.getDoi();
            String url = "<https://api.elsevier.com/content/abstract/doi/>" + doi;

            Mono<String> responseMono = webClient.get()
                    .uri(url)
                    .header("X-ELS-APIKey", openAiToken)
                    .retrieve()
                    .bodyToMono(String.class);

            String response = responseMono.block();

            if (response != null) {
                if (response.contains("RESOURCE_NOT_FOUND")) {
                    // scopus에 등록된 논문이 아닐 경우,
                    return paper;
                } else if (response.contains("<citedby-count>")) {
                    // 정상 응답 받은 경우,
                    int total = parseCitationFromResponse(response);
                    log.info(" ************ Response received citedby-count: {} ************ ", total);

                    if (total > 0) {
                        paper.updateCitation(total);
                    }
                } else {
                    // 응답이 예상치 못한 형태일 경우,
                    log.error(" ************ Unexpected response: {} ************ ", response);
                }
            }
        } catch (Exception e) {
            // try 블록 내에서 예외가 발생한 경우,
            log.error(" ************ An error occurred while processing the paper: ", e);
        }

        return paper;
    }

    private int parseCitationFromResponse(String response) throws Exception {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(new InputSource(new StringReader(response)));

            NodeList nodeList = doc.getElementsByTagName("citedby-count");
            if (nodeList.getLength() > 0) {
                String citedByCount = nodeList.item(0).getTextContent();
                return Integer.parseInt(citedByCount);
            } else {
                log.warn(" ************ No 'citedby-count' tag found in the XML response. ************ ");
                return 0;
            }
        } catch (Exception e) {
            log.error(" ************ Error while parsing XML response: ", e);
            return 0;
        }
    }
}