본문 바로가기
Java

스택트레이스를 적극 활용하자

by 미소여우 2022. 3. 8.
728x90

요약

요약
- 무조건 구글링만 하지 말고, 에러를 한 번 쭉 읽어보자. 에러를 통해 어느 부분이 문젠지 파악할 수 있다.
- casued by: 예외 부분을 잘 찾아서 봐보자.
- 스택트레이스를 볼 때는 아래에서 위 순으로 판단하자. (스택이니까)

개발을 하다보면 에러가 터지는 경우 많이 겪게 될 것이다.

그럴 때 마다 무작정 구글에다 에러 부분을 복붙하지 말고, 스택트레이스를 적극 활용해보자.

예전에 읽었던 글인데, 나 뿐만 아니라 다른 분도 많이 알아야 겠다 생각해서 정리해본다.

스택트레이스는 프로그램이 시작된 시점부터 현재 위치 까지의 메서드 호출 목록을 말한다.
JVM이 자동 생성해주며 이를 통해 어디서 예외가 발생했는지 파악할 수 있다.

스택이란 단어가 정말 많은 곳에서 사용됨을 볼 수 있는데, 스택이란게 아래서 위로 차곡차곡 쌓인 내용들을 의미하니, 거진 위에 있는 예외가 우리가 해결해야하는 부분이라 보면 된다.
(외부에서 만들어진 스프링 같은 프레임워크에서는 웬만한 오류는 다 잡아두었을텐, 우리의 코드가 문제가 있음을 의심하면 된다.)

예제를 살펴보자 (예제는 출처의 코드 부분을 갖고 왔다.)

java.lang.reflect.InvocationTargetException
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
     at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
     at java.lang.reflect.Method.invoke(Unknown Source)
     at com.mylibrary.ap.xul.builder.handler.MethodCallback.invokeCallback(MethodCallback.java:32)
     at com.mylibrary.ap.xul.builder.handler.ViewHandler.invokeActionResult(ViewHandler.java:581)
     at com.mylibrary.ap.xul.context.impl.ViewContextImpl$2.onActionResult(ViewContextImpl.java:283)
     at com.mylibrary.ap.xul.context.impl.ActionContextImpl.setResult(ActionContextImpl.java:90)
     at com.mylibrary.ap.xul.action.stock.DialogAction.handleDialogClose(DialogAction.java:156)
     at com.mylibrary.ap.xul.action.stock.DialogAction$1.windowClosed(DialogAction.java:142)
     at com.mylibrary.api.Window.fireWindowClosedEvent(Unknown Source)
     at com.mylibrary.api.Window.onClose(Unknown Source)
     at com.mylibrary.api.platform.WindowBase.BaseClose(Native Method)
     at com.mylibrary.api.platform.WindowBase.BaseClose(Unknown Source)
     at com.mylibrary.api.Window.close(Unknown Source)
     at com.mylibrary.ap.xul.ui.MessageDialog.handleButtonClick(MessageDialog.java:145)
     at com.mylibrary.ap.xul.ui.MessageDialog$1.handleClick(MessageDialog.java:134)
     at com.mylibrary.api.ClickableSupport.fireClickEvent(Unknown Source)
     at com.mylibrary.api.ClickableSupport.handleAction(Unknown Source)
     at com.mylibrary.api.Button.actionHook(Unknown Source)
     at com.mylibrary.api.Component.action(Unknown Source)
Caused by: java.lang.NullPointerException
     at com.mycompany.service.impl.PortalManagerImpl.deleteMenuItem(PortalManagerImpl.java:603)
     at com.mycompany.service.impl.PortalManagerImpl.deletePortal(PortalManagerImpl.java:358)
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
     at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
     at java.lang.reflect.Method.invoke(Unknown Source)
     at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
     at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182)
     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
     at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
     at org.springframework.security.intercept.method.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:66)
     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
     at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
     at $Proxy54.deletePortal(Unknown Source)
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
     at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
     at java.lang.reflect.Method.invoke(Unknown Source)
     at com.mycompany.util.SpringSecurityContextInvocationHandler.invoke(SpringSecurityContextInvocationHandler.java:62)
     at $Proxy84.deletePortal(Unknown Source)
     at com.mycompany.ui.binding.PortalDataProvider.doDelete(PortalDataProvider.java:81)
     at com.mycompany.ui.binding.PortalDataProvider.doDelete(PortalDataProvider.java:12)
     at com.mycompany.ui.binding.AbstractEISDataProvider.delete(AbstractEISDataProvider.java:105)
     at com.mylibrary.ap.xul.binding.dataset.impl.DatasetImpl.doCommit(DatasetImpl.java:90)
     at com.mylibrary.ap.xul.binding.dataset.impl.AbstractDataset.commit(AbstractDataset.java:251)
     at com.mylibrary.ap.xul.binding.dataset.impl.AbstractDataset.deleteRow(AbstractDataset.java:201)
     at com.mylibrary.ap.xul.action.dataset.DeleteDataRowAction.execute(DeleteDataRowAction.java:22)
     at com.mylibrary.ap.xul.context.impl.ViewContextImpl.execute(ViewContextImpl.java:294)
     at com.mycompany.ui.portal.PortalInfoHandler.onPostConfirmDeleteAction(PortalInfoHandler.java:192)
     ... 21 more

 

여기서 주의 깊게 봐야하는 부분은 Casued by: java.lang.NullPointerException 쪽이다.

무엇을 했기에 널포인터 예외가 터졌을까를 생각해보아야 한다.

우리가 주안으로 두어야 할점은 아래이다.

at com.mycompany.service.impl.PortalManagerImpl.deleteMenuItem(PortalManagerImpl.java:603)
at com.mycompany.service.impl.PortalManagerImpl.deletePortal(PortalManagerImpl.java:358)

위에서 스택은 아래서 차곡차곡 쌓인다 했으니, 아래에서 위로 읽어보면 된다.

PortalManagerImp.deletePortal을 호출하고, deleteMenuItem의 603번째 줄에서 문제가 발생한 것이라 이해하면 된다.

그러면 603번째 줄에 가서 문제를 파악해보면 된다.

if (item == null) {
    throw new NullArgumentException("item");
}

//중간 생략
List<PortalMenu> children = getMenuItems(item.getPortal().getId(), item.getId()); // 603번째 줄

for (PortalMenu child : children) {
    deleteMenuItem(child);
}

우리가 판단할 수 있는 경우의 수는 대략 아래와 같다. 하나씩 살펴보자.

  1. children
  2. item
  3. item.getPortal()
  4. item.getPortal().getId()
  5. item.getId()

1번의 경우에는 리스트에 null이 담겨져도 문제가 되지 않는다, 생긴다면 for문쪽에서 문제가 발생했을 것이다.

2번의 경우에는 위에서 먼저 item이 null인지 체크하니 문제가 되지 않는다.

5번의 경우에는 2번처럼 null인 경우에 걸러졌을 테니 문제가 되지 않는다.

정답은 3번이다. NullPointerException은 내가 참조한게 null인데, 필드나 메서드 호출 등 참조의 작업을 할 때 생기는 문제이다.

만약 3번 했을 때 호출이 되었으면 4번에서 될 수 있지 않냐. 할 수 있지만, 4번이 null이 나온다해도 매개변수로서 null이 들어갈 뿐 문제가 되지 않는다.

이런식으로 문제를 하나하나 해결하다보면, 어디서 코드가 문제 생겼는지 찾을 수 있고, 트러블 슈팅을 할 수 있다.


출처: https://okky.kr/article/338405

728x90

'Java' 카테고리의 다른 글

[자바] 자바 빈즈  (0) 2022.08.29
[JAVA] 어노테이션 설명  (0) 2022.03.20
[JAVA] Enum에 대해서  (0) 2022.02.22

댓글