TIL - StackOverflowError | 코드를 간단하게 | 동작 지연

su·2023년 6월 1일
0

TIL

목록 보기
13/93
post-thumbnail

문제1 - 추가 기능 구현하던 중 오류

1) 문제

아주 사소한 문제가 있었다.
추가 기능 구현 중에, 현재까지 판매된 누적 상품들을 출력하는 기능이 있었는데
그 기능 구현을 위해서 sellList라는 리스트와 getSellList()이라는 메소드를 만들었다.
그리고 해당 리스트에 상품들을 더할 수 있도록 addSellList() 메소드도 만들었다.

2) 시도

더한 내용을 출력해주기만 하면 되어서,
만들어둔 메소드를 원하는 부분에 호출해주었다.

List<Goods> goodsList = order.getSellList();                                   
for (Goods item: goodsList) {
System.out.printf("%-25s %5s %s\n", "- "+item.getName(), "|", "W " + item.getPrice()); 
}

코드를 작성하고 실행했는데 오류가 발생했다.

Exception in thread "main" java.lang.StackOverflowError at Order.getSellList(Order.java:14)

3) 해결

오류 내용을 찾아보니, 잘못된 재귀 메소드를 정의했을 경우 발생되는 오류라고 한다.
종료조건이 만족되지 않으면 계속해서 메소드가 실행되는 상황이 발생하기 때문인데,
나는 ,, getSellList()에서 그냥 sellList를 return 해준 것 밖에 없는데 ?
라고 생각하고 돌아가서 다시 코드를 살펴보니

public List<Goods> getSellList() { return getsellList(); } 

이렇게 되어있었다 ..
빠르게 기능을 구현하고 싶은 마음에 서둘렀던 건지 ^^ ,,
당연히 저런 내용의 오류가 발생할 수밖에 없다.
나는 재귀 메소드를 만든건데, 종료 조건이 없이 계속 같은 메소드를 호출하게 되어있으니까 ..

public List<Goods> getSellList() { return sellList; } 

내가 작성하고 싶었던 코드는 이거다.
코드를 수정하지 원하는 대로 잘 작동하는 모습을 볼 수 있었다

4) 배운 점

StackOverflowError라는 에러를 알게 되었다.
재귀 메소드를 아직 사용해보지 않아서 저 에러를 처음 접했는데,
의도치 않게 재귀 메소드를 만들어버려서 에러 종류를 하나 배우게 되었다.
StackOverflow가 너무 많이 쌓인다는 내용의 오류 같은데,
안에 종료 조건이 없이 계속 반복해서 자기 자신을 호출하는 형태로 작성하면 발생하는 오류같다.

다음에 재귀 메소드를 사용할 일이 있다면, 저 오류가 나지 않도록
종료조건을 잘 설정해주어야 겠다 !

문제2 - 코드 길이를 줄여보자

1) 문제

추가 기능까지 구현하고 나서 보니, 코드가 세상 정신없고 지저분했다.
읽기 쉽게 내용을 좀 정리하고, 주석도 달아주었는데 그래도 뭔가 정돈이 안된 느낌이었다.

그 중에서 getGood()라는 메소드와 getGood2() 메소드에서 겹치는 부분 때문에 고민이었다.

2) 시도

1) getGood2() 메소드

// getGood2() - 선택한 메뉴를 장바구니에 추가할 것인지 판단
public void getGood2(Goods goods) throws InterruptedException {
    System.out.printf("%-25s %s %5s %s %s", "\""+goods.getName(), "|", "W " +goods.getPrice(), "|", goods.getDetail()+"\"");   // 입력받은 상품 그대로 출력하기
    System.out.println("\n위 메뉴를 장바구니에 추가하시겠습니까?");
    System.out.printf("%-2s %-7s %-2s %-7s\n", "1.", "확인", "2.", "취소");
    String call = sc.nextLine();
    if ("1.확인".contains(call)) {                     // 1.확인을 눌렀다면
        System.out.print("수량을 입력해주세요: ");      // 상품 수량을 입력받음
        int i = sc.nextInt();
        sc.nextLine();
        System.out.println(goods.getName() + " 가 장바구니에 추가되었습니다.");
        goods.setNumber1(i);                           // 해당 goods 객체 안의 상품 개수를 입력받은 수량으로 set 해주는 메소드 호출
        order.addOrderList(goods, 1, i);               // 그 후 장바구니 리스트(Order 클래스의 orderList)에 담을 메소드 호출
        mainMenu();
    } else {                                           // 2. 취소를 눌렀다면
        mainMenu();                                    // 메인 메뉴로 돌아가기
    }
} // getGood2()

먼저 입력받은 상품의 이름과 가격을 출력해준다.
그리고 상품을 장바구니에 추가하겠다고 입력받으면 수량을 입력받고, 해당 상품의 수량을 설정해주고, 장바구니 리스트에 저장해준다.

2) getGood() 메소드

// getGood() - 선택한 메뉴의 사이즈를 입력받아 장바구니에 추가할 것인지 판단
public void getGood(Goods goods) throws InterruptedException{
    System.out.printf("%-25s %s %5s %s %s", "\""+goods.getName(), "|", "W " +goods.getPrice(), "|", goods.getDetail()+"\"");                    // 입력받은 상품 그대로 출력하기
    System.out.println("\n위 메뉴의 어떤 옵션으로 추가하시겠습니까?");
    System.out.printf("%-2s %-15s %-2s %-15s\n", "1.", "Small(W " + goods.getPrice() + ")", "2.", "Regular(W " + goods.getPrice2() +")");  // 1.Small(가격) 2.Regular(가격) 출력
    String s = sc.nextLine();
    if (s.contains("Small") || s.contains("1") || s.contains("small")) {                                                                        // 1.Small을 입력했다면
        System.out.printf("%-25s %s %5s %s %s", "\""+goods.getName(), "|", "W " +goods.getPrice(), "|", goods.getDetail()+"\"");                // 입력받은 상품 출력하기
        System.out.println("\n위 메뉴를 장바구니에 추가하시겠습니까?");
        System.out.printf("%-2s %-7s %-2s %-7s\n", "1.", "확인", "2.", "취소");
        String call = sc.nextLine();
        if ("1.확인".contains(call)) {                     // 1.확인을 눌렀다면
          System.out.print("수량을 입력해주세요: ");        // 상품 수량을 입력받음
          int i = sc.nextInt();
          sc.nextLine();
          System.out.println(goods.getName() + " 가 장바구니에 추가되었습니다.");
          goods.setNumber1(i);                            // 해당 goods 객체 안의 상품 개수를 입력받은 수량으로 set 해주는 메소드 호출
          order.addOrderList(goods, 1, i);                // 그 후 장바구니 리스트(Order 클래스의 orderList)에 담을 메소드 호출
        }
    } else if ((s.contains("Regular") || s.contains("2") || s.contains("regular"))){                                                            // 2.Regular를 입력했다면
        System.out.printf("%-25s %s %5s %s %s", "\""+goods.getName2(), "|", "W " +goods.getPrice2(), "|", goods.getDetail()+"\"");              // 찾아낸 Regular 상품 출력하기
        System.out.println("\n위 메뉴를 장바구니에 추가하시겠습니까?");
        System.out.printf("%-2s %-7s %-2s %-7s\n", "1.", "확인", "2.", "취소");
        String call = sc.nextLine();
        if ("1.확인".contains(call)) {                     // 1.확인을 눌렀다면
          System.out.print("수량을 입력해주세요: ");        // 상품 수량을 입력받음
          int i = sc.nextInt();
          sc.nextLine();
          System.out.println(goods.getName2() + " 가 장바구니에 추가되었습니다.");
          goods.setNumber2(i);                            // 해당 goods 객체 안의 상품 개수를 입력받은 수량으로 set 해주는 메소드 호출
          order.addOrderList(goods, 2, i);                // 그 후 장바구니 리스트(Order 클래스의 orderList)에 담을 메소드 호출
        }
    } // if ~ else 종료
    mainMenu(); // 다시 메인 메뉴로 돌아가기
} // getGood()

getGood2()와의 차이점은 옵션을 입력할 수 있다는 점이다.
상품의 내용을 우선 출력해주고, 가격을 출력해준다.
그리고 옵션을 선택할 수 있는 선택지를 보여준다.
Small을 선택한 경우는, 원래 상품을 다시 출력해주고, 장바구니에 더한다.
Regular를 선택한 경우에는, Regular 옵션에 해당하는 이름과 가격을 출력해주고,
그 옵션에 해당하는 상품의 개수 값을 설정 후, 상품을 장바구니에 추가한다.

다시 보니, Small를 선택한 경우가 getGood2() 메소드와 완전히 동일하다는 것을 알게 되었다.

3) 해결

// getGood() - 선택한 메뉴의 사이즈를 입력받아 장바구니에 추가할 것인지 판단
public void getGood(Goods goods) throws InterruptedException{
    System.out.printf("%-25s %s %5s %s %s", "\""+goods.getName(), "|", "W " +goods.getPrice(), "|", goods.getDetail()+"\"");                    // 입력받은 상품 그대로 출력하기
    System.out.println("\n위 메뉴의 어떤 옵션으로 추가하시겠습니까?");
    System.out.printf("%-2s %-15s %-2s %-15s\n", "1.", "Small(W " + goods.getPrice() + ")", "2.", "Regular(W " + goods.getPrice2() +")");  // 1.Small(가격) 2.Regular(가격) 출력
    String s = sc.nextLine();
    if (s.contains("Small") || s.contains("1") || s.contains("small")) {                                                                        // 1.Small을 입력했다면
        getGood2(goods)
    } else if ((s.contains("Regular") || s.contains("2") || s.contains("regular"))){                                                            // 2.Regular를 입력했다면
        System.out.printf("%-25s %s %5s %s %s", "\""+goods.getName2(), "|", "W " +goods.getPrice2(), "|", goods.getDetail()+"\"");              // 찾아낸 Regular 상품 출력하기
        System.out.println("\n위 메뉴를 장바구니에 추가하시겠습니까?");
        System.out.printf("%-2s %-7s %-2s %-7s\n", "1.", "확인", "2.", "취소");
        String call = sc.nextLine();
        if ("1.확인".contains(call)) {                     // 1.확인을 눌렀다면
          System.out.print("수량을 입력해주세요: ");        // 상품 수량을 입력받음
          int i = sc.nextInt();
          sc.nextLine();
          System.out.println(goods.getName2() + " 가 장바구니에 추가되었습니다.");
          goods.setNumber2(i);                            // 해당 goods 객체 안의 상품 개수를 입력받은 수량으로 set 해주는 메소드 호출
          order.addOrderList(goods, 2, i);                // 그 후 장바구니 리스트(Order 클래스의 orderList)에 담을 메소드 호출
        }
    } // if ~ else 종료
    mainMenu(); // 다시 메인 메뉴로 돌아가기
} // getGood()

겹치는 부분에 그냥 getGood2()를 넣어주면 된다.

4) 배운 점

우선 기능 구현을 하고자 코드를 쭉 작성해보았는데,
다시 읽어보니 코드를 간결하게 작성할 수 있는 방법들이 있었다.

겹치는 코드들이 있으면 그대로 두지 말고 간결하게 작성할 수 있는 방법을 생각해보는 것이 좋은 것 같다.

개인 과제 코드 정리 - https://azuressu.github.io/first/

동작을 지연하는 코드

개인 과제의 조건 중에, 다시 메인 메뉴판으로 돌아가기 전 3초를 기다렸다가 돌아가야 하는 조건이 있었다.
동작을 실행하기 전에 지연할 수 있는 코드가 있다. 예외 처리도 해주어야 한다.

1) Thread.sleep()

sleep() 안에 원하는 시간을 넣어주면 된다. 단, 시간은 밀리세컨드 단위로 넣어주어야 한다.

try {
	Thread.sleep(3000);                   // 3초를 지연시킴
} catch (InterruptedException e) {
	System.out.println(e.getMessage());   // 예외 메시지 출력
}

2) TimeUnit.SECONDS.sleep()

try {
	TimeUnit.SECONDS.sleep(3);            // 3초를 지연시킴
} catch (InterruptedException e) {
	System.out.println(e.getMessage());   // 예외 메시지 출력
}

만약 몇 분을 지연시키고 싶다면 SECONDS 대신 MINUTES를 넣어주면 된다고 한다.
둘의 코드는 딱히 .. 차이가 없는 듯 하다 !

출처: https://codechacha.com/ko/java-add-delay-few-seconds/

문자열 클래스 - String, StringBuffer, StringBuilder

  • String
    • 불변한다는 특징이 있고, 문자열을 조작하는 경우 유용하게 사용 가능
    • +로 다른 문자열을 붙여도 기존 문자열에 새로운 문자열을 붙이는 것이 아닌, 새로운 String 객체를 생성하여 생성된 String 객체에 연결된 문자열을 저장하고, 그 객체를 참조
  • StringBuffer
    • 멀티 스레드 환경에서도 동기화를 지원
    • 안전한 프로그램이 필요한 경우
  • StringBuilder
    • 멀티 스레드 환경에서 동기화를 보장하지 않음
    • 안전 여부와 관계 없는 프로그램을 개발하는 경우

사실 String을 제외하고 StringBuffer나 StringBuilder는 사용해본 적이 없어서 생소했다.
익숙해지려면 코드를 작성해서 익혀야 할 것 같다.

출처: https://12bme.tistory.com/42

profile
(❁´◡`❁)

0개의 댓글