본문 바로가기
Spring Boot

Spring Boot - 서버 간 통신 예제

by sinabeuro 2021. 9. 26.
728x90

예제 RestTemplate - getForEntity

서버 간의 통신을 위한 client 서버를 우선 만듭니다.

client 서버를 통해서 다른 서버와 http 통신을 하여 response를 받아옵니다.

package com.example.client.controller;

import com.example.client.dto.Req;
import com.example.client.dto.UserResponse;
import com.example.client.service.RestTemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/client")
public class ApiController {

    @Autowired
    private RestTemplateService restTemplateService;

    public ApiController(RestTemplateService restTemplateService) {
        this.restTemplateService = restTemplateService;
    }

    @GetMapping("/hello")
    public UserResponse getHello() {
        return restTemplateService.hello();
    }
}

 

package com.example.client.service;

import com.example.client.dto.Req;
import com.example.client.dto.UserRequest;
import com.example.client.dto.UserResponse;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;

@Service
public class RestTemplateService {

    // http://localhost/api/server/hello
    // response
    public UserResponse hello() {
        URI uri = UriComponentsBuilder
                .fromUriString("http://localhost:9090")
                .path("/api/server/hello")
                .queryParam("name", "steve")
                .queryParam("age", 10)
                .encode()
                .build()
                .toUri();

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

        RestTemplate restTemplate = new RestTemplate();
        //String result = restTemplate.getForObject(uri, String.class);
        ResponseEntity<UserResponse> result = restTemplate.getForEntity(uri, UserResponse.class);

        System.out.println(result.getStatusCode());
        System.out.println(result.getBody());

        return result.getBody();
    }
}

UriComponentsBuilder 라이브러리를 통해서 통신을 위한 uri를 만들어 줍니다.

fromUriString에는 도메인 주소를
path에는 url 경로를 

queryParam에는 get으로 보낼 파라미터를 기술하면 됩니다.

파라미터가 여러개인 경우는 queryParam를 여러번 나열해서 기술하면 됩니다.

 

package com.example.server.controller;

import com.example.server.dto.Req;
import com.example.server.dto.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

@RestController
@RequestMapping("/api/server")
@Slf4j
public class ServerApiController {

    @GetMapping("/hello")
    public User hello(@RequestParam String name, @RequestParam int age) {
        User user = new User();
        user.setName(name);
        user.setAge(age);
        return user;
    }
    
}

다른 프로젝트에서 서버간 통신을 위한 컨트롤러를 만듭니다.

client 서버에서 보낸 uri를 받을 수 있도록 get형식으로 만들고 파라미터들을 받아줍니다.

server 서버에서 return으로 response를 넘겨주면 client 서버에서 response를 받습니다.

 

clicnt 서버에서 RestTemplate 라이브러리로 인스턴스로 만든 부분에서 서버 간의 통신이 이루어집니다.

ResponseEntity<UserResponse> result = restTemplate.getForEntity(uri, UserResponse.class);

이 부분에서 서버간 request와 response가 모두 이루어지며, ResponseEntity<> 타입으로 response를 받게 됩니다.

 

RestTemplate  라이브러리에 대한 설명은 이전 글을 참고하시면 좋을 것 같습니다.

https://getthismoment.tistory.com/91

 

 


 

예제 RestTemplate - exchange

 

package com.example.client.controller;

import com.example.client.dto.Req;
import com.example.client.dto.UserResponse;
import com.example.client.service.RestTemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/client")
public class ApiController {

    @Autowired
    private RestTemplateService restTemplateService;

    public ApiController(RestTemplateService restTemplateService) {
        this.restTemplateService = restTemplateService;
    }

    @GetMapping("/genericExchange")
    public Req<UserResponse> genericExchange() {
        return restTemplateService.genericExchange();
    }
}

 

package com.example.client.service;

import com.example.client.dto.Req;
import com.example.client.dto.UserRequest;
import com.example.client.dto.UserResponse;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;

@Service
public class RestTemplateService {

    public Req<UserResponse> genericExchange() {

        URI uri = UriComponentsBuilder
                .fromUriString("http://localhost:9090")
                .path("/api/server/user/{userId}/name/{userName}")
                .encode()
                .build()
                .expand("100", "steve")     // PathVariable 에 넣어줄 값을 이어서 넣어준다.
                .toUri();
        System.out.println(uri);

        // http body -> object -> object mapper -> json -> rest template -> http body json

        UserRequest userRequest = new UserRequest();
        userRequest.setName("steve");
        userRequest.setAge(10);

        Req<UserRequest> req = new Req<>();
        req.setHeader(
                new Req.Header()
        );

        req.setHttpResponseBody(
                userRequest
        );

        RequestEntity<Req<UserRequest>> requestEntity = RequestEntity
                .post(uri)
                .contentType(MediaType.APPLICATION_JSON)
                .header("x-authorization", "abcd")
                .header("custom-header", "fffff")
                .body(req);

        RestTemplate restTemplate = new RestTemplate();

        // 이 부분에서 다른 서버에 요청을 주고 받는다.
        ResponseEntity<Req<UserResponse>> response
                = restTemplate.exchange(requestEntity, new ParameterizedTypeReference<Req<UserResponse>>(){});    // 제네릭은 class를 붙일 수 없기 때문에 ParameterizedTypeReference 객체를 이용한다.

        return response.getBody();
    }
}

RestTemplate 을 사용하되 getForEntity 예제와 달리 header에 값을 넣어서 보냅니다.

header에 값을 값을 넣기 위해서는 단순히 UriComponentsBuilder를 사용하여 uri를 만드는 것뿐만 아니라,

RequestEntity 를 사용하여 request 데이터를 만들어야합니다.

header를 담은 RequestEntity 를 ResponseEntity에 넘겨주어 서버간 통신을 하면, 통신한 서버에서 정상적으로 header 데이터를 읽을 수 있습니다.

 

 

요청 받은 서버의 코드는 다음과 같습니다.

package com.example.server.controller;

import com.example.server.dto.Req;
import com.example.server.dto.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

@RestController
@RequestMapping("/api/server")
@Slf4j
public class ServerApiController {

    @PostMapping("/user/{userId}/name/{userName}")
    public Req<User> post(
            //HttpEntity<String> entity,    client 가 무슨 파라미터를 보냈는지 알기위해 씀.
            @RequestBody Req<User> user,
            @PathVariable int userId,
            @PathVariable String userName,
            @RequestHeader("x-authorization") String authorization,
            @RequestHeader("custom-header") String customHeader)
    {
        log.info("userId : {}, userName : {}", userId, userName);
        log.info("authorization : {}, customHeader : {}", authorization, customHeader);
        log.info("client req : {} ", user);

        Req<User> response = new Req<>();
        Req.Header header = new Req.Header();
        header.setResponseCode(authorization);
        response.setHeader(header);
        response.setHttpResponseBody(user.getHttpResponseBody());
        
        return response;
    }
}

@RequestHeader 어노테이션으로 헤더 데이터를 받습니다.

response에 받은 헤더와 데이터를 그대로 전달합니다.

(별 다른 처리없이 서버 통신 확인용으로 데이터를 그대로 전달합니다.)

 

Req DTO 스키마는 다음과 같습니다.

package com.example.server.dto;

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

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Req<T> {

    private Header header;
    private T httpResponseBody;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Header {
        private String responseCode = "555";
    }

}

제네릭으로 파라미터를 받기 때문에 httpResponseBody 속성은 제네릭으로 받은 타입과 같은 속성을 같게 됩니다.

 

package com.example.server.dto;

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

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private String name;
    private int age;
}

 

 

 

response : <200 OK,
{
    data={
        status=400, message=required param strCd is null
    }, 
	totalCount=0, 
    responseCode=200, 
    responseMessage=success
},
{
    Set-Cookie=[JSESSIONID=2D101087844E6F355A35F6248A428EA9; Path=/; HttpOnly], 
    Vary=[Origin, Access-Control-Request-Method, Access-Control-Request-Headers], 
    X-Content-Type-Options=[nosniff], 
    X-XSS-Protection=[1; mode=block], 
    Cache-Control=[no-cache, 
    no-store, 
    max-age=0, must-revalidate], 
    Pragma=[no-cache], 
    Expires=[0], 
    X-Frame-Options=[DENY], 
    Content-Type=[application/json;charset=UTF-8], 
    Transfer-Encoding=[chunked], 
    Date=[Thu, 27 Jul 2023 05:17:14 GMT], 
    Keep-Alive=[timeout=60], 
    Connection=[keep-alive]
}
>

 

 

 

728x90

'Spring Boot' 카테고리의 다른 글

Spring Boot - 정적 파일 경로 지정  (0) 2021.11.05
Spring Boot - Swagger  (0) 2021.10.01
Spring Boot - 서버 간 통신 RestTemplate 정의  (0) 2021.09.25
Spring Boot - Interceptor  (0) 2021.09.23
Spring Boot - Exception 구현 예제  (0) 2021.09.13

댓글