Engineering Note

[Error Handling] 파일 업로드 테스트 에러 해결 본문

Error Handling

[Error Handling] 파일 업로드 테스트 에러 해결

Software Engineer Kim 2025. 9. 15. 10:05

파일 업로드를 테스트하며 에러가 발생해서 에러 발생 해결 과정을 정리하려고 한다.

 

 

에러메세지

java.lang.StringIndexOutOfBoundsException: Range [-1, 9) out of bounds for length 9

	at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:55)
	at java.base/jdk.internal.util.Preconditions$1.apply(Preconditions.java:52)
	at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:213)
	at java.base/jdk.internal.util.Preconditions$4.apply(Preconditions.java:210)
	at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:98)
	at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromToIndex(Preconditions.java:112)
	at java.base/jdk.internal.util.Preconditions.checkFromToIndex(Preconditions.java:349)
	at java.base/java.lang.String.checkBoundsBeginEnd(String.java:4602)
	at java.base/java.lang.String.substring(String.java:2715)
	at java.base/java.lang.String.substring(String.java:2688)
	at com.shop.service.FileService.uploadFile(FileService.java:15)
	at com.shop.service.ItemImgService.saveItemImg(ItemImgService.java:30)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:578)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:360)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:380)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:728)
	at com.shop.service.ItemImgService$$SpringCGLIB$$0.saveItemImg(<generated>)
	at com.shop.service.ItemService.saveItem(ItemService.java:38)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:578)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:360)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:380)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:728)
	at com.shop.service.ItemService$$SpringCGLIB$$0.saveItem(<generated>)
	at com.shop.service.ItemServiceTest.saveItem(ItemServiceTest.java:63)
	at java.base/java.lang.reflect.Method.invoke(Method.java:578)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

 

 

가장 위에 에러메세지를 보면 'java.lang.StringIndexOutOfBoundsException: Range [-1, 9) out of bounds for length 9' 으로 문자열에서 범위가 벗어난 인덱스를 참조하려고 해서 에러가 발생했다는 걸 알 수 있다.

 

그리고 at으로 적힌 스택트레이스를 통해 호출한 메서드를 보면서 에러가 발생한 지점을 찾아갈 수 있다. 우선 내가 적은 코드에서 에러가 발생했을 수도 있고, 라이브러리 자체에서 발생했을 수도 있지만, 내 프로젝트의 코드 어디서 에러가 발생했는데 패키지 명을 통해 에러가 발생한 지점을 찾았다.

11 번째 줄과 12번째 줄을 보면 ' at com.shop.service.FileService.uploadFile(FileService.java:15)
at com.shop.service.ItemImgService.saveItemImg(ItemImgService.java:30)' 이라고 적혀있어서 FileService 클래스의 15번 째 줄을 보았다. 

여기서 잠깐 정리하면 ItemImgService 의 30번 째 줄에서 FileService 15라인 의 uploadFile() 메서드를 호출했다는 의미이다.

 

FileService 15라인을 보면 originalFileName 문자열을 자르는 substring() 메서드가 사용되었고, 파라미터로 originalFileName.lastIndexOf(".")이 전달 되었다.

String extension = originalFileName.substring(originalFileName.lastIndexOf("."));

 

 

String 의 lastIndexOf() 메서드는 문자열의 뒤부터 인자로 전달된 문자를 찾아서 인덱스를 반환하는 메서드이다. 그래서 테스트 코드에서 처음 호출하는 코드를 찾았다. 이유는 테스트를 위해 전달해준 originalFileName에서 확장자를 구분하는 "."이 없어서 에러가 발생했다고 추론할 수 있기 때문이다.

 

최초 호출 지점은 가장 아래 쪽 at 부터 찾아 갈 수 있는데 아래서 4번째 줄을 보면, ItemServiceTest의 63번 라인에서 saveItem()에서 처음 호출된 걸 알 수 있다.

	at com.shop.service.ItemServiceTest.saveItem(ItemServiceTest.java:63)

 

 

63라인을 보면 이렇게 작성했는데 createMultipartFiles()는 테스트를 위해 만든 메서드로, Mock 객체로 Mock file을 객체를 전달해주는 메서드이다.

List<MultipartFile> multipartFileList = createMultipartFiles();
Long itemId = itemService.saveItem(itemFormDto, multipartFileList);

 

코드를 확인하고 createMultipartFiles() 메서드 내부를 확인해 보았다.

 

createMultipartFiles() 메서드

List<MultipartFile> createMultipartFiles() throws  Exception {
    List<MultipartFile> multipartFileList = new ArrayList<>();

    for(int i=0;i<5;i++){
        String path = "/tmp/item";
        String imageName = "image" + i + "jpg";
        MultipartFile multipartFile = new MockMultipartFile(path, imageName, "img/jpg", new byte[]{1,2,3,4});
        multipartFileList.add(multipartFile);
    }

    return multipartFileList;
}

 

메서든 내부를 보니 imageName에 "." 확장자 구분 문자열이 빠진걸 확인할 수 있었다.

 

 

문제 원인 발견 후 수정한 createMulipartFiles()

List<MultipartFile> createMultipartFiles() throws  Exception {
    List<MultipartFile> multipartFileList = new ArrayList<>();

    for(int i=0;i<5;i++){
        String path = "/Users/kim/myroot/tmp/item";
        String imageName = "image" + i + ".jpg";
        MultipartFile multipartFile = new MockMultipartFile(path, imageName, "img/jpg", new byte[]{1,2,3,4});
        multipartFileList.add(multipartFile);
    }

    return multipartFileList;
}

 

 

실행 결과

Comments