Dev/Springboot

HttpUrlConnection을 사용하여 외부URL Data Scrapping 하기

린네의 2024. 3. 14. 16:20

 

restfulAPI를 개발하다 보면 제공할 정보를 위해 만드는 경우도 많지만 반대로 필요한 OpenAPI 에 접근하여 원하는 데이터를 긁어와야 할 때도 많다. 

 

실무에서 꽤 자주 썼는데 따로 정리한적은 없는 것 같아서 내가 실제로 자주 썼던 샘플코드를 하나 가져와봤다.

 

 

개발 환경

IDE :  intelliJ 
FrameWork : springboot 3.2.2   /  JPA(Hibernate5) 
Launguage : java 17
BuildTool : Gradle
TestTool : Junit5

 

 

구현 내용은 다음과 같다

 

 

  • ResponseScrap
@Getter
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ResponseScrap {

    @NotNull
    private String status;
    @NotBlank
    private Object data;
    @NotNull
    private Object errors;
}

 

data scrap 시 대상API 에서 리턴되는 규격에 맞는 dto를 생성했다.  status, errors의 응답내용에 따라 원하는 대로 분기하여 서비스 로직을 구현할 수 있다. 응답데이터를 담는 용도이므로 @Setter 은 생략했다.

 

 

 

  • connectTarget 함수 

데이터를 긁어올 대상 URL 과  전송할 requestData를 파라미터로 지정한다. requestData는 필수사항은 아니고,  요청하는 url에 필요할 경우 상황에 맞게 사용할 수 있다.

 

private ResponseScrap connectTarget(String url, String requestScrap) throws IOException {

    String responseData = "";

    URL connectionTarget = new URL(url);
    ObjectMapper mapper = new ObjectMapper();
    StringBuilder sb = new StringBuilder();
    BufferedReader br;
    HttpURLConnection con = (HttpURLConnection) connectionTarget.openConnection();
    con.setRequestMethod("POST");
    //서버에 연결되는 Timeout 시간 설정 - 최소 1초 ~ 20초 명시 됨에 따라 20000 설정
    con.setConnectTimeout(20000);
    //InputStream 읽어 오는 Timeout 시간 설정 - 최소 1초 ~ 20초 명시 됨에 따라 20000 설정
    con.setReadTimeout(20000);
    // post data 가 있을 경우 true
    con.setDoOutput(true);
    //request setting
    con.setRequestProperty("Content-Type", "application/json");
    con.setRequestProperty("Accept", "application/json");

    //데이터 삽입

    log.info("requestScrap {} => " + requestScrap);

    OutputStream os = con.getOutputStream();
    byte[] input = requestScrap.getBytes("utf-8");
    os.write(input, 0, input.length);

    if(con.getResponseCode() == HttpStatus.OK.value()) {
        log.info(" connect success ! url = {} " + url);

        br = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }

        responseData = sb.toString();
        log.info("responseData {} => " + responseData);

        br.close();
    }

    ResponseScrap responseScrap = new ObjectMapper().readValue(responseData, ResponseScrap.class);

    if(responseScrap.getStatus().equals(ScrapStatus.FAIL.data())) {
            throw new ApiException(ApiErrorCode.CANNOT_CONNECT_SCRAP_SITE);
    }

    return responseScrap;
}

 

 

 

  • 테스트 코드 예제 
    @Test
    @DisplayName("스크래핑_외부연결")
    public void connectTargetUrl() throws IOException {

        // given
        String url = [원하는데이터];
        
        String token = "12354";

        JSONObject requestData = new JSONObject();
        requestData.appendField("token", token);
    
        URL connectionTarget = new URL(url);
        ObjectMapper mapper = new ObjectMapper();
        StringBuilder sb = new StringBuilder();
        BufferedReader br;
        HttpURLConnection con = (HttpURLConnection) connectionTarget.openConnection();
        con.setRequestMethod("POST");
        //서버에 연결되는 Timeout 시간 설정 - 최소 1초 ~ 20초 명시 됨에 따라 20000 설정
        con.setConnectTimeout(20000);
        //InputStream 읽어 오는 Timeout 시간 설정 - 최소 1초 ~ 20초 명시 됨에 따라 20000 설정
        con.setReadTimeout(20000);
        // post data 가 있을 경우 true
        con.setDoOutput(true);
        //request setting
        con.setRequestProperty("Content-Type", "application/json");
        con.setRequestProperty("Accept", "application/json");

        //데이터 삽입
        try(OutputStream os = con.getOutputStream()) {
            byte[] input = requestData.toJSONString().getBytes("utf-8");
            os.write(input, 0, input.length);
        }


        // when
        br = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }

        System.out.println(sb.toString());

        br.close();

        // then
        assertEquals(con.getResponseCode() , HttpStatus.OK.value(), "연결 실패");

    }

 

특정 토큰 인증값을 요구하는 API 에 대한 연결여부를 확인하는 테스트를 구성했다.  

 

 

위 코드를 기반으로 데이터를 스크랩하는 것뿐만 아니라  원하는 URL로 결괏값을 리턴하는 API를 생성할 수도 있다.