본문 바로가기
나만의 정리

스택 트레이스 읽는 방법(feat. NullPointerException)

by 코리늬 2019. 8. 5.

스택 트레이스르 정확히 읽을 줄 알아야 빠른 디버깅이 가능하고, 이는 에러 해결하는 시간을 줄여주는데 직결하기 때문에 알아두는게 좋을 것 같다.

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

위와 같은 에러가 있는 경우 무엇부터 잡아야할까??

나였다면 아마 제일 위의 에러코드 한 줄을 복사해서 바로 구글링을 했을 것이다.

하지만, 문제의 진정한 원인은 윗쪽의 Trace가 아닌 중간 쪽의 Caused By로 표시되는 널 포인터 에러다.

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으로 시작한 가장 상단의 메세지가 결론적으로 에러가 발생한 부분이다

  1. nullPointerException 발생
  2. com.mycompany.service.impl.PortalManagerImpl 클래스의 deletePortal 메소드 358번 라인에서 deleteMenuItem 메소드를 호출했는데 해당 메소드 603번 째 줄에서 널 포인터 예외가 발생했다.
  • PortalManagerImpl 소스

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

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

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

NullPointerException이 발생할 수 있는 부분

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

이렇게 5번이 있다.

하지만 NullPointerException 에러만 보고 바로 2, 3 번으로 경우를 좁힐 수 있어야 한다.

NullPointerException 는 단순히 변수에 널값이 들어가서 생기는 오류가 아니다.

명확하게 객체의 널 레퍼런스에 대해 메소드 호출이나 필드 참조 등의 작업을 했을 때 발생하는 문제다.

만일 4, 5번이 Null 이라면 널 레퍼런스 호출이 아닌 널값을 'getMenuItems' 메소드의 인자 값으로 넘기기 때문에 널포인터 예외의 원인이 될 수 없다.

물론 getMenuItems 메소드 안에서 널 값으로 인한 예외가 날 수 있지만, 그 경우에는 에러 트레이스가 다른 곳에서 발생했을 것이다.

다시 2, 3번으로 돌아오면 item 이거나, item.getPortal() 인 경우인데 item은 위에서 null 체크를 하기 때문에 정답은 3번이 된다.

스택 트레이스를 읽을 수 있음으로써 의미없는 검색, 질문시간을 줄일 수 있어 생산성의 차이는 엄청나게 커질 것이다.

초보 개발자로써 꼭 알아둬야 한다고 생각한다.

참고

https://okky.kr/article/338405

'나만의 정리' 카테고리의 다른 글

Maven VS Gradle  (0) 2020.06.02
컴파일과 런타임  (0) 2019.07.14
[웹] White Domain, RBL, SPF, DKIM  (0) 2019.07.14
자바 제네릭 완전정복  (2) 2019.03.21
빌더 패턴(Builder Pattern)  (0) 2019.03.21

댓글