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

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

이 글의 핵심

Swift 변수와 타입: var, let, 옵셔널. 변수와 상수·기본 타입.

들어가며

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

실무에서 마주한 현실

개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다. 특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.

1. 변수와 상수

var vs let

아래 코드는 swift를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 변수 (var): 변경 가능
var name = "홍길동"
name = "김철수"  // OK
print(name)  // 김철수
// 상수 (let): 변경 불가
let age = 25
// age = 26  // 컴파일 에러!
// 권장: 기본적으로 let 사용, 필요할 때만 var
let maxCount = 100
var currentCount = 0

타입 추론

아래 코드는 swift를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 타입 추론 (권장)
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. 기본 타입

정수 타입

다음은 swift를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 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

실수 타입

아래 코드는 swift를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 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로 명시

문자열

다음은 swift를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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 + "!"

불리언

아래 코드는 swift를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

let isActive: Bool = true
let isCompleted: Bool = false
// 논리 연산
let result1 = true && false  // false
let result2 = true || false  // true
let result3 = !true          // false

3. 옵셔널 (Optional)

옵셔널이란?

아래 코드는 swift를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// 일반 변수: nil 불가
var name: String = "홍길동"
// name = nil  // 에러!
// 옵셔널: nil 가능
var optionalName: String? = "홍길동"
optionalName = nil  // OK
print(optionalName)  // Optional("홍길동") 또는 nil

옵셔널 바인딩 (if let)

옵셔널 값을 안전하게 추출하는 방법입니다: 다음은 swift를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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: 다음은 swift를 활용한 상세한 구현 코드입니다. 함수를 통해 로직을 구현합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 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)
    // 여러 줄 로직...
}

여러 조건 체크: 아래 코드는 swift를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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)")
}

옵셔널 체이닝

아래 코드는 swift를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

struct User {
    var name: String
    var email: String?
}
let user: User? = User(name: "홍길동", email: "hong@example.com")
// 옵셔널 체이닝
let emailLength = user?.email?.count
print(emailLength)  // Optional(17)
// 여러 단계
let firstChar = user?.email?.first?.uppercased()

Nil 병합 연산자 (??)

아래 코드는 swift를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

let name: String? = nil
let displayName = name ?? "Guest"
print(displayName)  // Guest
// 체이닝
let email: String? = nil
let backup: String? = nil
let result = email ?? backup ?? "default@example.com"

강제 언래핑 (!)

아래 코드는 swift를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

let name: String? = "홍길동"
// 강제 언래핑 (위험!)
print(name!)  // 홍길동
// nil이면 크래시
let nilName: String? = nil
// print(nilName!)  // 런타임 에러!
// 암시적 언래핑 옵셔널
var implicitName: String! = "홍길동"
print(implicitName)  // 자동 언래핑

4. 타입 변환

아래 코드는 swift를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 정수 → 실수
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. 실전 예제

예제: 사용자 정보 처리

다음은 swift를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 함수를 통해 로직을 구현합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

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: "hong@example.com")
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 병합: ??로 기본값

다음 단계


관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3