이번 프로젝트에서 로그인 쪽 보안 강화 한다고

ip검증하고 otp 추가하는 요구사항이 들어왔는데

ip검증할때 api 호출하는 서비스를 만들어야 했다!

 

참고하라는 소스는 자바 HttpURLConnection을 사용했고

소스를 보니깐 불필요한 코드들이 많은 것 같아서

스프링 라이브러리를 사용하려고 GPT한테 물어보니깐

RestTemplate(스프링3.0부터), webClient(스프링5.0부터)이 있었다.

 

지금 프로젝트는 5.0보다 낮은 버전에 스프링을 사용하고 있어서

RestTemplate을 사용해서 구현하고 있다.

그 김에 정리 할 겸 싸- 악 정리해본다.

 

HttpURLConnection

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class ApiCaller {

    public static void main(String[] args) {
        String urlString = "https://api.example.com/data"; // 호출할 API URL
        String method = "GET"; // 요청 방법 (GET, POST 등)
        String apiKey = "your_api_key"; // 필요한 경우 API 키

        try {
            // URL 객체 생성
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            // 요청 메서드 설정
            conn.setRequestMethod(method);
            conn.setRequestProperty("Content-Type", "application/json");

            // 필요한 경우 API 키 헤더에 추가
            conn.setRequestProperty("Authorization", "Bearer " + apiKey);

            // POST 요청의 경우, 데이터를 전송하기 위해 출력 스트림 열기
            if ("POST".equalsIgnoreCase(method)) {
                conn.setDoOutput(true);
                String jsonInputString = "{\"key\": \"value\"}"; // 전송할 JSON 데이터
                try(OutputStream os = conn.getOutputStream()) {
                    byte[] input = jsonInputString.getBytes("utf-8");
                    os.write(input, 0, input.length);
                }
            }

            // 응답 코드 확인
            int responseCode = conn.getResponseCode();
            System.out.println("Response Code: " + responseCode);

            // 응답 읽기
            try(BufferedReader br = new BufferedReader(
                new InputStreamReader(conn.getInputStream(), "utf-8"))) {
                StringBuilder response = new StringBuilder();
                String responseLine;
                while ((responseLine = br.readLine()) != null) {
                    response.append(responseLine.trim());
                }
                System.out.println("Response: " + response.toString());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

  • URL url = new URL(urlString);: 호출할 API의 URL을 설정합니다.
  • conn.setRequestMethod(method);: HTTP 요청 방법을 설정합니다.
  • conn.setRequestProperty("Content-Type", "application/json");: 요청 헤더를 설정합니다.
  • conn.setRequestProperty("Authorization", "Bearer " + apiKey);: API 키를 헤더에 추가합니다 (필요한 경우).
  • conn.setDoOutput(true);: POST 요청의 경우, 출력 스트림을 열어 데이터를 전송할 준비를 합니다.
  • try(OutputStream os = conn.getOutputStream()) { ... }: 요청 본문 데이터를 전송합니다 (POST 요청의 경우).
  • int responseCode = conn.getResponseCode();: 응답 코드를 확인합니다.
  • try(BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"))) { ... }: 응답 데이터를 읽습니다.

실제 연결은 어느 시점에 되는걸까?

  • 연결 (명시적):connect() 메서드를 호출하면 이 시점에서 서버와 연결이 설정됩니다. 그러나 이것은 선택사항이며, 보통 생략됩니다.
  •  conn.connect();
  • 연결 (암시적): 만약 connect()를 명시적으로 호출하지 않더라도, 다음 메서드들이 호출되는 시점에 자동으로 연결이 설정됩니다:
    • conn.getInputStream()
    • conn.getOutputStream()
    • conn.getResponseCode()

 

 

  • 연결 (명시적):connect() 메서드를 호출하면 이 시점에서 서버와 연결이 설정됩니다. 그러나 이것은 선택사항이며, 보통 생략됩니다.
  • java
    코드 복사
    conn.connect();
  • 연결 (암시적): 만약 connect()를 명시적으로 호출하지 않더라도, 다음 메서드들이 호출되는 시점에 자동으로 연결이 설정됩니다:
    • conn.getInputStream()
    • conn.getOutputStream()
    • conn.getResponseCode()

 

 

 

RestTemplate

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

public class ApiCaller {

    public static void main(String[] args) {
        String url = "https://api.example.com/data"; // 호출할 API URL
        String method = "GET"; // 요청 방법 (GET, POST 등)
        String apiKey = "your_api_key"; // 필요한 경우 API 키

        // RestTemplate 객체 생성
        RestTemplate restTemplate = new RestTemplate();

        // 헤더 설정
        HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type", "application/json");
        headers.set("Authorization", "Bearer " + apiKey);

        // 요청 엔티티 생성 (헤더와 바디를 포함할 수 있음)
        HttpEntity<String> entity = new HttpEntity<>(headers);

        // 요청 실행
        ResponseEntity<String> response = null;
        if ("GET".equalsIgnoreCase(method)) {
            response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
        } else if ("POST".equalsIgnoreCase(method)) {
            String jsonInputString = "{\"key\": \"value\"}"; // 전송할 JSON 데이터
            HttpEntity<String> postEntity = new HttpEntity<>(jsonInputString, headers);
            response = restTemplate.exchange(url, HttpMethod.POST, postEntity, String.class);
        }

        // 응답 출력
        if (response != null) {
            System.out.println("Response Code: " + response.getStatusCodeValue());
            System.out.println("Response Body: " + response.getBody());
        }
    }
}

 

  • RestTemplate restTemplate = new RestTemplate();: RestTemplate 객체를 생성합니다.
  • HttpHeaders headers = new HttpHeaders();: 요청 헤더를 설정합니다.
  • headers.set("Content-Type", "application/json");: 콘텐츠 타입을 JSON으로 설정합니다.
  • headers.set("Authorization", "Bearer " + apiKey);: 필요한 경우 API 키를 헤더에 추가합니다.
  • HttpEntity<String> entity = new HttpEntity<>(headers);: 요청 엔티티를 생성합니다.
  • response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);: GET 요청을 실행합니다.
  • String jsonInputString = "{\"key\": \"value\"}";: POST 요청의 경우 전송할 JSON 데이터를 설정합니다.
  • HttpEntity<String> postEntity = new HttpEntity<>(jsonInputString, headers);: POST 요청을 위한 엔티티를 생성합니다.
  • response = restTemplate.exchange(url, HttpMethod.POST, postEntity, String.class);: POST 요청을 실행합니다.
  • response.getStatusCodeValue(), response.getBody(): 응답 코드를 확인하고 응답 본문을 출력합니다.

RestTemplate을 사용할 때 실제로 서버와의 연결이 이루어지는 시점exchange(), getForObject(), getForEntity(), postForObject(), postForEntity() 등 HTTP 요청을 수행하는 메서드가 호출되는 시점입니다. 이 메서드들이 호출되면 RestTemplate은 내부적으로 HTTP 연결을 설정하고, 요청을 전송하며, 응답을 받습니다.

 

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html

WebClient

import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

public class ApiCaller {

    public static void main(String[] args) {
        String url = "https://api.example.com/data"; // 호출할 API URL
        String apiKey = "your_api_key"; // 필요한 경우 API 키

        // WebClient 객체 생성
        WebClient webClient = WebClient.builder()
                .baseUrl(url)
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey)
                .build();

        // GET 요청
        Mono<String> getResponse = webClient.get()
                .retrieve()
                .bodyToMono(String.class);

        // 응답 처리
        getResponse.subscribe(response -> {
            System.out.println("GET Response: " + response);
        }, error -> {
            System.err.println("Error: " + error.getMessage());
        });

        // POST 요청
        String jsonInputString = "{\"key\": \"value\"}"; // 전송할 JSON 데이터
        Mono<String> postResponse = webClient.post()
                .bodyValue(jsonInputString)
                .retrieve()
                .bodyToMono(String.class);

        // 응답 처리
        postResponse.subscribe(response -> {
            System.out.println("POST Response: " + response);
        }, error -> {
            System.err.println("Error: " + error.getMessage());
        });

        // 비동기 호출이 완료될 때까지 메인 스레드 대기
        try {
            Thread.sleep(5000); // 비동기 호출이 완료될 때까지 대기 (예제 용도)
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

 

HttpURLConnection ,  RestTemplate ,  WebClient 비교

 

HttpURLConnection: 간단한 HTTP 요청을 보내거나, Spring과 같은 프레임워크를 사용하지 않는 경우.

RestTemplate: Spring 애플리케이션에서 동기식 REST 호출이 필요한 경우.

WebClient: Spring 애플리케이션에서 비동기 및 반응형 프로그래밍이 필요한 경우.

 

결론

 

  • 단순한 HTTP 요청 또는 기본 자바 라이브러리를 사용해야 하는 경우에는 HttpURLConnection을 선택할 수 있습니다.
  • 동기식 HTTP 요청이 필요한 Spring 애플리케이션에서는 RestTemplate을 사용할 수 있지만, 이는 이제 WebClient로 대체되고 있습니다.
  • 비동기 및 반응형 프로그래밍이 필요한 경우, 특히 높은 성능과 비동기 처리 요구사항이 있는 경우에는 WebClient가 적합합니다.

 

 

반응형

+ Recent posts