반응형

아래와 같이 @RestController  에 메소드가 있다고 생각해보자.

@PostMapping
public ResponseEntity<AccountResDto> getResDto(@RequestBody AccountReqDto dto){
    return ResponseEntity.ok(AccountResDto.builder().email("test@test.com")
    .loginId("test")
    .username("tester")
    .build()
  );
}

Post 요청에 대해서 AccountReqDto 라는 Object 형태의 파라메터를 받아서 Response 를 리턴하는 메소드 이다.

AccountReqDto 는 다음과 같이 되어있다.

@Getter
public class AccountReqDto {

    private String loginId;

    @Builder
    public AccountReqDto(String loginId) {
        this.loginId = loginId;
    }
}

그리고 나서 난 아무 생각없이 테스트 케이스를 돌렸다.

@Test
public void getResDtoTest() throws Exception {
  MvcResult mvcResult = this.mockMvc.perform(
      post("/api/v1/accounts")
      	.contentType(MediaType.APPLICATION_JSON_UTF8)
      	.content(objectMapper.writeValueAsString(reqDto)))
      .andExpect(status().isOk())
      .andDo(print())
      .andReturn();

  String contentAsString = mvcResult.getResponse().getContentAsString();
  AccountResDto accountResDto = objectMapper.readValue(contentAsString, AccountResDto.class);
  Assert.assertEquals("test", accountResDto.getLoginId());
}

당연히 성공할줄 알았던 테스트 케이스는 실패를 한다. 실패의 원인은 다음과 같다. 

 

Status expected:<200> but was:<400>
Expected :200
Actual   :400

 

아니 왜??? 왜 400 error 가 났을까??

결론부터 말하자면 AccountReqDto 클래스에 기본 생성자가 없기 때문에 위와 같은 에러가 발생 했다. 그렇다면 왜 기본 생성자가 있어야 하나??? 그걸 알기 위해서 찾아보다 보니 Serialization 이 연관이 있다는 것을 찾게 되었다. 

 

Serialization

Serialization 은 Object 의 상태를 byte stream 형태로 변환하는 것을 말한다. Deserialization 은 반대의 경우를 말한다. java  에서 Serialization 을 이용하면 객체를 네트워크를 통해 전송이 가능해진다. 

 

그럼 왜 Serialization에는 No-Argument Constructor 가 필요한걸까? 

To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime.
During deserialization, the fields of non-serializable classes will be initialized using the public or protected no-arg constructor of the class. A no-arg constructor must be accessible to the subclass that is serializable. The fields of serializable subclasses will be restored from the stream.

(출처 : https://docs.oracle.com/javase/10/docs/api/java/io/Serializable.html)

 

요약을 해보면 클래스의 상태를 초기화 할 때 No-Argument Constructor 를 이용한다는 의미이다. 만약 선언되어있지 않으면 에러가 발생한다고 되어있다. 

 

결과적으로 Serialization 을 위해서는 No-Argument Constructor  가 필요하고 그것을 사용하고 있는 Controller 의 RequestBody 에 들어가는 Object 에도 No-Argument Constructor 가 존재 해야 한다는 의미이다.

 

그래서 위 소스에서 테스트가 성공하기 위해서는 No-Argument Constructor 를 만들어 주거나 Lombok 을 사용한다면 @NoArgsConstructor 를 넣어주면 된다.

 

728x90
반응형
반응형

Spring Boot에서 RabbitMQ 를 연결을 해보자.

원래는 AWS에 있는 Ubuntu 서버에 설치를 한후 연결을 하려고 했다. 그런데 설치까지는 문제가 없이 잘 진행 됐는데 이상하게 SpringBoot에서 연결 하려고 하면 Connection Refuse가 계속 나와 몇일을 보냈다. port를 안연것도 아니고, management 콘솔은 15672포트로 잘만 접속 되는데 5672포트로 연결 하려고만 하면 안됐다. 오늘까지 하다가 안되면 GG 치려고 했다. 실제로 GG 치고 로컬에 깔고 진행을 했다.

그래서 정상 작동하는거 보고 혹시나 해서 다시 AWS에 있는 Rabbit MQ에 접속을 시도해 봤는데 됐다... 왜 되는거지???? --_--;;


대체 이것때문에 쌩쑈를 몇일을 했는데. 어쨌든 되니깐 좋다. 하아..


Rabbitmq 설치 방법은 홈페이지를 찾아보거나 MAC일 경우 내가 작성한 포스트를 보면 된다.


2017/10/27 - [Development/DataBase] - [RabbitMQ] MacOS에 RabbitMQ 설치


application.properties 파일에 접속 정보를 추가해준다.

1
2
3
4
5
myqueue=spring-study
spring.rabbitmq.host=localhost
spring.rabbitmq.username=rabbitmq
spring.rabbitmq.password=rabbitmq1!
spring.rabbitmq.port=5672

cs


Consumer.java

1
2
3
4
5
6
7
8
9
@Component
public class Consumer {
    private static final Logger logger = LoggerFactory.getLogger(Consumer.class);
 
    @RabbitListener(queues = "${myqueue}")
    public void handler(String message){
        logger.info("consumer....> " + message);
    }
}


cs


Consumer는 RabbitListener에 정의 되어있는 Queue를 감시하다가 메시지가 들어오면 handler로 전해준다.


Producer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
public class Producer {
    private static final Logger logger = LoggerFactory.getLogger(Producer.class);
 
    @Autowired
    private RabbitTemplate rabbitTemplate;
 
    public void sendTo(String routingKey, String message){
        logger.info("send message....");
 
        this.rabbitTemplate.convertAndSend(routingKey, message);
    }
 
}

cs


Producer에서는 routingKey 와 message 를 받는다. routingKey 가  Queue 이름이 된다. 메세지는 Exchange로 전송되며 Exchange는 Queue로 실어나른다.



StudyApplication.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Value("${myqueue}")
String queue;
 
@Bean
Queue queue(){
    return new Queue(queue, false);
}
 
@Autowired
Producer producer;
 
@Bean
CommandLineRunner sender(Producer producer){
       return args -> {
           producer.sendTo(queue, "Hello !!!");
    };
}
 
@Scheduled(fixedDelay = 500L)
public void sendScheduleMessage(){
    producer.sendTo(queue, "Message Delevery : " + new Date());
}
cs


RabbitMQ Management 에 들어가서 Connection, Channel, Queue를 확인 하면 아래처럼 나온다.





이거 하나 테스트 해보려고 대체 몇일을 날린줄 모르겠다. 여기에서는 간단히 설명했지만 RabbitMQ 도 Queue와 Exchange에 대한 내용들에 대한 정의를 알아야할 부분이 많다. 시간내서 한번 개념을 확실히 잡고 가야겠다.


소스 : https://github.com/blusky10/study_spring


(예제 내용은 "실전스프링워크북" 에 있는 내용을 실습한 내용입니다.) 


728x90
반응형

+ Recent posts