📅 프로젝트 기간: 2025.04.22 ~ 2025.04.25
🎯 구조: MVC 아키텍처 / 상태 기반 입력 처리 / 후위 표기법 기반 수식 계산기 💻 실행 환경: Python 3.12 / PyQt6 / Ubuntu 24.04
괄호 연산, 음수 처리, 연산자 우선순위, 오류 메시지 출력 등 실제 계산기 수준의 입력 UX를 구현한 PyQt6 기반 GUI 계산기입니다.
Shunting Yard 알고리즘과 스택 계산기를 직접 구현하여 정확한 수식 평가를 보장하며, MVC 구조 기반으로 유지보수성과 확장성을 모두 확보했습니다.
장진혁 (Jang Jin-Hyuk) GitHub Profile
항목 | 내용 |
---|---|
구조 | PyQt6 기반 MVC 아키텍처 |
수식 처리 | 괄호 포함 중위 → 후위 변환 (Shunting Yard) |
계산 로직 | 스택 기반 후위 표기 수식 평가 |
입력 UX | 숫자, 소수점, ±, 괄호, 초기화, 삭제 등 전체 UX 처리 |
오류 대응 | 0으로 나누기, 괄호 불일치 등 자동 복구 및 메시지 출력 |
상태 관리 | READY / INPUTTING / CALCULATED / ERROR 전이 설계 |
스타일 확장성 | Casio 스타일 테마 확장 가능 |
- 상단 디스플레이 (
lineEdit_2
): 현재 수식 표현 (토큰 기반 수식 표시) - 하단 디스플레이 (
lineEdit
): 현재 입력 숫자 또는 계산 결과 표시
버튼 구분 | 항목 | 설명 |
---|---|---|
숫자 버튼 | 0 ~ 9 |
숫자 입력 |
연산자 버튼 | + , - , × , ÷ |
사칙연산 입력 |
괄호 | ( , ) |
괄호 입력 |
계산 | = |
수식 평가 후 결과 출력 |
초기화 | AC |
전체 입력 초기화 |
삭제 | C |
마지막 항목 삭제 |
부호 전환 | ± |
숫자 부호 전환 (+ ↔ - ) |
소수점 | . |
소수점 입력 (중복 방지 포함) |
- 사용자 오류 및 안내 메시지를 일시적으로 표시하는 하단 텍스트 영역
예:"소수점은 한 번만 입력할 수 있습니다"
예:"닫는 괄호가 더 많을 수 없습니다"
+
,-
,*
,/
연산 모두 지원- 중첩 괄호, 우선순위 계산 완벽 지원
-(2+3)
입력 시 자동으로-1*(2+3)
변환
- 숫자 입력은 최대 20자리로 제한
- 소수점 중복 입력 방지: 한 숫자에
.
한 번만 허용- 예:
-0.001
은 유효,0.00.1
은 무효
- 예:
C
: 최근 입력 항목 1개 삭제AC
: 전체 수식 및 입력 초기화±
: 부호 전환 기능-12
↔12
0
↔-
- 괄호 수가 맞지 않을 경우 자동 보완
- 여는 괄호가 남아 있을 경우
)
자동 삽입 - 닫는 괄호가 더 많으면 무시 또는 삭제
- 여는 괄호가 남아 있을 경우
- 연산자 뒤에 괄호가 바로 올 경우 무효 처리
- 예:
(+3)
→ 무효
- 예:
- 숫자 뒤 괄호 입력 시 암시적 곱셈 처리
- 예:
2(3+4)
→2*(3+4)
로 자동 변환
- 예:
1 + )
처럼 닫는 괄호가 과도할 경우:"닫는 괄호가 더 많을 수 없습니다"
메시지 출력
- 괄호 불일치 시 자동 보완 후 계산 수행
- 계산 중 0으로 나누기 시
"Error"
메시지 출력
- 전체 프로그램을 Model, View, Controller로 명확히 분리
- Model과 View는 서로 직접 접근하지 않으며, Controller를 통해 간접 연결됨
사용자 입력 (View)
↓ signal
Controller (입력 핸들링 및 상태 갱신)
↓ Model 호출 및 계산 결과 전달
Model (계산 로직 및 상태 관리)
↑ 계산 결과 반환
Controller → View.update_xxx()로 결과 반영
구성 요소 | 역할 설명 |
---|---|
Controller | 중심 허브. View와 Model 모두를 알고 있으며, 사용자 입력을 받아 Model을 호출하고 View를 갱신 |
Model | 계산 로직과 상태 처리를 담당. 직접 UI를 업데이트하지 않음 |
View | 사용자 인터페이스 구성과 사용자 입력 signal 전달 담당. 자체 로직은 없음 |
qt-calculator/
├── main.py # 진입점
├── model.py # 수식 파싱 / 계산기 로직 / 상태 관리
├── view.py # PyQt6 UI 처리 / 메시지 표시
├── controller.py # 입력 처리 → 모델 호출 → 뷰 갱신
├── calculator.ui # Qt Designer 기반 UI 정의
├── img/ # 배너, 스크린샷 이미지
├── flow_chart/ # 기능별 순서도
└── doc/ # 발표자료 등 프로젝트 문서
계산기 내부 상태는 다음 4가지 중 하나로 존재하며, 사용자 행동에 따라 상태 전이가 발생합니다:
상태 | 설명 |
---|---|
READY |
초기 상태. 입력이 없는 대기 상태 |
INPUTTING |
숫자, 연산자, 괄호 등 수식이 입력되고 있는 중 |
CALCULATED |
계산 완료 후 결과가 출력된 상태 |
ERROR |
잘못된 수식, 0으로 나누기 등의 오류가 발생한 상태 |
READY
→INPUTTING
: 숫자나 괄호, 연산자 입력 시작INPUTTING
→CALCULATED
:=
입력 후 결과 계산INPUTTING
→ERROR
: 괄호 오류, 수식 오류 등 발생CALCULATED
→READY
또는INPUTTING
: 새 수식 입력 또는 연산자 추가ERROR
→READY
:C
,AC
또는 새 숫자 입력 시 복구
총 6가지 기능 분류 기준에 따라 총 45개의 테스트 시나리오를 설계하였으며, 각 시나리오는 입력값 → 상태 변화(CalcState) → 내부 변수 변화 → 출력 결과 흐름까지 전 과정을 검증합니다.
기능 분류 | 테스트 개수 | 설명 |
---|---|---|
초기 상태 입력 흐름 | 9 | READY 상태에서의 숫자/연산자 입력 처리 검증 |
오류 직후 입력 복구 흐름 | 9 | ERROR 상태 이후 입력 복원 가능 여부 확인 |
계산 후 입력 처리 흐름 | 9 | CALCULATED 상태에서 연산 연속성 및 초기화 검증 |
괄호/우선순위 처리 | 8 | 괄호 중첩, 연산자 우선순위, 암시적 곱셈 처리 검증 |
에러 처리 및 에러 방지 | 6 | 잘못된 수식에 대한 자동 보정 및 UX 보호 로직 확인 |
초기화 흐름 | 4 | C , AC 버튼에 따른 입력/상태 초기화 검증 |
각 테스트는 내부 변수(
current_input
,tokens
) 변화와 디스플레이(lineEdit
) 출력 결과까지 함께 추적합니다.
번호 | 입력값 | 예상 결과 | 설명 |
---|---|---|---|
TC_10 | 에러 발생 후 숫자 입력 | 정상 입력으로 복구됨 | 오류 상태에서도 새 입력으로 정상 복구 가능한지 검증 |
TC_20 | 계산 직후 연산자 입력 | 결과값에 연산자 추가됨 | 계산 완료 후 바로 연산자 입력이 자연스럽게 이어지는지 검증 |
TC_22 | 계산 직후 괄호 입력 | 결과 뒤 곱셈(*) 자동 삽입 후 괄호 입력됨 | 계산 완료된 수치 뒤에 괄호 입력 시 암시적 곱셈 처리 검증 |
TC_30 | 중첩 괄호 수식 계산 | 올바른 결과 반환 | 복잡한 괄호 중첩 구조에서 수식이 정확히 계산되는지 검증 |
TC_34 | 숫자 입력 후 괄호 입력 | 숫자 뒤에 곱셈(*) 자동 삽입 후 괄호 입력됨 | 숫자 뒤 괄호 입력 시 암시적 곱셈이 적용되는지 검증 |
TC_37 | 수식 끝에 연산자 입력 후 계산 | 오류 메시지 출력 | 수식 끝에 연산자가 남아있을 경우 에러를 감지하는지 검증 |
TC_38 | 닫는 괄호 부족 상태로 계산 | 자동으로 닫는 괄호 삽입 후 계산 수행 | 괄호가 불일치할 경우 자동 보완 로직이 정상 작동하는지 검증 |
TC_39 | 괄호를 이중으로 닫을 경우 | 무효 괄호 제거 및 수식 유지 | 여는 괄호 없이 닫는 괄호를 입력해도 시스템이 정상 복구하는지 검증 |
TC_40 | 괄호 닫기 직전에 연산자가 남은 경우 | 잘못된 연산자 제거 후 괄호 닫힘 처리 | 괄호 내부 수식이 불완전할 때도 자동 보정이 되는지 검증 |
TC_42 | 소수점 중복 입력 시도 | "소수점은 한 번만 입력할 수 있습니다" 메시지 출력 | 소수점 입력 UX 보호 기능이 정상 작동하는지 검증 |
입력: (1+(2+3))*2 =
목표 결과: 12
테스트 목적: 괄호 중첩 수식에 대한 정확한 평가 및 디스플레이 출력 흐름 검증
입력 | 상태 | current_input | tokens | lineEdit_2 (상단 수식) | lineEdit (하단 결과) |
---|---|---|---|---|---|
( |
INPUTTING | - | ['('] |
( | - |
1 |
INPUTTING | 1 | ['('] |
( | 1 |
+ |
INPUTTING | - | ['(', '1', '+'] |
(1+ | - |
( |
INPUTTING | - | ['(', '1', '+', '('] |
(1+( | - |
2 |
INPUTTING | 2 | ['(', '1', '+', '(', '2'] |
(1+( | 2 |
+ |
INPUTTING | - | ['(', '1', '+', '(', '2', '+'] |
(1+(2+ | - |
3 |
INPUTTING | 3 | ['(', '1', '+', '(', '2', '+', '3'] |
(1+(2+ | 3 |
) |
INPUTTING | - | ['(', '1', '+', '(', '2', '+', '3', ')'] |
(1+(2+3) | - |
) |
INPUTTING | - | ['(', '1', '+', '(', '2', '+', '3', ')', ')'] |
(1+(2+3)) | - |
2 |
INPUTTING | 2 | ['(', '1', '+', '(', '2', '+', '3', ')', ')', '*', '2'] |
(1+(2+3))* | 2 |
= |
CALCULATED | 12 | ['(', '1', '+', '(', '2', '+', '3', ')', ')', '*', '2'] |
(1+(2+3))*2 | 12 |
항목 | 수치 | 비고 |
---|---|---|
총 테스트 케이스 수 | 45개 | 정상 흐름 + 예외 흐름 포함 |
정상 흐름 테스트 수 | 31개 | 사칙연산, 괄호 처리 등 기능 정상 작동 검증 |
예외 흐름 테스트 수 | 14개 | 오류 입력 방어, UX 보호 기능 검증 |
정상 흐름 성공률 | 100% | 모든 정상 케이스 기대 동작 성공 |
예외 흐름 성공률 | 100% | 모든 예외 케이스 보호 로직 정상 작동 |
전체 테스트 성공률 | 100% | 전체 45개 테스트 모두 성공 |
git clone https://github.com/jinhyuk2me/qt-calculator.git
cd qt-calculator
pip install PyQt6
python main.py
장진혁 (Jang Jin-Hyuk)
📧 Email: jinhyuk2ya@gmail.com
🌐 GitHub: @jinhyuk2me