개발 일기

스프링부트 Posting 통신에러 해결 - Deserialize error, 400, 500, 200 에러 본문

Java&Spring/Project

스프링부트 Posting 통신에러 해결 - Deserialize error, 400, 500, 200 에러

flow123 2021. 12. 1. 23:53

첫 에러메시지

 

2021-12-01 17:19:04.925  WARN 28048 --- [nio-8080-exec-9] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.lang.String` from Array value (token `JsonToken.START_ARRAY`); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.lang.String` from Array value (token `JsonToken.START_ARRAY`)<LF> at [Source: (PushbackInputStream); line: 1, column: 45] (through reference chain: com.example.thebestmeal_test.dto.PostDto["postingCat"])]

 

(2) 형변환 일치

Posting Emo는 [] 배열에 넘어왔기 때문에, List<String>으로 받아야한다. 

@Getter
@Setter
public class PostDto {
    private String postingFoodName;
    private List<String> postingEmo;
    private List<String> postingCat;
    private String foodImgUrl;
    private String postingMemo;
}

 

원래 regex split 을 쓰려고 했는데, String이 들어가야 해서 List 가 아닌 String 형을 썼었다. 

알고보니, 내가 코드 형변환을  잘못 이해하고 있었다. 

 

(1)  split들어간 regex코드 삭제해야함   

나는 애초에 btn_val =[]로 받아왔기 때문에 array, 리스트로 보냈다.

다시보니, 해당 예제는  String으로 받았기 때문에, 구분자로 regex 를 써서, 다시 List 로 형변환을 해야했다.

반면, 내 코드는 애초에 [] 빈칸에 배열을 받기 때문에, 변환코드를 쓸 필요가 없다! 

 

해당 코드를 삭제하고, 

        List<String> items1 = Arrays.asList(postDto.getPostingTag().split("\\s*,\\s*"));

items1을 postDto.getPostingTag로 바로 넣어주면 된다. 

 

#

 서버와 프론트간의 데이터 불일치 확인하는 법. 

 

(1)400 에러가 났다.

위의 api 에 값을 넘겨줄 때, 값이 잘 매핑이 안되서 나는 에러는 400 코드로 표시된다.  

-Dto의 key값과, 프론트js의 ajax에서 보내주는 key 값이 일치하지 않았을 때,  

이를 확인할 수 있는 법은 (1) 콘솔에서 브레이크 포인트를 찍어서 js 를 날리면, 아래에 객체속성을 확인할 수 있다.  

(2) 프론트 단에서는 콘솔 -> 네트워크 -> post -> 

 

 

 

(2) 500에러

값은 매핑이 되어서 로직은 잘 탔음. 그런데 Api 에는 진입을 했는데. Unique result 아니라는 에러가 나옴 

이건 Internal server error 임. 이 코드에서 멈춰서 해결이 안되었다. food 객체에서 문제가 있다는 것(즉 food 가 중복되고 있다는 뜻이다) 알고보니 아래 food 2가 고유값이어야 하는데, (getPostingFoodName)을 기준으로 찾아오니까. 여러값을 찾아서 서버가 이게 뭐지? 하고 혼란스러워하고 있다. 

 

Foodfood=newFood(postDto);

Food food2 = foodRepository.findByName(postDto.getPostingFoodName());

 

(3)

*200에러는 아직 해결중이다. 

200에러: 서버는 제대로 도는 , 프론트에서 뭔가 로직에 이상이 있어서 로딩이 안되는 .

 200이 에러 ->  API  정상이다라는 뜻이다. 

 

(5) 이럴 알아보는 . Food 브레이크 포인트 찍고 영속성 확인..

브레이크 포인트 찍고, save(posting 요청 눌러지느 전송 버튼) 누르면 postDto 객체 내부에 컬럼들이 나옴

 

front 와 서버단의 객체 데이터가 일치한다. Payload로 보면 console.log를 일일이 안찍어도 되서 편하다. 

 

#

Db에서 직접 조회.

 

query console확인하는 법. 이걸로 SQL Select 문을 날려보면 실제로 중복 데이터가 있는지 확인할 수 있다. 

당연히 중복데이터가 없을 것이라고 확신했는데 알고보니 js 쿼리를 여러번 날리면서 여러번 저장되었다 ㅜ.ㅜ 

쉽게 확신하지 말고 DB 뜯어보자.

PostController 의 핵심 기능은 repository 에 음식 정보를 save하는 것이다. 그러니 data에 어떤 변화가 일어나는지 감지해야한다. 나는 프론트단에서 통신이 되지 않으니, 당연히 db가 넘어가지도 않을 것이라고 생각했다. 

 

-> 추측하는 게 아니라 확인하고 다음스텝 확인. 

아래 쿼리는 FOODNAME 기준으로 테이블에 존재하는 지 확인하는 SQL 문이다. 

select food0_.id as id1_1_, food0_.created_at as created_2_1_, food0_.modified_at as modified3_1_, food0_.image_url as image_ur4_1_, food0_.name as name5_1_ from food food0_ where food0_.name='호박삼겹살'

그렇기 때문에, UI 없이 postman/arc 로 API통신을 하는 것이 중요하다. Postman으로 확인하는 것은 console로 찍는 것, payload 로 보는거랑 똑같다.  프론트와 백엔드가 같이 있으면 뭐가 잘못됐는지 확인하기가 더 복잡하다. 디버깅은 프로젝트 경험이 쌓이면서 느는데, 지금은 디버깅 실력이 높지 않으니, 최대한 잘게 쪼개서 해결해야해. #이것보다 더 가벼운 것: testcode 만들기가 있는데 다음에 시도해보자.

 

참고로 로그인 데이터도 (token 이 있는) postman 통신이 가능하다. headers 에 token 옵션을 넣어준다면! 

 

최초 코드 

  @PostMapping("/post")
    public void postFood(@RequestBody PostDto postDto, @AuthenticationPrincipal UserDetailsImpl userDetails) {
        User user = userRepository.findByUsername(userDetails.getUsername()).orElseThrow(
                () -> new NullPointerException("그런 사람 없는데요?")
        );
        //food
        Food food = new Food(postDto);
        foodRepository.save(food);
        //category
        List<String> items1 = Arrays.asList(postDto.getPostingTag().split("\\s*,\\s*"));
        List<Tag> tags1 = items1.stream().map(tag -> new Tag(food, tag, "emotion")).collect(Collectors.toList());
        tagRepository.saveAll(tags1);
        //emotion
        List<String> items2 = Arrays.asList(postDto.getPostingCat().split("\\s*,\\s*"));
        List<Tag> tags2 = items2.stream().map(tag -> new Tag(food, tag, "category")).collect(Collectors.toList());
        tagRepository.saveAll(tags2);
        //posting 저장 (foodId, userId, post 내용 저장)
        Food food2 = foodRepository.findByName(postDto.getPostingFoodName());
        Posting posting = new Posting(postDto, user, food2);
        postingRepository.save(posting);
        
    }
}

최종코드 

    @PostMapping("/post")
    public void postFood(@RequestBody PostDto postDto, @AuthenticationPrincipal UserDetailsImpl userDetails) {
        User user = userRepository.findByUsername(userDetails.getUsername()).orElseThrow(
                () -> new NullPointerException("그런 사람 없는데요?")
        );

       //food
        Food food = new Food(postDto);
        foodRepository.save(food);
        //category
        List<Tag> tags1 = postDto.getPostingEmo().stream().map(tag -> new Tag(food, tag, "emotion")).collect(Collectors.toList());
        tagRepository.saveAll(tags1);
        //emotion
        List<Tag> tags2 = postDto.getPostingCat().stream().map(tag -> new Tag(food, tag, "category")).collect(Collectors.toList());
        tagRepository.saveAll(tags2);
        //posting 저장 (foodId, userId, post 내용 저장)
        Food food2 = foodRepository.findByName(postDto.getPostingFoodName());
        Posting posting = new Posting(postDto, user, food2);
        postingRepository.save(posting);

    }
}

#
이미 있는 호박죽 데이터를 보내면 500에러가 뜬다. 

DB에는 저장이 되는데, getPostingFoodname 을넣어서 food2에 foodid 를 받아올 떄, 중복값이라서 500에러가 생긴다. 

 

새로 데이터를 보내면 통신성공이다! 

Comments