WEB

CodexException: 500 Server Error / Generic.ver

PSAwesome 2019. 8. 25. 21:27
반응형

서론: 이전 포스팅에서 사용자 정의한 타입의 객체를 리스트로 받는 예제를 다루었고, 그때 Default Construct가 없어 직렬화/역직렬화를 할 수 없었던 내용의 두 번째 방법에 대한 포스팅이다.

 

(이전 포스팅)

불만으로 localhost:8080/blocking 호출 시 List<User>의 반환만 받아서 가지고 올 수 있다는 점이었다.

생각 1. (글쓴이는 User, workpart 등의 객체들을 받아올 수 있는 다이나믹한 메서드로 구현하고 싶었다.)

 

본론 : 리팩토링

- getBlocking()을 GenericType으로 변경하기

	@GetMapping(value = "/blocking")
	public <T> List<T> getBlocking(){
		{...}
	}

1. 반환타입 앞에 <T> 를 선언하고, List<T>로 변경한다.

@GetMapping(value = "/blocking")
	{...}
        
        ResponseEntity<List<T>> response = restTemplate.exchange(uri, HttpMethod.GET, null, new ParameterizedTypeReference<List<T>>() {});
        List<T> result = response.getBody();
        
        {...}

2. Generic Type 정보 수정 (위치 목록)

  • List
  • new ParameterizedTypeReference 구현부의 List
  • response.getBody()의 반환 타입

참 간단한 코드 수정으로 blokcing 호출은 이제 restTemplate이 반환해서 받아오는 객체형 리스트를 가져와 로그를 찍을 수 있게 되었다.

완성된 getBlocking 코드

...더보기
    @GetMapping(value = "/blocking")
    public <T> List<T> getBlocking() throws ClassNotFoundException {
        boolean type = getType();
        log.info("Starting BLOCKING, type = " + type);
        final String uri = getSlowServiceUri(type);
        var restTemplate = new RestTemplate();
        ResponseEntity<List<T>> response = restTemplate.exchange(uri, HttpMethod.GET, null, new ParameterizedTypeReference<List<T>>() {});
        List<T> result = response.getBody();
        result.forEach(o -> log.info(o.toString()));
        log.info("Exiting BLOCKING");
        return result;
    }

글쓴이가 원했던 다른 객체도 반환할 수 있게 되었다는 것을 확인할 수 있는 몇 가지의 추가/수정 내용이 필요하다.

  1. /slow-service-workparts 에서 workparts를 반환하는 request
  2. user 또는 workparts를 반환하게 할 장치
    (호출 했을 때 parameter를 전달하지 않아도 네가 가지고 있는 어떤 것을 주길 바람.

먼저 Workaprt 객체를 정의한다.

Workpart 부서 정의

...더보기
package com.example.webclientdemo.demo.repository;

public class Workpart {
    private String title;
    private String location;

//    public Workpart() {
//    }

    public Workpart(String title, String location) {
        this.title = title;
        this.location = location;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    @Override
    public String toString() {
        return "Workpart{" +
                "title='" + title + '\'' +
                ", location='" + location + '\'' +
                '}';
    }
}

 

Workparts Request 시 객체를 반환해 줄 getAllWorkpart 정의

...더보기
    @GetMapping("/slow-service-workparts")
    public List<Workpart> getAllWorkPart() throws InterruptedException {
        Thread.sleep(3000L);
        return Arrays.asList(
                new Workpart("ps", "awesome")
                , new Workpart("my", "friend")
                , new Workpart("Thread", "Local")
                , new Workpart("MSA", "Cloud Native")
        );
    }

getSlowServiceUri()의 수정

    private String getSlowServiceUri(boolean getA) {
        return getA ? "http://localhost:8080/slow-service-users" : "http://localhost:8080/slow-service-workparts";
    }

알아서 가져올 수 있도록 계산하는 메서드 추가

    private boolean getType(){
        double v = ((int)(Math.random() * 10)) % 2;
        return v == 1 ? true : false;
    }

 

준비가 다 되었다. 우리의 결과물이 정상적으로 되는지 테스트를 해보자.

글쓴이는 .http 파일을 이용해 Run을 실행하여 내용을 확인해보았다.

(랜덤으로 객체를 가져오는 것의 단점은 원하는 것이 나올 때까지 계속 호출해야 한다는 점..)

호출된 내용의 사진들

.http 파일의 내용
User 객체 테스트
Workpart 객체 테스트

 

추가적인 내용) 이전 포스팅의 내용에서 기본 생성자가 없었다는 에러를 기억하는가?

우리의 User 객체는 현재 기본 생성자가 정의되어 있지 않다.

 

package com.example.webclientdemo.demo.repository;

public class User {
    private String name;
    private String email;

//    public User() {
//    }
    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }
// getter/setter
}

workpart도 마찬가지.

이 예제의 표면에서 확인할 수 있는 내용은

  1. ParameterizedType에서 List의 Generic Type이 사용자가 정의한 객체일 경우 ParameterizedTypeReference 구현부에서 List<>는 해당 객체의 Default Construct를 통해 객체를 생성하고 getter/setter를 활용하여 직렬화/역직렬화를 수행한다.
  2. 반면 Generic Type으로 표현했을 경우는 기본 생성자를 필요로 하지 않는다.
    (이것은 이후 포스팅에서 다룰 예정이다.)

결론 : Type Parameter T로 실제 객체를 받을 때 타입을 받을 수 있도록 구현한다면 기본 생성자는 사용되지 않는다. ( 예 - List<T>)

 

#수정 2019. 08. 27

rowType일 경우의 테스트와 <T>의 반환 타입을 추가 확인하여 내용을 정정하게 되었습니다.

...더보기

현재 우리의 코드에서 

ResponseEntity<List<T>> response = restTemplate.exchange(uri, HttpMethod.GET, null, new ParameterizedTypeReference<List<T>>() {});

<T>의 반환 타입은 LinkedHashMap 타입입니다. responseBody를 생성할 때 content-type이 application/json은 Map 형태로 converting이 가능하기 때문에 이 interface를 구현해 사용하는 것으로 확인된다.

이후 response.getBody() 호출 후 o.toString()으로 객체 내부에 있던 값을 출력한 것으로 위에 작성한 내용의 기본 생성자 생성이 아닌 LinkedHashMap을 활용하기 때문에 User 객체에 접근할 필요가 없었던 것이다.

 

이후 내용은 이 포스팅을 작성하게 된 이유라고 할 수 있다. 

이 예제에서의 의문점으로 두 가지가 남게 되었다.

  1. ParameterizedTypeReference의 작동 원리
  2. Generic Type의 작동원리

해당 내용은 다른 포스팅에서 공유할 예정입니다.

 

예제 소스코드

반응형