4번을 선택하면 print 내용이 출력되고 nextLine()에서 입력을 받은 값을 recheck변수에 넣어 endProgram 함수에서 최종 종료
확인을 받아야 하는데 바로 프로그램이 종료되는 이슈.


nextLine입력안했는데 넘어간것처럼 보이는 이유
scanner는 입력을 읽을 때 줄바꿈을 포함해서 모든 입력을 처리한다. 하지만 nextInt()나 nextDouble() 같은 메서드는 버퍼가 숫자만 읽고 줄바꿈 문자를 버퍼에 남겨놓는다! 그래서 다음 nextLine()이 호출되면 남아 있던 줄바꿈 문자를 읽어서 입력을 제대로 받지 못함.
결국 빈 문자열을 반환하는 것.

결국에는 내 코드에서는 첫번째 choice에서 숫자를 입력하니 아직 버퍼에는 줄바꿈 문자가 남아있었던 것이다.
그래서 nextInt() 메서드를 사용한 뒤 바로 다음 코드에서 nextLine() 메서드를 사용한 후 버퍼에있는 줄바꿈 문자를 날려줬다.
적용 후, 결과 확인

이제 슬슬 LV2를 마무리하며 LV3로 넘어가려고 했다.. 하지만 내가 자바 공부를 여태 똑바로 하지 않은 것인가 Enum, Generic, 람다, 스트림을 도입해야하는데 처음에는 머릿속으로 "아 이렇게 하면 되겠네!" 했지만 실상은 문법도 제대로 몰랐던 나였다.. 그래서 목요일부터 다음주 수요일까지 제출이니 하루에 하나씩 공부하고 적용해보자 계획을 세웠다.
Enum에 들어가며 공부를 하고 나니 10시에 시작한 공부가 점심 시간을 제외하고 3시인가 4시 정도에 끝이났다. 어느정도 이해한 상태로 코드를 작성하려 했는데 아뿔싸 손도 안가고 뭐라도 끄적거렸는데 적용이 잘 안됐다 ㅎㅎㅎ!! 하지만 정리해놓은 내용을 유심히 살펴보고 계속 시도를 했다. 근데 Lv3 과제 Enum 도입에서 "Enum 타입을 활용하여 연산자 타입에 대한 정보를 관리"를 하라했다. 근데 난 솔직히
의아했다. 왜 연산자 타입에서 관리만할까? 그냥 Enum 클래스 안에 연산자를 가지고 연산을 하면 안될까? 하고
public enum OperatorType {
PLUS("+"),
SUB("-"),
MULTI("*"),
DIVIDE("/");
private final String operate;
OperatorType(String operate) {
this.operate = operate;
}
public String getOperate() {
return operate;
}
~계산기능~
}
그래도 아직 나는 배우는 상태이기에 더 효율적인 방법이 있을거라 생각해서 튜터님을 찾아갔다.
튜터님이 말씀하시기를 "이게 Enum 클래스 안에서 연산을 해도 되지만 비추 드린다, 자신이 실무에 있을 때도 회사에서 Enum 클래스에 로직 넣는 걸 하지 않았다고.. 넣는 사람도 있었지만 가독성이 떨어진다" 라고 하셨다. 그래서 나는 깔끔하게 내 생각을 버렸다 ㅋㅋㅋ
그래서 다시 자리로 돌아와 어떻게 할까.. 계속 고민했다 진짜 2시간 이상은 고민만 한 것 같았다.
그리고 저녁을 먹고 결국 생각해낸 것이 사용자에게 입력 받은 연산 기호를 Enum클래스에서 Enum에 있는 상수로 바꿔서 반환 시키는 메서드를 만드는 것이었따.
public enum OperatorType {
PLUS("+"),
SUB("-"),
MULTI("*"),
DIVIDE("/");
private final String operate;
OperatorType(String operate) {
this.operate = operate;
}
public String getOperate() {
return operate;
}
public static OperatorType fromStringToOperatorType(String operate) {
for (OperatorType value : OperatorType.values()) {
if (value.getOperate().equals(operate)) {
return value;
}
}
return null;
}
}
일단 예외처리는 나중에 생각하자 해서 Enum 클래스를 사용하는 곳에서 null 체크만 간단히 해줬다.
//CalculatorClass
public void calculate(String x, String operator,String y) {
int result;
OperatorType operatorType = OperatorType.fromStringToOperatorType(operator);
if (operatorType != null) { // fromStringToOperatorType 메서드에 반환값이 null도 있으니 null 체크
switch (operatorType) {
case PLUS -> {
result = x + y;
System.out.println(result);
}
case SUB -> {
result = x - y;
System.out.println(result);
}
case MULTI -> {
result = x * y;
System.out.println(result);
}
case DIVIDE -> {
if (y == 0) {
System.out.println("두 번째 수는 0이 될 수 없습니다.");
}
result = x / y;
System.out.println(result);
}
}
}
일단 x, y, result 타입들은 무시해주세여.. 제네릭때 바꿀 예정입니다.
진짜 개고생했다.. 솔직히 저 당시 해탈의 경지에서 코드를 작성했는데 정신 차리고 보니 내가 Enum에 대해서 제대로 알고 쓴 건가? 일단 쓰고 에러 안나니깐 다 됐다고 생각하는건가..? 아직도 나에게 너무 의심이 가는 하루였다. 뭔가 아직도 찝찝하다..
원래 오늘 제네릭을 공부해서 제네릭을 도입할 생각이었는데 이 부분은 도저히 안되겠다는 생각이 났다.. 5시간 정도 고민해봤는데도 내 계산기 코드를 더 망치는 느낌이 나기도 하고 어떻게 접근해야할까? 라는 생각만 머리 속에 맴돌았다. 그래서 일단 제네릭은 냅두고 이제 남은 람다와 스트림을 사용한 특정 조회 기능을 추가하려한다. 하지만 이 기능을 Calculator 클래스에 추가하려했는데 이러다 보니 연산만 하는 클래스가 연산뿐만 아니라 스택에 추가하고 조회, 삭제, 등등 다른 기능도 같이하고 있어서 가독성이 떨어진다 보았고, 무엇보다도 나는 java 코드를 짜는데 객체지향의 설계와는 거리가 멀어진다고 생각했다. 내가 이 생각할 정도면 다른 사람이 보면 경악을 금치 못할 것이다.. 그래서 찾아보니 책임을 분리하는 단일 책임 원칙(SRP)라는 객체지향 원칙이 있었다. 그래서 일단 기능을 추가하기 전에 연산과 결과값들 을 관리하는 기능들을 클래스로 분리하기로 했다. 코드를 생각나는대로 짜서 아마 대공사가 되지 않을까싶다..
public class CalculatorClass {
private Stack<Integer> resultStack = new Stack<>();
~~ 계산 로직 ~~
// 과거 결과 값 조회 코드
public Stack<Integer> getResult() {
return resultStack;
}
// 과거 결과 값 변경 코드
public void setResult(Stack<Integer> resultStack) {
this.resultStack = resultStack;
}
// 제일 최근 결과 값 1개 삭제 코드
public void removeResult() {
resultStack.pop();
System.out.println("최근 한 건 내역이 삭제 되었습니다." + getResult());
}
}
리팩토링 전 코드는 이랬다. 계산 하는 클래스에서 이러한 작업들까지 하니 너무 복잡해져서 가독성이 떨어지는 상태였다.
package lv3;
import java.util.Stack;
public class ResultStorage {
private Stack<Integer> stack = new Stack<>();
// 결과 추가
public void addResult(int result) {
stack.push(result);
}
// 결과 내역 조회
public Stack<Integer> getResult() {
return stack;
}
// 결과 내역 중 최근 1건 삭제
public void removeResult() {
if (stack.isEmpty()) {
stack.pop();
System.out.println("삭제가 완료 되었습니다.");
} else {
System.out.println("결과 내역이 존재하지 않습니다.");
}
}
// 과거 결과 값 변경 코드
public void setResult(Stack<Integer> resultStack) {
this.stack = resultStack;
}
}
그래서 일단 결과값 관리 하는 클래스를 따로 빼줬다. 계산 클래스도 깔끔해져서 그래도 한 눈에 보기 편했다.
근데 문제가 있었다.. 이렇게 빼주고 나니깐 CalculatorClass에서 ResultStorage 클래스를 인스턴스화 시키고 연산 후 결과 값들도 push를 잘 해서 넣어줬는데 main 클래스에서 조회를 하려하니깐 자꾸 빈 배열이 뜨는게 아닌가..
근데 진짜 이걸 1시간 동안 고민했다.. 보니깐 main 클래스에서도 ResultStroage 클래스를 새롭게 또 인스턴스화 해버렸다.. 이러니
결과값 인스턴스가 2개나 만들어지면서 빈배열이 조회된거지... 아마 CalculatorClass에서는 잘 저장되었을거다.
즉, CalculatorClass 내부에서 사용하는 ResultStorage 객체와 CalculatorMain에서 사용하는 ResultStorage 객체가 서로 다르므로, CalculatorClass에서 추가된 값이 CalculatorMain에서 조회되지 않는 것...
그래도 자바를 기본은 안다 생각했는데 이렇게 기본도 안되어있다.. 현재 토요일 아침 7시인데 이번 주말은 레벨3를 하는 것 보다는 자바
객체 공부를 더 해야겠다는 생각이 든다 ㅠ
ResultStrorage와 CalculatorClass를 Main에서 생성해줘서 ResultStorage를 CalculatorClass에 객체를 주입해주었다.
// main
public class CalculatorMain {
public static void main(String[] args) {
ResultStorage resultStorage = new ResultStorage();
CalculatorClass calculatorClass = new CalculatorClass(resultStorage);
}
// CalculatorClass
public class CalculatorClass {
ResultStorage resultStorage;
public CalculatorClass(ResultStorage resultStorage) {
this.resultStorage = resultStorage;
}
}
Main 클래스에서 new ResultStorage() (주소값 예시. x001)을 생성해주고 이 객체를 new CalculatorClass 생성자에 주입해준다.
1. (Main) new Calculator(x001) ->
2. (CalculatorClass) public CalculatorClass(ResultStorage resultStrorage = x001) {
this.resultStorage = x001;
}
3. (CalculatorClass) ResultStorage resultStorage = x001;
4. 이렇게되면서 CalculatorClass에서도 Main 클래스에서 만든 resultStrorage를 공유 받을 수 있다.
람다와 스트림 도입
https://1000-e.tistory.com/31 람다와 스트림을 공부한 후 프로젝트에 람다와 스트림을 도입해보기로했다.
람다와 스트림 자체가 너무 낯설어서 공부하면서 꽤 애먹었지만 계속 찾아보면서 하니깐 괜찮긴했다..! 이것도 다른 문법 처럼 계속 하다보면 적응하겠지..?
// 특정 값 보다 큰 값들 나열
public void getLargestNumbers(Scanner scanner) {
System.out.print("특정 값을 입력해주세요 : ");
int specificValue = scanner.nextInt();
scanner.nextLine();
System.out.println("특정 값보다 큰 수들을 나열하겠습니다.");
List<Integer> filterNumbers = stack.stream()
.filter(numbers -> numbers > specificValue)
.collect(Collectors.toList());
if (filterNumbers.isEmpty()) {
System.out.println("특정 값보다 큰 수가 없습니다.");
} else {
System.out.println( specificValue +" 값보다 큰 수 : " + filterNumbers);
}
}
이건 실제 과제에서 주어진 필수 기능이기에 예외처리도 해주었다.
람다와 스트림 자체가 어색해서 연습겸 손에 익을라고 나는 기능들을 추가해주기로 했다.
기능들은 람다와 스트림만 온전히 연습하려고 예외 처리는 해주지 않았다.
//결과 내역 중 중복 값들 제외
public void getDistinctResults() {
List<Integer> distinctNumbers =stack.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("중복 값 제외 결과 내역입니다." + distinctNumbers);
}
//결과 내역들을 전부 더하는 기능
public void getResultSum() {
int sum = stack.stream()
.reduce(0, Integer::sum);
System.out.println("결과 내역들의 총 합입니다 : " + sum);
}
//결과 내역 중 중복을 제거하고 전부 더하기
public void getDistinctResultSum() {
Integer sum = stack.stream()
.distinct()
.reduce(0, Integer::sum);
System.out.println("결과 내역 중 중복을 제외한 총 합입니다 : " + sum);
}
//결과 내역 중 중복을 제거하고 정렬하기
public void getDistinctSort() {
List<Integer> sorted = stack.stream()
.distinct()
.sorted()
.collect(Collectors.toList());
System.out.println("결과 내역 중 중복을 제외한 정렬입니다. " + sorted);
}
//결과 내역 중 중복을 제거하고 짝수만 출력
public void getDistinctEven() {
List<Integer> distinctEven = stack.stream()
.distinct()
.filter(result -> result % 2 == 0)
.collect(Collectors.toList());
System.out.println("결과 내역 중 중복을 제거하고 짝수만 출력합니다. " + distinctEven);
}'Project' 카테고리의 다른 글
| (Java / Spring) TossPayments PG 윗젯 Version2 연동 (0) | 2025.02.14 |
|---|---|
| JPA를 활용한 일정 앱 프로젝트 트러블 슈팅 및 회고 (0) | 2024.12.12 |
| 일정 관리 앱 프로젝트 트러블 슈팅 및 회고 (2) | 2024.12.09 |
| 키오스크 프로젝트 마무리 및 회고(feat.의존성) (2) | 2024.11.28 |
| 키오스크 프로젝트 트러블 슈팅 및 회고 (0) | 2024.11.22 |