-
그래서 예외처리는요?Java & Kotlin 2021. 12. 5. 05:30반응형
1) 서론
개발자로서 취업 후 가장 많이 배우는 것 중 하나는 예외처리일 것 같습니다.
혼자서 개발할 때는 모든 상황의 예외를 파악할 수 있습니다. "이 부분에서는 절대로 에러가 나지 않는다"라는 확신이 들기도 합니다. 왜냐하면 혼자서 정해진대로 테스트를 진행하기 때문에, 예상 가능한 문제만 발생합니다.
하지만 실제 업무에서는 절대로 예상할 수 없습니다. 당연히 들어와야 하는 파라미터가, 그냥 안 들어오는 경우도 있습니다. 사용자는 정해진대로 움직이지 않고, 복잡한 시스템은 항상 예기치 못 한 에러를 만들어냅니다. 당연히 있어야 할 것이, 당연히 없는 경우가 대부분입니다.
신입으로서 코드를 작성할 때 가장 많이 들은 질문입니다. "그래서 예외처리는요?"
2) 고작 파라미터 받는데요?
이번 글에서는 흔하게 발생하는 NPE를 예로 들겠습니다.
public class ExceptionTest { public static void main(String[] args) { List<String> list = null; // null npeTest(list); // NPE 발생 } public static void npeTest(List<String> list) { String a = list.get(0); String b = list.get(1); } }
위는 npeTest()는 파라미터로 List 타입을 받고 있습니다.
하지만 정작 list 변수의 값으로 null이 입력됩니다. 그리고 실행하면 당연히 NPE가 발생합니다.
public void npeTest(User user) { int id = user.corp.id(); System.out.println(id); }
위는 다른 예시입니다.
user와 매핑된 법인의 id를 가지고 오려고 합니다. 이때 예상치 못 하게 해당 유저에 법인 정보가 매핑되지 않아, NPE가 발생할 수 있습니다.
현실 세계에서는 수많은 에러가 발생할 수 있습니다. 하지만 NPE 혹은 단순 문법 오류는 개발자의 실수라고 생각합니다. 그리고 이것은 발생시키면 안 된다고 생각합니다. 왜냐하면 충분히 방어 코드 혹은 예외처리를 통해서 방지할 수 있는 방법이 분명히 있기 때문입니다.
특히 NPE의 경우에는 어떤 것이 문제인지 파악하기 매우 어려운데요. 이는 아무런 에러 메시지가 없이, 단순히 NPE만 발생시키기 때문입니다. 위의 코드에서도 user, corp, id 어떤 것이 null인지 JVM이 출력해주는 메세지로는 알 수 없습니다.
이러한 이유 때문에 NPE는 반드시 방지해야 한다고 생각합니다.
openJDK 14에서는 NPE를 돕기 위해서 발생 시점, 이유가 메시지로 함께 나오게 개선되기도 했습니다.
3) 예외처리와 방어코드
사실 예외처리와 방어 코드에는 정답이 없다고 생각합니다. 상황에 따라 다양한 방법이 있을 것이라고 생각합니다.
3. 1) NPE 발생 시 정의된 에러 throw
public class ExceptionTest { public static void main(String[] args) { List<String> list = null; // null try { npeTest(list); } catch (NullPointerException e) { throw new CustomException(" 특정 로직에서 어떤 에러가 발생했습니다. "); } } public static void npeTest(List<String> list) { String a = list.get(0); String b = list.get(1); } }
Java 공식 문서를 확인하면 NPE는 객체를 호출할 때 null인 경우 발생합니다. 즉 null값이 들어있는 list를 호출할 때 NPE 발생합니다.
위의 코드는 try-catch문으로 감쌉니다.
단순히 에러를 발생시키는 것이 아닌, 직접 정의한 CustomException을 활용해 클라이언트 측에서 알 수 있는 에러를 throw 합니다.
단순히 NPE가 발생되고 로직이 끝나는 것이 아닌, 메시지와 함께 사전에 정의된 에러를 발생시킵니다. 덕분에 클라이언트(프런트엔드)측에서는 사전에 정의된 에러들에 대해서 대처할 수 있습니다. 이를 바탕으로 사용자에게 알맞은 안내 팝업을 띄울 수도 있습니다.
3. 2) Null이라면 다른 값 입력
public class ExceptionTest { public static void main(String[] args) { List<String> list = null; // null if (list != null) { // null이 아닐 때 npeTest(list); } else { // null인 경우 List<String> newList = new ArrayList<>(); newList.add("NPE 시 임의의 값 입력1"); newList.add("NPE 시 임의의 값 입력2"); npeTest(newList); } } public static void npeTest(List<String> list) { String a = list.get(0); String b = list.get(1); System.out.println(a); System.out.println(b); } }
if - else문을 추가해서 null임을 사전에 확인합니다.
즉 list가 null이라면, else문에서 새로운 newList 객체를 생성 후 값을 넣어줍니다. 최초에 원했던 값은 아닐 수 있으나, 어찌 됐든 NPE가 터져서 로직이 멈추는 일은 없습니다.
예를 들어 사용자가 회원가입을 하는데 중요하지 않은 정보이고 해당 값이 null이라면, NPE를 바로 던지는 것이 아닌, "" empty로 입력 후 추후에 사용자가 직접 수정하게 할 수도 있습니다.
3. 3) Constant를 활용한 NPE 방지
public class ExceptionTest { public static void main(String[] args) { String test = null; if (!test.equals("test")) { test = "test"; } npeTest(test); } public static void npeTest(String test) { System.out.println(test); } }
test 변수의 값이 "test"인지 비교를 해야 하는데, 애초에 test 변수가 null입니다. 그래서 호출할 때 NPE 발생합니다.
public class ExceptionTest { public static void main(String[] args) { String constant = "test"; String test = null; if (!constant.equals(test)) { // 옵션1. "test" 상수를 비교해서 NPE 방지 test = "test"; } npeTest(test); } public static void npeTest(String test) { System.out.println(test); } }
NPE를 방지하기 위해 constant 변수라는 상수를 만듭니다. 그리고 이것을 test 변수와 비교합니다.
첫 번째 코드와 다른 점은 constant 변수는 상수로서 항상 존재합니다. 그렇기 때문에 일치하지 않더라도 NPE가 발생하지 않고, if 문에서 false 후 body가 실행됩니다.
4) 결론
앞서 언급했듯이, 취업 전 혼자서 개발할 때는 예외처리에 대해서 진지하게 고민해보지 않았습니다. 직접 다 만들고, 예상 가능한 범위에서만 동작을 해봤기 때문입니다.
하지만 실제 업무에서는 정말 다양한 이유로, 다양한 에러가 발생합니다. 이때 예외를 처리하는 방법에는 정답이 없다고 생각합니다. 각각의 로직마다 의도하는 것이 다를 테니까요.
하지만 최소한 다양한 Exception이 발생할 수 있다는 것은 항상 인지한 채 코드를 작성해야 한다고 생각합니다.
반응형'Java & Kotlin' 카테고리의 다른 글
[Intellij] Google Java auto-formatting 적용 (0) 2022.02.07 Stream은 일회용품이다. (0) 2021.12.12 @Async, 생각보다 까다롭다. (2) 2021.11.01 인터페이스의 default, static 메소드 (0) 2021.10.06 과거의 나에게...(와인 검색 서비스 회고) (0) 2021.10.02