본문 바로가기
미니프로젝트/spring

에러메세지 기록

by rewind 2024. 9. 16.

날짜 : 2024.09.16

 

소스유형 : 자바

소스이름 : BoardContoller

소스위치 : myapp/board/contoller/BoardController 내 remove 메서드

 

에러메세지 원문

Cannot construct instance of com.project.myapp.dto.SearchCondition (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('SearchCondition{page=1, pageSize=10, option='', keyword=''}') at [Source: UNKNOWN; byte offset: #UNKNOWN]

 

에러메세지 의미

JSON 문자열을 객체로 변환하려고 할 때 해당 객체에 기본 생성자 또는 적절한 생성자가 없음

 

에러발생 상황

게시글 삭제 버튼을 눌렀을때 서버에서 값을 받지 못한다(값 자체는 오나 파싱을 못함)

 

에러원인

삭제기능 구현중에 삭제버튼을 클릭시 클라이언트에서 전달한 JSON데이터를 특정객체(SearchCondition)로 변환하려고 할때 해당 클래스에 적절한 생성자나 팩토리 메서드가 없어서 발생 (클라이언트에서 해당 객체 요소값을 JSON타입으로 변환하지 않아서)

기존에는 form 전송을 사용했기 때문에 스프링 내부에서 알아서 바인딩이 잘 됐다

 

해결방법

- 클라이언트 : 전달해야할 각각의 값을 전부 JSON형식으로 변환해줌

- 서버 : 값을 받은 뒤 Object타입으로 변환해줌

 

해결여부 : 해결완료

 

아래는 해당부분 코드

/* 클라이언트 : Javascript - /WEB-INF/views/board/board.jsp */
$("#removeBtn").on("click", function (event) {
    if (!confirm("정말로 삭제하시겠습니까?")) {
        return;
    }
    event.preventDefault();
    let sc = {
        "page": "${searchCondition.page}",
        "pageSize": "${searchCondition.pageSize}",
        "option": "${searchCondition.option}",
        "keyword": "${searchCondition.keyword}"
    }

    let removeData = {
        "bno": bno,
        "sc": sc
    }

    $.ajax({
        url: '/board/remove',
        type: "post",
        xhrFields: {
            withCredentials: true
        },
        headers: {
            'access': tokenData,
            'Content-Type': 'application/json'
        },
        data: JSON.stringify(removeData),
        success: function (response) {
            location.href = response;
        }
    })
})

 

/* 서버코드 Java(Spring) */
@ResponseBody
@PostMapping("/remove")
public String remove(@RequestBody Map<String, Object> removeData, RedirectAttributes rattr) {
/* 문제부분 여기부터 시작 */
    Integer bno = Integer.parseInt((String)removeData.get("bno"));
    ObjectMapper mapper = new ObjectMapper();
    SearchCondition sc = mapper.convertValue(removeData.get("sc"), SearchCondition.class);
/* 문제부분 여기까지 끝*/

    log.info("sc = {}", sc);
    log.info("bno = {}", bno);

    CustomDetails userDetails = (CustomDetails)SecurityContextHolder.getContext()
        .getAuthentication()
        .getPrincipal();
    String writer = userDetails.getUser().getId();

    log.info("writer for remove = {}", writer);
    log.info("writer for bno = {}", bno);

    String msg = "DEL_OK";
    try {
        int result = this.boardService.deleteByIdNBno(bno, writer);
        if (result != 1) {
            throw new Exception("Delete failed");
        }
    } catch (Exception e) {
        e.printStackTrace();
        msg = "DEL_ERR";
    }
    rattr.addFlashAttribute("msg", msg);
    return "/board/boardList" + sc.getQueryString();
}

 

주의사항

* 기존에 session과 jsp로 구현했었던 프로젝트를 스프링 시큐리티 + OAuth2 + jwt 방식으로 전환하면서 session이 stateless설정되어 기존 기능들을 비동기 방식으로 수정중에 나타남

 

* SSR 방식인 jsp에서는 jwt를 사용하는 것보다 기존의 세션+스프링 시큐리티만 적용하는 방향으로 하는것이 효율적이고 실제로 옳은 방법이다.(SSR에서는 서버에 의해 렌더링 되는데 굳이 session을 stateless로 사용한다는 것에 대한 의문점)

 

* 처음에는 공부의 목적이니 우선 jwt, oauth2인증을 구현까지는 해보는 것을 목표로 하여 해당 기능을 구현하였으나 구현하고 나니 기존의 모든 기능들을 비동기방식으로 수정해야 하며 api 요청시 로컬스토리지의 토큰값을 꺼내 항상 헤더에 토큰값을 추가해서 보내야 한다. 덤으로 비동기이기 때문에 ajax 응답 결과에 따라서 요소값을 전부 다시 수정 해줘야 하는데 이게 맞나 싶다. stateless 의미랑 jwt를 CSR 방식에서 사용해야 한다는 의미를 뼈저리게 깨닫는중