Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
575 changes: 575 additions & 0 deletions CalculatorTest/CalculatorTest.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
6 changes: 6 additions & 0 deletions CalculatorTest/CalculatorTest/Assets.xcassets/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
17 changes: 17 additions & 0 deletions CalculatorTest/CalculatorTest/CalculatorApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// CalculatorApp.swift
// CalculatorTest
//
// Created by 김나연 on 12/26/25.
//

import SwiftUI

@main
struct CalculatorApp: App {
var body: some Scene {
WindowGroup {
CalculatorView()
}
}
}
53 changes: 53 additions & 0 deletions CalculatorTest/CalculatorTest/CalculatorButtonView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// CalculatorButtonView.swift
// CalculatorTest
//
// Created by 김나연 on 12/26/25.
//

import SwiftUI

struct CalculatorButtonView: View {
let button: CalculatorButton
let action: () -> Void

var body: some View {
Button(action: action) {
Text(button.title)
.font(.system(size: 32, weight: .medium))
.foregroundColor(buttonForegroundColor)
.frame(width: buttonWidth, height: buttonHeight)
.background(buttonBackgroundColor)
.cornerRadius(buttonWidth / 2)
}
}

private var buttonWidth: CGFloat {
// 0 버튼은 2배 너비
if case .digit(0) = button {
return (UIScreen.main.bounds.width - 5 * 12) / 4 * 2 + 12
}
return (UIScreen.main.bounds.width - 5 * 12) / 4
}

private var buttonHeight: CGFloat {
return (UIScreen.main.bounds.width - 5 * 12) / 4
}

private var buttonBackgroundColor: Color {
switch button.backgroundColor {
case "orange":
return Color.orange
case "lightGray":
return Color(white: 0.8)
case "darkGray":
return Color(white: 0.3)
default:
return Color.gray
}
}

private var buttonForegroundColor: Color {
button.backgroundColor == "lightGray" ? .black : .white
}
}
115 changes: 115 additions & 0 deletions CalculatorTest/CalculatorTest/CalculatorModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//
// CalculatorModel.swift
// CalculatorTest
//
// Created by 김나연 on 12/26/25.
//

import Foundation

enum CalculatorOperation {
case add, subtract, multiply, divide, none
}

// Protocol for dependency injection
protocol CalculatorModelProtocol {
var displayValue: String { get }
mutating func inputNumber(_ number: String)
mutating func inputOperation(_ operation: CalculatorOperation)
mutating func performOperation()
mutating func reset()
mutating func toggleSign()
mutating func percentage()
}

struct CalculatorModel: CalculatorModelProtocol {
private var accumulator: Double = 0
private var currentOperation: CalculatorOperation = .none
private var isNewNumber: Bool = true

var displayValue: String = "0"

// 숫자 입력 처리
mutating func inputNumber(_ number: String) {
if isNewNumber {
displayValue = number
isNewNumber = false
} else {
if number == "." && displayValue.contains(".") {
return // 소수점 중복 방지
}
displayValue += number
}
}

// 연산자 입력 처리
mutating func inputOperation(_ operation: CalculatorOperation) {
if let currentValue = Double(displayValue) {
if !isNewNumber {
performOperation()
}
accumulator = currentValue
currentOperation = operation
isNewNumber = true
}
}

// 계산 실행
mutating func performOperation() {
guard let currentValue = Double(displayValue) else { return }

switch currentOperation {
case .add:
accumulator += currentValue
case .subtract:
accumulator -= currentValue
case .multiply:
accumulator *= currentValue
case .divide:
if currentValue != 0 {
accumulator /= currentValue
} else {
displayValue = "Error"
reset()
return
}
case .none:
accumulator = currentValue
}

displayValue = formatNumber(accumulator)
currentOperation = .none
isNewNumber = true
}

// 초기화
mutating func reset() {
accumulator = 0
currentOperation = .none
isNewNumber = true
displayValue = "0"
}

// +/- 부호 변경
mutating func toggleSign() {
if let value = Double(displayValue) {
displayValue = formatNumber(-value)
}
}

// 퍼센트 계산
mutating func percentage() {
if let value = Double(displayValue) {
displayValue = formatNumber(value / 100)
}
}

// 숫자 포맷팅 (불필요한 소수점 제거)
private func formatNumber(_ number: Double) -> String {
if number.truncatingRemainder(dividingBy: 1) == 0 {
return String(format: "%.0f", number)
} else {
return String(number)
}
}
}
67 changes: 67 additions & 0 deletions CalculatorTest/CalculatorTest/CalculatorView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// CalculatorView.swift
// CalculatorTest
//
// Created by 김나연 on 12/26/25.
//

import SwiftUI

struct CalculatorView: View {
@StateObject private var viewModel = CalculatorViewModel()

let buttons: [[CalculatorButton]] = [
[.clear, .toggleSign, .percentage, .operation(.divide)],
[.digit(7), .digit(8), .digit(9), .operation(.multiply)],
[.digit(4), .digit(5), .digit(6), .operation(.subtract)],
[.digit(1), .digit(2), .digit(3), .operation(.add)],
[.digit(0), .decimal, .equals]
]

var body: some View {
ZStack {
Color.black.ignoresSafeArea()

VStack(spacing: 12) {
Spacer()

// 디스플레이
displayView

// 버튼 그리드
buttonGrid
}
.padding()
}
}

// MARK: - Display View
private var displayView: some View {
HStack {
Spacer()
Text(viewModel.displayText)
.font(.system(size: 80, weight: .light))
.foregroundColor(.white)
.lineLimit(1)
.minimumScaleFactor(0.5)
.padding(.horizontal)
}
.frame(maxWidth: .infinity, alignment: .trailing)
.padding(.bottom, 20)
}

// MARK: - Button Grid
private var buttonGrid: some View {
VStack(spacing: 12) {
ForEach(buttons.indices, id: \.self) { row in
HStack(spacing: 12) {
ForEach(buttons[row], id: \.title) { button in
CalculatorButtonView(button: button) {
viewModel.buttonTapped(button)
}
}
}
}
}
}
}
Loading