Swift 변수와 타입 | var, let, 옵셔널

Swift 변수와 타입 | var, let, 옵셔널

이 글의 핵심

Swift 변수와 타입에 대한 실전 가이드입니다. var, let, 옵셔널 등을 예제와 함께 설명합니다.

들어가며

타입 추론과 **옵셔널(?)**으로 “값이 없을 수 있음”을 타입에 표시합니다. guard let·if let으로 언랩 흐름을 명확히 두는 패턴이 흔합니다.


1. 변수와 상수

var vs let

// 변수 (var): 변경 가능
var name = "홍길동"
name = "김철수"  // OK
print(name)  // 김철수

// 상수 (let): 변경 불가
let age = 25
// age = 26  // 컴파일 에러!

// 권장: 기본적으로 let 사용, 필요할 때만 var
let maxCount = 100
var currentCount = 0

타입 추론

// 타입 추론 (권장)
let name = "홍길동"  // String으로 추론
let age = 25         // Int로 추론
let pi = 3.14        // Double로 추론

// 명시적 타입 (필요 시)
let name: String = "홍길동"
let age: Int = 25
let pi: Double = 3.14

2. 기본 타입

정수 타입

// Int (플랫폼에 따라 32bit 또는 64bit)
let int: Int = 10
let negativeInt: Int = -10

// UInt (부호 없는 정수)
let uint: UInt = 10
// let negativeUInt: UInt = -10  // 에러!

// 크기별 정수
let int8: Int8 = 127
let int16: Int16 = 32767
let int32: Int32 = 2147483647
let int64: Int64 = 9223372036854775807

// 최소/최대값
print(Int.min)  // -9223372036854775808
print(Int.max)  // 9223372036854775807

실수 타입

// Double (64bit, 기본)
let double: Double = 3.14159265359

// Float (32bit)
let float: Float = 3.14

// 타입 명시 필요
let pi = 3.14  // Double로 추론
let piFloat: Float = 3.14  // Float로 명시

문자열

let text: String = "Hello, Swift!"

// 문자열 보간
let name = "홍길동"
let age = 25
let message = "이름: \(name), 나이: \(age)"
print(message)

// 여러 줄 문자열
let multiline = """
첫 번째 줄
두 번째 줄
세 번째 줄
"""

// 문자열 연산
let hello = "Hello"
let world = "World"
let greeting = hello + ", " + world + "!"

불리언

let isActive: Bool = true
let isCompleted: Bool = false

// 논리 연산
let result1 = true && false  // false
let result2 = true || false  // true
let result3 = !true          // false

3. 옵셔널 (Optional)

옵셔널이란?

// 일반 변수: nil 불가
var name: String = "홍길동"
// name = nil  // 에러!

// 옵셔널: nil 가능
var optionalName: String? = "홍길동"
optionalName = nil  // OK

print(optionalName)  // Optional("홍길동") 또는 nil

옵셔널 바인딩 (if let)

옵셔널 값을 안전하게 추출하는 방법입니다:

var name: String? = "홍길동"

// if let: 옵셔널 바인딩
if let unwrappedName = name {
    // name이 nil이 아니면 이 블록 실행
    // unwrappedName: String 타입 (옵셔널 아님)
    // name의 값이 unwrappedName에 언래핑되어 할당됨
    print("이름: \(unwrappedName)")
    // unwrappedName은 이 블록 안에서만 유효
} else {
    // name이 nil이면 이 블록 실행
    print("이름 없음")
}

// 여러 옵셔널 동시 바인딩
let firstName: String? = "홍"
let lastName: String? = "길동"

if let first = firstName, let last = lastName {
    // 둘 다 nil이 아닐 때만 실행
    print("이름: \(first)\(last)")  // 이름: 홍길동
}

// guard let: 조기 반환 패턴
func greet(name: String?) {
    // guard: 조건이 false면 else 블록 실행 후 반환
    guard let name = name else {
        // name이 nil이면 여기 실행
        print("이름 없음")
        return  // 함수 종료 (guard는 반드시 return/throw/break 필요)
    }
    
    // 여기서는 name이 nil이 아님을 보장
    // name: String 타입 (언래핑됨)
    // guard let으로 추출한 변수는 함수 끝까지 유효
    print("안녕하세요, \(name)님!")
    // 추가 로직 작성 가능 (들여쓰기 깊이 감소)
}

greet(name: "홍길동")  // 안녕하세요, 홍길동님!
greet(name: nil)       // 이름 없음

if let vs guard let:

// if let: 옵셔널이 있을 때 처리
func processUser(user: User?) {
    if let user = user {
        // user 처리 (들여쓰기 깊어짐)
        print(user.name)
        // 여러 줄 로직...
    }
}

// guard let: 옵셔널이 없으면 조기 반환 (권장)
func processUser(user: User?) {
    guard let user = user else {
        return  // nil이면 즉시 종료
    }
    
    // 정상 흐름 (들여쓰기 얕음)
    print(user.name)
    // 여러 줄 로직...
}

여러 조건 체크:

func validateUser(name: String?, age: Int?, email: String?) {
    // 모든 값이 nil이 아닌지 한 번에 체크
    guard let name = name,
          let age = age,
          let email = email,
          age >= 18,  // 추가 조건도 가능
          email.contains("@") else {
        print("유효하지 않은 사용자")
        return
    }
    
    // 모든 조건을 통과한 경우
    print("유효한 사용자: \(name), \(age)세, \(email)")
}

옵셔널 체이닝

struct User {
    var name: String
    var email: String?
}

let user: User? = User(name: "홍길동", email: "[email protected]")

// 옵셔널 체이닝
let emailLength = user?.email?.count
print(emailLength)  // Optional(17)

// 여러 단계
let firstChar = user?.email?.first?.uppercased()

Nil 병합 연산자 (??)

let name: String? = nil
let displayName = name ?? "Guest"
print(displayName)  // Guest

// 체이닝
let email: String? = nil
let backup: String? = nil
let result = email ?? backup ?? "[email protected]"

강제 언래핑 (!)

let name: String? = "홍길동"

// 강제 언래핑 (위험!)
print(name!)  // 홍길동

// nil이면 크래시
let nilName: String? = nil
// print(nilName!)  // 런타임 에러!

// 암시적 언래핑 옵셔널
var implicitName: String! = "홍길동"
print(implicitName)  // 자동 언래핑

4. 타입 변환

// 정수 → 실수
let intValue: Int = 10
let doubleValue: Double = Double(intValue)

// 문자열 → 정수
let str = "123"
if let number = Int(str) {
    print(number)  // 123
}

// 실패 시 nil
let invalid = Int("abc")  // nil

5. 실전 예제

예제: 사용자 정보 처리

struct User {
    let name: String
    var age: Int
    var email: String?
}

func printUserInfo(user: User?) {
    guard let user = user else {
        print("사용자 없음")
        return
    }
    
    print("이름: \(user.name)")
    print("나이: \(user.age)")
    
    if let email = user.email {
        print("이메일: \(email)")
    } else {
        print("이메일 미등록")
    }
}

let user1 = User(name: "홍길동", age: 25, email: "[email protected]")
let user2 = User(name: "김철수", age: 30, email: nil)

printUserInfo(user: user1)
printUserInfo(user: user2)

정리

핵심 요약

  1. var: 변수(변경 가능), let: 상수(변경 불가)
  2. 기본 타입: Int, Double, String, Bool
  3. 옵셔널: nil 가능, ?로 표시
  4. 옵셔널 바인딩: if let, guard let
  5. 옵셔널 체이닝: ?.로 안전 접근
  6. Nil 병합: ??로 기본값

다음 단계

  • Swift 함수와 클로저
  • Swift 클래스와 구조체
  • Swift 프로토콜

관련 글

  • Java 변수와 타입 | 기본 타입, 참조 타입, 형변환
  • Kotlin 변수와 타입 | val, var, 기본 타입 완벽 정리
  • C++ numeric_limits |
  • C++ 템플릿 |
  • C++ 변수와 자료형 | int, double, string 완벽 정리 [초보자용]