본문 바로가기

Development/Java

(객체지향 기초) Java로 계좌 관리 프로그램 만들기(2)

발생한 문제와 해결과정

 

 

 

Java와 객체지향 초보다 보니 기초적인 내용임에도 작업 과정에서 문제가 많이 발생했는데, 해결 과정에서 시간이 너무 오래 걸리게 되었다. 그래서 일단 그냥 넘어갔다 실력이 쌓이고 다시 풀어 보려고 했으나 생각을 바꿔 끝까지 완성하게 되었다. 넘어가면 당장의 시간은 덜 쓰겠지만, 확실히 학습하지 못한 개념은 이후의 학습 과정에 장애 요소가 되어 결과적으로 시간을 더 소모하게 될 수 있다. 다 적으면 끝도 없겠지만, 복습 과정에서 활용할 수 있도록 실행 과정에서 발생했던 문제 위주로 간략하게 적어 보겠다.

 

 

 

nextInt() 메소드를 사용했을 때 입력창이 skip되는 문제

 

 

 

 

위와 같이 계좌번호를 입력하지 못하고 계좌 수를 입력하는 창이 바로 나오게 되는데, 검색을 통해 이게 nextInt 메소드의 문제라는 사실을 알게 되었다. nextInt 메소드는 enter키를 입력받지 않고 다음으로 넘겨 다음의 nextLine 메소드에서 enter키가 입력되게 된다고 한다.

 

해결법은 nextLine 메소드로 입력하고 parseInt 메소드를 통해 정수형으로 변환해 주는 방법과 내가 한 것처럼 그냥 숫자를 String 타입으로 사용하는 방법이 있을 것이다.

 

해결하는 데 오래 걸렸던 문제는 아니다. 위의 throws Exception은 아래 발생한 문제를 해결할 때 이것저것 시도해 보면서 입력하게 된 것이다.

 

 

NullPointerException

 

 

이것이 진짜 문제다. 이 문제를 해결하는 데에 많은 시간을 소모했다.

 

 

 

 

드디어 완성했나 싶은 기쁨도 잠시, 이 NullPointerException은 생각보다 크게 발목을 잡았다. 사실 실행하기 전에도 NullPointerException이 발생할 거라는 경고는 읽었다. 금방 해결할 수 있을 줄 알고 크게 대수롭지 않게 생각했으나 도무지 해결이 되지 않았다. 괜히 자주 발생한다는 예외가 아닌 것 같다.

 

 

 

 

if 조건식에 null이 있어서 그런가 싶어 getAccountNum 메소드와 equals 메소드를 이용해 봤는데, 될 리가 없다. 초기값이 모두 null인 배열에서 method 호출하는 과정 자체에 문제가 있는 것으로 보인다. 오히려 NullPointerException의 발생이 앞으로 당겨져 버렸다.

 

 

 

 

getAccountNum 메소드의 길이를 이용해 봤다. 계좌번호가 7자리이니 7자리가 아닌 경우 조건문을 시작하는 방식이다. 역시 account[i]의 값이 null이라 안 되는 것으로 보인다.

 

 

 

 

아직 학습하지는 않았지만 책에서 예외처리 부분을 찾아서 적용해 봤다. 이번에는 될 줄 알았는데 안 된다... 어차피 뒷부분도 전부 NullPointerException에 걸려 있다. 가장 먼저 나온 놈이 매를 맞고 있을 뿐이다.

 

 

 

 

if 조건식을 변경하고 try를 조건문 안에 삽입했다. finally까지 넣어 본 모습이다. 안 된다.

 

 

 

 

이걸 해결하기 위한 과정에서 조건문은 사라지고 이전 글에 나왔던 numberOfAccounts 변수의 카운트를 올리는 방식으로 바뀌었다. 조건식이라 예외처리가 잘 작동을 안 하나 싶었다. 하지만 그렇지 않다ㅠㅠ

 

 

 

 

여기서 assert account[numberOfAccounts] != null 를 넣어 보라고 하는 제안이 눈에 들어왔다. 그렇게 솔깃할 수가 없다. 이게 바로 내가 찾던 거다. 가장 큰 문제는 Account 타입 배열 초기값을 강제로 넣어줄 수 없다는 거였다. 도저히 null이 아니게 만들어 줄 수가 없었다. 그런데 이 놈은 null이 아니라고 설득을 해 보라는 것이다.

 

 

 

 

이거 하나 넣었을 뿐인데 신기하게도 NullPointerException 경고가 사라졌다. 정말 기뻤다.

 

 

 

 

하지만 기쁨은 오래가지 않았다. 그냥 빛 좋은 개살구였을 뿐이다. 이 놈은 그냥 NullPointerException이 아닌 척만 해 줬던 것이다... 본질은 바뀌지 않는다.

 

이 외에도 다양한 방법을 시도했으나, 아무리 해도 먹히는 방법이 없어 결국 공식문서를 찾아보게 되었다.

 

 

 

 

약간 의역해 보면 다음과 같다.

 

1. null 객체의 intstance method를 호출할 때

2. null 객체의 field에 접근하거나 field를 변경하려 할 때

3. 값이 null인 배열의 길이를 산출하려 할 때

4. 값이 null인 배열의 원소에 접근하거나 변경하려 할 때

5. null을 직접 발생시키려 할 때

 

4번에서 발생한 문제가 얼핏 내 문제인 것 같지만 내가 겪는 문제와 다르다. 4번은 배열 변수 자체의 초기값이 null인 경우를 언급하는 듯하다. 사실상 배열이 아닌 것으로 취급하고 있는데, 원소를 넣을 수 없고 길이도 변경할 수 없으니 그럴 만하다. 배열의 길이가 없으니 길이를 산출하거나 특정 index에 접근할 수가 없다. 내 경우는 배열의 길이는 100으로 설정됐으나 원소의 값만 null인 상황이다. 호출하려는 method가 instance는 맞지만 null 객체에 속한 것도 아니다.

 

내 상황은 위의 케이스에 해당하지 않고, 그냥 배열 원소의 값이 null인 상황에서 직접 대입하지 않고 method 호출해서 값을 대입하려 하면 NullPointerException이 발생하는 모양이다. 아마 객체 배열은 생성됐지만, 원소 각각의 객체는 생성되지 않아 참조하는 객체가 없어 method를 호출하지 못하는 것 같다. 나는 입력값 조정을 위해 반드시 Setter를 null인 배열에 호출해야만 하고, null이라 메소드 호출이 안 되는데 Account 타입이라 null일 수밖에 없으니 대체 어떻게 하란 말인가?

 

 

Account accountInfo = new Account();
accountInfo.setAccountNum(accountNumInput);
accountInfo.setName(nameInput);
accountInfo.setDeposit(depositInput);

account[numberOfAccounts] = accountInfo;

 

 

결국 해결한 방법은 위에서 본 것처럼 새로 객체를 생성해서 이걸 이용해 Setter 호출한 뒤, account 배열의 각 원소마다 저장된 값을 직접 대입해 주는 것이다. 이렇게 하면 null 값을 가진 배열의 원소가 직접 method를 호출하는 일을 피할 수 있다.

 

조금 더 구글링을 해 보니 역시 객체 배열의 경우 각 원소마다 객체 생성해 줘야 한다고 한다. 그렇다면 다음과 같이 설계해 볼 수도 있을 것이다.

 

 

public Account(String accountNum, String name, String balance) {
}

 

 

우선 Account 클래스에서 생성자를 선언한 뒤,

 

 

account[numberOfAccounts] = new Account("A", "A", "A");
account[numberOfAccounts].setAccountNum(accountNumInput);
account[numberOfAccounts].setName(nameInput);
account[numberOfAccounts].setDeposit(depositInput);

 

 

BankApplication 클래스의 1번 계좌 생성에서 임의 객체를 생성하고 다시 Setter 메소드를 호출해 값을 저장하는 것이다.

 

 

int i = 0;
while (i < numberOfAccounts) {
    System.out.print(account[i].getAccountNum() + "    ");
    System.out.print(account[i].getName() + "    ");
    System.out.println(account[i].getBalance());
    ++i;
}

 

 

2번 계좌 목록에서는 위와 같이 while의 조건식을 바꾸면 될 것이다.

 

예상대로 잘 작동한다.

 

이제 막 객체지향을 공부하기 시작한 초보라 발생할 수 있었던 해프닝이다. 이런 경험도 해당 시기에만 할 수 있는 것이므로, 투입된 시간을 아까워하기보다는 소중한 경험으로 여기고 더욱 발전하는 계기로 삼을 것을 다짐해 본다.