chaSunil / troubleShooting

작업을 진행하며, 문제가 생겨서 정리해놓은 트러블 슈팅을 모아놓는 공간
0 stars 0 forks source link

Trouble Shooting (2) https 환경에서 서버 비동기 처리 요청 에러 #6

Open chaSunil opened 23 hours ago

chaSunil commented 23 hours ago

프로젝트 명 : final project

담당 파트 : 주문 및 결제

작성자 명 : 차선일

작성 일자 : 2024-09-19

chaSunil commented 23 hours ago




현상

기존 localhost 환경에서는 동시주문으로 해당 상품의 재고가 부족하면, 서버에서 예외처리를 하고 난 뒤에, 비동기 방식으로 alert처리 하여, 메세지가 오게 로직이 짜여져있었고, 올바르게 실행이 되었다.

허나, 아마존 EC2 환경에 서버를 연동하여 올렸고, https 환경에서 테스트 결제를 진행하는 도중에, 재고가 부족해서 결제 처리가 안되고 막히는 로직까지는 실행이 되지만, alert로 알람 메세지가 오지 않는 현상이 발견되었음.

image




원인

다른 도메인 간의 요청이 필요한 경우, 서버에서 자체적으로 CORS 설정이 필요하다. CORS 설정이 되어있지 않으면 요청이 차단되는 경우가 발생할 수 있다.

기존 Controller

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        // 예외에 대한 로그 기록
        ErrorType errorType = determineErrorType(ex);
        log.error("오류 발생: {} - 요청 URL: {}", errorType.getMessage(), request.getRequestURI(), ex);

        // 상태 코드 가져오기 (기본값으로 500 설정)
        int statusCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;

        // 예외에 따라 상태 코드 설정
        if (ex instanceof NoHandlerFoundException || ex instanceof NoResourceFoundException) {
            statusCode = HttpServletResponse.SC_NOT_FOUND; // 404 Not Found
        } else if (ex instanceof HttpRequestMethodNotSupportedException) {
            statusCode = HttpServletResponse.SC_METHOD_NOT_ALLOWED; // 405 Method Not Allowed
        }

        System.out.println("statusCode = " + statusCode);
        // 예외에 따라 적절한 상태 코드와 에러 페이지 설정
        // 비동기 요청인지 동기 요청인지 판별
        boolean isAjax = "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));

        // 예외에 따라 적절한 상태 코드와 에러 페이지 설정
        if (isAjax) {
            response.setStatus(statusCode);
            response.setContentType("application/json; charset=UTF-8");

            try {
                System.out.println("@@@@ 에러메세지 = " + errorType.getMessage());
                String jsonResponse = String.format("{\"message\": \"%s\"}", errorType.getMessage());
                response.getWriter().write(jsonResponse);
                response.getWriter().flush();
            } catch (IOException e) {
                log.error("JSON 응답 작성 중 오류 발생", e);
            }
            return new ModelAndView(); // 비동기 요청은 ModelAndView를 사용하지 않음
        } else {
            // 동기 요청인 경우 에러 페이지로 이동
            ModelAndView mv = new ModelAndView();
            mv.addObject("errorMessage", errorType.getMessage());
            mv.addObject("statusCode", statusCode);
            mv.setViewName("errorPage/error");
            return mv;
        }
    }




결과

Controller

@Slf4j
public class MyCustomExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        // 예외에 대한 로그 기록
        ErrorType errorType = determineErrorType(ex);
        log.error("오류 발생: {} - 요청 URL: {}", errorType.getMessage(), request.getRequestURI(), ex);

        // 상태 코드 가져오기 (기본값으로 500 설정)
        int statusCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;

        // 예외에 따라 상태 코드 설정
        if (ex instanceof NoHandlerFoundException || ex instanceof NoResourceFoundException) {
            statusCode = HttpServletResponse.SC_NOT_FOUND; // 404 Not Found
        } else if (ex instanceof HttpRequestMethodNotSupportedException) {
            statusCode = HttpServletResponse.SC_METHOD_NOT_ALLOWED; // 405 Method Not Allowed
        }

        System.out.println("statusCode = " + statusCode);
        // 예외에 따라 적절한 상태 코드와 에러 페이지 설정
        // 비동기 요청인지 동기 요청인지 판별
        boolean isAjax = "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));

        // 예외에 따라 적절한 상태 코드와 에러 페이지 설정
        if (isAjax) {
            handleAjaxError(response, statusCode, errorType);
            return new ModelAndView();
        } else {
            return handleSynchronousError(statusCode, errorType);
        }

    }



여기서 setHeader로 CORS 설정을 해준다.

    /**
     *
     * @param response
     * @param statusCode
     * @param errorType
     * 비동기 요청 처리 로직
     */
    private void handleAjaxError(HttpServletResponse response, int statusCode, ErrorType errorType) {
        response.setStatus(statusCode);
        response.setContentType("application/json; charset=UTF-8");
        response.setHeader("Access-Control-Allow-Origin", "*");  // CORS 헤더 추가
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Requested-With");

        try {
            String jsonResponse = new ObjectMapper().writeValueAsString(
                    Map.of("message", errorType.getMessage(), "status", statusCode)
            );
            response.getWriter().write(jsonResponse);
            response.getWriter().flush();
            log.debug("AJAX 에러 응답 전송: {}", jsonResponse);
        } catch (IOException e) {
            log.error("JSON 응답 작성 중 오류 발생", e);
        }
    }
    /**
     *
     * @param statusCode
     * @param errorType
     * @return
     * 동기 요청 처리 로직
     */
    private ModelAndView handleSynchronousError(int statusCode, ErrorType errorType) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("errorMessage", errorType.getMessage());
        mv.addObject("statusCode", statusCode);
        mv.setViewName("errorPage/error");
        return mv;
    }




결과화면

https에서 실행했을때 올바르게 재고 부족에 관련된 메세지가 alert로 올바르게 출력이 된다.




깨달은점

localhost 환경은 기본적으로 http://localhost로 http를 사용하기 때문에, SSL 인증서가 필요하지 않아 보안에 대해서 취약하고 설정이 되어있지 않지만, https에서는 SSL 프로토콜을 사용하여 데이터를 암호화하고, 서버와 클라이언트 간의 보안 연결을 제공한다.

보안 부분에서 두 부분은 확연한 차이를 보이기 때문에, https에 맞는 환경에 따라 개발하는것을 중점으로 두어야겠다.




참고 사이트 및 URL