Engineering Note

[SW Engineering] AJAX 응답 타입 불일치로 인한 TypeError 해결기 본문

SW Engineering

[SW Engineering] AJAX 응답 타입 불일치로 인한 TypeError 해결기

Software Engineer Kim 2025. 9. 27. 15:33

이어 지는 글

장바구니 주문 기능을 개발하던 중 발생한 흔한 실수와 그 해결 과정을 공유합니다. 프론트엔드와 백엔드 간의 데이터 타입 불일치로 인해 발생한 문제로, 많은 개발자가 겪을 수 있는 사례입니다.

 

문제 상황

사용자가 장바구니에서 주문 버튼을 클릭했을 때 다음과 같은 현상이 발생했습니다.

 

사용자 관점의 문제:

- 주문버튼을 눌렀지만 아무런 반응이 없음

- 에러메세지가 표시되지 않아 무엇이 잘못되었는지 알 수 없음

 

시스템 관점의 문제:

- HTTP 400 Bad Request 응답

- Javascript TypeError 발생

 

에러 분석

1단계 : 프론트엔드 에러 확인

브라우저 개발자 도구에서 다음 에러를 확인했습니다.

Uncaught TypeError: Cannot read properties of undefined (reading 'message')
    at Object.error (cart:163:50)
    at c (jquery-3.5.1.min.js:2:28294)
    at Object.fireWith [as rejectWith] (jquery-3.5.1.min.js:2:29039)
    at l (jquery-3.5.1.min.js:2:79825)
    at XMLHttpRequest.<anonymous> (jquery-3.5.1.min.js:2:82254)

네트워크 탭에서는 '/cart/orders' API 호출이 HTTP 400 상태로 실패한 것을 확인했습니다.

 

2단계: 서버 로그 분석

처음에는 info 레벨로그에서 요청 로그를 찾을 수 없어 컨트롤러에 요청이 도달하지 않은 것으로 판단했습니다. 하지만 debug 레벨로 로그를 변경한 후 확인해보니:

  • 요청은 정상적으로 컨트롤러로 도달
  • 서버의 유효성 검사에서 잘못된 요청으로 400 응답 반환

 

서버 에러 로그

2025-09-27T15:16:22.865+09:00 DEBUG 13417 --- [shop] [nio-8080-exec-9] o.s.security.web.FilterChainProxy        : Secured POST /cart/orders
2025-09-27T15:16:22.865+09:00 DEBUG 13417 --- [shop] [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet        : POST "/cart/orders", parameters={}
2025-09-27T15:16:22.865+09:00 DEBUG 13417 --- [shop] [nio-8080-exec-9] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.shop.controller.CartController#orderCartItem(CartOrderDto, Principal)
2025-09-27T15:16:22.865+09:00 DEBUG 13417 --- [shop] [nio-8080-exec-9] o.j.s.OpenEntityManagerInViewInterceptor : Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
2025-09-27T15:16:22.866+09:00 DEBUG 13417 --- [shop] [nio-8080-exec-9] m.m.a.RequestResponseBodyMethodProcessor : Read "application/json;charset=UTF-8" to [com.shop.dto.CartOrderDto@22f3ccf0]
2025-09-27T15:16:22.866+09:00 DEBUG 13417 --- [shop] [nio-8080-exec-9] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Using 'application/json', given [application/json, text/javascript, */*;q=0.01] and supported [text/plain, */*, application/json, application/*+json, application/yaml]
2025-09-27T15:16:22.866+09:00 DEBUG 13417 --- [shop] [nio-8080-exec-9] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Writing ["주문할 상품을 선택해주세요."]
2025-09-27T15:16:22.866+09:00 DEBUG 13417 --- [shop] [nio-8080-exec-9] o.j.s.OpenEntityManagerInViewInterceptor : Closing JPA EntityManager in OpenEntityManagerInViewInterceptor
2025-09-27T15:16:22.866+09:00 DEBUG 13417 --- [shop] [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet        : Completed 400 BAD_REQUEST

 

 

백엔드 응답 코드

if(cartOrderDtoList == null || cartOrderDtoList.size() == 0){
    return new ResponseEntity<String>("주문할 상품을 선택해주세요.", HttpStatus.BAD_REQUEST);
}

 

백엔드에서는 에러메세지를 String 타입으로 응답했습니다.

 

 

프론트 엔드 에러 처리 코드

error : function(jqXHR, status, error){
    if(jqXHR.status == '401'){
        alert('로그인 후 이용해주세요');
        location.href='/members/login';
    } else{
        console.log("주문 실패")
        alert(jqXHR.responseJSON.message); // 여기서 TypeError 발생
    }
}

 

프론트엔드에서는 jqXHR.responseJSON.message로 JSON 객체의 message 속성에 접근하려 했지만, 서버에서는 단순 문자열을 반환했기 때문에 jqXHR.responseJSON이 undefined가 되어 TypeError가 발생했습니다.

 

해결 방법

초기 임시 해결 방법 : 프론트엔드 수정

빠른 해결을 위해 프론트엔드에서 문자열 응답을 처리하도록 수정했습니다.

 

error : function(jqXHR, status, error){
    if(jqXHR.status == '401'){
        alert('로그인 후 이용해주세요');
        location.href='/members/login';
    } else{
        console.log("주문 실패")
        alert(jqXHR.responseText); // responseJSON.message → responseText로 변경
    }
}

 

 

최종 해결책 : 백엔드 수정

 

// 에러 응답 DTO 클래스 생성
public class ErrorResponse {
    private String message;
    
    public ErrorResponse(String message) {
        this.message = message;
    }
    
    // getter, setter
}

// 컨트롤러 수정
if(cartOrderDtoList == null || cartOrderDtoList.size() == 0){
    ErrorResponse errorResponse = new ErrorResponse("주문할 상품을 선택해주세요.");
    return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.BAD_REQUEST);
}

 

 

임시 수정 이후, 일관된 API 응답 제공을 위해 에러 응답 DTO 클래스를 만들어 JSON 형태로 응답하도록 코드 품질 개선 과정도 거쳤습니다. 

배운 점과 최종 해결책

1. API 응답 형식 통일의 중요성

  • 성공 응답과 에러 응답의 데이터 타입을 일관되게 유지
  • API 문서에 응답 형식을 명확히 정의

2. 방어적 프로그래밍

  • 프론트엔드에서 서버 응답을 처리할 때 타입 체크
error : function(jqXHR, status, error){
    if(jqXHR.status == '401'){
        alert('로그인 후 이용해주세요');
        location.href='/members/login';
    } else{
        console.log("주문 실패");
        
        // 안전한 에러 메시지 처리
        let errorMessage = "주문 처리 중 오류가 발생했습니다.";
        if(jqXHR.responseJSON && jqXHR.responseJSON.message) {
            errorMessage = jqXHR.responseJSON.message;
        } else if(jqXHR.responseText) {
            errorMessage = jqXHR.responseText;
        }
        
        alert(errorMessage);
    }
}

 

 

결론

이번 사례는 프론트엔드와 백엔드 간의 데이터 타입 불일치로 인한 일반적인 문제였습니다. 단순한 수정으로 해결되었지만, 근본적으로는 API 설계의 일관성과 유효성 타입체크 등 방어적 프로그래밍의 중요성을 다시 한번 깨닫게 해준 경험이었습니다.

 

 

Comments