관리 메뉴

CASSIE'S BLOG

101-1강 스프링 부트 예외 처리하기 본문

PROGRAMMING/슈퍼코딩 강의 정리

101-1강 스프링 부트 예외 처리하기

ITSCASSIE1107 2024. 3. 7. 16:13

기존 자바 예외처리와 ResponseEntity
Spring Advice로 우아하게 처리하기
Spring AOP 간단히 처리하기

Spring예외처리 Advice Controller로 처리하는 법 익히고 Spring AOP 써보자

예외처리 미처리시 Http status 500 발생

Try-catch 사용하기
호출상위 메소드로 예외미루기
사용자 예외 던지기
예외/에러 로그 남기기

적절한 예외전파 적용이 핵심
<If 외부 전파 예외>
1. 해당 예외 -> Controller layer까지 전파
2. Controller -> Http error 코드 + 메세지 응답 전달

<If 내부 전파 예외>
1. 내부 다른 Default값으로 치환
2. 상위 내부 layer 전파 관리

실습 부분 일단 넘어감


상속할 때 generate해서 constructor해서 메세지 만드는 거 상속할 수 있음

 

 

Set계열은 저장 순서가 없어서 중복저장이 불가능하고 저장된 위치를 모르기 때문에 위치값으로 참조하는 것도 불가능하다. 따라서 저장된 데이터를 출력하는 용도로만 사용한다. Set은 인터페이스이기 때문에 Set형태로 데이터를 저장해서 사용하려면 반드시 하위클래스인 HashSet을 통해 구현해야 한다.

 

 

public List<Ticket> findUserFavoritePlaceTickets(Integer userId, String ticketType) {
//필요한 Repository: UserRepository, airLineTicket Repository
//1. 유저를 userId로 가져와서, 선호하는 여행지 도출
//2. 선호하는 여행지와 ticketType으로 AirLineTicket Table 질의해서 필요한 AirLineTicket 들고오기
//3. 이 둘의 정보를 조합해서 Ticket DTO를 만든다.

Set<String> ticketTypeSet = new HashSet<>(Arrays.asList("편도, 왕복"));

UserEntity userEntity = userRepository.findUserById(userId);

String likePlace = userEntity.getLikeTravelPlace();

List<AirlineTicket> airlineTickets = airLineTicketRepository.findAllAirlineTicketsWithPlaceAndTicketType(likePlace, ticketType);

List<Ticket> tickets = airlineTickets.stream().map(Ticket:: new).collect(Collectors.toList());
return tickets;
}


지금 이걸 만들고 있는데 왜 굳이 HashSet로 만드는거야? 출력할거라서 List로 만드는것보다 HashSet가 더 가볍나? 그리고 왜 Set<String> ticketTypeSet = new HashSet<>(Arrays.asList("편도, 왕복")); 이거를 만드는 방법 중에서 매개변수를 (Arrays.asList("편도, 왕복")) 이렇게 넣어준거야? 배열을 List로 왜 만들어서 HashSet를 넣어주는거야? 그냥 배열을 넣어줄수가없어?

 

HashSet을 사용하는 이유는 중복된 값을 허용하지 않고, 순서가 중요하지 않은 경우에 유용합니다. 또한, HashSet은 빠른 검색 속도를 제공하기 때문에 데이터를 빠르게 확인할 수 있습니다.

 

Arrays.asList("편도, 왕복")을 사용하여 배열을 리스트로 변환한 후 HashSet에 넣어주는 이유는 다음과 같습니다:

  1. Arrays.asList("편도, 왕복")은 배열을 리스트로 변환합니다. 그리고 이를 HashSet의 생성자에 전달하여 HashSet을 초기화합니다.
  2. 배열을 바로 HashSet에 전달할 수 없는 이유는 HashSet 생성자의 매개변수가 Collection 또는 Iterable 인터페이스를 구현한 객체를 요구하기 때문입니다. 배열은 Collection 인터페이스를 직접 구현하지 않으므로 바로 HashSet 생성자에 전달할 수 없습니다.

 

Java에서 Collection은 인터페이스이며, 주요 구현 클래스로는 List, Set, Queue 등이 있습니다.

  • List: 순서가 있는 데이터의 집합으로, 중복된 요소를 허용합니다. 구현 클래스로는 ArrayList, LinkedList, Vector 등이 있습니다.
  • Set: 순서가 없는 데이터의 집합으로, 중복된 요소를 허용하지 않습니다. 구현 클래스로는 HashSet, LinkedHashSet, TreeSet 등이 있습니다.
  • Queue: 선입선출(FIFO, First-In-First-Out) 방식의 데이터 구조입니다. 구현 클래스로는 LinkedList, PriorityQueue 등이 있습니다.
  • Map: 키-값(Key-Value) 쌍의 데이터를 저장하는 구조입니다. 구현 클래스로는 HashMap, LinkedHashMap, TreeMap 등이 있습니다.

그리고 Collection 인터페이스는 다음과 같은 주요 메서드를 정의합니다:

  • add(E e): 요소를 컬렉션에 추가합니다.
  • remove(Object o): 지정된 요소를 컬렉션에서 제거합니다.
  • contains(Object o): 지정된 요소가 컬렉션에 포함되어 있는지 확인합니다.
  • size(): 컬렉션의 요소 수를 반환합니다.
  • isEmpty(): 컬렉션이 비어 있는지 여부를 확인합니다.
  • iterator(): 컬렉션의 요소를 반복하는 데 사용되는 반복자를 반환합니다.

이러한 인터페이스와 구현 클래스들은 Java 컬렉션 프레임워크의 핵심입니다. 이를 이용하여 데이터를 구조화하고 관리할 수 있습니다.

 

빠르게 검색하려고 HashSet 쓴다. 이정도만 알아둬

 

HASHSET이 단순히 자료들을 출력해서 뽑아낼때 백엔드 개발자들은 이 자료구조를 쓰겠네?
 
 
 

네, 백엔드 개발자들은 HashSet과 같은 자료구조를 다양한 상황에서 사용합니다.

HashSet은 중복을 허용하지 않고, 순서가 없는 데이터를 저장하는데 사용됩니다. 이러한 특징 때문에 백엔드 개발에서 다음과 같은 상황에 주로 사용됩니다:

  1. 중복 요소 제거: 데이터에서 중복된 요소를 제거해야 할 때 HashSet을 사용합니다. 예를 들어, 중복된 이메일 주소를 제거하거나, 중복된 값이 없는 데이터를 확인할 때 사용할 수 있습니다.
  2. 빠른 검색 속도: HashSet은 해시 테이블을 기반으로 하기 때문에 검색 속도가 매우 빠릅니다. 데이터를 빠르게 검색해야 하는 상황에서 HashSet을 사용합니다.
  3. 집합 연산: 두 집합을 합치거나 교집합, 차집합을 계산해야 할 때 HashSet을 사용합니다.
  4. 데이터의 순서가 중요하지 않은 경우: HashSet은 데이터를 저장할 때 순서를 보장하지 않습니다. 따라서 데이터의 순서가 중요하지 않은 경우에 사용됩니다.

예를 들어, 데이터베이스에서 가져온 결과를 중복 없이 저장하고 검색해야 할 때, 혹은 어떤 작업에서 중복을 제거하고 빠르게 데이터를 검색해야 할 때 HashSet을 사용할 수 있습니다.

요약하자면, HashSet은 백엔드 개발에서 데이터를 효율적으로 관리하고 검색하기 위해 자주 사용되는 자료구조 중 하나입니다.

 

 

chapter 97에서 만들었던거를 왕복말고 왕으로 해서 잘 되나테스트해보면 됨. 

 

왜 이 에러가 뜨는거지? 맞는데?

 

2024-04-30 10:52:12.862 [http-nio-9090-exec-3] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.github.supercoding.service.exceptions.InValidException: 해당 TicketType왕복은 지원하지않습니다.] with root cause
com.github.supercoding.service.exceptions.InValidException: 해당 TicketType왕복은 지원하지않습니다.
at com.github.supercoding.service.AirReservationService.findUserFavoritePlaceTickets(AirReservationService.java:55)
at com.github.supercoding.service.AirReservationService$$FastClassBySpringCGLIB$$c2435c93.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy.invokeMethod(CglibAopProxy.java:386)
at org.springframework.aop.framework.CglibAopProxy.access$000(CglibAopProxy.java:85)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:704)
at com.github.supercoding.service.AirReservationService$$EnhancerBySpringCGLIB$$c7f792c.findUserFavoritePlaceTickets(<generated>)
at cohttp://m.github.supercoding.web.controller.AirReservationController.findAirlineTickets(AirReservationController.java:27)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:529)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:623)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:840)

 

 

Set<String> ticketTypeSet = new HashSet<>(Arrays.asList("편도", "왕복"));

 

아까 이거를 "편도, 왕복" 이렇게 해서 그런거임

 

@GetMapping("/tickets")
public TicketResponse findAirlineTickets(@RequestParam("user-id") Integer userId,
@RequestParam("airline-ticket-type") String ticketType){
try {
List<Ticket> tickets = airReservationService.findUserFavoritePlaceTickets(userId, ticketType);
return new TicketResponse(tickets);
} catch (InValidValueException) {

}


}

 

이걸 지금 하고 있는데 ppt에서 말했다싶이...ReponseEntity로 바꿔야한다고함. 

 

그대로 기존에 있는 Entity를 유지할수가 없다고함. ResponseEntity로 바꿔야한다고함. 

 

ry-catch 블록을 추가함으로써 메서드에서 발생하는 예외를 적절하게 처리하고자 합니다. 이 경우에는 예외가 발생했을 때 클라이언트에게 적절한 응답을 보내기 위해 ResponseEntity를 사용해야 합니다.

그 이유는 다음과 같습니다:

에러 핸들링:
예외가 발생하면 클라이언트에게 적절한 에러 메시지를 보내는 것이 중요합니다. 클라이언트는 예외에 대한 정보를 받고 그에 따른 조치를 취할 수 있습니다.
try-catch 블록을 추가함으로써 예외를 처리하고, 그에 따른 응답을 제공할 수 있습니다.


ResponseEntity 사용 이유:
ResponseEntity를 사용하면 HTTP 응답 코드, 헤더, 본문(body) 등 모든 응답 정보를 포함할 수 있습니다.
클라이언트에게 응답을 보낼 때 ResponseEntity를 사용하여 응답을 래핑합니다. 이를 통해 클라이언트에게 적절한 HTTP 응답 코드와 메시지를 보낼 수 있습니다.

@GetMapping("/tickets")
public ResponseEntity<TicketResponse> findAirlineTickets(@RequestParam("user-id") Integer userId,
                                                         @RequestParam("airline-ticket-type") String ticketType) {
    try {
        List<Ticket> tickets = airReservationService.findUserFavoritePlaceTickets(userId, ticketType);
        return ResponseEntity.ok(new TicketResponse(tickets));
    } catch (InvalidValueException e) {
        return ResponseEntity.badRequest().build(); // 예시로 임의의 처리
    }
}
이제 예외가 발생했을 때 적절한 HTTP 응답 코드와 함께 클라이언트에게 응답을 보낼 수 있습니다. 이렇게 함으로써 클라이언트는 예외에 대한 정보를 받고, 그에 따른 조치를 취할 수 있습니다.

ResponseEntity를 사용하지않고 Response 그냥 클래스를 쓰면 HTTP 응답 코드, 헤더, 본문(body) 등 모든 응답 정보를 포함할 수 없는거지?

네, 맞습니다. Response 클래스는 ResponseEntity처럼 HTTP 응답 코드, 헤더 및 본문(body)과 같은 모든 응답 정보를 포함하지 않습니다. 따라서 Spring MVC 컨트롤러에서 클라이언트에게 응답을 보낼 때는 ResponseEntity를 사용하여 응답을 래핑하는 것이 바람직합니다.

ResponseEntity는 Response보다 더 많은 기능과 유연성을 제공합니다. 클라이언트에게 보낼 응답에 대해 HTTP 상태 코드, 헤더, 본문(body) 등을 쉽게 설정할 수 있습니다.

따라서 Spring MVC 컨트롤러에서는 일반적으로 ResponseEntity를 사용하여 클라이언트에게 응답을 보냅니다.


@GetMapping("/tickets")
public ResponseEntity<TicketResponse> findAirlineTickets(@RequestParam("user-id") Integer userId,
                                                         @RequestParam("airline-ticket-type") String ticketType) {
    try {
        List<Ticket> tickets = airReservationService.findUserFavoritePlaceTickets(userId, ticketType);
        return ResponseEntity.ok(new TicketResponse(tickets));
    } catch (InvalidValueException e) {
        return ResponseEntity.badRequest().build(); // 예시로 임의의 처리
    }
}


위의 코드에서 ResponseEntity를 사용하여 클라이언트에게 적절한 응답을 보내고 있습니다. 이를 통해 HTTP 응답 코드, 헤더, 본문(body) 등 모든 응답 정보를 포함할 수 있습니다.

@GetMapping("/tickets")
public ResponseEntity findAirlineTickets(@RequestParam("user-id") Integer userId,
@RequestParam("airline-ticket-type") String ticketType){
try {
List<Ticket> tickets = airReservationService.findUserFavoritePlaceTickets(userId, ticketType);
TicketResponse ticketResponse = new TicketResponse(tickets);
return new ResponseEntity(ticketResponse, HttpStatus.OK);
} catch (InValidValueException ive) {
log.error("Client 요청에 문제가 있어 다음처럼 출력합니다." + ive.getMessage()); // 우리 쪽 위한거

}




log에 빨간 줄 뜨거든? 어노테이션 붙여줘야함

@Slf4j 

 

 

queryForObject가 무조건 1이여야하는데 0이되서 문제가 생기는거임. 

 

 

2024-04-30 12:17:02.787 [http-nio-9090-exec-3] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0] with root cause
org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
at org.springframework.dao.support.DataAccessUtils.nullableSingleResult(DataAccessUtils.java:97)
at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:895)
at cohttp://m.github.supercoding.repository.users.UserJdbcTemplateDao.findUserById(UserJdbcTemplateDao.java:27)
at cohttp://m.github.supercoding.repository.users.UserJdbcTemplateDao$FastClassBySpringCGLIB$bbcf8baf.invoke(<generated>)

 

쌤이 sql문까지 내려가는 경우에는 그대로 전파를 하지않고 이거를 적절한 값으로 바꾸고 내부적으로 최적화한 후에 사용한다고함.

Optional로 먼저 바꿀려고함. 

 

1.build.gradle에 이거 추가

 


//로깅
implementation 'org.springframework.boot:spring-boot-starter-log4j2'
}

 

2. 로그레벨을 설정해야함.

 

server:
port: 9090

logging:
config: classpath:logback-spring-dev.xml
level:
com.github.supercoding: DEBUG

 

 

근데 Client 요청에.. 이런 문구가 안 찍혀

 

 

 

몰랐는데 코드에 그.. 화살표 모양 내려가는 걸 누르면.. 그거를 따라가서 찾을 수 있음 메소드 쓴거를

 

 

@Override
public Optional<Passenger> findPassengerByUserId(Integer userId) {
try {
return Optional.ofNullable(template.queryForObject("SELECT * FROM passenger WHERE user_id = ?", passengerRowMapper, userId));
} catch (Exception e ){
return Optional.empty()
}
}

 

saveReservation 일단 하는 실습 통과함. 

반응형