Backend/개발
[Spring Boot] List의 null값 처리하기2 - 일급컬렉션, @RequestBody
지수쓰
2022. 5. 15. 16:04
반응형
컨트롤러에서 List로 된 요청을 받아오는 과정에서, null이나 empty List가 왔을 때 처리하는 방법에 대한 글이다.
이전에 올렸던 [Spring Boot]List의 null 값 처리하기1 - CustomConverter로 @RequestParam 에서 좀 애매한 부분이 있었는데, 다른 해결 방안이 될 수 있을 것 같아서 적어본다.
이전 글은 param, List <String>이고 이번 글은 body, List <DtoObject>라 조금 다를 수도 있지만 일단 기록용 ㅎㅎ;;
요청으로 받을 DTO를 json파일로 받아와 게임을 등록하는 API가 있었는데, 이 json파일을 리스트로 한번에 받아올 수 있게 하려고 한다.
public ResponseEntity<RiotReplayDtos> saveGameRecords(
@RequestBody List<RiotReplayDto> riotReplayDtos) throws IOException {
if (riotReplayDtos.isEmpty()) throw new MayoException(ErrorCode.INVALID_INPUT_VALUE, "no replays");
GameRecordDto gameRecordDto = gameRecordV2Service.registerGameRecord(riotReplayDto, mayoId);
return ResponseEntity.ok(riotReplayDtos);
}
원래는 이렇게 riotReplayDtos.isEmpty() 또는 riotReplayDtos == null 을 검증해줘야 한다.
최근에 컬렉션으로 된 필드 하나만 가지고 포장하는 일급컬렉션을 사용한 리팩터링을 진행해보았어서, 이렇게 컨트롤러에서 받아올 때도 일급 컬렉션을 사용할 수 있을까 싶어 적용해보았다.
public ResponseEntity<RiotReplayDtos> saveGameRecords(
@RequestBody @Valid RiotReplayDtos riotReplayDtos) throws IOException {
GameRecordDto gameRecordDto = gameRecordV2Service.registerGameRecord(riotReplayDto, mayoId);
return ResponseEntity.ok(riotReplayDtos);
}
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class RiotReplayDtos {
@ApiModelProperty(value = "리플레이 json파일을 입력해주세요.")
@NotEmpty(message = "리플레이 json파일을 입력해주세요.")
private List<RiotReplayDto> riotReplayDtos;
public RiotReplayDtos(List<RiotReplayDto> riotReplayDtos) {
this.riotReplayDtos = riotReplayDtos;
}
public boolean isEmpty() {
return riotReplayDtos.isEmpty();
}
}
일급 컬렉션을 final로 생성해보려고 했는데 그건 잘 안돼서 일단은 불변성은 처리하지 못했지만, @NotEmpty 조건을 걸어주고 @Valid로 설정해서 테스트를 해보았다.
성공 테스트
@Test
@DisplayName("게임 등록 - 여러게임 한번에 등록 ")
void saveGameRecorsTest() throws Exception {
ObjectMapper mapper = new ObjectMapper();
RiotReplayDto riotReplayDto = RiotReplayDto.builder()
.gameId(12L)
.gameType(GameTypeCode.REGULAR)
.gameCreationTime(123L)
.gameLength(123L)
.statsJson(null)
.build();
Map<String, Object> map = new HashMap<>();
map.put("riotReplayDtos", Lists.newArrayList(riotReplayDto, riotReplayDto));
MvcResult mvcResult = mvc.perform(post("/api/game/records")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(mapper.writeValueAsString(map)))
.andDo(print())
.andExpect(status().isOk())
.andReturn();
}
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", Content-Type:"application/json;charset=UTF-8"]
Content type = application/json;charset=UTF-8
Body = {"riotReplayDtos":[{"gameId":12,"gameCreationTime":123,"gameType":"REGULAR","gameLength":123,"statsJson":null},{"gameId":12,"gameCreationTime":123,"gameType":"REGULAR","gameLength":123,"statsJson":null}],"empty":false}
Forwarded URL = null
Redirected URL = null
Cookies = []
실패 테스트
@Test
@DisplayName("게임 등록 - null일 경우 여러게임 한번에 등록 실패 ")
void saveGameRecorsNullTest() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = new HashMap<>();
map.put("riotReplayDtos", null);
MvcResult mvcResult = mvc.perform(post("/api/game/records")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(mapper.writeValueAsString(map)))
.andDo(print())
.andExpect(status().isBadRequest())
.andReturn();
}
@Test
@DisplayName("게임 등록 - empty일 경우 여러게임 한번에 등록 실패 ")
void saveGameRecorsEmptyTest() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = new HashMap<>();
map.put("riotReplayDtos", Lists.newArrayList());
MvcResult mvcResult = mvc.perform(post("/api/game/records")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(mapper.writeValueAsString(map)))
.andDo(print())
.andExpect(status().isBadRequest())
.andReturn();
}
MockHttpServletResponse:
Status = 400
Error message = null
Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", Content-Type:"application/json;charset=UTF-8"]
Content type = application/json;charset=UTF-8
Body = {"status":400,"code":"C001","message":"Invalid input value","errors":[{"field":"riotReplayDtos","value":"null","message":"리플레이 json파일을 입력해주세요."}]}
Forwarded URL = null
Redirected URL = null
Cookies = []
일급 컬렉션을 이용해서 유효성 검증을 좀 더 편리하게 할 수 있을 것 같다!