Swift 기초 문법 완벽 가이드
소프트웨어스튜디오2 - 2주차 교재
📚 목차
- Chapter 1. 변수, 상수, 타입 시스템
- Chapter 2. 컬렉션 타입 마스터하기
- Chapter 3. 제어 구조와 흐름 제어
- Chapter 4. 함수와 파라미터
- Chapter 5. 종합 실습 프로젝트
Chapter 1. 변수, 상수, 타입 시스템
학습목표: Swift의 강력한 타입 시스템을 이해하고, 변수와 상수를 적절히 활용할 수 있다.
1.1 변수(var)와 상수(let)의 이해
Swift는 Python과 달리 변수의 가변성을 명시적으로 구분합니다. 이는 코드의 안정성을 높이고 의도하지 않은 값 변경을 방지합니다.
// ===== 상수(let) - Immutable Values ===== // 한 번 값을 할당하면 절대 변경할 수 없습니다. // Python의 tuple이나 frozenset과 개념이 유사하지만, 모든 타입에 적용 가능합니다. let maxLoginAttempts = 3 // 최대 로그인 시도 횟수 (변경 불가) let pi = 3.14159 // 수학 상수 π let universityName = "동서대학교" // 학교명 (불변) let foundedYear = 1992 // 설립년도 // ❌ 에러 발생 예시 // maxLoginAttempts = 5 // Cannot assign to value: 'maxLoginAttempts' is a 'let' constant // ===== 변수(var) - Mutable Values ===== // 값을 언제든지 변경할 수 있습니다. // Python의 일반 변수와 동일한 개념입니다. var currentLoginAttempt = 0 // 현재 로그인 시도 횟수 var userScore = 0 // 사용자 점수 var isLoggedIn = false // 로그인 상태 var userName = "Guest" // 사용자 이름 // ✅ 값 변경 가능 currentLoginAttempt += 1 // 시도 횟수 증가 userScore = 100 // 점수 업데이트 isLoggedIn = true // 상태 변경 userName = "김철수" // 이름 변경 // ===== 실무 Best Practice ===== // 기본적으로 let을 사용하고, 꼭 필요한 경우에만 var를 사용합니다. // 이는 함수형 프로그래밍 패러다임과도 일치합니다. let studentId = "20241234" // 학번은 변경되지 않음 var currentSemester = 3 // 학기는 매 학기 변경 var totalCredits = 65 // 이수 학점은 계속 증가 let major = "소프트웨어학과" // 전공 (보통 불변)
💡 Pro Tip: Xcode는 var로 선언했지만 값이 변경되지 않는 변수를 자동으로 감지하여 let으로 변경하도록 제안합니다. 이는 코드 최적화와 안정성 향상에 도움이 됩니다.
1.2 타입 추론과 명시적 타입 선언
Swift는 강력한 타입 추론 기능을 제공하지만, Python과 달리 한 번 정해진 타입은 변경할 수 없습니다.
// ===== 타입 추론 (Type Inference) ===== // Swift 컴파일러가 초기값을 보고 타입을 자동으로 결정합니다. // Python의 동적 타이핑과는 다르게, 컴파일 시점에 타입이 확정됩니다. let implicitInteger = 42 // Int 타입으로 추론 let implicitDouble = 3.14159 // Double 타입으로 추론 let implicitString = "Hello" // String 타입으로 추론 let implicitBool = true // Bool 타입으로 추론 // 타입 확인하기 (디버깅용) print(type(of: implicitInteger)) // "Int" print(type(of: implicitDouble)) // "Double" // ===== 명시적 타입 선언 (Type Annotation) ===== // Python의 타입 힌트와 유사하지만, Swift에서는 강제됩니다. // 타입을 명시하면 코드의 의도가 더 명확해집니다. let explicitDouble: Double = 70 // 70을 Double로 저장 let explicitFloat: Float = 4.0 // Float 타입 명시 var explicitInt32: Int32 = 100 // 32비트 정수 var explicitUInt: UInt = 50 // 부호 없는 정수 // ===== 타입 안정성 (Type Safety) ===== // Python과 달리 타입이 한 번 정해지면 변경 불가능합니다. var myVariable = 42 // Int 타입으로 추론 myVariable = 50 // ✅ OK: 같은 Int 타입 // myVariable = "Hello" // ❌ 에러: Int 변수에 String 할당 불가 // myVariable = 3.14 // ❌ 에러: Int 변수에 Double 할당 불가 // ===== 타입 변환 (Type Casting) ===== // Python은 자동 타입 변환을 지원하지만, Swift는 명시적 변환이 필요합니다. let integerValue = 42 let doubleValue = 3.14 // Python: result = integerValue + doubleValue # 자동 변환 // Swift: 명시적 변환 필요 let result1 = Double(integerValue) + doubleValue // Int를 Double로 변환 let result2 = integerValue + Int(doubleValue) // Double을 Int로 변환 (소수점 버림) print("Double 결과: \(result1)") // 45.14 print("Int 결과: \(result2)") // 45
🔄 Python vs Swift 타입 시스템
| 특징 | Python | Swift |
|---|---|---|
| 타입 결정 시점 | 런타임 (동적) | 컴파일 타임 (정적) |
| 타입 변경 | 가능 | 불가능 |
| 타입 변환 | 자동 (암시적) | 수동 (명시적) |
| 타입 안정성 | 낮음 | 높음 |
1.3 문자열 보간법과 다양한 타입들
// ===== 문자열 보간법 (String Interpolation) ===== // Python의 f-string과 매우 유사한 기능입니다. let name = "김철수" let age = 23 let gpa = 3.85 // Swift: 백슬래시와 괄호 사용 let message1 = "\(name)님은 \(age)세이고, 학점은 \(gpa)입니다." // Python 비교: f"{name}님은 {age}세이고, 학점은 {gpa}입니다." // 표현식도 사용 가능 let mathResult = "10 + 20 = \(10 + 20)" let comparison = "나이가 20세 이상? \(age >= 20)" // 포맷 지정 (소수점 자리수 등) let formattedGPA = String(format: "학점: %.2f", gpa) // "학점: 3.85" // ===== 기본 데이터 타입들 ===== // 1. 정수 타입 (Integer Types) let int8Value: Int8 = 127 // -128 ~ 127 let int16Value: Int16 = 32767 // -32,768 ~ 32,767 let int32Value: Int32 = 2147483647 // 약 ±21억 let int64Value: Int64 = 9223372036854775807 // 매우 큰 수 let intValue: Int = 42 // 플랫폼 의존 (보통 Int64) // 2. 부호 없는 정수 (Unsigned Integer) let uintValue: UInt = 42 // 0 이상의 정수만 let uint8Value: UInt8 = 255 // 0 ~ 255 (1바이트) // 3. 부동소수점 타입 let floatValue: Float = 3.14 // 32비트 부동소수점 (정밀도 6자리) let doubleValue: Double = 3.141592653589793 // 64비트 (정밀도 15자리) // 4. 불리언 타입 let isStudent: Bool = true let hasGraduated: Bool = false // 5. 문자와 문자열 let grade: Character = "A" // 단일 문자 let courseName: String = "iOS 개발" // 문자열 // ===== 타입 별칭 (Type Alias) ===== // 복잡한 타입에 별명을 붙여 가독성 향상 typealias StudentID = String typealias Score = Int typealias GradePoint = Double let myID: StudentID = "20241234" let myScore: Score = 95 let myGPA: GradePoint = 4.3
⚠️ 주의사항: Swift에서는 정수 오버플로우가 런타임 에러를 발생시킵니다. 안전한 연산을 위해 오버플로우 연산자(&+, &-, &*)를 사용하거나, 적절한 크기의 타입을 선택하세요.
Chapter 2. 컬렉션 타입 마스터하기
학습목표: 배열, 딕셔너리, 집합, 튜플 등 Swift의 컬렉션 타입을 완벽히 이해하고 활용할 수 있다.
2.1 배열 (Array)
Swift의 배열은 Python의 리스트와 유사하지만, 같은 타입의 요소만 저장할 수 있다는 중요한 차이점이 있습니다.
// ===== 배열 선언과 초기화 ===== // Swift 배열은 제네릭 타입으로, 요소의 타입을 명시합니다. // 1. 빈 배열 생성 - 다양한 방법 var emptyArray1: [Int] = [] // 타입 명시 + 빈 리터럴 var emptyArray2 = [Int]() // 초기화 구문 var emptyArray3: Array<String> = [] // 제네릭 문법 // 2. 초기값이 있는 배열 var fruits = ["사과", "바나나", "오렌지"] // [String] 타입 추론 let scores: [Int] = [85, 90, 78, 92, 88] let temperatures = [23.5, 24.1, 22.8, 25.3] // [Double] // 3. 반복 요소로 배열 생성 let fiveZeros = Array(repeating: 0, count: 5) // [0, 0, 0, 0, 0] let threeDoubles = Array(repeating: 2.5, count: 3) // [2.5, 2.5, 2.5] // ===== 배열 접근과 수정 ===== // 1. 요소 접근 (인덱스는 0부터 시작) print(fruits[0]) // "사과" print(fruits.first) // Optional("사과") - 안전한 접근 print(fruits.last) // Optional("오렌지") // 2. 요소 추가 fruits.append("딸기") // 끝에 추가 fruits.insert("포도", at: 1) // 특정 위치에 삽입 fruits += ["수박", "멜론"] // 여러 요소 한번에 추가 // 3. 요소 수정 fruits[0] = "청사과" // 단일 요소 수정 fruits[2...4] = ["망고", "키위", "레몬"] // 범위 수정 // 4. 요소 삭제 fruits.remove(at: 1) // 인덱스로 삭제 fruits.removeLast() // 마지막 요소 삭제 fruits.removeFirst() // 첫 요소 삭제 fruits.removeAll() // 모든 요소 삭제 // ===== 배열 속성과 메서드 ===== var numbers = [3, 1, 4, 1, 5, 9, 2, 6] // 1. 배열 정보 print(numbers.count) // 8 - 요소 개수 print(numbers.isEmpty) // false - 비어있는지 확인 print(numbers.capacity) // 내부 버퍼 크기 // 2. 검색 if numbers.contains(5) { print("5가 포함되어 있습니다") } if let index = numbers.firstIndex(of: 4) { print("4의 인덱스: \(index)") // 2 } // 3. 정렬 numbers.sort() // 원본 배열 정렬 (오름차순) numbers.sort(by: >) // 내림차순 정렬 let sortedNumbers = numbers.sorted() // 정렬된 새 배열 반환 // 4. 필터링과 변환 (고차 함수) let evenNumbers = numbers.filter { $0 % 2 == 0 } // 짝수만 필터 let doubled = numbers.map { $0 * 2 } // 모든 요소 2배 let sum = numbers.reduce(0, +) // 합계 계산 // ===== 2차원 배열 ===== var matrix: [[Int]] = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] print(matrix[1][2]) // 6 - 2행 3열 요소 matrix[0][0] = 10 // 1행 1열 수정
🎯 실습 문제: 배열 활용
학생들의 시험 점수를 관리하는 프로그램을 작성하세요:
- 점수 배열에서 최고점, 최저점 찾기
- 평균 점수 계산
- 80점 이상인 학생 수 세기
- 점수를 학점(A, B, C, D, F)으로 변환
2.2 딕셔너리 (Dictionary)
// ===== 딕셔너리 선언과 초기화 ===== // Python의 dict와 매우 유사하지만, 타입이 고정됩니다. // 1. 빈 딕셔너리 생성 var emptyDict1: [String: Int] = [:] // 타입 명시 var emptyDict2 = [String: Int]() // 초기화 구문 var emptyDict3 = Dictionary<String, Int>() // 제네릭 // 2. 초기값이 있는 딕셔너리 var studentScores: [String: Int] = [ "김철수": 85, "이영희": 92, "박민수": 78, "최지우": 88 ] // 3. 다양한 타입의 딕셔너리 let httpHeaders = [ "Content-Type": "application/json", "Authorization": "Bearer token123" ] let courseCredits = [ "iOS개발": 3.0, "알고리즘": 3.0, "데이터베이스": 4.0 ] // ===== 딕셔너리 접근과 수정 ===== // 1. 값 접근 (옵셔널 반환에 주의!) let score1 = studentScores["김철수"] // Optional(85) let score2 = studentScores["홍길동"] // nil (키가 없음) // 안전한 접근 - nil 병합 연산자 사용 let safeScore = studentScores["홍길동"] ?? 0 // 0 (기본값) // 2. 값 추가/수정 studentScores["홍길동"] = 95 // 새로운 키-값 추가 studentScores["김철수"] = 90 // 기존 값 수정 // updateValue 메서드 - 이전 값 반환 let oldValue = studentScores.updateValue(87, forKey: "박민수") print("이전 점수: \(oldValue ?? 0)") // 78 // 3. 값 삭제 studentScores["최지우"] = nil // nil 할당으로 삭제 studentScores.removeValue(forKey: "박민수") // 메서드로 삭제 // ===== 딕셔너리 순회 ===== // 1. 키-값 쌍 순회 for (name, score) in studentScores { print("\(name): \(score)점") } // 2. 키만 순회 for name in studentScores.keys { print("학생: \(name)") } // 3. 값만 순회 for score in studentScores.values { print("점수: \(score)") } // 4. 정렬된 순서로 순회 let sortedNames = studentScores.keys.sorted() for name in sortedNames { print("\(name): \(studentScores[name]!)점") } // ===== 딕셔너리 고급 활용 ===== // 1. 딕셔너리 병합 var dict1 = ["a": 1, "b": 2] let dict2 = ["c": 3, "d": 4] dict1.merge(dict2) { (_, new) in new } // 중복 시 새 값 사용 // 2. 기본값이 있는 딕셔너리 var letterCounts = [Character: Int]() let word = "hello" for letter in word { letterCounts[letter, default: 0] += 1 // 기본값 사용 } print(letterCounts) // ["h": 1, "e": 1, "l": 2, "o": 1] // 3. 딕셔너리 필터링 let highScorers = studentScores.filter { $0.value >= 90 } print(highScorers) // 90점 이상인 학생만
2.3 집합 (Set)과 튜플 (Tuple)
// ===== 집합 (Set) ===== // Python의 set과 동일한 개념 - 중복 없는 유일한 값들의 모음 // 1. Set 생성 var letters = Set<Character>() // 빈 Set var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"] var primes: Set = [2, 3, 5, 7, 11] // Set타입 추론 // 2. Set 조작 favoriteGenres.insert("Jazz") // 요소 추가 favoriteGenres.remove("Rock") // 요소 삭제 // 중복 요소는 무시됨 var numbers: Set = [1, 2, 3, 3, 3] print(numbers) // [1, 2, 3] - 순서는 보장되지 않음 // 3. Set 연산 (집합 연산) let oddDigits: Set = [1, 3, 5, 7, 9] let evenDigits: Set = [0, 2, 4, 6, 8] let primeDigits: Set = [2, 3, 5, 7] // 합집합 (Union) let allDigits = oddDigits.union(evenDigits) print(allDigits) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // 교집합 (Intersection) let oddPrimes = oddDigits.intersection(primeDigits) print(oddPrimes) // [3, 5, 7] // 차집합 (Subtracting) let nonPrimeOdds = oddDigits.subtracting(primeDigits) print(nonPrimeOdds) // [1, 9] // 대칭 차집합 (Symmetric Difference) let oddOrPrime = oddDigits.symmetricDifference(primeDigits) print(oddOrPrime) // [1, 2, 9] // 4. Set 관계 확인 let houseAnimals: Set = ["🐶", "🐱"] let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"] let cityAnimals: Set = ["🐦", "🐭"] houseAnimals.isSubset(of: farmAnimals) // true - 부분집합 farmAnimals.isSuperset(of: houseAnimals) // true - 상위집합 farmAnimals.isDisjoint(with: cityAnimals) // true - 교집합 없음 // ===== 튜플 (Tuple) ===== // Python의 tuple과 유사하지만 더 유연함 - 이름 지정 가능 // 1. 기본 튜플 let coordinates = (3.0, 5.0) // (Double, Double) let httpError = (404, "Not Found") // (Int, String) // 2. 이름이 있는 튜플 (Named Tuple) let person = (name: "김철수", age: 23, gpa: 3.85) print(person.name) // "김철수" print(person.age) // 23 print(person.gpa) // 3.85 // 3. 튜플 분해 (Decomposition) let (x, y) = coordinates print("x: \(x), y: \(y)") let (statusCode, statusMessage) = httpError print("에러 \(statusCode): \(statusMessage)") // 일부만 사용하고 싶을 때는 _ 사용 let (justName, _, _) = person print(justName) // "김철수" // 4. 함수에서 여러 값 반환 func minMax(array: [Int]) -> (min: Int, max: Int)? { guard !array.isEmpty else { return nil } var currentMin = array[0] var currentMax = array[0] for value in array { if value < currentMin { currentMin = value } else if value > currentMax { currentMax = value } } return (currentMin, currentMax) } if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) { print("최소값: \(bounds.min), 최대값: \(bounds.max)") } // 5. 튜플 비교 (7개 요소까지만 가능) let tuple1 = (1, "apple") let tuple2 = (2, "banana") let tuple3 = (1, "bird") print(tuple1 < tuple2) // true (1 < 2) print(tuple1 < tuple3) // true ("apple" < "bird")
💡 언제 어떤 컬렉션을 사용할까?
- Array: 순서가 중요하고, 인덱스로 접근해야 할 때
- Set: 중복을 제거하거나 집합 연산이 필요할 때
- Dictionary: 키-값 쌍으로 빠른 검색이 필요할 때
- Tuple: 간단한 관련 값들을 임시로 그룹화할 때
Chapter 3. 제어 구조와 흐름 제어
학습목표: 조건문, 반복문, 제어 전송문을 활용하여 프로그램의 흐름을 제어할 수 있다.
3.1 조건문 - if, switch
// ===== if-else 구문 ===== // Python과 매우 유사하지만, 조건에 괄호는 선택사항입니다. let score = 85 var grade: String // 1. 기본 if-else if score >= 90 { grade = "A" } else if score >= 80 { grade = "B" } else if score >= 70 { grade = "C" } else if score >= 60 { grade = "D" } else { grade = "F" } // 2. 복합 조건 let age = 20 let hasID = true let isStudent = true if age >= 19 && hasID { print("입장 가능") if isStudent { print("학생 할인 적용") } } else { print("입장 불가") } // 3. 삼항 연산자 (Ternary Operator) let canVote = age >= 18 ? "투표 가능" : "투표 불가" let discount = isStudent ? 0.2 : 0.0 // ===== switch 구문 ===== // Python의 match보다 훨씬 강력하고 유연합니다. // 모든 케이스를 처리해야 하며(exhaustive), break가 필요 없습니다. // 1. 기본 switch let dayOfWeek = "월요일" switch dayOfWeek { case "월요일": print("한 주의 시작!") case "금요일": print("불금!") case "토요일", "일요일": // 여러 케이스 한번에 print("주말이다!") default: print("평일") } // 2. 범위 매칭 (Interval Matching) let testScore = 76 switch testScore { case 90...100: print("A 학점 - 우수") case 80..<90: print("B 학점 - 양호") case 70..<80: print("C 학점 - 보통") case 60..<70: print("D 학점 - 미흡") case 0..<60: print("F 학점 - 재수강") default: print("잘못된 점수") } // 3. 튜플 매칭 (Tuple Matching) let somePoint = (1, 1) switch somePoint { case (0, 0): print("원점") case (_, 0): // x는 무엇이든, y는 0 print("x축 위의 점") case (0, _): // x는 0, y는 무엇이든 print("y축 위의 점") case (-2...2, -2...2): print("원점 근처") default: print("어딘가의 점") } // 4. 값 바인딩 (Value Binding) let anotherPoint = (2, 0) switch anotherPoint { case (let x, 0): // y가 0일 때 x값을 바인딩 print("x축 위의 점, x = \(x)") case (0, let y): // x가 0일 때 y값을 바인딩 print("y축 위의 점, y = \(y)") case let (x, y): // 모든 경우에 x, y 바인딩 print("점 (\(x), \(y))") } // 5. where 절을 사용한 추가 조건 let yetAnotherPoint = (1, -1) switch yetAnotherPoint { case let (x, y) where x == y: print("x == y인 대각선 위의 점") case let (x, y) where x == -y: print("x == -y인 대각선 위의 점") case let (x, y): print("일반적인 점 (\(x), \(y))") } // 6. fallthrough (다음 케이스도 실행) let integerToDescribe = 5 var description = "The number \(integerToDescribe) is" switch integerToDescribe { case 2, 3, 5, 7, 11, 13, 17, 19: description += " a prime number, and also" fallthrough // 다음 케이스도 실행 default: description += " an integer." } print(description) // "The number 5 is a prime number, and also an integer."
⚠️ 주의: Swift의 switch는 암시적 fallthrough가 없습니다. 각 케이스가 실행되면 자동으로 switch를 빠져나갑니다. 다음 케이스도 실행하려면 명시적으로 fallthrough를 사용해야 합니다.
3.2 반복문 - for, while, repeat-while
// ===== for-in 루프 ===== // Python의 for 루프와 매우 유사합니다. // 1. 배열 순회 let names = ["Anna", "Alex", "Brian", "Jack"] for name in names { print("Hello, \(name)!") } // 2. 인덱스와 함께 순회 (Python의 enumerate와 동일) for (index, name) in names.enumerated() { print("\(index + 1). \(name)") } // 3. 딕셔너리 순회 let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] for (animalName, legCount) in numberOfLegs { print("\(animalName)s have \(legCount) legs") } // 4. 범위 순회 (Python의 range와 유사) for index in 1...5 { print("\(index) times 5 is \(index * 5)") } // 반개방 범위 for i in 0..<5 { print("인덱스: \(i)") // 0, 1, 2, 3, 4 } // 5. 역순 순회 for i in (1...5).reversed() { print("카운트다운: \(i)") // 5, 4, 3, 2, 1 } // 6. stride 사용 (Python의 range(start, stop, step)과 유사) for i in stride(from: 0, to: 50, by: 5) { print(i) // 0, 5, 10, 15, 20, 25, 30, 35, 40, 45 } for i in stride(from: 50, through: 0, by: -10) { print(i) // 50, 40, 30, 20, 10, 0 } // 7. 값이 필요 없을 때는 _ 사용 let base = 3 let power = 10 var answer = 1 for _ in 1...power { answer *= base } print("\(base) to the power of \(power) is \(answer)") // ===== while 루프 ===== // Python의 while과 동일합니다. // 1. 기본 while var countdown = 5 while countdown > 0 { print("\(countdown)...") countdown -= 1 } print("Blast off! 🚀") // 2. 조건이 복잡한 while var sum = 0 var number = 1 while sum < 100 && number <= 20 { sum += number number += 1 } print("Sum: \(sum), Last number: \(number - 1)") // ===== repeat-while 루프 ===== // Python에는 없는 do-while 스타일 루프입니다. // 최소 한 번은 실행이 보장됩니다. // 1. 기본 repeat-while var userInput: Int repeat { userInput = Int.random(in: 1...10) // 사용자 입력 시뮬레이션 print("입력값: \(userInput)") } while userInput != 7 print("7을 입력했습니다!") // 2. 메뉴 시스템 예제 var menuChoice = 0 repeat { print(""" === 메뉴 === 1. 시작 2. 설정 3. 종료 """) menuChoice = Int.random(in: 1...4) // 선택 시뮬레이션 print("선택: \(menuChoice)") switch menuChoice { case 1: print("게임을 시작합니다...") case 2: print("설정을 엽니다...") case 3: print("프로그램을 종료합니다.") default: print("잘못된 선택입니다.") } } while menuChoice != 3 // ===== 제어 전송문 ===== // 1. continue - 현재 반복을 건너뛰고 다음 반복 진행 for i in 1...10 { if i % 2 == 0 { continue // 짝수는 건너뛰기 } print("홀수: \(i)") } // 2. break - 반복문 완전히 종료 var searchNumber = 7 let numbers = [1, 3, 5, 7, 9, 11] for num in numbers { if num == searchNumber { print("찾았다! \(num)") break } print("확인 중: \(num)") } // 3. 레이블이 있는 구문 (중첩 루프 제어) outerLoop: for i in 1...3 { for j in 1...3 { if i == 2 && j == 2 { break outerLoop // 외부 루프까지 종료 } print("(\(i), \(j))") } }
🎯 실습 문제: 구구단 프로그램
다음 요구사항을 만족하는 구구단 프로그램을 작성하세요:
- 2단부터 9단까지 출력
- 각 단의 결과가 50을 초과하면 그 단은 중단
- 5단은 건너뛰기
- switch문을 사용하여 특별한 단(3, 7, 9)은 강조 표시
Chapter 4. 함수와 파라미터
학습목표: Swift의 강력한 함수 기능을 이해하고, 다양한 파라미터 타입을 활용할 수 있다.
4.1 함수의 기본 구조
// ===== 함수 정의와 호출 ===== // Swift 함수는 Python보다 더 명시적이고 타입 안전합니다. // 1. 가장 간단한 함수 func sayHello() { print("Hello, World!") } sayHello() // 함수 호출 // 2. 파라미터가 있는 함수 func greet(name: String) { print("Hello, \(name)!") } greet(name: "김철수") // 파라미터 레이블 필수 // 3. 반환값이 있는 함수 func square(number: Int) -> Int { return number * number } let result = square(number: 5) print(result) // 25 // 4. 단일 표현식 함수 (return 생략 가능) func multiply(a: Int, b: Int) -> Int { a * b // return 생략 } // 5. 여러 값 반환 (튜플 사용) func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) { var min = scores[0] var max = scores[0] var sum = 0 for score in scores { if score < min { min = score } if score > max { max = score } sum += score } return (min, max, sum) } let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9]) print(statistics.min) // 3 print(statistics.max) // 100 print(statistics.sum) // 120 // 6. 옵셔널 반환 타입 func findIndex(of value: String, in array: [String]) -> Int? { for (index, element) in array.enumerated() { if element == value { return index } } return nil // 찾지 못한 경우 } let fruits = ["apple", "banana", "orange"] if let index = findIndex(of: "banana", in: fruits) { print("Found at index \(index)") } // 7. 함수 타입 func addTwoInts(_ a: Int, _ b: Int) -> Int { return a + b } func multiplyTwoInts(_ a: Int, _ b: Int) -> Int { return a * b } // 함수 타입 변수 var mathFunction: (Int, Int) -> Int = addTwoInts print(mathFunction(2, 3)) // 5 mathFunction = multiplyTwoInts print(mathFunction(2, 3)) // 6
4.2 파라미터 레이블과 기본값
// ===== 파라미터 레이블 (Argument Labels) ===== // Swift의 독특한 기능 - 외부 레이블과 내부 이름 분리 // 1. 외부 레이블과 내부 파라미터 이름 func greet(person name: String, from hometown: String) { // 외부: person, from // 내부: name, hometown print("Hello \(name)! Glad you could visit from \(hometown).") } greet(person: "김철수", from: "부산") // 2. 외부 레이블 생략 (_) func add(_ a: Int, _ b: Int) -> Int { return a + b } let sum = add(5, 3) // 레이블 없이 호출 // 3. 의미 있는 레이블 사용 func move(from start: Int, to end: Int, by step: Int = 1) { for i in stride(from: start, through: end, by: step) { print("Position: \(i)") } } move(from: 0, to: 10, by: 2) // 가독성 높은 호출 move(from: 0, to: 5) // 기본값 사용 // ===== 기본값 파라미터 (Default Parameters) ===== // 1. 기본값 설정 func createUser( name: String, age: Int = 20, isStudent: Bool = true, major: String = "Computer Science" ) -> String { var description = "\(name), \(age)세" if isStudent { description += ", \(major) 전공" } return description } // 다양한 호출 방법 print(createUser(name: "김철수")) // 모든 기본값 사용 print(createUser(name: "이영희", age: 22)) print(createUser(name: "박민수", age: 25, isStudent: false)) // ===== 가변 파라미터 (Variadic Parameters) ===== // 1. 가변 개수의 파라미터 (Python의 *args와 유사) func calculateAverage(numbers: Double...) -> Double { guard !numbers.isEmpty else { return 0 } let total = numbers.reduce(0, +) return total / Double(numbers.count) } print(calculateAverage(numbers: 1, 2, 3, 4, 5)) // 3.0 print(calculateAverage(numbers: 90.5, 85.0, 92.3)) // 89.27 // 2. 가변 파라미터와 일반 파라미터 혼용 func printScores(studentName: String, scores: Int...) { print("\(studentName)의 점수:") for (index, score) in scores.enumerated() { print(" 과목 \(index + 1): \(score)점") } let average = scores.reduce(0, +) / scores.count print(" 평균: \(average)점") } printScores(studentName: "김철수", scores: 85, 90, 78, 92) // ===== In-Out 파라미터 ===== // 함수 내에서 파라미터 값을 변경하고 그 변경사항을 유지 // 1. 값 교환 함수 func swapTwoInts(_ a: inout Int, _ b: inout Int) { let temporaryA = a a = b b = temporaryA } var someInt = 3 var anotherInt = 107 swapTwoInts(&someInt, &anotherInt) // & 필수 print("someInt: \(someInt), anotherInt: \(anotherInt)") // someInt: 107, anotherInt: 3 // 2. 배열 수정 함수 func doubleAllValues(_ array: inout [Int]) { for i in 0..2 } } var numbers = [1, 2, 3, 4, 5] doubleAllValues(&numbers) print(numbers) // [2, 4, 6, 8, 10]
💡 함수 설계 베스트 프랙티스:
- 파라미터 레이블로 가독성 향상
- 기본값은 가장 일반적인 사용 케이스에 맞춰 설정
- inout은 꼭 필요한 경우에만 사용 (부작용 주의)
- 복잡한 반환값은 튜플보다 구조체 고려
Chapter 5. 종합 실습 프로젝트
프로젝트 목표: 2주차에 학습한 기초 문법만을 활용하여 간단한 계산기와 학생 관리 프로그램을 구현한다.
5.1 미니 프로젝트 1: 계산기 프로그램
배운 내용을 활용하여 간단한 계산기를 만들어봅니다.
// ===== 간단한 계산기 프로그램 ===== // 2주차에 배운 내용만 사용합니다 // 1. 기본 계산 함수들 func add(_ a: Double, _ b: Double) -> Double { return a + b } func subtract(_ a: Double, _ b: Double) -> Double { return a - b } func multiply(_ a: Double, _ b: Double) -> Double { return a * b } func divide(_ a: Double, _ b: Double) -> Double? { // 0으로 나누기 방지 if b == 0 { print("❌ 0으로 나눌 수 없습니다!") return nil } return a / b } // 2. 계산 기록 저장 var calculationHistory: [String] = [] // 3. 계산 수행 함수 func performCalculation(num1: Double, num2: Double, operation: String) { var result: Double? var resultString = "" // switch문으로 연산 선택 switch operation { case "+": result = add(num1, num2) resultString = "\(num1) + \(num2) = \(result!)" case "-": result = subtract(num1, num2) resultString = "\(num1) - \(num2) = \(result!)" case "*": result = multiply(num1, num2) resultString = "\(num1) × \(num2) = \(result!)" case "/": if let divResult = divide(num1, num2) { result = divResult resultString = "\(num1) ÷ \(num2) = \(result!)" } else { return // 0으로 나누기 에러 } default: print("❌ 잘못된 연산자입니다!") return } // 결과 출력 및 저장 print("✅ 결과: \(resultString)") calculationHistory.append(resultString) } // 4. 계산 기록 출력 func showHistory() { if calculationHistory.isEmpty { print("📭 계산 기록이 없습니다.") } else { print("\n📜 계산 기록:") for (index, record) in calculationHistory.enumerated() { print(" \(index + 1). \(record)") } } } // 5. 메인 실행 func runCalculator() { print("🧮 간단한 계산기 v1.0") print("====================") // 테스트 계산 수행 performCalculation(num1: 10, num2: 5, operation: "+") performCalculation(num1: 20, num2: 8, operation: "-") performCalculation(num1: 7, num2: 3, operation: "*") performCalculation(num1: 15, num2: 3, operation: "/") performCalculation(num1: 10, num2: 0, operation: "/") // 에러 테스트 // 기록 출력 showHistory() } // 프로그램 실행 runCalculator()
5.2 미니 프로젝트 2: 학생 점수 관리
딕셔너리와 배열을 활용한 간단한 학생 관리 시스템입니다.
// ===== 학생 점수 관리 시스템 ===== // 딕셔너리와 배열을 활용한 간단한 버전 // 1. 학생 정보 저장용 딕셔너리 // 키: 학생 이름, 값: 점수 배열 var students: [String: [Int]] = [:] // 2. 학생 추가 함수 func addStudent(name: String, scores: [Int]) { // 중복 체크 if students[name] != nil { print("⚠️ \(name) 학생은 이미 등록되어 있습니다.") return } // 점수 유효성 검사 for score in scores { if score < 0 || score > 100 { print("❌ 유효하지 않은 점수: \(score)") return } } // 학생 추가 students[name] = scores print("✅ \(name) 학생이 추가되었습니다.") } // 3. 평균 계산 함수 func calculateAverage(scores: [Int]) -> Double { if scores.isEmpty { return 0.0 } var sum = 0 for score in scores { sum += score } return Double(sum) / Double(scores.count) } // 4. 학점 계산 함수 func getGrade(average: Double) -> String { switch average { case 90...100: return "A" case 80..<90: return "B" case 70..<80: return "C" case 60..<70: return "D" default: return "F" } } // 5. 개별 학생 정보 출력 func printStudentInfo(name: String) { // 학생 존재 확인 if let scores = students[name] { let average = calculateAverage(scores: scores) let grade = getGrade(average: average) print(""" === \(name) 학생 정보 === 과목 점수: \(scores) 평균: \(String(format: "%.1f", average))점 학점: \(grade) """) } else { print("❌ \(name) 학생을 찾을 수 없습니다.") } } // 6. 전체 학생 목록 출력 func printAllStudents() { if students.isEmpty { print("📭 등록된 학생이 없습니다.") return } print("\n📊 전체 학생 성적") print("==================") for (name, scores) in students { let average = calculateAverage(scores: scores) let grade = getGrade(average: average) print("\(name): 평균 \(String(format: "%.1f", average))점 (\(grade))") } } // 7. 우수 학생 찾기 func findTopStudent() { if students.isEmpty { print("📭 등록된 학생이 없습니다.") return } var topName = "" var topAverage = 0.0 for (name, scores) in students { let average = calculateAverage(scores: scores) if average > topAverage { topAverage = average topName = name } } print("\n🏆 최우수 학생: \(topName) (평균: \(String(format: "%.1f", topAverage))점)") } // 8. 메인 실행 함수 func runStudentManager() { print("📚 학생 점수 관리 시스템") print("======================") // 샘플 데이터 추가 addStudent(name: "김철수", scores: [85, 90, 78, 92, 88]) addStudent(name: "이영희", scores: [92, 88, 95, 90, 93]) addStudent(name: "박민수", scores: [78, 82, 75, 80, 77]) addStudent(name: "최지우", scores: [95, 98, 92, 96, 94]) // 개별 학생 조회 printStudentInfo(name: "김철수") // 전체 목록 출력 printAllStudents() // 최우수 학생 찾기 findTopStudent() } // 프로그램 실행 runStudentManager()
🎯 스스로 해보기
위 프로젝트를 다음과 같이 개선해보세요:
- 계산기: 제곱, 제곱근 기능 추가
- 학생 관리: 과목별 최고/최저 점수 찾기
- 학생 관리: 특정 점수 이상인 학생만 필터링
- 두 프로그램을 하나로 합친 통합 프로그램 만들기
5.2 핵심 개념 정리
🎓 2주차 학습 요약
✅ 마스터한 개념들
- 변수와 상수: let과 var의 차이, 불변성의 중요성
- 타입 시스템: 타입 추론, 타입 안정성, 명시적 타입 변환
- 컬렉션: Array, Dictionary, Set, Tuple의 활용
- 제어 구조: if-else, switch의 강력한 패턴 매칭
- 반복문: for-in, while, repeat-while 활용
- 함수: 파라미터 레이블, 기본값, 가변 파라미터, inout
🔄 Python과의 주요 차이점
| 개념 | Python | Swift |
|---|---|---|
| 타입 시스템 | 동적 타이핑 | 정적 타이핑 |
| 변수 선언 | 단순 할당 | let/var 구분 |
| 타입 변환 | 자동 (암시적) | 명시적 필수 |
| switch문 | match (3.10+) | 패턴 매칭 강력 |
| 함수 호출 | 위치 기반 | 레이블 기반 |
⚠️ 주의사항 및 실수하기 쉬운 부분
- Swift의 배열 인덱스 범위를 벗어나면 런타임 에러 발생
- 딕셔너리 접근 시 항상 옵셔널 처리 필요
- switch문은 모든 경우를 처리해야 함 (exhaustive)
- 함수 호출 시 첫 번째 파라미터도 레이블 필요 (생략하려면 _)
- inout 파라미터는 호출 시 & 기호 필수
📚 다음 주 예고: SwiftUI 입문
3주차에는 본격적으로 SwiftUI를 시작합니다. 이번 주에 학습한 Swift 기초 문법을 바탕으로 실제 iOS 앱의 사용자 인터페이스를 구현하게 됩니다. View, Modifier, Stack 등 SwiftUI의 핵심 개념들을 학습하고, 첫 번째 iOS 앱을 만들어 볼 예정입니다.
💻 개발 환경 준비사항
- Xcode 15.0 이상 설치 (App Store에서 무료 다운로드)
- macOS Sonoma 14.0 이상 권장
- 최소 8GB RAM, 10GB 여유 저장공간
- Apple Developer 계정 생성 (무료)
'강의 > iOS개발 강의' 카테고리의 다른 글
| 상태 관리와 MVVM 기초 (0) | 2025.09.30 |
|---|---|
| SwiftUI 컴포넌트 가이드 (0) | 2025.09.30 |
| Week4_가위바위보 게임 (0) | 2025.09.23 |
| SwiftUI - Part2 (1) | 2025.09.16 |
| SwitfUI - Part 1 (0) | 2025.09.16 |