JavaScript

웹 개발의 필수 언어

동적인 웹 페이지 구현을 위한 핵심 프로그래밍 언어.

Java

객체지향 프로그래밍

안정적이고 확장성 있는 백엔드 개발의 대표 언어.

HTML

웹의 기초

웹 페이지의 구조를 정의하는 마크업 언어.

React

현대적 UI 라이브러리

효율적인 사용자 인터페이스 구축을 위한 JavaScript 라이브러리.

CSS

웹 디자인의 핵심

웹 페이지의 시각적 표현을 담당하는 스타일 언어.

Spring

자바 웹 프레임워크

기업급 애플리케이션 개발을 위한 강력한 프레임워크.

Java/실습 예제

계산기 만들기

lamarcK 2025. 5. 22. 00:50

인터페이스 정의 단계

전체적인 구현을 하는 단계

사용하려는 메서드 등 필요한 기능들을 미리 정의

interface Calculator {
    double add(double num1, double num2); //더하기
    double subtract(double num1, double num2); //빼기
    double multiply(double num1, double num2); //곱하기
    double divide(double num1, double num2) throws ArithmeticException; //나누기
}

계산 메서드용 클래스(BasicCalculator)

간단한 사칙 연산 구현

class BasicCalculator implements Calculator {
    @Override
    public double add(double num1, double num2) {
        return num1 + num2;
    }

    @Override
    public double subtract(double num1, double num2) {
        return num1 - num2;
    }

    @Override
    public double multiply(double num1, double num2) {
        return num1 * num2;
    }

    @Override
    public double divide(double num1, double num2) throws ArithmeticException {
        if (num2 == 0) {
            throw new ArithmeticException("0으로 나눌 수 없습니다.");
        }
        return num1 / num2;
    }
}

@Override 애너테이션(Annotation)은 "이 메서드는 부모 클래스의 메서드를 재정의했다"는 걸 표시하는 일종의 라벨이다. 없어도 상관은 없다. 하지만 @Override를 명시해두면 컴파일 단계에서 아래 기능을 수행하기 때문에 실수 방지나 코드의 가독성 향상 측면에서 사용하는 것이 권장된다.

  1. 상위 타입(부모 클래스/인터페이스)에 해당 메서드가 실제로 존재하는지 검사
  2. 메서드 이름이나 매개변수가 정확히 일치하는지 검사
 @Override
    public double add(double num1, double num2) {
        return num1 + num2;
    }

이 코드는 "인터페이스에 이미 정의된 add 메서드를 오버라이딩했다."는 의미다.

인터페이스 미 구현 시 컴파일 오류 발생

class BasicCalculator implements Calculator {
    @Override
    public double add(double num1, double num2) {
        return num1 + num2;
    }
  }

또한 클래스가 인터페이스를 implements 하면, 해당 인터페이스에 정의된 모든 메서드를 구현해야 한다. 그렇지 않으면 컴파일 에러가 발생한다.

java: BasicCalculator is not abstract and does not override abstract method subtract(double,double) in Calculator

BasicCalculator 클래스가 추상 클래스가 아닌데(is not abstract) Calculator 인터페이스의 subtract(double,double) 메서드를 구현(오버라이드)하지 않았다"라는 뜻이다.

해결 방법은 2가지가 있는데 해당 클래스를 추상클래스로 만들거나 인터페이스에 정의된 모든 메서드를 클래스 내에 구현하는 것이다.

추상 클래스로 변환

abstract class BasicCalculator implements Calculator {
    @Override
    public double add(double num1, double num2) {
        return num1 + num2;
    }
}
  • 추상 클래스는 구현되지 않은 메서드(추상 메서드)를 가질 수 있다.
  • 추상 클래스는 직접 인스턴스화(객체 생성)할 수 없음
  • 추상 클래스를 상속받는 구체 클래스가 나머지 메서드들을 구현해야 함

abstract 제어자를 붙여서 추상 클래스로 만들경우 컴파일 오류가 해결된다.

모든 메서드 구현

class BasicCalculator implements Calculator {
    @Override
    public double add(double num1, double num2) {
        return num1 + num2;
    }

    @Override
    public double subtract(double num1, double num2) {
        return num1 - num2;
    }

    @Override
    public double multiply(double num1, double num2) {
        return num1 * num2;
    }

    @Override
    public double divide(double num1, double num2) throws ArithmeticException {
        if (num2 == 0) {
            throw new ArithmeticException("0으로 나눌 수 없습니다.");
        }
        return num1 / num2;
    }
}

사용자 입력을 처리하는 클래스(CalculatorInput)

계산기를 사용하기 위해서는 값을 받는 부분을 구현해야한다.

class CalculatorInput {
    private Scanner scanner;

    public CalculatorInput() {
        scanner = new Scanner(System.in);
    }
    
    public String getOperator() {
        return scanner.nextLine();
    }
}

 

Scanner는 Java에서 사용자로부터 입력을 받기 위해서 제공하는 내장 클래스(built-in class)로써 java.util 패키지에 포함되어 있다. 때문에 사용을 위해서는 import java.util.Scanner; 보통 파일 상단에 다음과 같은 import 문이 필요하다.

CalculatorInput(), getOperator()는 각각 별개의 메서드로 CalculatorInput 클래스의 인스턴스를 생성해서 사용할 수 있다.

생성자 (Constructor)

public CalculatorInput() {
    scanner = new Scanner(System.in);
}
  • 클래스가 처음 생성될 때 실행
  • Scanner 객체를 초기화

getOperator() 메서드

 public String getOperator() {
        return scanner.nextLine();
    }
  • 문자를 입력받는 기능

scanner.nextLine()은 사용자가 입력한 한 줄의 문자열을 읽어오는 메서드다.

  • 엔터키를 치기 전까지의 모든 입력을 문자열로 읽음
  • 엔터키(\n)를 만나면 읽기를 멈춤
  • 공백도 포함해서 읽음

scanner.nextLine()으로 읽어온 문자열을 input에 할당하고 input을 반환한다.


계산기 실행을 관리하는 클래스(CalculatorManager)

사용자 상호작용과 계산 실행을 담당하는 기능을 구현한다.

class CalculatorManager {
    private Calculator calculator;
    private CalculatorInput input;

    public CalculatorManager() {
        calculator = new BasicCalculator();
        input = new CalculatorInput();
    }

    /**
     * 계산기 실행 메소드
     * 사용자 입력을 받고 결과를 출력
     */
    public void run() {
        while (true) {
            try {
                System.out.println("첫 번째 숫자를 입력하세요 (종료: q): ");
                String firstInput = input.getOperator();
                if (firstInput.equalsIgnoreCase("q")) break;

                double num1 = Double.parseDouble(firstInput);
                
                System.out.println("연산자를 입력하세요 (+, -, *, /): ");
                String operator = input.getOperator();

                System.out.println("두 번째 숫자를 입력하세요: ");
                String secondInput = input.getOperator();
                double num2 = Double.parseDouble(secondInput);

                double result = calculateResult(num1, num2, operator);
                System.out.println("결과: " + result);

            } catch (NumberFormatException e) {
                System.out.println("올바른 숫자를 입력해주세요.");
            } catch (ArithmeticException e) {
                System.out.println(e.getMessage());
            } catch (Exception e) {
                System.out.println("오류가 발생했습니다: " + e.getMessage());
            }
        }
    }

    /**
     * 실제 계산을 수행하는 메소드
     * @param num1 첫 번째 숫자
     * @param num2 두 번째 숫자
     * @param operator 연산자
     * @return 계산 결과
     * @throws IllegalArgumentException 잘못된 연산자 입력 시
     */
    private double calculateResult(double num1, double num2, String operator) {
        switch (operator) {
            case "+": return calculator.add(num1, num2);
            case "-": return calculator.subtract(num1, num2);
            case "*": return calculator.multiply(num1, num2);
            case "/": return calculator.divide(num1, num2);
            default: throw new IllegalArgumentException("잘못된 연산자입니다.");
        }
    }
}

필드(멤버) 변수 선언

private Calculator calculator;     // Calculator 타입의 필드
private CalculatorInput input;     // CalculatorInput 타입의 필드
  • 해당 클래스 내에서 지속적으로 사용할 객체들을 저장
  • 클래스의 여러 메서드에서 이 객체들을 공유해서 사용
  • 객체의 상태를 유지

사용 위치가 여러개인 변수를 클래스 내부 상단에 선언한다. 이는 해당 변수를 클래스 내부에서 다양한 곳에서 사용하기 때문인데 각각 별도의 변수를 사용하는 것이 아닌 하나의 공통된 변수를 공유해서 사용하는 것이 적합하기 때문에 필드 변수를 선언한 것이다.

생성자 생성

  public CalculatorManager() {
        calculator = new BasicCalculator();
        input = new CalculatorInput();
    }

BasicCalculator()와 CalculatorInput() 클래스의 메서드를 활용하기 위해 new를 통해 인스턴스를 생성한다.

calculator는 BasicCalculator 클래스의 add, subtract, multiply, divide 메서드를 포함하고 있는 계산을 위한 인스턴스고

input은 CalculatorInput 클래스의 getOperator 메서드를 포함하고 있으며, 입력을 받고 형변환을 하는 인스턴스다.

실행(run) 메서드 구현

public void run() {
    while (true) {
        try {
            System.out.println("첫 번째 숫자를 입력하세요 (종료: q): ");
            String firstInput = input.getOperator();
            if (firstInput.equalsIgnoreCase("q")) break;

            double num1 = Double.parseDouble(firstInput);

            System.out.println("연산자를 입력하세요 (+, -, *, /): ");
            String operator = input.getOperator();

            System.out.println("두 번째 숫자를 입력하세요: ");
            String secondInput = input.getOperator();
            double num2 = Double.parseDouble(secondInput);

            double result = calculateResult(num1, num2, operator);
            System.out.println("결과: " + result);

        } catch (NumberFormatException e) {
            System.out.println("올바른 숫자를 입력해주세요.");
        } catch (ArithmeticException e) {
            System.out.println(e.getMessage());
        } catch (Exception e) {
            System.out.println("오류가 발생했습니다: " + e.getMessage());
        }
    }
}

 

while 문을 사용해서 무한반복

while (true) {
try {}
catch {}
}

1번째 입력 값 받기

//최초 실행시 안내 메시지 출력
System.out.println("첫 번째 숫자를 입력하세요 (종료: q): ");
//변수에 메서드의 실행 결과를 할당
String firstInput = input.getOperator();
//q를 입력하면 break로 메서드 종료
if (firstInput.equalsIgnoreCase("q")) break;
//firstInput 문자열을 double 타입으로 변환하여 num1에 할당
double num1 = Double.parseDouble(firstInput);

엔터를 입력할 경우 getOperator() 메서드가 종료된다. 엔터로 메서드가 종료되는 것은 scanner.nextLine()의 동작 방식 때문인데 getOperator 메서드가 스캐너의 nextLine 메서드를 사용하기 때문이다.

public String getOperator() {
    return scanner.nextLine();
}

nextLine()`의 특징

  1. 엔터키(`\n`)를 기준으로 한 줄의 입력이 끝났다고 판단
  2. 엔터키 이전까지의 모든 문자를 하나의 문자열로 반환
  3. 반환하면서 메서드가 종료됨

nextLine()`의 동작 예시

// 사용자 입력: "5" + 엔터키
String firstInput = input.getOperator();

// 내부적으로 이렇게 동작:
// 1. "5\n" (5와 엔터키) 입력 받음
// 2. 엔터키(\n)를 기준으로 "5"만 추출
// 3. "5"를 반환하고 메서드 종료

또한 입력의 결과(firstInput)를 형변환하고 변수에 할당하는 과정에서 숫자가 아닌 경우 오류가 발생하게 된다.

double num1 = Double.parseDouble(firstInput);


엔터키를 누른 시점에서 해당 메서드는 종료된다.

연산자 입력 값 받기

때문에 다시 입력값을 받으려면 다시 input.getOperator();로 메서드를 호출해야한다.

//연산자 입력 안내 메시지 출력
System.out.println("연산자를 입력하세요 (+, -, *, /): ");
//변수에 메서드의 실행 결과(사용자 입력값)를 할당
String operator = input.getOperator();
public String getOperator() {
        return scanner.nextLine();
    }

해당 메서드는 스캐너를 사용해서 문자열을 할당 받는다.

2번째 입력 값 받기

마찬가지로 input.getOperator();로 메서드를 호출한다.

System.out.println("두 번째 숫자를 입력하세요: ");
String secondInput = input.getOperator();
double num2 = Double.parseDouble(secondInput);

과정은 1번째 입력 값 받기와 동일하다.

연산하기

변수 num1, num2, operator를 calculateResult에 매개변수로 전달한다.

double result = calculateResult(num1, num2, operator);
System.out.println("결과: " + result);
private double calculateResult(double num1, double num2, String operator) {
        switch (operator) {
            case "+": return calculator.add(num1, num2);
            case "-": return calculator.subtract(num1, num2);
            case "*": return calculator.multiply(num1, num2);
            case "/": return calculator.divide(num1, num2);
            default: throw new IllegalArgumentException("잘못된 연산자입니다.");
        }
    }

클래스 내부의 변수를 사용하는 것이기 때문에 private로 선언해서 스코프를 클래스 내부로 제한한다.

내부에선 switch 문을 사용해 분기로 각각 연산자에 대한 결과를 처리한다.

여기서 사용되는 메서드는 BasicCalculator 클래스에서 구현된 메서드들인데 CalculatorManager 클래스 내부에서 생성자를 생성하여 calculator에 할당했기 때문에 calculator 인스턴스를 사용해서 호출한다.

메인 클래스(CalculatorApp)

public class CalculatorApp {
    public static void main(String[] args) {
        System.out.println("계산기 프로그램을 시작합니다.");
        CalculatorManager manager = new CalculatorManager();
        manager.run();
        System.out.println("프로그램을 종료합니다.");
    }
}

실제로 계산기의 동작을 모아둔 하는 CalculatorManager 클래스를 manager 인스턴스로 만들고 manager.run을 통해 run 메서드를 호출한다.