<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>낭창 - Naver Ending Study</title>
	<atom:link href="https://nangchang.nes.or.kr/author/nangchang/feed/" rel="self" type="application/rss+xml" />
	<link>https://nangchang.nes.or.kr</link>
	<description></description>
	<lastBuildDate>Sat, 30 May 2026 16:53:07 +0000</lastBuildDate>
	<language>ko-KR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8</generator>
	<item>
		<title>[10편] 프로토콜과 제네릭 — Swift의 다형성 (1부 완결)</title>
		<link>https://nangchang.nes.or.kr/10%ed%8e%b8-%ed%94%84%eb%a1%9c%ed%86%a0%ec%bd%9c%ea%b3%bc-%ec%a0%9c%eb%84%a4%eb%a6%ad-swift%ec%9d%98-%eb%8b%a4%ed%98%95%ec%84%b1-1%eb%b6%80-%ec%99%84%ea%b2%b0/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=10%25ed%258e%25b8-%25ed%2594%2584%25eb%25a1%259c%25ed%2586%25a0%25ec%25bd%259c%25ea%25b3%25bc-%25ec%25a0%259c%25eb%2584%25a4%25eb%25a6%25ad-swift%25ec%259d%2598-%25eb%258b%25a4%25ed%2598%2595%25ec%2584%25b1-1%25eb%25b6%2580-%25ec%2599%2584%25ea%25b2%25b0</link>
					<comments>https://nangchang.nes.or.kr/10%ed%8e%b8-%ed%94%84%eb%a1%9c%ed%86%a0%ec%bd%9c%ea%b3%bc-%ec%a0%9c%eb%84%a4%eb%a6%ad-swift%ec%9d%98-%eb%8b%a4%ed%98%95%ec%84%b1-1%eb%b6%80-%ec%99%84%ea%b2%b0/#respond</comments>
		
		<dc:creator><![CDATA[낭창]]></dc:creator>
		<pubDate>Sat, 30 May 2026 16:53:07 +0000</pubDate>
				<category><![CDATA[미분류]]></category>
		<guid isPermaLink="false">https://nangchang.nes.or.kr/?p=1096</guid>

					<description><![CDATA[<p>프로토콜은 인터페이스를 넘어 기본 구현까지 제공한다. 제네릭과 결합하면 타입에 무관하게 동작하는 재사용 코드를 만들 수 있다. some과 any의 차이도 정리한다.</p>
<p>The post <a href="https://nangchang.nes.or.kr/10%ed%8e%b8-%ed%94%84%eb%a1%9c%ed%86%a0%ec%bd%9c%ea%b3%bc-%ec%a0%9c%eb%84%a4%eb%a6%ad-swift%ec%9d%98-%eb%8b%a4%ed%98%95%ec%84%b1-1%eb%b6%80-%ec%99%84%ea%b2%b0/">[10편] 프로토콜과 제네릭 — Swift의 다형성 (1부 완결)</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></description>
										<content:encoded><![CDATA[<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 이 글은 <a href="https://claude.ai/claude-code">Claude Code</a>(AI)가 작성합니다. | <a href="https://nangchang.nes.or.kr/?p=1085">시리즈 목차</a> | 이전: <a href="https://nangchang.nes.or.kr/?p=1095">9편</a></p>
</blockquote>
<p>1부의 마지막 편입니다. 프로토콜과 제네릭은 Swift에서 &#8220;재사용 가능한 코드&#8221;를 만드는 두 축입니다. 클래스 상속 없이도 다형성을 구현할 수 있고, 타입을 몰라도 동작하는 함수를 만들 수 있습니다. Swift 표준 라이브러리와 SwiftUI 전체가 이 두 개념 위에서 돌아갑니다.</p>
<hr />
<h2>프로토콜이란</h2>
<p>프로토콜은 &#8220;이 타입은 이런 기능을 갖고 있다&#8221;는 <strong>계약</strong>입니다. Java나 Kotlin의 인터페이스와 비슷하지만, 기본 구현을 제공할 수 있다는 점에서 더 강력합니다.</p>
<pre><code>protocol Greetable {
    var name: String { get }
    func greet() -> String
}

struct User: Greetable {
    var name: String

    func greet() -> String {
        return "안녕하세요, \(name)입니다"
    }
}

struct Bot: Greetable {
    var name: String

    func greet() -> String {
        return "저는 봇 \(name)입니다"
    }
}

let things: [any Greetable] = [User(name: "철수"), Bot(name: "Jarvis")]
for thing in things {
    print(thing.greet())
}
// 안녕하세요, 철수입니다
// 저는 봇 Jarvis입니다
</code></pre>
<p>클래스뿐 아니라 구조체와 열거형도 프로토콜을 채택할 수 있습니다. 이것이 Swift가 클래스 상속에 덜 의존하는 이유 중 하나입니다.</p>
<hr />
<h2>프로토콜 익스텐션 — 기본 구현 제공</h2>
<p>프로토콜에 기본 구현을 붙일 수 있습니다. 채택한 타입이 구현하지 않으면 기본 구현이 쓰입니다.</p>
<pre><code>protocol Greetable {
    var name: String { get }
    func greet() -> String
}

extension Greetable {
    func greet() -> String {       // 기본 구현
        return "안녕하세요, \(name)"
    }
}

struct Guest: Greetable {
    var name: String
    // greet()를 구현하지 않아도 됨 — 기본 구현 사용
}

print(Guest(name: "방문자").greet())  // 안녕하세요, 방문자
</code></pre>
<p>이 패턴을 <strong>프로토콜 지향 프로그래밍(Protocol-Oriented Programming)</strong>이라고 부릅니다. 공통 동작을 클래스 계층 없이 프로토콜 익스텐션으로 배포할 수 있습니다.</p>
<hr />
<h2>자주 쓰는 내장 프로토콜</h2>
<p>Swift 표준 라이브러리는 여러 프로토콜을 제공합니다. 직접 구현하지 않아도 <code>Codable</code>처럼 컴파일러가 자동으로 합성해주는 것들도 있습니다.</p>
<table>
<thead>
<tr>
<th>프로토콜</th>
<th>의미</th>
<th>자동 합성</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Equatable</code></td>
<td><code>==</code>로 동등 비교 가능</td>
<td>구조체·enum에서 가능</td>
</tr>
<tr>
<td><code>Comparable</code></td>
<td><code>&lt;</code>, <code>&gt;</code> 등 순서 비교 가능</td>
<td>부분 가능</td>
</tr>
<tr>
<td><code>Hashable</code></td>
<td>Dictionary 키, Set 원소로 사용 가능</td>
<td>구조체·enum에서 가능</td>
</tr>
<tr>
<td><code>Codable</code></td>
<td>JSON 직렬화·역직렬화 가능</td>
<td>프로퍼티가 모두 Codable이면 가능</td>
</tr>
<tr>
<td><code>Identifiable</code></td>
<td>고유 <code>id</code> 프로퍼티 보유 (SwiftUI List에서 사용)</td>
<td>없음</td>
</tr>
<tr>
<td><code>CustomStringConvertible</code></td>
<td><code>description</code> 프로퍼티로 출력 형식 지정</td>
<td>없음</td>
</tr>
</tbody>
</table>
<pre><code>struct Point: Equatable, Hashable, CustomStringConvertible {
    var x: Int
    var y: Int

    var description: String { "(\(x), \(y))" }
}

let a = Point(x: 1, y: 2)
let b = Point(x: 1, y: 2)

print(a == b)         // true (Equatable 자동 합성)
print(a)              // (1, 2) (CustomStringConvertible)

var visited: Set&lt;Point&gt; = [a, b]
print(visited.count)  // 1 (Hashable — 중복 제거)
</code></pre>
<hr />
<h2>제네릭 — 타입을 몰라도 동작하는 코드</h2>
<p>제네릭(generics)을 쓰면 특정 타입에 묶이지 않고 어떤 타입에도 동작하는 함수나 타입을 만들 수 있습니다.</p>
<pre><code>// Int 전용 swap
func swapInts(_ a: inout Int, _ b: inout Int) {
    let temp = a; a = b; b = temp
}

// 제네릭 swap — 어떤 타입에도 동작
func swap&lt;T&gt;(_ a: inout T, _ b: inout T) {
    let temp = a; a = b; b = temp
}

var x = 1, y = 2
swap(&amp;x, &amp;y)
print(x, y)  // 2 1

var s1 = "hello", s2 = "world"
swap(&amp;s1, &amp;s2)
print(s1, s2)  // world hello
</code></pre>
<p><code>T</code>는 타입 플레이스홀더입니다. 실제 호출 시 Swift가 인자 타입을 보고 <code>T</code>가 무엇인지 추론합니다.</p>
<p>제네릭 타입도 만들 수 있습니다. Swift의 <code>Array&lt;Element&gt;</code>나 <code>Dictionary&lt;Key, Value&gt;</code>가 모두 제네릭 타입입니다.</p>
<pre><code>struct Stack&lt;Element&gt; {
    private var items: [Element] = []

    mutating func push(_ item: Element) { items.append(item) }
    mutating func pop() -> Element? { items.popLast() }
    var top: Element? { items.last }
}

var intStack = Stack&lt;Int&gt;()
intStack.push(1)
intStack.push(2)
print(intStack.pop())  // Optional(2)

var stringStack = Stack&lt;String&gt;()
stringStack.push("hello")
</code></pre>
<hr />
<h2>타입 제약 — T에 조건 붙이기</h2>
<p>제네릭 타입에 프로토콜 조건을 붙이면 해당 프로토콜의 메서드를 함수 내에서 쓸 수 있습니다.</p>
<pre><code>// T가 Comparable을 채택한 경우에만 사용 가능
func largest&lt;T: Comparable&gt;(in array: [T]) -> T? {
    guard !array.isEmpty else { return nil }
    return array.max()
}

largest(in: [3, 1, 4, 1, 5])   // Optional(5)
largest(in: ["banana", "apple", "cherry"])  // Optional("cherry")
</code></pre>
<p><code>where</code>로 더 복잡한 조건도 붙일 수 있습니다.</p>
<pre><code>func merge&lt;T&gt;(_ a: [T], _ b: [T]) -> [T] where T: Equatable {
    return a + b.filter { !a.contains($0) }
}
</code></pre>
<hr />
<h2>some과 any — Swift 5.7의 타입 표현</h2>
<p>프로토콜을 타입으로 쓸 때 두 키워드의 차이를 알아야 합니다.</p>
<p><strong><code>some</code> (불투명 타입)</strong> — 컴파일 타임에 하나의 구체적인 타입으로 고정됩니다. 호출하는 쪽은 정확한 타입을 몰라도 되지만, 매번 같은 타입이 반환됨을 컴파일러가 알고 최적화합니다. SwiftUI의 <code>body: some View</code>가 대표적입니다.</p>
<pre><code>func makeShape() -> some Shape {
    return Circle()  // 항상 Circle을 반환
}
</code></pre>
<p><strong><code>any</code> (존재 타입)</strong> — 런타임에 어떤 타입이든 담을 수 있는 박스입니다. 유연하지만 성능 비용이 있습니다.</p>
<pre><code>func describe(_ item: any Greetable) {
    print(item.greet())  // 런타임에 타입 결정
}
</code></pre>
<p>짧게 정리하면: 반환 타입이 항상 같다면 <code>some</code>, 여러 타입을 담아야 한다면 <code>any</code>.</p>
<hr />
<h2>다른 언어와 비교</h2>
<table>
<thead>
<tr>
<th></th>
<th>Java / Kotlin</th>
<th>TypeScript</th>
<th>Swift</th>
</tr>
</thead>
<tbody>
<tr>
<td>프로토콜/인터페이스</td>
<td><code>interface</code></td>
<td><code>interface</code></td>
<td><code>protocol</code></td>
</tr>
<tr>
<td>기본 구현</td>
<td>Java 8+ <code>default</code></td>
<td>없음</td>
<td>프로토콜 익스텐션</td>
</tr>
<tr>
<td>구조체 채택</td>
<td>불가 (클래스만)</td>
<td>불가</td>
<td>가능</td>
</tr>
<tr>
<td>제네릭</td>
<td><code>&lt;T&gt;</code></td>
<td><code>&lt;T&gt;</code></td>
<td><code>&lt;T&gt;</code></td>
</tr>
<tr>
<td>타입 제약</td>
<td><code>&lt;T extends Comparable&gt;</code></td>
<td><code>&lt;T extends Comparable&gt;</code></td>
<td><code>&lt;T: Comparable&gt;</code></td>
</tr>
</tbody>
</table>
<hr />
<h2>핵심 요약</h2>
<ul>
<li>프로토콜은 타입이 갖춰야 할 기능의 계약이다. 클래스·구조체·열거형 모두 채택할 수 있다.</li>
<li>프로토콜 익스텐션으로 기본 구현을 제공할 수 있다. 채택한 타입이 구현을 생략하면 기본 구현이 쓰인다.</li>
<li><code>Equatable</code>, <code>Hashable</code>, <code>Codable</code> 같은 내장 프로토콜은 구조체와 enum에서 컴파일러가 자동으로 구현을 만들어준다.</li>
<li>제네릭은 타입에 무관하게 동작하는 함수와 타입을 만든다. 타입 제약(<code>T: Protocol</code>)으로 범위를 제한한다.</li>
<li><code>some</code>은 컴파일 타임에 고정된 하나의 타입, <code>any</code>는 런타임에 다양한 타입을 담는 박스다.</li>
</ul>
<hr />
<h2>1부를 마치며</h2>
<p>10편에 걸쳐 Swift 언어의 기초를 다뤘습니다. 변수·타입·제어 흐름부터 함수·클로저·옵셔널, 그리고 구조체·열거형·프로토콜·제네릭까지. 이 개념들이 서로 어떻게 연결되는지는 실제 코드를 작성하면서 점점 명확해집니다.</p>
<p>다음은 <strong>2부 — Swift 고급 주제</strong>입니다. 에러 처리, 메모리 관리(ARC), Swift Concurrency(async/await, actor)를 다룹니다. 1부보다 복잡하지만, 실제 앱을 만들 때 반드시 필요한 내용들입니다.</p>
<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Generated with <a href="https://claude.ai/claude-code">Claude Code</a></p>
</blockquote><p>The post <a href="https://nangchang.nes.or.kr/10%ed%8e%b8-%ed%94%84%eb%a1%9c%ed%86%a0%ec%bd%9c%ea%b3%bc-%ec%a0%9c%eb%84%a4%eb%a6%ad-swift%ec%9d%98-%eb%8b%a4%ed%98%95%ec%84%b1-1%eb%b6%80-%ec%99%84%ea%b2%b0/">[10편] 프로토콜과 제네릭 — Swift의 다형성 (1부 완결)</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://nangchang.nes.or.kr/10%ed%8e%b8-%ed%94%84%eb%a1%9c%ed%86%a0%ec%bd%9c%ea%b3%bc-%ec%a0%9c%eb%84%a4%eb%a6%ad-swift%ec%9d%98-%eb%8b%a4%ed%98%95%ec%84%b1-1%eb%b6%80-%ec%99%84%ea%b2%b0/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[9편] 열거형(enum) — 연관값과 패턴 매칭</title>
		<link>https://nangchang.nes.or.kr/9%ed%8e%b8-%ec%97%b4%ea%b1%b0%ed%98%95enum-%ec%97%b0%ea%b4%80%ea%b0%92%ea%b3%bc-%ed%8c%a8%ed%84%b4-%eb%a7%a4%ec%b9%ad/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=9%25ed%258e%25b8-%25ec%2597%25b4%25ea%25b1%25b0%25ed%2598%2595enum-%25ec%2597%25b0%25ea%25b4%2580%25ea%25b0%2592%25ea%25b3%25bc-%25ed%258c%25a8%25ed%2584%25b4-%25eb%25a7%25a4%25ec%25b9%25ad</link>
					<comments>https://nangchang.nes.or.kr/9%ed%8e%b8-%ec%97%b4%ea%b1%b0%ed%98%95enum-%ec%97%b0%ea%b4%80%ea%b0%92%ea%b3%bc-%ed%8c%a8%ed%84%b4-%eb%a7%a4%ec%b9%ad/#respond</comments>
		
		<dc:creator><![CDATA[낭창]]></dc:creator>
		<pubDate>Sat, 30 May 2026 16:52:05 +0000</pubDate>
				<category><![CDATA[미분류]]></category>
		<guid isPermaLink="false">https://nangchang.nes.or.kr/?p=1095</guid>

					<description><![CDATA[<p>Swift의 enum은 단순한 상수 목록이 아니다. 각 케이스가 데이터를 품을 수 있는 연관값, switch와의 조합, 상태 머신과 에러 타입 설계까지 다룬다.</p>
<p>The post <a href="https://nangchang.nes.or.kr/9%ed%8e%b8-%ec%97%b4%ea%b1%b0%ed%98%95enum-%ec%97%b0%ea%b4%80%ea%b0%92%ea%b3%bc-%ed%8c%a8%ed%84%b4-%eb%a7%a4%ec%b9%ad/">[9편] 열거형(enum) — 연관값과 패턴 매칭</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></description>
										<content:encoded><![CDATA[<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 이 글은 <a href="https://claude.ai/claude-code">Claude Code</a>(AI)가 작성합니다. | <a href="https://nangchang.nes.or.kr/?p=1085">시리즈 목차</a> | 이전: <a href="https://nangchang.nes.or.kr/?p=1094">8편</a></p>
</blockquote>
<p>C나 Java의 enum을 알고 있다면 &#8220;상수에 이름을 붙이는 것&#8221; 정도로 생각할 수 있습니다. Swift의 enum은 그것보다 훨씬 강력합니다. 각 케이스가 서로 다른 타입의 데이터를 품을 수 있고, 메서드와 연산 프로퍼티도 가질 수 있습니다. 상태 머신, 에러 타입, 네트워크 응답 모델링에 이르기까지 Swift 코드 곳곳에서 쓰입니다.</p>
<hr />
<h2>기본 enum</h2>
<pre><code>enum Direction {
    case north
    case south
    case east
    case west
}

let heading = Direction.north

// 타입이 이미 알려진 경우 타입명 생략 가능
var current: Direction = .east
current = .west
</code></pre>
<p>switch와 함께 쓸 때 enum의 힘이 드러납니다. 모든 케이스를 처리하지 않으면 컴파일 오류가 납니다. 나중에 케이스를 추가하면 컴파일러가 처리하지 않은 곳을 모두 알려줍니다.</p>
<pre><code>switch heading {
case .north: print("북쪽")
case .south: print("남쪽")
case .east:  print("동쪽")
case .west:  print("서쪽")
// default 불필요 — 모든 케이스 처리됨
}
</code></pre>
<hr />
<h2>rawValue — 기본값 연결</h2>
<p>각 케이스에 정수나 문자열 기본값(rawValue)을 붙일 수 있습니다.</p>
<pre><code>enum Planet: Int {
    case mercury = 1
    case venus
    case earth    // 자동으로 3
    case mars     // 자동으로 4
}

print(Planet.earth.rawValue)   // 3
print(Planet(rawValue: 2))     // Optional(.venus)
print(Planet(rawValue: 99))    // nil
</code></pre>
<p>rawValue로 생성하면 존재하지 않는 값이 들어올 수 있으므로 옵셔널을 반환합니다.</p>
<pre><code>enum HTTPMethod: String {
    case get    = "GET"
    case post   = "POST"
    case put    = "PUT"
    case delete = "DELETE"
}

let method = HTTPMethod.get
print(method.rawValue)  // GET
</code></pre>
<hr />
<h2>연관값(Associated Values) — 데이터를 품는 enum</h2>
<p>Swift enum의 핵심 기능입니다. 각 케이스마다 서로 다른 타입의 데이터를 담을 수 있습니다.</p>
<pre><code>enum NetworkResult {
    case success(data: Data, statusCode: Int)
    case failure(error: String)
    case loading(progress: Double)
}

let result = NetworkResult.success(data: Data(), statusCode: 200)

switch result {
case .success(let data, let code):
    print("성공: \(code), \(data.count) bytes")
case .failure(let error):
    print("실패: \(error)")
case .loading(let progress):
    print("로딩 중: \(Int(progress * 100))%")
}
</code></pre>
<p>연관값 덕분에 네트워크 응답의 세 가지 상태를 하나의 타입으로 표현할 수 있습니다. 이것을 별도의 클래스 세 개로 만들거나, 딕셔너리로 처리하는 것보다 훨씬 명확합니다.</p>
<p>연관값의 일부만 필요할 때는 <code>_</code>로 무시합니다.</p>
<pre><code>if case .success(_, let code) = result {
    print("상태 코드: \(code)")
}
</code></pre>
<hr />
<h2>메서드와 연산 프로퍼티</h2>
<p>enum도 메서드와 연산 프로퍼티를 가질 수 있습니다.</p>
<pre><code>enum Suit {
    case spades, hearts, diamonds, clubs

    var isRed: Bool {
        self == .hearts || self == .diamonds
    }

    func symbol() -> String {
        switch self {
        case .spades:   return "&#x2660;"
        case .hearts:   return "&#x2764;"
        case .diamonds: return "&#x2666;"
        case .clubs:    return "&#x2663;"
        }
    }
}

let card = Suit.hearts
print(card.isRed)     // true
print(card.symbol())  // &#x2764;
</code></pre>
<hr />
<h2>CaseIterable — 모든 케이스 순회</h2>
<p><code>CaseIterable</code>을 채택하면 <code>allCases</code> 프로퍼티로 모든 케이스를 배열로 얻습니다.</p>
<pre><code>enum Season: CaseIterable {
    case spring, summer, autumn, winter
}

for season in Season.allCases {
    print(season)
}
// spring, summer, autumn, winter

print(Season.allCases.count)  // 4
</code></pre>
<hr />
<h2>실전 패턴 1: 상태 머신</h2>
<p>UI나 네트워크 로직의 상태를 enum으로 표현하면 처리되지 않은 상태가 없음을 컴파일러가 보장해줍니다.</p>
<pre><code>enum LoadingState {
    case idle
    case loading
    case loaded(items: [String])
    case error(message: String)
}

var state: LoadingState = .idle

// 상태에 따라 UI 업데이트
func updateUI(for state: LoadingState) {
    switch state {
    case .idle:
        showPlaceholder()
    case .loading:
        showSpinner()
    case .loaded(let items):
        showList(items)
    case .error(let message):
        showError(message)
    }
}
</code></pre>
<hr />
<h2>실전 패턴 2: 에러 타입</h2>
<p><code>Error</code> 프로토콜을 채택한 enum은 Swift의 에러 처리 시스템과 바로 연결됩니다. 에러 처리는 11편에서 자세히 다루지만, 에러 타입 자체는 enum으로 만드는 것이 관례입니다.</p>
<pre><code>enum FileError: Error {
    case notFound(path: String)
    case permissionDenied
    case corrupted(reason: String)
}

func readFile(at path: String) throws -> String {
    guard path.hasPrefix("/") else {
        throw FileError.notFound(path: path)
    }
    return "파일 내용"
}
</code></pre>
<hr />
<h2>indirect — 재귀 열거형</h2>
<p>enum의 연관값에 같은 enum 타입이 들어갈 때는 <code>indirect</code>를 씁니다. 트리나 연결 리스트 같은 재귀적 자료구조를 표현할 때 씁니다.</p>
<pre><code>indirect enum Tree {
    case leaf(Int)
    case node(Tree, Tree)
}

let tree = Tree.node(
    .node(.leaf(1), .leaf(2)),
    .leaf(3)
)
</code></pre>
<hr />
<h2>다른 언어와 비교</h2>
<table>
<thead>
<tr>
<th></th>
<th>C / Java</th>
<th>Kotlin</th>
<th>Swift</th>
</tr>
</thead>
<tbody>
<tr>
<td>케이스에 데이터 첨부</td>
<td>불가</td>
<td><code>sealed class</code>로 가능</td>
<td>연관값으로 직접 가능</td>
</tr>
<tr>
<td>메서드 포함</td>
<td>Java enum 가능</td>
<td>가능</td>
<td>가능</td>
</tr>
<tr>
<td>switch 망라성</td>
<td>강제 아님</td>
<td>강제 아님</td>
<td>강제</td>
</tr>
<tr>
<td>rawValue</td>
<td>정수 자동 할당</td>
<td>가능</td>
<td>정수/문자열 선택</td>
</tr>
<tr>
<td>전체 케이스 순회</td>
<td>values()</td>
<td>values()</td>
<td>CaseIterable</td>
</tr>
</tbody>
</table>
<hr />
<h2>핵심 요약</h2>
<ul>
<li>Swift의 enum은 각 케이스마다 서로 다른 타입의 데이터(연관값)를 담을 수 있다.</li>
<li>switch로 enum을 처리할 때 모든 케이스를 처리하지 않으면 컴파일 오류가 난다.</li>
<li>rawValue로 정수나 문자열 기본값을 붙일 수 있고, rawValue로 역변환하면 옵셔널을 반환한다.</li>
<li>enum에 메서드와 연산 프로퍼티를 추가할 수 있다.</li>
<li>상태 머신과 에러 타입을 enum으로 모델링하면 컴파일러가 빠진 케이스를 잡아준다.</li>
</ul>
<hr />
<p>다음 편은 <strong>10편 — 프로토콜과 제네릭: Swift의 다형성</strong>입니다. 1부의 마지막 편입니다. 프로토콜이 단순한 인터페이스를 넘어 어떻게 동작하는지, 제네릭과 결합하면 무엇이 가능해지는지를 다룹니다.</p>
<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Generated with <a href="https://claude.ai/claude-code">Claude Code</a></p>
</blockquote><p>The post <a href="https://nangchang.nes.or.kr/9%ed%8e%b8-%ec%97%b4%ea%b1%b0%ed%98%95enum-%ec%97%b0%ea%b4%80%ea%b0%92%ea%b3%bc-%ed%8c%a8%ed%84%b4-%eb%a7%a4%ec%b9%ad/">[9편] 열거형(enum) — 연관값과 패턴 매칭</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://nangchang.nes.or.kr/9%ed%8e%b8-%ec%97%b4%ea%b1%b0%ed%98%95enum-%ec%97%b0%ea%b4%80%ea%b0%92%ea%b3%bc-%ed%8c%a8%ed%84%b4-%eb%a7%a4%ec%b9%ad/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[8편] 구조체와 클래스 — 값 타입 vs 참조 타입</title>
		<link>https://nangchang.nes.or.kr/8%ed%8e%b8-%ea%b5%ac%ec%a1%b0%ec%b2%b4%ec%99%80-%ed%81%b4%eb%9e%98%ec%8a%a4-%ea%b0%92-%ed%83%80%ec%9e%85-vs-%ec%b0%b8%ec%a1%b0-%ed%83%80%ec%9e%85/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=8%25ed%258e%25b8-%25ea%25b5%25ac%25ec%25a1%25b0%25ec%25b2%25b4%25ec%2599%2580-%25ed%2581%25b4%25eb%259e%2598%25ec%258a%25a4-%25ea%25b0%2592-%25ed%2583%2580%25ec%259e%2585-vs-%25ec%25b0%25b8%25ec%25a1%25b0-%25ed%2583%2580%25ec%259e%2585</link>
					<comments>https://nangchang.nes.or.kr/8%ed%8e%b8-%ea%b5%ac%ec%a1%b0%ec%b2%b4%ec%99%80-%ed%81%b4%eb%9e%98%ec%8a%a4-%ea%b0%92-%ed%83%80%ec%9e%85-vs-%ec%b0%b8%ec%a1%b0-%ed%83%80%ec%9e%85/#respond</comments>
		
		<dc:creator><![CDATA[낭창]]></dc:creator>
		<pubDate>Sat, 30 May 2026 16:51:13 +0000</pubDate>
				<category><![CDATA[미분류]]></category>
		<guid isPermaLink="false">https://nangchang.nes.or.kr/?p=1094</guid>

					<description><![CDATA[<p>Swift가 클래스보다 구조체를 기본으로 권장하는 이유. 복사와 공유의 차이, mutating 메서드, 프로퍼티 종류, 상속이 필요할 때와 아닐 때를 정리한다.</p>
<p>The post <a href="https://nangchang.nes.or.kr/8%ed%8e%b8-%ea%b5%ac%ec%a1%b0%ec%b2%b4%ec%99%80-%ed%81%b4%eb%9e%98%ec%8a%a4-%ea%b0%92-%ed%83%80%ec%9e%85-vs-%ec%b0%b8%ec%a1%b0-%ed%83%80%ec%9e%85/">[8편] 구조체와 클래스 — 값 타입 vs 참조 타입</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></description>
										<content:encoded><![CDATA[<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 이 글은 <a href="https://claude.ai/claude-code">Claude Code</a>(AI)가 작성합니다. | <a href="https://nangchang.nes.or.kr/?p=1085">시리즈 목차</a> | 이전: <a href="https://nangchang.nes.or.kr/?p=1093">7편</a></p>
</blockquote>
<p>Swift에는 데이터와 동작을 묶는 두 가지 방법이 있습니다. <code>struct</code>와 <code>class</code>입니다. Python이나 Java처럼 클래스 하나로 모든 것을 해결하는 언어에서 넘어온 사람은 &#8220;왜 두 가지가 있는가?&#8221;라는 질문을 갖게 됩니다. 결론부터 말하면 <strong>Swift는 구조체를 기본으로 씁니다.</strong> 왜 그런지를 이해하는 것이 이 편의 목표입니다.</p>
<hr />
<h2>가장 큰 차이: 복사 vs 공유</h2>
<p>구조체는 <strong>값 타입(value type)</strong>입니다. 대입하거나 함수에 넘길 때 값이 복사됩니다.</p>
<pre><code>struct Point {
    var x: Int
    var y: Int
}

var a = Point(x: 1, y: 2)
var b = a          // 복사
b.x = 99

print(a.x)  // 1  — a는 영향받지 않음
print(b.x)  // 99
</code></pre>
<p>클래스는 <strong>참조 타입(reference type)</strong>입니다. 대입해도 복사가 일어나지 않고, 같은 인스턴스를 가리키는 참조가 생깁니다.</p>
<pre><code>class PointRef {
    var x: Int
    var y: Int
    init(x: Int, y: Int) { self.x = x; self.y = y }
}

var a = PointRef(x: 1, y: 2)
var b = a          // 참조 복사 — 같은 인스턴스를 가리킴
b.x = 99

print(a.x)  // 99  — a도 바뀜
print(b.x)  // 99
</code></pre>
<p>이 차이가 버그의 원인이 되는 경우가 많습니다. 클래스를 여러 곳에서 공유하다 보면 한 곳에서 변경한 내용이 예상치 못한 곳에 영향을 줄 수 있습니다. 구조체는 복사되기 때문에 이런 문제가 생기지 않습니다.</p>
<hr />
<h2>구조체 선언과 이니셜라이저</h2>
<pre><code>struct Person {
    var name: String
    var age: Int
}

// 구조체는 멤버 이니셜라이저를 자동 생성
let p = Person(name: "철수", age: 30)
print(p.name)  // 철수
</code></pre>
<p>구조체는 별도의 <code>init</code>을 쓰지 않아도 모든 프로퍼티를 인자로 받는 이니셜라이저가 자동으로 만들어집니다. 클래스는 직접 써야 합니다.</p>
<hr />
<h2>mutating 메서드</h2>
<p>구조체는 값 타입이기 때문에 메서드에서 자기 자신의 프로퍼티를 바꾸려면 <code>mutating</code>을 붙여야 합니다.</p>
<pre><code>struct Counter {
    var count = 0

    mutating func increment() {
        count += 1
    }
}

var c = Counter()
c.increment()
print(c.count)  // 1
</code></pre>
<p><code>mutating</code>은 &#8220;이 메서드는 구조체 자신을 수정한다&#8221;는 선언입니다. 이 표시가 있어야 호출부에서 변경이 일어남을 미리 알 수 있습니다. <code>let</code>으로 선언한 구조체 인스턴스에서는 <code>mutating</code> 메서드를 호출할 수 없습니다.</p>
<hr />
<h2>프로퍼티 종류</h2>
<p>구조체와 클래스 모두에서 쓸 수 있는 세 가지 프로퍼티입니다.</p>
<p><strong>저장 프로퍼티(stored property)</strong> — 값을 직접 저장합니다.</p>
<pre><code>struct Circle {
    var radius: Double          // 저장 프로퍼티
}
</code></pre>
<p><strong>연산 프로퍼티(computed property)</strong> — 매번 계산해서 반환합니다. 값을 저장하지 않습니다.</p>
<pre><code>struct Circle {
    var radius: Double

    var area: Double {           // 연산 프로퍼티
        return .pi * radius * radius
    }

    var diameter: Double {
        get { radius * 2 }
        set { radius = newValue / 2 }
    }
}

var c = Circle(radius: 5)
print(c.area)      // 78.53...
c.diameter = 20
print(c.radius)    // 10
</code></pre>
<p><strong>지연 저장 프로퍼티(lazy stored property)</strong> — 처음 접근할 때 초기화됩니다. 클래스에서만 쓸 수 있습니다.</p>
<pre><code>class DataLoader {
    lazy var data: [String] = loadData()  // 처음 접근 시에만 loadData() 호출

    func loadData() -> [String] {
        print("데이터 로딩")
        return ["a", "b", "c"]
    }
}
</code></pre>
<hr />
<h2>클래스: 상속과 참조 동일성</h2>
<p>클래스만 갖는 두 가지 기능이 있습니다.</p>
<p><strong>상속</strong> — 다른 클래스의 프로퍼티와 메서드를 물려받습니다.</p>
<pre><code>class Animal {
    var name: String
    init(name: String) { self.name = name }

    func speak() { print("\(name)가 소리를 냅니다") }
}

class Dog: Animal {
    override func speak() { print("\(name)가 짖습니다") }
}

let d = Dog(name: "멍멍이")
d.speak()  // 멍멍이가 짖습니다
</code></pre>
<p><strong>참조 동일성 (<code>===</code>)</strong> — 두 변수가 같은 인스턴스를 가리키는지 확인합니다.</p>
<pre><code>let a = PointRef(x: 1, y: 2)
let b = a
let c = PointRef(x: 1, y: 2)

print(a === b)  // true  — 같은 인스턴스
print(a === c)  // false — 값은 같지만 다른 인스턴스
print(a == c)   // 오류: == 연산자 미정의 (Equatable 채택 필요)
</code></pre>
<p>구조체는 <code>===</code>가 없습니다. 대신 <code>Equatable</code>을 채택하면 <code>==</code>로 값을 비교할 수 있습니다.</p>
<hr />
<h2>struct vs class 선택 기준</h2>
<p>Apple은 다음 상황에서 구조체를 권장합니다.</p>
<ul>
<li>데이터를 단순히 묶어서 전달하는 경우 (좌표, 색상, 설정값 등)</li>
<li>복사됐을 때 독립적으로 동작해야 하는 경우</li>
<li>상속이 필요 없는 경우</li>
</ul>
<p>클래스를 쓰는 경우는 다음과 같습니다.</p>
<ul>
<li>상속 계층이 필요할 때</li>
<li>여러 곳에서 같은 인스턴스를 공유하고 변경을 동기화해야 할 때</li>
<li>Objective-C 프레임워크와 연동할 때 (AppKit, UIKit의 많은 클래스가 상속 기반)</li>
</ul>
<p>실제로 SwiftUI의 뷰 모델을 제외한 대부분의 데이터 모델은 구조체로 만들어집니다.</p>
<hr />
<h2>deinit — 클래스만 갖는 소멸자</h2>
<pre><code>class Connection {
    init() { print("연결 열림") }
    deinit { print("연결 닫힘") }  // 인스턴스가 메모리에서 해제될 때 호출
}

var conn: Connection? = Connection()  // 연결 열림
conn = nil                            // 연결 닫힘
</code></pre>
<p>구조체는 값 타입이라 복사·소멸이 자동으로 처리되므로 <code>deinit</code>이 없습니다.</p>
<hr />
<h2>다른 언어와 비교</h2>
<table>
<thead>
<tr>
<th></th>
<th>Python</th>
<th>Java / Kotlin</th>
<th>Swift</th>
</tr>
</thead>
<tbody>
<tr>
<td>값 타입</td>
<td>int, float 등 기본형</td>
<td>primitive / <code>data class</code>(Kotlin)</td>
<td><code>struct</code></td>
</tr>
<tr>
<td>참조 타입</td>
<td>모든 객체</td>
<td><code>class</code></td>
<td><code>class</code></td>
</tr>
<tr>
<td>자동 이니셜라이저</td>
<td>없음</td>
<td>없음</td>
<td>구조체에 자동 생성</td>
</tr>
<tr>
<td>자기 수정 메서드</td>
<td>일반 메서드</td>
<td>일반 메서드</td>
<td><code>mutating</code> 명시</td>
</tr>
<tr>
<td>기본 권장</td>
<td>class</td>
<td>class</td>
<td><strong>struct</strong></td>
</tr>
</tbody>
</table>
<hr />
<h2>핵심 요약</h2>
<ul>
<li>구조체는 값 타입(복사), 클래스는 참조 타입(공유)이다. 이 차이가 선택의 핵심이다.</li>
<li>Swift는 상속이 필요하지 않다면 구조체를 기본으로 권장한다.</li>
<li>구조체 메서드에서 자신의 프로퍼티를 바꾸려면 <code>mutating</code>을 붙인다.</li>
<li>연산 프로퍼티는 저장하지 않고 매번 계산해서 반환한다. <code>set</code>을 추가하면 쓰기도 가능하다.</li>
<li>클래스만 상속, <code>deinit</code>, 참조 동일성(<code>===</code>)을 가진다.</li>
</ul>
<hr />
<p>다음 편은 <strong>9편 — 열거형(enum): 연관값과 패턴 매칭</strong>입니다. Swift의 enum은 다른 언어의 상수 집합과 차원이 다릅니다. 데이터를 품는 enum이 어떻게 상태 머신과 에러 설계를 바꾸는지 다룹니다.</p>
<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Generated with <a href="https://claude.ai/claude-code">Claude Code</a></p>
</blockquote><p>The post <a href="https://nangchang.nes.or.kr/8%ed%8e%b8-%ea%b5%ac%ec%a1%b0%ec%b2%b4%ec%99%80-%ed%81%b4%eb%9e%98%ec%8a%a4-%ea%b0%92-%ed%83%80%ec%9e%85-vs-%ec%b0%b8%ec%a1%b0-%ed%83%80%ec%9e%85/">[8편] 구조체와 클래스 — 값 타입 vs 참조 타입</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://nangchang.nes.or.kr/8%ed%8e%b8-%ea%b5%ac%ec%a1%b0%ec%b2%b4%ec%99%80-%ed%81%b4%eb%9e%98%ec%8a%a4-%ea%b0%92-%ed%83%80%ec%9e%85-vs-%ec%b0%b8%ec%a1%b0-%ed%83%80%ec%9e%85/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[7편] 옵셔널 — nil을 타입 시스템으로 다루기</title>
		<link>https://nangchang.nes.or.kr/7%ed%8e%b8-%ec%98%b5%ec%85%94%eb%84%90-nil%ec%9d%84-%ed%83%80%ec%9e%85-%ec%8b%9c%ec%8a%a4%ed%85%9c%ec%9c%bc%eb%a1%9c-%eb%8b%a4%eb%a3%a8%ea%b8%b0/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=7%25ed%258e%25b8-%25ec%2598%25b5%25ec%2585%2594%25eb%2584%2590-nil%25ec%259d%2584-%25ed%2583%2580%25ec%259e%2585-%25ec%258b%259c%25ec%258a%25a4%25ed%2585%259c%25ec%259c%25bc%25eb%25a1%259c-%25eb%258b%25a4%25eb%25a3%25a8%25ea%25b8%25b0</link>
					<comments>https://nangchang.nes.or.kr/7%ed%8e%b8-%ec%98%b5%ec%85%94%eb%84%90-nil%ec%9d%84-%ed%83%80%ec%9e%85-%ec%8b%9c%ec%8a%a4%ed%85%9c%ec%9c%bc%eb%a1%9c-%eb%8b%a4%eb%a3%a8%ea%b8%b0/#respond</comments>
		
		<dc:creator><![CDATA[낭창]]></dc:creator>
		<pubDate>Sat, 30 May 2026 16:49:55 +0000</pubDate>
				<category><![CDATA[미분류]]></category>
		<guid isPermaLink="false">https://nangchang.nes.or.kr/?p=1093</guid>

					<description><![CDATA[<p>Swift가 null 대신 Optional을 쓰는 이유. if let, guard let, ??, 옵셔널 체이닝까지. 이 개념을 이해하면 Swift가 왜 안전한 언어인지 실감할 수 있다.</p>
<p>The post <a href="https://nangchang.nes.or.kr/7%ed%8e%b8-%ec%98%b5%ec%85%94%eb%84%90-nil%ec%9d%84-%ed%83%80%ec%9e%85-%ec%8b%9c%ec%8a%a4%ed%85%9c%ec%9c%bc%eb%a1%9c-%eb%8b%a4%eb%a3%a8%ea%b8%b0/">[7편] 옵셔널 — nil을 타입 시스템으로 다루기</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></description>
										<content:encoded><![CDATA[<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 이 글은 <a href="https://claude.ai/claude-code">Claude Code</a>(AI)가 작성합니다. | <a href="https://nangchang.nes.or.kr/?p=1085">시리즈 목차</a> | 이전: <a href="https://nangchang.nes.or.kr/?p=1092">6편</a></p>
</blockquote>
<p>Swift를 처음 배우는 사람이 가장 자주 막히는 지점이 옵셔널입니다. <code>String?</code>이 <code>String</code>과 다른 타입이라는 것, 값을 꺼내려면 반드시 언래핑해야 한다는 것, 곳곳에 붙어 있는 <code>?</code>와 <code>!</code>. 처음에는 번잡하게 느껴지지만, 이것이 Swift의 안전성을 뒷받침하는 핵심 메커니즘입니다.</p>
<hr />
<h2>왜 null 대신 Optional인가</h2>
<p>Tony Hoare는 1965년 ALGOL에 null 참조를 도입하며 나중에 이를 &#8220;<strong>10억 달러짜리 실수</strong>&#8220;라고 불렀습니다. null은 어떤 변수에든 조용히 들어올 수 있고, 그 변수를 아무 경고 없이 사용하다가 런타임에 프로그램이 터집니다.</p>
<pre><code>// Java / JavaScript 스타일 (개념 설명용)
String name = null;
int length = name.length();  // NullPointerException — 실행 중에 터짐
</code></pre>
<p>Swift는 이 문제를 타입 시스템으로 해결합니다. &#8220;값이 없을 수도 있다&#8221;는 가능성을 타입에 명시합니다.</p>
<pre><code>var name: String = "철수"   // 반드시 String 값이 있어야 함
var name: String? = nil     // String이거나 nil일 수 있음
</code></pre>
<p><code>String?</code>은 <code>Optional&lt;String&gt;</code>의 축약 표현입니다. 두 가지 상태를 가질 수 있는 열거형입니다.</p>
<pre><code>// Swift 내부 구현 (개념 이해용)
enum Optional&lt;Wrapped&gt; {
    case some(Wrapped)  // 값이 있는 경우
    case none           // 값이 없는 경우 (nil)
}
</code></pre>
<p>이 설계의 핵심은 <strong>nil이 들어올 수 있는 변수와 그렇지 않은 변수를 컴파일 타임에 구분한다</strong>는 것입니다. <code>String</code> 타입 변수에는 절대 nil이 들어올 수 없고, 컴파일러가 이를 보장합니다.</p>
<hr />
<h2>옵셔널 바인딩 — if let</h2>
<p>옵셔널에서 값을 꺼내는 가장 기본적인 방법입니다. 값이 있을 때만 내부 블록이 실행됩니다.</p>
<pre><code>let input: String? = "42"

if let number = Int(input ?? "") {
    print("숫자: \(number)")
} else {
    print("변환 실패")
}
</code></pre>
<p>Swift 5.7부터는 같은 이름으로 간단하게 쓸 수 있습니다.</p>
<pre><code>var username: String? = "Alice"

if let username {           // if let username = username 의 축약
    print("환영합니다, \(username)")
}
</code></pre>
<p>여러 옵셔널을 한 번에 바인딩할 수도 있습니다. 하나라도 nil이면 전체 블록이 실행되지 않습니다.</p>
<pre><code>let a: Int? = 3
let b: Int? = 4

if let a, let b {
    print(a + b)  // 7
}
</code></pre>
<hr />
<h2>guard let — 조기 탈출과 함께</h2>
<p>4편에서 배운 <code>guard</code>가 옵셔널 바인딩과 자주 짝을 이룹니다. nil이면 탈출, 그렇지 않으면 이하 코드에서 언래핑된 값을 바로 씁니다.</p>
<pre><code>func process(input: String?) {
    guard let input else {
        print("입력값 없음")
        return
    }
    // 여기서부터 input은 String (옵셔널 아님)
    print("처리 중: \(input)")
}
</code></pre>
<p><code>if let</code>은 블록 안에서만 언래핑된 값을 쓸 수 있지만, <code>guard let</code>은 이후 모든 코드에서 쓸 수 있습니다. 함수 초반부에 전제 조건을 검사하는 패턴에 자연스럽게 어울립니다.</p>
<hr />
<h2>nil 합병 연산자 ??</h2>
<p>옵셔널 값이 nil일 때 대신 쓸 기본값을 지정합니다.</p>
<pre><code>let nickname: String? = nil
let displayName = nickname ?? "익명"
print(displayName)  // 익명

let score: Int? = 85
let finalScore = score ?? 0
print(finalScore)  // 85
</code></pre>
<p>중첩해서 쓸 수도 있습니다.</p>
<pre><code>let a: String? = nil
let b: String? = nil
let c: String? = "값"

let result = a ?? b ?? c ?? "기본값"
print(result)  // 값
</code></pre>
<hr />
<h2>옵셔널 체이닝 ?.</h2>
<p>옵셔널 값의 프로퍼티나 메서드에 접근할 때 <code>?.</code>을 씁니다. 체인 중 하나라도 nil이면 전체 표현식이 nil이 됩니다.</p>
<pre><code>struct Address {
    var city: String
}

struct Person {
    var name: String
    var address: Address?
}

let person = Person(name: "철수", address: Address(city: "서울"))
let city = person.address?.city  // Optional("서울")

let person2 = Person(name: "영희", address: nil)
let city2 = person2.address?.city  // nil
</code></pre>
<p>체이닝 결과는 항상 옵셔널입니다. 마지막에 <code>?? "기본값"</code>으로 처리하는 패턴이 자주 쓰입니다.</p>
<pre><code>let displayCity = person2.address?.city ?? "주소 없음"
print(displayCity)  // 주소 없음
</code></pre>
<hr />
<h2>강제 언래핑 ! — 언제 써도 되는가</h2>
<p>옵셔널에서 값을 강제로 꺼냅니다. nil이면 런타임 크래시가 납니다.</p>
<pre><code>let value: String? = "안녕"
print(value!)  // 안녕

let nothing: String? = nil
print(nothing!)  // 런타임 크래시
</code></pre>
<p>강제 언래핑은 &#8220;이 값이 nil일 리 없다&#8221;는 개발자의 단언입니다. 틀리면 앱이 죽으므로, 신중하게 써야 합니다. 실제로 쓸 수 있는 경우는 한정적입니다.</p>
<ul>
<li>바로 위에서 nil 확인을 마쳤고, 이후 코드에서 쓸 때</li>
<li>테스트 코드에서 실패 시 즉시 알 수 있도록 의도적으로 쓸 때</li>
<li>앱 번들에 항상 존재하는 리소스를 로드할 때</li>
</ul>
<p>프로덕션 코드에서 <code>!</code>가 많이 보인다면 설계를 다시 검토하는 것이 좋습니다.</p>
<hr />
<h2>암묵적 언래핑 옵셔널 String!</h2>
<p>타입 뒤에 <code>!</code>를 붙이면 접근할 때마다 자동으로 언래핑하는 옵셔널입니다.</p>
<pre><code>var label: UILabel!   // 접근 시 자동 언래핑
</code></pre>
<p>주로 Interface Builder(Storyboard)와 연결된 아울렛(outlet)에서 쓰입니다. &#8220;초기에는 nil이지만, 이후엔 반드시 값이 있다&#8221;는 의도를 표현합니다. 일반 코드에서는 가급적 피하는 것이 좋습니다.</p>
<hr />
<h2>다른 언어와 비교</h2>
<table>
<thead>
<tr>
<th></th>
<th>Python</th>
<th>TypeScript</th>
<th>Swift</th>
</tr>
</thead>
<tbody>
<tr>
<td>null 표현</td>
<td><code>None</code></td>
<td><code>null</code> / <code>undefined</code></td>
<td><code>nil</code></td>
</tr>
<tr>
<td>null 가능 표시</td>
<td>없음 (타입 힌트 <code>Optional[str]</code>)</td>
<td><code>string | null</code></td>
<td><code>String?</code></td>
</tr>
<tr>
<td>null 안전 접근</td>
<td>없음</td>
<td><code>obj?.prop</code></td>
<td><code>obj?.prop</code></td>
</tr>
<tr>
<td>기본값 연산자</td>
<td><code>x or "기본값"</code></td>
<td><code>x ?? "기본값"</code></td>
<td><code>x ?? "기본값"</code></td>
</tr>
<tr>
<td>null 검사 강제 여부</td>
<td>없음</td>
<td>strictNullChecks 옵션</td>
<td>항상 강제</td>
</tr>
</tbody>
</table>
<hr />
<h2>핵심 요약</h2>
<ul>
<li>옵셔널(<code>String?</code>)은 값이 있을 수도, nil일 수도 있다는 것을 타입으로 표현한다. nil 가능성을 컴파일 타임에 추적한다.</li>
<li><code>if let</code>은 값이 있을 때만 블록을 실행하며 언래핑된 값을 블록 안에서 쓴다.</li>
<li><code>guard let</code>은 nil이면 탈출하고, 이후 코드에서 언래핑된 값을 쓴다.</li>
<li><code>??</code>는 nil일 때 대신 쓸 기본값을 지정한다.</li>
<li><code>?.</code>은 체인 중 nil이 있으면 전체를 nil로 만든다.</li>
<li>강제 언래핑 <code>!</code>은 nil이면 크래시난다. 꼭 필요한 경우에만 쓴다.</li>
</ul>
<hr />
<p>다음 편은 <strong>8편 — 구조체와 클래스: 값 타입 vs 참조 타입</strong>입니다. Swift가 클래스보다 구조체를 선호하는 이유, 그리고 둘을 언제 나눠 쓰는지를 다룹니다.</p>
<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Generated with <a href="https://claude.ai/claude-code">Claude Code</a></p>
</blockquote><p>The post <a href="https://nangchang.nes.or.kr/7%ed%8e%b8-%ec%98%b5%ec%85%94%eb%84%90-nil%ec%9d%84-%ed%83%80%ec%9e%85-%ec%8b%9c%ec%8a%a4%ed%85%9c%ec%9c%bc%eb%a1%9c-%eb%8b%a4%eb%a3%a8%ea%b8%b0/">[7편] 옵셔널 — nil을 타입 시스템으로 다루기</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://nangchang.nes.or.kr/7%ed%8e%b8-%ec%98%b5%ec%85%94%eb%84%90-nil%ec%9d%84-%ed%83%80%ec%9e%85-%ec%8b%9c%ec%8a%a4%ed%85%9c%ec%9c%bc%eb%a1%9c-%eb%8b%a4%eb%a3%a8%ea%b8%b0/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[6편] 클로저 — 이름 없는 함수 블록, @escaping, 캡처 리스트</title>
		<link>https://nangchang.nes.or.kr/6%ed%8e%b8-%ed%81%b4%eb%a1%9c%ec%a0%80-%ec%9d%b4%eb%a6%84-%ec%97%86%eb%8a%94-%ed%95%a8%ec%88%98-%eb%b8%94%eb%a1%9d-escaping-%ec%ba%a1%ec%b2%98-%eb%a6%ac%ec%8a%a4%ed%8a%b8/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=6%25ed%258e%25b8-%25ed%2581%25b4%25eb%25a1%259c%25ec%25a0%2580-%25ec%259d%25b4%25eb%25a6%2584-%25ec%2597%2586%25eb%258a%2594-%25ed%2595%25a8%25ec%2588%2598-%25eb%25b8%2594%25eb%25a1%259d-escaping-%25ec%25ba%25a1%25ec%25b2%2598-%25eb%25a6%25ac%25ec%258a%25a4%25ed%258a%25b8</link>
					<comments>https://nangchang.nes.or.kr/6%ed%8e%b8-%ed%81%b4%eb%a1%9c%ec%a0%80-%ec%9d%b4%eb%a6%84-%ec%97%86%eb%8a%94-%ed%95%a8%ec%88%98-%eb%b8%94%eb%a1%9d-escaping-%ec%ba%a1%ec%b2%98-%eb%a6%ac%ec%8a%a4%ed%8a%b8/#respond</comments>
		
		<dc:creator><![CDATA[낭창]]></dc:creator>
		<pubDate>Sat, 30 May 2026 16:48:54 +0000</pubDate>
				<category><![CDATA[미분류]]></category>
		<guid isPermaLink="false">https://nangchang.nes.or.kr/?p=1092</guid>

					<description><![CDATA[<p>클로저는 코드 블록을 값처럼 전달하는 Swift의 핵심 도구다. 후행 클로저 축약 문법, 값 캡처, @escaping, [weak self]까지 순서대로 정리한다.</p>
<p>The post <a href="https://nangchang.nes.or.kr/6%ed%8e%b8-%ed%81%b4%eb%a1%9c%ec%a0%80-%ec%9d%b4%eb%a6%84-%ec%97%86%eb%8a%94-%ed%95%a8%ec%88%98-%eb%b8%94%eb%a1%9d-escaping-%ec%ba%a1%ec%b2%98-%eb%a6%ac%ec%8a%a4%ed%8a%b8/">[6편] 클로저 — 이름 없는 함수 블록, @escaping, 캡처 리스트</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></description>
										<content:encoded><![CDATA[<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 이 글은 <a href="https://claude.ai/claude-code">Claude Code</a>(AI)가 작성합니다. | <a href="https://nangchang.nes.or.kr/?p=1085">시리즈 목차</a> | 이전: <a href="https://nangchang.nes.or.kr/?p=1091">5편</a></p>
</blockquote>
<p>5편에서 함수를 변수에 담을 수 있다는 것을 배웠습니다. 클로저(closure)는 그것의 확장입니다. 이름을 붙이지 않고, 그 자리에서 바로 정의해서 넘길 수 있는 함수 블록입니다.</p>
<p>Swift에서 <code>map</code>, <code>filter</code>, <code>sort</code> 같은 메서드를 쓸 때, 비동기 작업의 완료 콜백을 넘길 때, 버튼의 동작을 정의할 때 — 모두 클로저를 씁니다. 클로저를 이해하지 않으면 Swift 코드의 절반을 읽을 수 없습니다.</p>
<hr />
<h2>클로저 기본 문법</h2>
<p>클로저는 <code>{ }</code> 안에 파라미터, 반환 타입, 본문을 모두 씁니다.</p>
<pre><code>{ (파라미터) -> 반환타입 in
    본문
}
</code></pre>
<p>5편에서 함수로 만들었던 덧셈을 클로저로 쓰면 이렇습니다.</p>
<pre><code>// 함수 방식
func add(_ a: Int, _ b: Int) -> Int { a + b }

// 클로저 방식
let add = { (a: Int, b: Int) -> Int in
    return a + b
}

add(3, 4)  // 7
</code></pre>
<p>타입은 컨텍스트에서 추론될 때 생략할 수 있고, 표현식이 하나면 <code>return</code>도 생략됩니다.</p>
<pre><code>let add: (Int, Int) -> Int = { a, b in a + b }
</code></pre>
<hr />
<h2>후행 클로저와 축약 문법</h2>
<p>클로저를 함수의 마지막 인자로 넘길 때는 괄호 밖으로 꺼내 쓸 수 있습니다. 이것을 <strong>후행 클로저(trailing closure)</strong>라고 합니다.</p>
<pre><code>let numbers = [3, 1, 4, 1, 5, 9]

// 일반 방식
let sorted = numbers.sorted(by: { (a: Int, b: Int) -> Bool in
    return a < b
})

// 후행 클로저
let sorted = numbers.sorted { (a: Int, b: Int) -> Bool in
    return a < b
}

// 타입 추론으로 생략
let sorted = numbers.sorted { a, b in a < b }

// $0, $1 축약 인자
let sorted = numbers.sorted { $0 < $1 }

// 연산자 함수로 더 짧게
let sorted = numbers.sorted(by: <)
</code></pre>
<p>모두 완전히 동일한 코드입니다. 상황에 따라 적절한 수준의 축약을 씁니다. 너무 줄이면 읽기 어려워지므로, 한 줄에 다 들어오고 의미가 명확할 때만 <code>$0</code>, <code>$1</code> 축약을 씁니다.</p>
<p>3편에서 봤던 <code>map</code>, <code>filter</code>도 클로저를 받는 함수입니다.</p>
<pre><code>let numbers = [1, 2, 3, 4, 5]

let doubled = numbers.map { $0 * 2 }       // [2, 4, 6, 8, 10]
let evens   = numbers.filter { $0 % 2 == 0 } // [2, 4]
let total   = numbers.reduce(0) { $0 + $1 }  // 15
</code></pre>
<hr />
<h2>값 캡처</h2>
<p>클로저는 자신이 정의된 스코프의 변수를 <strong>캡처(capture)</strong>합니다. 함수가 끝난 뒤에도 그 변수를 계속 참조할 수 있습니다.</p>
<pre><code>func makeCounter() -> () -> Int {
    var count = 0
    let increment = {
        count += 1
        return count
    }
    return increment
}

let counter = makeCounter()
print(counter())  // 1
print(counter())  // 2
print(counter())  // 3
</code></pre>
<p><code>makeCounter()</code>가 반환되고 나서도 클로저 <code>increment</code>는 <code>count</code>를 살아있게 유지합니다. 클로저가 <code>count</code>를 캡처했기 때문입니다. Python의 클로저나 JavaScript의 클로저와 동일한 개념입니다.</p>
<hr />
<h2>@escaping — 클로저가 함수보다 오래 사는 경우</h2>
<p>클로저를 함수 인자로 받을 때, 그 클로저가 함수 실행이 끝난 뒤에도 살아남아야 한다면 <code>@escaping</code>을 표시합니다.</p>
<pre><code>var completionHandlers: [() -> Void] = []

func register(handler: @escaping () -> Void) {
    completionHandlers.append(handler)  // 함수 종료 후에도 handler가 살아남음
}
</code></pre>
<p>가장 흔한 사례는 비동기 작업의 완료 콜백입니다.</p>
<pre><code>func fetchData(completion: @escaping (String) -> Void) {
    DispatchQueue.global().async {
        // 백그라운드에서 작업
        let result = "데이터"
        DispatchQueue.main.async {
            completion(result)  // 함수 반환 이후에 호출됨
        }
    }
}
</code></pre>
<p><code>@escaping</code>이 없는 클로저(non-escaping)는 함수 내에서만 실행되고 사라집니다. Swift가 기본을 non-escaping으로 둔 이유는 성능 최적화와 메모리 안전성 때문입니다. 탈출하지 않는다는 보장이 있으면 컴파일러가 더 적극적으로 최적화할 수 있습니다.</p>
<hr />
<h2>캡처 리스트와 [weak self]</h2>
<p>클로저가 클래스 인스턴스를 캡처하면 강한 참조(strong reference)가 생깁니다. 그 인스턴스가 클로저를 소유하고, 클로저가 다시 그 인스턴스를 참조하면 <strong>순환 참조(retain cycle)</strong>가 생겨 메모리 누수가 발생합니다.</p>
<pre><code>class NetworkManager {
    var onComplete: (() -> Void)?

    func start() {
        onComplete = {
            print(self.description)  // self를 강하게 캡처
            // NetworkManager → onComplete → 클로저 → NetworkManager 순환
        }
    }
}
</code></pre>
<p>이를 막기 위해 <strong>캡처 리스트</strong>로 약한 참조를 씁니다.</p>
<pre><code>class NetworkManager {
    var onComplete: (() -> Void)?

    func start() {
        onComplete = { [weak self] in
            guard let self = self else { return }
            print(self.description)
        }
    }
}
</code></pre>
<p><code>[weak self]</code>는 "self를 약하게 캡처한다"는 선언입니다. 약한 참조는 인스턴스가 해제되면 자동으로 <code>nil</code>이 됩니다. 그래서 <code>guard let self = self</code>로 먼저 nil 여부를 확인합니다.</p>
<p>언제 <code>[weak self]</code>가 필요한가:</p>
<ul>
<li>클로저가 <code>@escaping</code>이고</li>
<li>클로저가 <code>self</code>를 참조하고</li>
<li><code>self</code>가 그 클로저를 (직접 또는 간접으로) 소유하고 있을 때</li>
</ul>
<p>세 조건이 모두 해당되면 순환 참조 가능성이 있으니 <code>[weak self]</code>를 씁니다. 메모리 관리는 12편에서 ARC와 함께 더 자세히 다룹니다.</p>
<hr />
<h2>다른 언어와 비교</h2>
<table>
<thead>
<tr>
<th></th>
<th>Python</th>
<th>JavaScript</th>
<th>Swift</th>
</tr>
</thead>
<tbody>
<tr>
<td>익명 함수</td>
<td><code>lambda x: x*2</code></td>
<td><code>x => x*2</code></td>
<td><code>{ x in x*2 }</code> 또는 <code>{ $0*2 }</code></td>
</tr>
<tr>
<td>값 캡처</td>
<td>있음</td>
<td>있음</td>
<td>있음</td>
</tr>
<tr>
<td>캡처 방식 제어</td>
<td>없음</td>
<td>없음</td>
<td>캡처 리스트 <code>[weak self]</code></td>
</tr>
<tr>
<td>탈출 여부 명시</td>
<td>없음</td>
<td>없음</td>
<td><code>@escaping</code></td>
</tr>
<tr>
<td>후행 클로저 문법</td>
<td>없음</td>
<td>없음</td>
<td>있음</td>
</tr>
</tbody>
</table>
<hr />
<h2>핵심 요약</h2>
<ul>
<li>클로저는 이름 없는 함수 블록이다. <code>{ 파라미터 in 본문 }</code> 형태로 쓴다.</li>
<li>함수의 마지막 인자가 클로저면 괄호 밖으로 꺼내는 후행 클로저 문법을 쓸 수 있다.</li>
<li><code>$0</code>, <code>$1</code>은 클로저의 첫 번째, 두 번째 인자를 가리키는 축약 표현이다.</li>
<li>클로저는 자신이 정의된 스코프의 변수를 캡처한다. 함수가 끝난 뒤에도 그 변수를 유지한다.</li>
<li><code>@escaping</code>은 클로저가 함수 반환 이후에도 살아남는다는 표시다. 비동기 콜백에 많이 쓰인다.</li>
<li>순환 참조를 막으려면 <code>[weak self]</code> 캡처 리스트를 쓴다.</li>
</ul>
<hr />
<p>다음 편은 <strong>7편 — 옵셔널: nil을 타입 시스템으로 다루기</strong>입니다. Swift에서 가장 낯선 개념이지만, 이것을 이해하면 Swift가 왜 "안전한 언어"인지 실감할 수 있습니다.</p>
<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Generated with <a href="https://claude.ai/claude-code">Claude Code</a></p>
</blockquote><p>The post <a href="https://nangchang.nes.or.kr/6%ed%8e%b8-%ed%81%b4%eb%a1%9c%ec%a0%80-%ec%9d%b4%eb%a6%84-%ec%97%86%eb%8a%94-%ed%95%a8%ec%88%98-%eb%b8%94%eb%a1%9d-escaping-%ec%ba%a1%ec%b2%98-%eb%a6%ac%ec%8a%a4%ed%8a%b8/">[6편] 클로저 — 이름 없는 함수 블록, @escaping, 캡처 리스트</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://nangchang.nes.or.kr/6%ed%8e%b8-%ed%81%b4%eb%a1%9c%ec%a0%80-%ec%9d%b4%eb%a6%84-%ec%97%86%eb%8a%94-%ed%95%a8%ec%88%98-%eb%b8%94%eb%a1%9d-escaping-%ec%ba%a1%ec%b2%98-%eb%a6%ac%ec%8a%a4%ed%8a%b8/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[5편] 함수 — 파라미터 레이블이 두 개인 이유</title>
		<link>https://nangchang.nes.or.kr/5%ed%8e%b8-%ed%95%a8%ec%88%98-%ed%8c%8c%eb%9d%bc%eb%af%b8%ed%84%b0-%eb%a0%88%ec%9d%b4%eb%b8%94%ec%9d%b4-%eb%91%90-%ea%b0%9c%ec%9d%b8-%ec%9d%b4%ec%9c%a0/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=5%25ed%258e%25b8-%25ed%2595%25a8%25ec%2588%2598-%25ed%258c%258c%25eb%259d%25bc%25eb%25af%25b8%25ed%2584%25b0-%25eb%25a0%2588%25ec%259d%25b4%25eb%25b8%2594%25ec%259d%25b4-%25eb%2591%2590-%25ea%25b0%259c%25ec%259d%25b8-%25ec%259d%25b4%25ec%259c%25a0</link>
					<comments>https://nangchang.nes.or.kr/5%ed%8e%b8-%ed%95%a8%ec%88%98-%ed%8c%8c%eb%9d%bc%eb%af%b8%ed%84%b0-%eb%a0%88%ec%9d%b4%eb%b8%94%ec%9d%b4-%eb%91%90-%ea%b0%9c%ec%9d%b8-%ec%9d%b4%ec%9c%a0/#respond</comments>
		
		<dc:creator><![CDATA[낭창]]></dc:creator>
		<pubDate>Sat, 30 May 2026 16:47:56 +0000</pubDate>
				<category><![CDATA[미분류]]></category>
		<guid isPermaLink="false">https://nangchang.nes.or.kr/?p=1091</guid>

					<description><![CDATA[<p>Swift 함수에는 외부 이름과 내부 이름이 따로 있다. 왜 그렇게 설계됐는지, 기본값·가변 인자·inout·튜플 반환까지. 함수가 일급 시민이라는 것의 의미도 다룬다.</p>
<p>The post <a href="https://nangchang.nes.or.kr/5%ed%8e%b8-%ed%95%a8%ec%88%98-%ed%8c%8c%eb%9d%bc%eb%af%b8%ed%84%b0-%eb%a0%88%ec%9d%b4%eb%b8%94%ec%9d%b4-%eb%91%90-%ea%b0%9c%ec%9d%b8-%ec%9d%b4%ec%9c%a0/">[5편] 함수 — 파라미터 레이블이 두 개인 이유</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></description>
										<content:encoded><![CDATA[<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 이 글은 <a href="https://claude.ai/claude-code">Claude Code</a>(AI)가 작성합니다. | <a href="https://nangchang.nes.or.kr/?p=1085">시리즈 목차</a> | 이전: <a href="https://nangchang.nes.or.kr/?p=1090">4편</a></p>
</blockquote>
<p>Swift 함수를 처음 보는 사람은 파라미터 이름이 두 개씩 붙어 있는 것을 보고 어리둥절해집니다.</p>
<pre><code>func move(from start: Int, to end: Int) { ... }
move(from: 3, to: 7)
</code></pre>
<p><code>from</code>과 <code>to</code>는 호출부에서 쓰고, <code>start</code>와 <code>end</code>는 함수 본문에서 씁니다. 이 설계가 왜 존재하는지부터 풀어봅니다.</p>
<hr />
<h2>파라미터 레이블: 외부 이름과 내부 이름</h2>
<p>Swift 함수의 파라미터는 <strong>외부 이름(argument label)</strong>과 <strong>내부 이름(parameter name)</strong>을 따로 가질 수 있습니다.</p>
<pre><code>func greet(person name: String) {
    print("안녕하세요, \(name)!")  // 내부에서는 name 사용
}

greet(person: "철수")  // 호출부에서는 person 사용
</code></pre>
<p>외부 이름은 호출 코드를 영어 문장처럼 읽히게 만들기 위한 장치입니다. <code>greet(person: "철수")</code>는 &#8220;철수라는 사람에게 인사해&#8221;라고 읽힙니다. 내부 이름은 함수 본문에서 실제로 쓰기 편한 짧은 이름을 쓸 수 있게 합니다.</p>
<p>외부 이름을 생략하고 싶을 때는 <code>_</code>를 씁니다. 이 경우 호출부에서 레이블 없이 값만 씁니다.</p>
<pre><code>func square(_ n: Int) -> Int {
    return n * n
}

square(5)   // 레이블 없이 호출
// square(n: 5) 는 오류
</code></pre>
<p>외부 이름과 내부 이름을 따로 쓰지 않으면 같은 이름이 두 역할을 모두 합니다.</p>
<pre><code>func add(a: Int, b: Int) -> Int {
    return a + b
}

add(a: 3, b: 4)  // 외부 이름 = 내부 이름 = a, b
</code></pre>
<hr />
<h2>반환 타입</h2>
<p>반환 타입은 <code>-></code> 뒤에 씁니다.</p>
<pre><code>func celsius(from fahrenheit: Double) -> Double {
    return (fahrenheit - 32) * 5 / 9
}

let temp = celsius(from: 98.6)  // 37.0
</code></pre>
<p>반환값이 없으면 <code>-> Void</code>를 쓰거나 아예 생략합니다. 둘은 동일합니다.</p>
<pre><code>func log(_ message: String) {
    print(message)
}
</code></pre>
<p>함수 본문이 표현식 하나로만 이루어진 경우 <code>return</code>을 생략할 수 있습니다.</p>
<pre><code>func square(_ n: Int) -> Int { n * n }
</code></pre>
<hr />
<h2>튜플로 여러 값 반환</h2>
<p>Swift는 튜플(tuple)을 이용해 여러 값을 한 번에 반환할 수 있습니다.</p>
<pre><code>func minMax(of array: [Int]) -> (min: Int, max: Int) {
    var min = array[0]
    var max = array[0]
    for n in array {
        if n < min { min = n }
        if n > max { max = n }
    }
    return (min, max)
}

let result = minMax(of: [3, 1, 8, 2, 5])
print(result.min)  // 1
print(result.max)  // 8
</code></pre>
<hr />
<h2>기본값 파라미터</h2>
<p>파라미터에 기본값을 지정하면, 호출 시 그 인자를 생략할 수 있습니다.</p>
<pre><code>func repeat(text: String, times: Int = 3) -> String {
    return String(repeating: text, count: times)
}

repeat(text: "안녕")        // "안녕안녕안녕"
repeat(text: "안녕", times: 5)  // "안녕안녕안녕안녕안녕"
</code></pre>
<p>기본값이 있는 파라미터는 보통 뒤쪽에 배치하는 것이 관례입니다.</p>
<hr />
<h2>가변 인자</h2>
<p>타입 뒤에 <code>...</code>을 붙이면 같은 타입의 값을 개수 제한 없이 받습니다. 함수 내부에서는 배열로 취급됩니다.</p>
<pre><code>func sum(_ numbers: Int...) -> Int {
    return numbers.reduce(0, +)
}

sum(1, 2, 3)         // 6
sum(10, 20, 30, 40)  // 100
</code></pre>
<hr />
<h2>inout — 함수 안에서 외부 값 변경</h2>
<p>Swift 함수는 기본적으로 인자를 복사해서 받습니다. 함수 내부에서 파라미터를 바꿔도 원본에는 영향이 없습니다.</p>
<pre><code>func double(_ n: Int) {
    var n = n
    n *= 2
    // 원본은 그대로
}

var x = 5
double(x)
print(x)  // 5 (변하지 않음)
</code></pre>
<p>원본을 변경하고 싶다면 <code>inout</code>을 씁니다. 호출부에서는 변수 앞에 <code>&amp;</code>를 붙입니다.</p>
<pre><code>func doubleInPlace(_ n: inout Int) {
    n *= 2
}

var x = 5
doubleInPlace(&amp;x)
print(x)  // 10
</code></pre>
<p><code>&amp;</code>는 &#8220;이 변수의 주소를 넘긴다&#8221;는 신호입니다. 함수가 원본을 바꿀 수 있다는 사실을 호출부에서도 명시적으로 표시하게 합니다. 표준 라이브러리의 <code>swap(_:_:)</code>이 이 패턴으로 구현됩니다.</p>
<hr />
<h2>함수는 일급 시민이다</h2>
<p>Swift에서 함수는 <strong>일급 시민(first-class citizen)</strong>입니다. 이는 함수를 값처럼 다룰 수 있다는 의미입니다.</p>
<ul>
<li>변수에 담을 수 있다</li>
<li>다른 함수의 인자로 넘길 수 있다</li>
<li>함수의 반환값으로 돌려줄 수 있다</li>
</ul>
<pre><code>func add(_ a: Int, _ b: Int) -> Int { a + b }
func multiply(_ a: Int, _ b: Int) -> Int { a * b }

// 함수를 변수에 담기
var operation: (Int, Int) -> Int = add
print(operation(3, 4))  // 7

operation = multiply
print(operation(3, 4))  // 12

// 함수를 인자로 넘기기
func apply(_ op: (Int, Int) -> Int, to a: Int, and b: Int) -> Int {
    return op(a, b)
}

apply(add, to: 3, and: 4)       // 7
apply(multiply, to: 3, and: 4)  // 12
</code></pre>
<p>함수 타입은 <code>(파라미터 타입들) -> 반환 타입</code>으로 표기합니다. <code>(Int, Int) -> Int</code>는 &#8220;두 Int를 받아 Int를 반환하는 함수 타입&#8221;입니다.</p>
<p>이 개념이 왜 중요한지는 다음 편의 클로저에서 본격적으로 드러납니다.</p>
<hr />
<h2>다른 언어와 비교</h2>
<table>
<thead>
<tr>
<th></th>
<th>Python</th>
<th>JavaScript</th>
<th>Swift</th>
</tr>
</thead>
<tbody>
<tr>
<td>파라미터 레이블</td>
<td>키워드 인자 (<code>def f(*, name)</code>)</td>
<td>없음</td>
<td>외부/내부 이름 분리</td>
</tr>
<tr>
<td>기본값</td>
<td>있음</td>
<td>있음</td>
<td>있음</td>
</tr>
<tr>
<td>가변 인자</td>
<td><code>*args</code></td>
<td><code>...args</code></td>
<td><code>타입...</code></td>
</tr>
<tr>
<td>참조 전달</td>
<td>객체는 기본 참조</td>
<td>객체는 기본 참조</td>
<td>명시적 <code>inout</code></td>
</tr>
<tr>
<td>다중 반환</td>
<td>튜플/리스트</td>
<td>객체/배열</td>
<td>이름 있는 튜플</td>
</tr>
<tr>
<td>함수 일급 시민</td>
<td>있음</td>
<td>있음</td>
<td>있음</td>
</tr>
</tbody>
</table>
<hr />
<h2>핵심 요약</h2>
<ul>
<li>파라미터는 외부 이름(호출부)과 내부 이름(본문)을 따로 가질 수 있다. 호출 코드를 자연어처럼 읽히게 만들기 위한 설계다.</li>
<li><code>_</code>로 외부 이름을 생략하면 호출 시 레이블 없이 값만 넘긴다.</li>
<li>튜플로 여러 값을 한 번에 반환할 수 있다.</li>
<li><code>inout</code>은 함수가 원본 변수를 직접 수정할 수 있게 한다. 호출부에서 <code>&amp;</code>로 표시한다.</li>
<li>함수는 일급 시민이다. 변수에 담고, 인자로 넘기고, 반환값으로 돌려줄 수 있다.</li>
</ul>
<hr />
<p>다음 편은 <strong>6편 — 클로저: 함수를 변수에 담기</strong>입니다. 5편에서 잠깐 나온 함수 타입을 발전시켜, 이름 없는 함수 블록인 클로저와 Swift의 간결한 축약 문법을 다룹니다.</p>
<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Generated with <a href="https://claude.ai/claude-code">Claude Code</a></p>
</blockquote><p>The post <a href="https://nangchang.nes.or.kr/5%ed%8e%b8-%ed%95%a8%ec%88%98-%ed%8c%8c%eb%9d%bc%eb%af%b8%ed%84%b0-%eb%a0%88%ec%9d%b4%eb%b8%94%ec%9d%b4-%eb%91%90-%ea%b0%9c%ec%9d%b8-%ec%9d%b4%ec%9c%a0/">[5편] 함수 — 파라미터 레이블이 두 개인 이유</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://nangchang.nes.or.kr/5%ed%8e%b8-%ed%95%a8%ec%88%98-%ed%8c%8c%eb%9d%bc%eb%af%b8%ed%84%b0-%eb%a0%88%ec%9d%b4%eb%b8%94%ec%9d%b4-%eb%91%90-%ea%b0%9c%ec%9d%b8-%ec%9d%b4%ec%9c%a0/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[4편] 제어 흐름 — if, switch, guard, for, while</title>
		<link>https://nangchang.nes.or.kr/4%ed%8e%b8-%ec%a0%9c%ec%96%b4-%ed%9d%90%eb%a6%84-if-switch-guard-for-while/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=4%25ed%258e%25b8-%25ec%25a0%259c%25ec%2596%25b4-%25ed%259d%2590%25eb%25a6%2584-if-switch-guard-for-while</link>
					<comments>https://nangchang.nes.or.kr/4%ed%8e%b8-%ec%a0%9c%ec%96%b4-%ed%9d%90%eb%a6%84-if-switch-guard-for-while/#respond</comments>
		
		<dc:creator><![CDATA[낭창]]></dc:creator>
		<pubDate>Sat, 30 May 2026 16:46:50 +0000</pubDate>
				<category><![CDATA[미분류]]></category>
		<guid isPermaLink="false">https://nangchang.nes.or.kr/?p=1090</guid>

					<description><![CDATA[<p>Swift의 switch는 fallthrough가 없고 패턴 매칭을 지원한다. guard는 왜 if의 반대 방향으로 동작하는가. 조기 탈출 패턴이 코드 가독성을 어떻게 높이는지.</p>
<p>The post <a href="https://nangchang.nes.or.kr/4%ed%8e%b8-%ec%a0%9c%ec%96%b4-%ed%9d%90%eb%a6%84-if-switch-guard-for-while/">[4편] 제어 흐름 — if, switch, guard, for, while</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></description>
										<content:encoded><![CDATA[<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 이 글은 <a href="https://claude.ai/claude-code">Claude Code</a>(AI)가 작성합니다. | <a href="https://nangchang.nes.or.kr/?p=1085">시리즈 목차</a> | 이전: <a href="https://nangchang.nes.or.kr/?p=1089">3편</a></p>
</blockquote>
<p>제어 흐름은 어떤 언어에서나 비슷해 보입니다. <code>if</code>로 분기하고, 반복문으로 돌리고. 하지만 Swift에는 다른 언어와 확연히 다른 두 가지가 있습니다. <code>switch</code>의 작동 방식과 <code>guard</code>라는 키워드입니다. 이 두 가지를 중심으로 Swift의 제어 흐름을 살펴봅니다.</p>
<hr />
<h2>if / else if / else</h2>
<p>기본 구조는 다른 언어와 같습니다. 한 가지 차이는 조건식을 괄호로 감쌀 필요가 없다는 것입니다(감싸도 되지만 Swift 관례는 생략).</p>
<pre><code>let temperature = 22

if temperature > 30 {
    print("덥다")
} else if temperature > 20 {
    print("쾌적하다")
} else {
    print("춥다")
}
// 쾌적하다
</code></pre>
<p>Swift의 <code>if</code>는 반드시 <code>Bool</code> 조건만 받습니다. 2편에서 언급했듯 <code>0</code>이나 빈 문자열 같은 &#8220;거짓 같은 값(falsy)&#8221;을 암묵적으로 처리하지 않습니다.</p>
<pre><code>let count = 0
if count {          // 오류: Bool이 아님
    print("empty")
}
if count == 0 {     // 올바른 방식
    print("empty")
}
</code></pre>
<hr />
<h2>switch — fallthrough 없는 패턴 매칭</h2>
<p>Swift의 <code>switch</code>는 C, Java, JavaScript의 것과 크게 다릅니다. 차이점을 먼저 나열하면:</p>
<ul>
<li><strong>fallthrough가 기본적으로 없다</strong> — 각 <code>case</code>가 끝나면 자동으로 <code>switch</code>를 빠져나온다</li>
<li><strong>모든 경우를 반드시 처리해야 한다</strong> — <code>default</code>로 나머지를 처리하거나, 가능한 모든 케이스를 명시해야 한다</li>
<li><strong>패턴 매칭을 지원한다</strong> — 단순 값 비교를 넘어 범위, 튜플, 타입까지 매칭할 수 있다</li>
</ul>
<pre><code>let score = 85

switch score {
case 90...100:
    print("A")
case 80..&lt;90:
    print("B")
case 70..&lt;80:
    print("C")
default:
    print("F")
}
// B
</code></pre>
<p><code>90...100</code>은 90 이상 100 이하(닫힌 범위), <code>80..&lt;90</code>은 80 이상 90 미만(반열린 범위)입니다.</p>
<p>여러 값을 한 케이스에 묶을 수 있습니다.</p>
<pre><code>let day = "토"

switch day {
case "토", "일":
    print("주말")
case "월", "화", "수", "목", "금":
    print("평일")
default:
    print("알 수 없음")
}
</code></pre>
<p><code>where</code>로 추가 조건을 붙일 수도 있습니다.</p>
<pre><code>let point = (3, -2)

switch point {
case (0, 0):
    print("원점")
case (let x, 0):
    print("x축 위: \(x)")
case (0, let y):
    print("y축 위: \(y)")
case (let x, let y) where x == y:
    print("y=x 선 위")
case (let x, let y):
    print("(\(x), \(y))")
}
// (3, -2)
</code></pre>
<p>C 언어 스타일처럼 의도적으로 다음 케이스로 흘러가게 하고 싶다면 <code>fallthrough</code> 키워드를 명시적으로 씁니다. 하지만 실제로 쓸 일은 거의 없습니다.</p>
<hr />
<h2>for-in</h2>
<p>3편에서 컬렉션 순회에 썼던 것과 같습니다. 숫자 범위에도 쓸 수 있습니다.</p>
<pre><code>for i in 1...5 {
    print(i)  // 1, 2, 3, 4, 5
}

for i in 1..&lt;5 {
    print(i)  // 1, 2, 3, 4
}
</code></pre>
<p>반복 변수가 필요 없을 때는 <code>_</code>로 무시합니다.</p>
<pre><code>for _ in 1...3 {
    print("반복")
}
</code></pre>
<p><code>stride</code>로 증감 폭을 지정할 수 있습니다.</p>
<pre><code>for i in stride(from: 0, to: 10, by: 2) {
    print(i)  // 0, 2, 4, 6, 8
}
</code></pre>
<hr />
<h2>while / repeat-while</h2>
<p>조건이 참인 동안 반복합니다.</p>
<pre><code>var n = 1
while n &lt; 100 {
    n *= 2
}
print(n)  // 128
</code></pre>
<p><code>repeat-while</code>은 본문을 최소 한 번 실행하고 조건을 확인합니다. Python의 <code>while True: ... if ...: break</code> 패턴이나 C의 <code>do-while</code>에 해당합니다.</p>
<pre><code>var input = 0
repeat {
    input += 1
} while input &lt; 3
print(input)  // 3
</code></pre>
<hr />
<h2>guard — 조기 탈출 패턴</h2>
<p><code>guard</code>는 Swift에서만 볼 수 있는 키워드입니다. <code>if</code>의 반대 방향으로 동작한다고 생각하면 됩니다.</p>
<p><code>if</code>는 &#8220;조건이 참이면 실행한다&#8221;이고, <code>guard</code>는 &#8220;조건이 거짓이면 지금 블록에서 탈출한다&#8221;입니다.</p>
<pre><code>func greet(name: String?) {
    guard let name = name else {
        print("이름이 없습니다")
        return  // 반드시 탈출해야 함
    }
    // 여기서부터 name은 String (옵셔널 아님)
    print("안녕하세요, \(name)!")
}

greet(name: "철수")   // 안녕하세요, 철수!
greet(name: nil)      // 이름이 없습니다
</code></pre>
<p><code>guard</code>가 왜 존재하는지는 <code>if let</code>과 비교하면 분명해집니다.</p>
<pre><code>// if let 방식 — 유효한 경우가 중첩 안으로 들어감
func process(value: String?) {
    if let v = value {
        if v.count > 0 {
            if let n = Int(v) {
                print("숫자: \(n)")
            }
        }
    }
}

// guard 방식 — 실패 케이스를 먼저 처리하고, 정상 흐름은 들여쓰기 없이 이어짐
func process(value: String?) {
    guard let v = value else { return }
    guard v.count > 0 else { return }
    guard let n = Int(v) else { return }

    print("숫자: \(n)")
}
</code></pre>
<p>두 번째 방식이 훨씬 읽기 쉽습니다. &#8220;이 조건을 만족하지 않으면 여기서 끝낸다&#8221;는 예외 케이스를 함수 앞쪽에서 정리하고, 정상 경로는 들여쓰기 없이 선형으로 이어집니다. 이것을 <strong>조기 탈출(early return)</strong> 패턴이라고 합니다.</p>
<p><code>guard</code>는 반드시 <code>return</code>, <code>throw</code>, <code>break</code>, <code>continue</code> 중 하나로 현재 스코프를 탈출해야 합니다. 탈출 코드가 없으면 컴파일 오류가 납니다.</p>
<hr />
<h2>break와 continue</h2>
<pre><code>// break: 반복문 즉시 종료
for i in 1...10 {
    if i == 5 { break }
    print(i)  // 1, 2, 3, 4
}

// continue: 현재 순회 건너뛰기
for i in 1...10 {
    if i % 2 == 0 { continue }
    print(i)  // 1, 3, 5, 7, 9
}
</code></pre>
<p>중첩 반복문에서 바깥 반복문을 제어할 때는 레이블을 씁니다.</p>
<pre><code>outer: for i in 1...3 {
    for j in 1...3 {
        if j == 2 { continue outer }  // 안쪽 반복 건너뛰고 바깥 다음 순회로
        print("\(i)-\(j)")
    }
}
// 1-1, 2-1, 3-1
</code></pre>
<hr />
<h2>다른 언어와 비교</h2>
<table>
<thead>
<tr>
<th></th>
<th>Python</th>
<th>JavaScript / C</th>
<th>Swift</th>
</tr>
</thead>
<tbody>
<tr>
<td>if 조건 괄호</td>
<td>불필요</td>
<td>필수</td>
<td>불필요 (관례)</td>
</tr>
<tr>
<td>switch fallthrough</td>
<td>없음 (match)</td>
<td>기본 동작</td>
<td>없음 (명시 시만)</td>
</tr>
<tr>
<td>switch 망라성</td>
<td>&#8211;</td>
<td>불필요</td>
<td>모든 경우 처리 필수</td>
</tr>
<tr>
<td>do-while</td>
<td>없음</td>
<td>있음</td>
<td><code>repeat-while</code></td>
</tr>
<tr>
<td>조기 탈출 키워드</td>
<td>없음</td>
<td>없음</td>
<td><code>guard</code></td>
</tr>
</tbody>
</table>
<hr />
<h2>핵심 요약</h2>
<ul>
<li>Swift의 <code>if</code>는 반드시 <code>Bool</code> 조건을 받는다. 암묵적 truthy/falsy 없음.</li>
<li><code>switch</code>는 기본적으로 fallthrough가 없다. 범위, 튜플, <code>where</code> 조건으로 강력한 패턴 매칭이 가능하다.</li>
<li><code>guard</code>는 조건이 거짓일 때 현재 스코프를 탈출한다. 예외 케이스를 먼저 처리하고 정상 흐름을 선형으로 유지하는 패턴이다.</li>
<li><code>for-in</code>은 컬렉션과 범위 모두에 쓸 수 있다. <code>stride</code>로 증감 폭을 지정할 수 있다.</li>
<li>반복 변수가 필요 없을 때는 <code>_</code>로 무시한다.</li>
</ul>
<hr />
<p>다음 편은 <strong>5편 — 함수: 파라미터 레이블, 기본값, inout</strong>입니다. Swift 함수가 외부 이름과 내부 이름을 따로 갖는 이유와, 함수가 일급 시민이라는 것의 의미를 다룹니다.</p>
<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Generated with <a href="https://claude.ai/claude-code">Claude Code</a></p>
</blockquote><p>The post <a href="https://nangchang.nes.or.kr/4%ed%8e%b8-%ec%a0%9c%ec%96%b4-%ed%9d%90%eb%a6%84-if-switch-guard-for-while/">[4편] 제어 흐름 — if, switch, guard, for, while</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://nangchang.nes.or.kr/4%ed%8e%b8-%ec%a0%9c%ec%96%b4-%ed%9d%90%eb%a6%84-if-switch-guard-for-while/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[3편] 컬렉션 타입 — Array, Dictionary, Set 언제 무엇을 쓰는가</title>
		<link>https://nangchang.nes.or.kr/3%ed%8e%b8-%ec%bb%ac%eb%a0%89%ec%85%98-%ed%83%80%ec%9e%85-array-dictionary-set-%ec%96%b8%ec%a0%9c-%eb%ac%b4%ec%97%87%ec%9d%84-%ec%93%b0%eb%8a%94%ea%b0%80/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=3%25ed%258e%25b8-%25ec%25bb%25ac%25eb%25a0%2589%25ec%2585%2598-%25ed%2583%2580%25ec%259e%2585-array-dictionary-set-%25ec%2596%25b8%25ec%25a0%259c-%25eb%25ac%25b4%25ec%2597%2587%25ec%259d%2584-%25ec%2593%25b0%25eb%258a%2594%25ea%25b0%2580</link>
					<comments>https://nangchang.nes.or.kr/3%ed%8e%b8-%ec%bb%ac%eb%a0%89%ec%85%98-%ed%83%80%ec%9e%85-array-dictionary-set-%ec%96%b8%ec%a0%9c-%eb%ac%b4%ec%97%87%ec%9d%84-%ec%93%b0%eb%8a%94%ea%b0%80/#respond</comments>
		
		<dc:creator><![CDATA[낭창]]></dc:creator>
		<pubDate>Sat, 30 May 2026 16:45:44 +0000</pubDate>
				<category><![CDATA[미분류]]></category>
		<guid isPermaLink="false">https://nangchang.nes.or.kr/?p=1089</guid>

					<description><![CDATA[<p>여러 값을 묶어 다루는 Swift의 세 가지 컬렉션. 순서가 필요하면 Array, 키로 찾으면 Dictionary, 중복 없이 모으면 Set. for-in 순회와 map·filter·reduce 맛보기.</p>
<p>The post <a href="https://nangchang.nes.or.kr/3%ed%8e%b8-%ec%bb%ac%eb%a0%89%ec%85%98-%ed%83%80%ec%9e%85-array-dictionary-set-%ec%96%b8%ec%a0%9c-%eb%ac%b4%ec%97%87%ec%9d%84-%ec%93%b0%eb%8a%94%ea%b0%80/">[3편] 컬렉션 타입 — Array, Dictionary, Set 언제 무엇을 쓰는가</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></description>
										<content:encoded><![CDATA[<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 이 글은 <a href="https://claude.ai/claude-code">Claude Code</a>(AI)가 작성합니다. | <a href="https://nangchang.nes.or.kr/?p=1085">시리즈 목차</a> | 이전: <a href="https://nangchang.nes.or.kr/?p=1088">2편</a></p>
</blockquote>
<p>실제 프로그램에서 값을 하나씩 따로 다루는 경우는 드뭅니다. 사용자 목록, 설정 항목, 방문한 페이지의 기록처럼 &#8220;여러 값을 묶어서&#8221; 다뤄야 하는 상황이 훨씬 많습니다. Swift는 이를 위해 세 가지 기본 컬렉션 타입을 제공합니다.</p>
<ul>
<li><strong>Array</strong> — 순서가 있는 목록</li>
<li><strong>Dictionary</strong> — 키로 값을 찾는 쌍</li>
<li><strong>Set</strong> — 중복 없는 집합</li>
</ul>
<p>셋 다 Python의 <code>list</code>, <code>dict</code>, <code>set</code>에 대응하지만 동작 방식에 중요한 차이가 있습니다. 하나씩 살펴보겠습니다.</p>
<hr />
<h2>Array — 순서가 있는 목록</h2>
<p>가장 자주 쓰는 컬렉션입니다. 값이 삽입된 순서를 유지하고, 인덱스(0부터 시작)로 접근합니다.</p>
<pre><code>var fruits = ["사과", "바나나", "체리"]

print(fruits[0])  // 사과
print(fruits[2])  // 체리
print(fruits.count)  // 3
</code></pre>
<p>타입을 명시하면 <code>[String]</code> 또는 <code>Array&lt;String&gt;</code>으로 씁니다. 두 표기는 완전히 동일합니다.</p>
<pre><code>var scores: [Int] = [95, 82, 76]
var names: Array&lt;String&gt; = ["Alice", "Bob"]  // 같은 의미, [String]이 관례
</code></pre>
<p>배열 조작의 기본 메서드입니다.</p>
<pre><code>var items = ["a", "b", "c"]

items.append("d")           // 끝에 추가 → ["a", "b", "c", "d"]
items.insert("z", at: 0)    // 특정 위치에 삽입 → ["z", "a", "b", "c", "d"]
items.remove(at: 1)         // 인덱스로 삭제 → ["z", "b", "c", "d"]
items.removeLast()          // 마지막 삭제 → ["z", "b", "c"]

print(items.isEmpty)        // false
print(items.contains("b"))  // true
print(items.first)          // Optional("z") ← 빈 배열일 수 있으므로 옵셔널
print(items.last)           // Optional("c")
</code></pre>
<p><code>first</code>와 <code>last</code>가 옵셔널을 반환하는 이유는 배열이 비어 있을 수 있기 때문입니다. Swift는 이런 &#8220;실패 가능성&#8221;을 타입으로 표현합니다(옵셔널은 7편에서 자세히 다룹니다).</p>
<p><code>let</code>으로 선언한 배열은 변경이 불가능합니다.</p>
<pre><code>let fixed = [1, 2, 3]
fixed.append(4)  // 오류: let으로 선언된 배열은 수정 불가
</code></pre>
<hr />
<h2>Dictionary — 키로 값을 찾는 쌍</h2>
<p>키(key)와 값(value)을 쌍으로 저장하고, 키를 이용해 값을 빠르게 찾는 컬렉션입니다. Python의 <code>dict</code>와 거의 같습니다.</p>
<pre><code>var capitals = ["한국": "서울", "일본": "도쿄", "프랑스": "파리"]

print(capitals["한국"])   // Optional("서울")
print(capitals["독일"])   // nil
</code></pre>
<p>타입 표기는 <code>[Key: Value]</code> 형식입니다.</p>
<pre><code>var scores: [String: Int] = ["Alice": 95, "Bob": 82]
</code></pre>
<p>딕셔너리 조회 결과가 <strong>항상 옵셔널</strong>인 것에 주목합니다. 해당 키가 없으면 <code>nil</code>이 반환되기 때문입니다. Swift는 이 가능성을 타입 수준에서 강제로 인식하게 만듭니다.</p>
<pre><code>// 값이 있을 때만 처리하는 안전한 패턴
if let city = capitals["한국"] {
    print("수도는 \(city)입니다")  // 수도는 서울입니다
}
</code></pre>
<p>값 추가·수정·삭제는 직관적입니다.</p>
<pre><code>var ages = ["Alice": 30, "Bob": 25]

ages["Carol"] = 28        // 새 키 추가
ages["Alice"] = 31        // 기존 값 수정
ages["Bob"] = nil         // 키 삭제

print(ages)  // ["Alice": 31, "Carol": 28]
</code></pre>
<p>딕셔너리는 <strong>순서를 보장하지 않습니다.</strong> 같은 딕셔너리를 출력해도 순서가 달라질 수 있습니다. 순서가 필요하다면 Array를 쓰거나, 키를 정렬한 뒤 순회해야 합니다.</p>
<hr />
<h2>Set — 중복 없는 집합</h2>
<p>같은 값이 두 번 들어갈 수 없고, 순서도 없습니다. 중복을 자동으로 제거하거나, 두 집합 사이의 관계(교집합·합집합 등)를 계산할 때 씁니다.</p>
<pre><code>var tags: Set&lt;String&gt; = ["swift", "ios", "macos", "swift"]
print(tags)         // {"swift", "ios", "macos"} — 중복 제거됨
print(tags.count)   // 3
</code></pre>
<p>Array 리터럴과 문법이 같아서 타입을 명시해야 Set으로 인식됩니다. 타입 어노테이션 없이 쓰면 Swift는 Array로 추론합니다.</p>
<pre><code>let a: Set = [1, 2, 3, 4]
let b: Set = [3, 4, 5, 6]

print(a.union(b))         // {1, 2, 3, 4, 5, 6}  합집합
print(a.intersection(b))  // {3, 4}               교집합
print(a.subtracting(b))   // {1, 2}               차집합
print(a.isDisjoint(with: b))  // false (공통 원소 있음)
</code></pre>
<p>Set의 조회는 Array보다 빠릅니다. Array에서 특정 값을 찾으려면 처음부터 끝까지 하나씩 확인해야 하지만(O(n)), Set은 해시 기반이라 거의 즉시(O(1)) 찾습니다. 순서가 필요 없고 &#8220;이 값이 들어 있는가?&#8221;만 자주 확인한다면 Set이 더 적합합니다.</p>
<hr />
<h2>for-in 으로 순회하기</h2>
<p>세 컬렉션 모두 <code>for-in</code>으로 순회할 수 있습니다.</p>
<pre><code>// Array
let fruits = ["사과", "바나나", "체리"]
for fruit in fruits {
    print(fruit)
}

// 인덱스가 필요할 때
for (index, fruit) in fruits.enumerated() {
    print("\(index): \(fruit)")
}
// 0: 사과
// 1: 바나나
// 2: 체리

// Dictionary
let capitals = ["한국": "서울", "일본": "도쿄"]
for (country, city) in capitals {
    print("\(country)의 수도는 \(city)")
}

// Set (순서 불정)
let tags: Set = ["swift", "ios", "macos"]
for tag in tags {
    print(tag)
}
</code></pre>
<hr />
<h2>고차 함수 맛보기 — map, filter, reduce</h2>
<p>컬렉션을 다룰 때 반복문을 직접 쓰는 대신, 의도를 더 명확하게 표현하는 고차 함수(higher-order function)를 자주 씁니다. 지금 단계에서 완벽히 이해하지 않아도 되지만, 어떤 역할을 하는지 눈에 익혀두면 나중에 편합니다.</p>
<p><strong><code>map</code></strong> — 각 원소를 변환해 새 배열 반환</p>
<pre><code>let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
print(doubled)  // [2, 4, 6, 8, 10]

let names = ["alice", "bob", "carol"]
let upper = names.map { $0.uppercased() }
print(upper)  // ["ALICE", "BOB", "CAROL"]
</code></pre>
<p><strong><code>filter</code></strong> — 조건을 만족하는 원소만 추려 새 배열 반환</p>
<pre><code>let scores = [88, 42, 95, 61, 73]
let passing = scores.filter { $0 >= 70 }
print(passing)  // [88, 95, 73]
</code></pre>
<p><strong><code>reduce</code></strong> — 모든 원소를 하나의 값으로 누적</p>
<pre><code>let prices = [1200, 3500, 800]
let total = prices.reduce(0) { $0 + $1 }
print(total)  // 5500
</code></pre>
<p><code>$0</code>, <code>$1</code>은 클로저의 첫 번째, 두 번째 인자를 가리키는 축약 표현입니다. 클로저는 6편에서 자세히 다룹니다. 지금은 &#8220;각 원소에 적용할 동작을 <code>{ }</code> 안에 쓴다&#8221;는 패턴만 기억해도 충분합니다.</p>
<hr />
<h2>세 컬렉션 선택 기준</h2>
<table>
<thead>
<tr>
<th>상황</th>
<th>선택</th>
</tr>
</thead>
<tbody>
<tr>
<td>순서가 중요하고, 같은 값이 여러 번 나올 수 있다</td>
<td>Array</td>
</tr>
<tr>
<td>이름(키)으로 값을 빠르게 찾아야 한다</td>
<td>Dictionary</td>
</tr>
<tr>
<td>중복 없이 모아야 하거나, 집합 연산이 필요하다</td>
<td>Set</td>
</tr>
</tbody>
</table>
<hr />
<h2>다른 언어와 비교</h2>
<table>
<thead>
<tr>
<th></th>
<th>Python</th>
<th>JavaScript</th>
<th>Swift</th>
</tr>
</thead>
<tbody>
<tr>
<td>순서 있는 목록</td>
<td><code>list</code></td>
<td><code>Array</code></td>
<td><code>Array</code> / <code>[T]</code></td>
</tr>
<tr>
<td>키-값 쌍</td>
<td><code>dict</code></td>
<td><code>Object</code> / <code>Map</code></td>
<td><code>Dictionary</code> / <code>[K: V]</code></td>
</tr>
<tr>
<td>중복 없는 집합</td>
<td><code>set</code></td>
<td><code>Set</code></td>
<td><code>Set&lt;T&gt;</code></td>
</tr>
<tr>
<td>딕셔너리 조회 결과</td>
<td>없으면 <code>KeyError</code></td>
<td>없으면 <code>undefined</code></td>
<td>없으면 <code>nil</code> (옵셔널)</td>
</tr>
<tr>
<td><code>let</code>으로 선언 시</td>
<td>(불변 컬렉션 없음)</td>
<td><code>const</code>도 내용 수정 가능</td>
<td>내용 수정 불가</td>
</tr>
</tbody>
</table>
<p>Swift에서 <code>let</code>으로 선언한 컬렉션은 원소 추가·삭제 모두 불가합니다. JavaScript의 <code>const</code>는 재할당만 막을 뿐 내용 수정은 허용하는 것과 다릅니다.</p>
<hr />
<h2>핵심 요약</h2>
<ul>
<li>Array는 순서 있는 목록, Dictionary는 키-값 쌍, Set은 중복 없는 집합이다.</li>
<li>Dictionary 조회 결과는 항상 옵셔널이다. 해당 키가 없을 수 있기 때문이다.</li>
<li>Set은 중복을 자동 제거하고, 값 존재 여부 확인이 Array보다 빠르다.</li>
<li><code>let</code>으로 선언한 컬렉션은 내용을 변경할 수 없다.</li>
<li><code>map</code>, <code>filter</code>, <code>reduce</code>는 반복문 대신 컬렉션 변환 의도를 간결하게 표현하는 고차 함수다.</li>
</ul>
<hr />
<p>다음 편은 <strong>4편 — 제어 흐름: if, switch, guard, for, while</strong>입니다. Swift의 <code>switch</code>가 다른 언어와 얼마나 다른지, <code>guard</code>가 왜 존재하는지를 다룹니다.</p>
<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Generated with <a href="https://claude.ai/claude-code">Claude Code</a></p>
</blockquote><p>The post <a href="https://nangchang.nes.or.kr/3%ed%8e%b8-%ec%bb%ac%eb%a0%89%ec%85%98-%ed%83%80%ec%9e%85-array-dictionary-set-%ec%96%b8%ec%a0%9c-%eb%ac%b4%ec%97%87%ec%9d%84-%ec%93%b0%eb%8a%94%ea%b0%80/">[3편] 컬렉션 타입 — Array, Dictionary, Set 언제 무엇을 쓰는가</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://nangchang.nes.or.kr/3%ed%8e%b8-%ec%bb%ac%eb%a0%89%ec%85%98-%ed%83%80%ec%9e%85-array-dictionary-set-%ec%96%b8%ec%a0%9c-%eb%ac%b4%ec%97%87%ec%9d%84-%ec%93%b0%eb%8a%94%ea%b0%80/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[2편] 변수·상수·기본 타입 — Int, Double, String, Bool 완전 정리</title>
		<link>https://nangchang.nes.or.kr/2%ed%8e%b8-%eb%b3%80%ec%88%98%c2%b7%ec%83%81%ec%88%98%c2%b7%ea%b8%b0%eb%b3%b8-%ed%83%80%ec%9e%85-int-double-string-bool-%ec%99%84%ec%a0%84-%ec%a0%95%eb%a6%ac/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=2%25ed%258e%25b8-%25eb%25b3%2580%25ec%2588%2598%25c2%25b7%25ec%2583%2581%25ec%2588%2598%25c2%25b7%25ea%25b8%25b0%25eb%25b3%25b8-%25ed%2583%2580%25ec%259e%2585-int-double-string-bool-%25ec%2599%2584%25ec%25a0%2584-%25ec%25a0%2595%25eb%25a6%25ac</link>
					<comments>https://nangchang.nes.or.kr/2%ed%8e%b8-%eb%b3%80%ec%88%98%c2%b7%ec%83%81%ec%88%98%c2%b7%ea%b8%b0%eb%b3%b8-%ed%83%80%ec%9e%85-int-double-string-bool-%ec%99%84%ec%a0%84-%ec%a0%95%eb%a6%ac/#respond</comments>
		
		<dc:creator><![CDATA[낭창]]></dc:creator>
		<pubDate>Sat, 30 May 2026 16:44:29 +0000</pubDate>
				<category><![CDATA[미분류]]></category>
		<guid isPermaLink="false">https://nangchang.nes.or.kr/?p=1088</guid>

					<description><![CDATA[<p>Swift의 네 가지 기본 타입을 깊이 파고든다. 왜 암묵적 형변환이 없는지, 문자열 보간은 어떻게 쓰는지, let을 기본으로 써야 하는 진짜 이유.</p>
<p>The post <a href="https://nangchang.nes.or.kr/2%ed%8e%b8-%eb%b3%80%ec%88%98%c2%b7%ec%83%81%ec%88%98%c2%b7%ea%b8%b0%eb%b3%b8-%ed%83%80%ec%9e%85-int-double-string-bool-%ec%99%84%ec%a0%84-%ec%a0%95%eb%a6%ac/">[2편] 변수·상수·기본 타입 — Int, Double, String, Bool 완전 정리</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></description>
										<content:encoded><![CDATA[<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 이 글은 <a href="https://claude.ai/claude-code">Claude Code</a>(AI)가 작성합니다. | <a href="https://nangchang.nes.or.kr/?p=1085">시리즈 목차</a> | 이전: <a href="https://nangchang.nes.or.kr/?p=1087">1편</a></p>
</blockquote>
<p>1편에서 <code>var</code>와 <code>let</code>, 타입 추론을 간단히 살펴봤습니다. 이번 편에서는 같은 내용을 더 깊이 파고듭니다. Swift의 기본 타입이 Python이나 JavaScript의 그것과 어떻게 다른지, 그리고 왜 그렇게 설계됐는지를 이해하는 것이 목표입니다.</p>
<hr />
<h2><code>let</code>이 기본이어야 하는 이유</h2>
<p>1편에서 <code>var</code>는 변수, <code>let</code>은 상수라고 배웠습니다. Swift 공식 가이드라인은 <strong>값이 바뀔 필요가 없다면 항상 <code>let</code>을 쓰라</strong>고 권장합니다. 처음엔 다소 귀찮게 느껴지는 이 원칙에는 이유가 있습니다.</p>
<p>코드가 복잡해질수록 &#8220;이 변수가 지금 어떤 값을 갖고 있지?&#8221;라는 추적이 어려워집니다. 변수가 여러 곳에서 변경될 수 있다면 버그를 찾기도 힘들어집니다. <code>let</code>으로 선언된 값은 선언 시점 이후에 절대 바뀌지 않는다는 보장이 생기고, 이 보장이 코드를 읽는 사람에게 신뢰를 줍니다.</p>
<pre><code>let maxRetries = 3         // 이 값은 절대 바뀌지 않는다
var currentRetry = 0      // 이 값은 반복하면서 바뀐다
</code></pre>
<p>코드를 읽는 사람은 <code>let</code>을 보는 순간 &#8220;이 값은 추적할 필요가 없다&#8221;고 판단할 수 있습니다. 이것이 코드 가독성에 직접적으로 기여합니다.</p>
<p>Xcode도 이 원칙을 강제합니다. <code>var</code>로 선언했지만 값이 한 번도 변경되지 않으면, 컴파일러가 경고를 표시하며 <code>let</code>으로 바꾸라고 제안합니다.</p>
<hr />
<h2>네 가지 기본 타입</h2>
<h3>Int — 정수</h3>
<p>소수점 없는 숫자입니다.</p>
<pre><code>let age = 30
let temperature = -5
let population = 51_000_000  // 밑줄로 자릿수 구분 가능 (값에 영향 없음)
</code></pre>
<p>Swift의 <code>Int</code>는 실행 중인 기기의 아키텍처에 따라 크기가 자동으로 결정됩니다. 요즘 Mac과 iPhone은 모두 64비트이므로 <code>Int</code>는 <code>Int64</code>와 같습니다. 표현 가능한 범위는 약 −9.2조 ~ +9.2조로 일반적인 정수 연산에서 넘칠 일이 없습니다.</p>
<p>특정 크기가 필요할 때는 명시할 수 있습니다(<code>Int8</code>, <code>Int16</code>, <code>Int32</code>, <code>Int64</code>). 하지만 특별한 이유가 없다면 그냥 <code>Int</code>를 씁니다.</p>
<h3>Double — 부동소수점 수</h3>
<p>소수점이 있는 숫자입니다.</p>
<pre><code>let pi = 3.14159
let height = 172.5
let tiny = 0.000_001
</code></pre>
<p>Swift에는 <code>Double</code>(64비트, 약 15자리 정밀도)과 <code>Float</code>(32비트, 약 7자리 정밀도)가 있습니다. 특별한 이유가 없다면 <code>Double</code>을 씁니다. Apple 공식 API도 대부분 <code>Double</code>을 기준으로 설계되어 있습니다.</p>
<h3>Bool — 참·거짓</h3>
<p>논리값입니다. <code>true</code> 아니면 <code>false</code>, 딱 두 가지만 가질 수 있습니다.</p>
<pre><code>let isLoggedIn = true
let hasError = false
</code></pre>
<p>Swift의 <code>Bool</code>은 다른 타입과 절대 혼용되지 않습니다. C나 Python에서는 <code>0</code>이 <code>false</code>로, 빈 문자열이 <code>false</code>로 취급되는 암묵적 변환이 있지만, Swift에서는 그런 것이 없습니다.</p>
<pre><code>let count = 0
if count {  // 오류: Int는 Bool이 아님
    print("비어 있음")
}

if count == 0 {  // 올바른 방식
    print("비어 있음")
}
</code></pre>
<h3>String — 문자열</h3>
<p>텍스트를 담는 타입입니다. Swift의 <code>String</code>은 처음부터 <strong>유니코드 완전 지원</strong>을 전제로 설계됐습니다. 한국어, 이모지, 아랍어 등 어떤 문자도 <code>String</code> 하나로 처리합니다.</p>
<pre><code>let greeting = "안녕하세요"
let emoji = "Swift &#x1f680;"
let empty = ""
</code></pre>
<p>여러 줄 문자열은 큰따옴표 세 개(<code>"""</code>)로 감쌉니다.</p>
<pre><code>let message = """
안녕하세요.
오늘 날씨가 좋네요.
"""
</code></pre>
<hr />
<h2>문자열 보간 — <code>\()</code></h2>
<p>문자열 안에 변수나 표현식의 값을 삽입할 때는 <code>\()</code> 문법을 씁니다.</p>
<pre><code>let name = "철수"
let age = 25

print("이름: \(name), 나이: \(age)")
// 출력: 이름: 철수, 나이: 25

print("10년 후엔 \(age + 10)살")
// 출력: 10년 후엔 35살
</code></pre>
<p>괄호 안에는 단순 변수뿐 아니라 어떤 표현식도 넣을 수 있습니다. Python의 f-string, JavaScript의 템플릿 리터럴과 같은 역할입니다.</p>
<hr />
<h2>타입 변환 — 암묵적 변환이 없는 이유</h2>
<p>Swift에서는 서로 다른 타입 사이의 연산이 자동으로 이루어지지 않습니다.</p>
<pre><code>let a: Int = 10
let b: Double = 3.14

let result = a + b  // 오류: Int와 Double을 더할 수 없음
</code></pre>
<p>Python이라면 <code>10 + 3.14</code>는 자동으로 <code>13.14</code>가 됩니다. Swift는 이를 허용하지 않습니다. 대신 명시적으로 변환해야 합니다.</p>
<pre><code>let result = Double(a) + b  // 13.14 — Int를 Double로 명시 변환
</code></pre>
<p>번거로워 보이지만 이 제약에도 이유가 있습니다. 암묵적 변환은 때로 예상치 못한 정밀도 손실이나 오버플로를 일으킵니다. Swift는 그 선택을 프로그래머가 의식적으로 하도록 요구합니다.</p>
<p>자주 쓰는 변환 패턴을 정리합니다.</p>
<pre><code>// 숫자 → 문자열
let n = 42
let s = String(n)       // "42"

// 문자열 → 숫자 (실패할 수 있으므로 옵셔널 반환)
let parsed = Int("42")  // Optional(42)
let failed = Int("abc") // nil

// Int &#x2194; Double
let d = Double(n)       // 42.0
let i = Int(3.99)       // 3 (소수점 버림)
</code></pre>
<p><code>Int("42")</code>가 <code>Optional(42)</code>를 반환하는 이유는 문자열이 숫자가 아닐 수도 있기 때문입니다. 옵셔널은 7편에서 자세히 다루지만, 지금은 &#8220;변환이 실패할 수도 있다는 신호&#8221;로만 이해해도 충분합니다.</p>
<hr />
<h2>타입 명시가 필요한 경우</h2>
<p>대부분은 타입 추론에 맡기면 됩니다. 하지만 다음 경우에는 타입을 명시하는 것이 좋습니다.</p>
<pre><code>// 1. 초기값 없이 선언할 때
var score: Int
score = 100  // 나중에 값 대입

// 2. 추론되는 타입이 원하는 타입과 다를 때
let ratio = 1 / 3          // Int 나눗셈 → 0
let ratio: Double = 1 / 3  // 여전히 0 (Int 리터럴끼리 나눗셈)
let ratio = 1.0 / 3.0      // Double → 0.333...  ← 이렇게 쓰는 것이 일반적

// 3. 코드를 읽는 사람에게 타입을 명확히 알려주고 싶을 때
let maxFileSize: Int64 = 1_073_741_824  // Int64임을 강조
</code></pre>
<hr />
<h2>다른 언어와 비교</h2>
<table>
<thead>
<tr>
<th></th>
<th>Python</th>
<th>JavaScript</th>
<th>Swift</th>
</tr>
</thead>
<tbody>
<tr>
<td>정수</td>
<td><code>int</code> (크기 무제한)</td>
<td><code>number</code></td>
<td><code>Int</code> (64비트)</td>
</tr>
<tr>
<td>소수</td>
<td><code>float</code></td>
<td><code>number</code></td>
<td><code>Double</code> / <code>Float</code></td>
</tr>
<tr>
<td>문자열</td>
<td><code>str</code></td>
<td><code>string</code></td>
<td><code>String</code></td>
</tr>
<tr>
<td>참·거짓</td>
<td><code>bool</code> (<code>0</code>도 <code>False</code>)</td>
<td><code>boolean</code> (falsy 개념 있음)</td>
<td><code>Bool</code> (오직 <code>true</code>/<code>false</code>)</td>
</tr>
<tr>
<td>암묵적 형변환</td>
<td>있음</td>
<td>있음 (<code>==</code> vs <code>===</code>)</td>
<td>없음</td>
</tr>
<tr>
<td>문자열 보간</td>
<td><code>f"{name}"</code></td>
<td><code>`${name}`</code></td>
<td><code>"\(name)"</code></td>
</tr>
</tbody>
</table>
<hr />
<h2>핵심 요약</h2>
<ul>
<li>값이 바뀔 필요가 없으면 <code>let</code>을 쓴다. Xcode가 불필요한 <code>var</code>를 경고한다.</li>
<li>기본 숫자 타입은 <code>Int</code>와 <code>Double</code>, 특별한 이유 없이 이 둘을 쓴다.</li>
<li>Swift의 <code>Bool</code>은 오직 <code>true</code>/<code>false</code>만 허용한다. <code>0</code>이나 빈 문자열을 <code>false</code>로 다루는 암묵적 변환이 없다.</li>
<li>문자열 보간은 <code>"\(변수)"</code> 문법을 쓴다. 괄호 안에 표현식도 들어간다.</li>
<li>타입 간 변환은 항상 명시적으로 해야 한다. <code>Double(someInt)</code>, <code>String(someInt)</code> 같은 형태.</li>
</ul>
<hr />
<p>다음 편은 <strong>3편 — 컬렉션 타입: Array, Dictionary, Set</strong>입니다. 여러 값을 묶어서 다루는 세 가지 방법과, 각각 언제 쓰는지를 다룹니다.</p>
<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Generated with <a href="https://claude.ai/claude-code">Claude Code</a></p>
</blockquote><p>The post <a href="https://nangchang.nes.or.kr/2%ed%8e%b8-%eb%b3%80%ec%88%98%c2%b7%ec%83%81%ec%88%98%c2%b7%ea%b8%b0%eb%b3%b8-%ed%83%80%ec%9e%85-int-double-string-bool-%ec%99%84%ec%a0%84-%ec%a0%95%eb%a6%ac/">[2편] 변수·상수·기본 타입 — Int, Double, String, Bool 완전 정리</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://nangchang.nes.or.kr/2%ed%8e%b8-%eb%b3%80%ec%88%98%c2%b7%ec%83%81%ec%88%98%c2%b7%ea%b8%b0%eb%b3%b8-%ed%83%80%ec%9e%85-int-double-string-bool-%ec%99%84%ec%a0%84-%ec%a0%95%eb%a6%ac/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[1편] Swift 소개와 환경 설정 — Xcode 설치부터 첫 줄 실행까지</title>
		<link>https://nangchang.nes.or.kr/1%ed%8e%b8-swift-%ec%86%8c%ea%b0%9c%ec%99%80-%ed%99%98%ea%b2%bd-%ec%84%a4%ec%a0%95-xcode-%ec%84%a4%ec%b9%98%eb%b6%80%ed%84%b0-%ec%b2%ab-%ec%a4%84-%ec%8b%a4%ed%96%89%ea%b9%8c%ec%a7%80/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=1%25ed%258e%25b8-swift-%25ec%2586%258c%25ea%25b0%259c%25ec%2599%2580-%25ed%2599%2598%25ea%25b2%25bd-%25ec%2584%25a4%25ec%25a0%2595-xcode-%25ec%2584%25a4%25ec%25b9%2598%25eb%25b6%2580%25ed%2584%25b0-%25ec%25b2%25ab-%25ec%25a4%2584-%25ec%258b%25a4%25ed%2596%2589%25ea%25b9%258c%25ec%25a7%2580</link>
					<comments>https://nangchang.nes.or.kr/1%ed%8e%b8-swift-%ec%86%8c%ea%b0%9c%ec%99%80-%ed%99%98%ea%b2%bd-%ec%84%a4%ec%a0%95-xcode-%ec%84%a4%ec%b9%98%eb%b6%80%ed%84%b0-%ec%b2%ab-%ec%a4%84-%ec%8b%a4%ed%96%89%ea%b9%8c%ec%a7%80/#respond</comments>
		
		<dc:creator><![CDATA[낭창]]></dc:creator>
		<pubDate>Sat, 30 May 2026 15:27:17 +0000</pubDate>
				<category><![CDATA[미분류]]></category>
		<guid isPermaLink="false">https://nangchang.nes.or.kr/?p=1087</guid>

					<description><![CDATA[<p>Swift는 왜 만들어졌는가, 정적 타입 언어란 무엇인가, Xcode와 Playground로 첫 줄을 실행하기까지. macOS·Swift 개발 완전 초보자를 위한 시작점.</p>
<p>The post <a href="https://nangchang.nes.or.kr/1%ed%8e%b8-swift-%ec%86%8c%ea%b0%9c%ec%99%80-%ed%99%98%ea%b2%bd-%ec%84%a4%ec%a0%95-xcode-%ec%84%a4%ec%b9%98%eb%b6%80%ed%84%b0-%ec%b2%ab-%ec%a4%84-%ec%8b%a4%ed%96%89%ea%b9%8c%ec%a7%80/">[1편] Swift 소개와 환경 설정 — Xcode 설치부터 첫 줄 실행까지</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></description>
										<content:encoded><![CDATA[<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 이 글은 <a href="https://claude.ai/claude-code">Claude Code</a>(AI)가 작성합니다. | <a href="https://nangchang.nes.or.kr/?p=1085">시리즈 목차</a></p>
</blockquote>
<p>이 시리즈의 첫 편입니다. 코드를 한 줄 실행하기 전에, 먼저 Swift라는 언어가 왜 존재하는지 이해하는 것부터 시작합니다. 도구를 쓰기 전에 그 도구가 어떤 문제를 해결하기 위해 만들어졌는지 알면, 나중에 낯선 문법을 만났을 때 &#8220;왜 이렇게 생겼지?&#8221;라는 질문에 스스로 답할 수 있게 됩니다.</p>
<hr />
<h2>Swift는 왜 만들어졌는가</h2>
<p>2014년 이전까지 Apple 플랫폼(macOS, iOS)의 앱은 <strong>Objective-C</strong>라는 언어로 만들었습니다. Objective-C는 1980년대 설계된 언어로, C 언어에 객체 지향 문법을 덧붙인 형태였습니다. 기능은 충분했지만 문법이 낯설고 실수하기 쉬웠습니다.</p>
<p>2014년 Apple은 Swift를 발표하며 이렇게 말했습니다. <em>&#8220;Objective-C without the C.&#8221;</em> C 언어의 짐을 내려놓고, 현대적인 언어 설계 원칙을 처음부터 적용한 새 언어라는 선언이었습니다.</p>
<p>Swift가 해결하려 한 핵심 문제는 세 가지였습니다.</p>
<ul>
<li><strong>안전성</strong> — 프로그램이 실행되는 도중에 터지는 오류(런타임 크래시)를 코드를 작성하는 시점에 미리 잡는다</li>
<li><strong>속도</strong> — Python처럼 편리하지만, C에 가까운 실행 속도를 낸다</li>
<li><strong>표현력</strong> — 같은 의도를 더 적은 코드로, 더 읽기 좋게 쓸 수 있다</li>
</ul>
<p>지금은 Swift 없이 Apple 플랫폼 개발을 논하기 어렵습니다. Objective-C는 여전히 쓰이지만, 새 프로젝트의 표준은 Swift입니다.</p>
<hr />
<h2>정적 타입 언어 vs 동적 타입 언어</h2>
<p>Swift를 이해하는 데 가장 중요한 개념 하나를 고르라면 <strong>정적 타입(static typing)</strong>입니다.</p>
<p>Python이나 JavaScript는 <strong>동적 타입</strong> 언어입니다. 변수에 어떤 종류의 값이 들어가는지를 프로그램이 실행될 때 비로소 확인합니다.</p>
<pre><code># Python
x = 42        # 정수
x = "hello"   # 문자열로 바꿔도 오류 없음
x = [1, 2, 3] # 리스트로 바꿔도 오류 없음
</code></pre>
<p>유연하지만, 그만큼 실수가 숨어들기 쉽습니다. <code>x</code>가 숫자라고 믿고 계산을 했는데 실제로는 문자열이었다면, 프로그램이 실행되는 순간까지 그 오류를 모릅니다.</p>
<p>Swift는 <strong>정적 타입</strong> 언어입니다. 모든 변수의 타입이 코드를 <em>컴파일하는 시점</em>에 결정되고, 한번 정해진 타입은 바뀌지 않습니다.</p>
<pre><code>// Swift
var x = 42       // x는 Int 타입으로 결정됨
x = "hello"      // 컴파일 오류: Int에 String을 대입할 수 없음
</code></pre>
<p>불편해 보일 수 있지만, 이 제약이 오히려 안전망이 됩니다. 실수를 코드 작성 단계에서 잡아주기 때문에, 앱이 사용자 손에서 실행되는 도중에 터지는 일이 줄어듭니다.</p>
<hr />
<h2>컴파일 언어란 무엇인가</h2>
<p>Python은 코드를 한 줄씩 읽으면서 바로 실행하는 <strong>인터프리터</strong> 방식입니다. Swift는 코드 전체를 먼저 기계어로 번역(<strong>컴파일</strong>)한 뒤 실행합니다.</p>
<p>컴파일 단계가 있다는 것은 두 가지를 의미합니다.</p>
<ol>
<li><strong>실행 속도가 빠르다</strong> — 이미 기계어로 변환된 코드를 실행하므로, 매번 해석하는 과정이 없다</li>
<li><strong>오류를 미리 잡는다</strong> — 컴파일러가 전체 코드를 훑으며 타입 오류, 존재하지 않는 함수 호출 등을 실행 전에 알려준다</li>
</ol>
<p>Xcode에서 코드를 작성하다 빨간 밑줄이 그어지는 것이 바로 컴파일러가 미리 오류를 잡아주는 것입니다.</p>
<hr />
<h2>Xcode 설치</h2>
<p>Swift 개발 환경은 <strong>Xcode</strong>입니다. macOS에서만 동작하는 Apple 공식 IDE(통합 개발 환경)로, Mac App Store에서 무료로 받을 수 있습니다.</p>
<p>App Store에서 &#8220;Xcode&#8221;를 검색해 설치합니다. 설치 용량이 10GB를 넘으니 시간이 걸립니다. 설치가 끝나면 한 번 실행해 추가 구성 요소 설치를 완료합니다.</p>
<blockquote>
<p><strong>버전 확인:</strong> 이 시리즈는 Xcode 16, Swift 5.9 이상을 기준으로 합니다. Xcode 메뉴 → About Xcode에서 버전을 확인할 수 있습니다.</p>
</blockquote>
<hr />
<h2>Playground: 가장 빠른 Swift 실행 환경</h2>
<p>Xcode를 설치했다면 앱 프로젝트를 만들기 전에 <strong>Playground</strong>를 먼저 써보겠습니다. Playground는 코드를 타이핑하는 즉시 결과를 오른쪽에 보여주는 대화형 실행 환경입니다. Python의 REPL(인터랙티브 셸)과 비슷한 역할입니다.</p>
<p>Playground를 여는 방법:</p>
<ol>
<li>Xcode 실행</li>
<li>메뉴에서 File → New → Playground 선택</li>
<li>Blank 템플릿 선택 후 저장</li>
</ol>
<p>처음 열면 이런 코드가 보입니다.</p>
<pre><code>import Foundation

var greeting = "Hello, playground"
</code></pre>
<p>오른쪽 패널에 <code>"Hello, playground"</code>가 표시되면 정상입니다. 이 환경에서 이 시리즈의 코드 예제들을 직접 실행해볼 수 있습니다.</p>
<hr />
<h2>첫 코드 — print, 변수, 타입 추론</h2>
<p>Playground에서 아래 코드를 입력해보겠습니다.</p>
<pre><code>print("Hello, Swift!")
</code></pre>
<p>콘솔 하단에 <code>Hello, Swift!</code>가 출력됩니다.</p>
<p>이제 변수를 선언해봅니다.</p>
<pre><code>var name = "Alice"
var age = 30
var height = 165.5
var isStudent = true

print(name)    // Alice
print(age)     // 30
print(height)  // 165.5
</code></pre>
<p>Swift는 <strong>타입 추론(type inference)</strong>을 지원합니다. <code>var name = "Alice"</code>라고 쓰면 Swift 컴파일러가 오른쪽 값 <code>"Alice"</code>를 보고 <code>name</code>이 <code>String</code> 타입임을 스스로 판단합니다. 타입을 직접 명시할 수도 있습니다.</p>
<pre><code>var name: String = "Alice"
var age: Int = 30
var height: Double = 165.5
var isStudent: Bool = true
</code></pre>
<p>둘 다 동일하게 동작합니다. 타입이 명확한 경우에는 추론에 맡기는 것이 관례입니다.</p>
<p>다음으로 상수를 선언해봅니다. <code>var</code> 대신 <code>let</code>을 쓰면 값을 바꿀 수 없는 상수가 됩니다.</p>
<pre><code>let pi = 3.14159
pi = 3.0  // 오류: 상수는 값을 변경할 수 없음
</code></pre>
<p>Swift는 <strong>변경이 필요하지 않으면 <code>let</code>을 쓰는 것을 권장</strong>합니다. 의도치 않게 값이 바뀌는 실수를 컴파일러가 막아주기 때문입니다.</p>
<hr />
<h2>다른 언어와 비교</h2>
<table>
<thead>
<tr>
<th></th>
<th>Python</th>
<th>JavaScript</th>
<th>Swift</th>
</tr>
</thead>
<tbody>
<tr>
<td>타입 결정 시점</td>
<td>런타임</td>
<td>런타임</td>
<td>컴파일 타임</td>
</tr>
<tr>
<td>변수 선언</td>
<td><code>x = 1</code></td>
<td><code>let x = 1</code></td>
<td><code>var x = 1</code></td>
</tr>
<tr>
<td>상수 선언</td>
<td>(없음, 관례로 대문자)</td>
<td><code>const x = 1</code></td>
<td><code>let x = 1</code></td>
</tr>
<tr>
<td>출력</td>
<td><code>print(x)</code></td>
<td><code>console.log(x)</code></td>
<td><code>print(x)</code></td>
</tr>
<tr>
<td>문자열 보간</td>
<td><code>f"안녕 {name}"</code></td>
<td><code>`안녕 ${name}`</code></td>
<td><code>"안녕 \(name)"</code></td>
</tr>
<tr>
<td>실행 방식</td>
<td>인터프리터</td>
<td>JIT 컴파일</td>
<td>AOT 컴파일</td>
</tr>
</tbody>
</table>
<p>JavaScript의 <code>let</code>은 변경 가능한 변수이고, Swift의 <code>let</code>은 상수입니다. 같은 키워드가 반대 의미를 가지므로 주의가 필요합니다.</p>
<hr />
<h2>핵심 요약</h2>
<ul>
<li>Swift는 2014년 Apple이 Objective-C를 대체하기 위해 만든 언어다. 안전성·속도·표현력이 설계 목표다.</li>
<li>정적 타입 언어는 타입 오류를 컴파일 시점에 잡는다. 실수가 앱 실행 중 크래시가 되기 전에 알 수 있다.</li>
<li>Xcode의 Playground는 앱 프로젝트 없이 Swift 코드를 즉시 실행해볼 수 있는 환경이다.</li>
<li><code>var</code>는 변경 가능한 변수, <code>let</code>은 변경 불가능한 상수다. Swift는 <code>let</code>을 기본으로 쓰길 권장한다.</li>
<li>타입을 명시하지 않아도 Swift 컴파일러가 초기값을 보고 타입을 추론한다.</li>
</ul>
<hr />
<p>다음 편은 <strong><a href="https://nangchang.nes.or.kr/?p=1085">2편 — 변수·상수·기본 타입</a></strong>입니다. <code>Int</code>, <code>Double</code>, <code>String</code>, <code>Bool</code> 네 가지 기본 타입을 더 깊이 파고들고, 타입 변환이 왜 명시적으로만 허용되는지를 다룹니다.</p>
<blockquote>
<p><img src="https://s.w.org/images/core/emoji/15.1.0/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Generated with <a href="https://claude.ai/claude-code">Claude Code</a></p>
</blockquote><p>The post <a href="https://nangchang.nes.or.kr/1%ed%8e%b8-swift-%ec%86%8c%ea%b0%9c%ec%99%80-%ed%99%98%ea%b2%bd-%ec%84%a4%ec%a0%95-xcode-%ec%84%a4%ec%b9%98%eb%b6%80%ed%84%b0-%ec%b2%ab-%ec%a4%84-%ec%8b%a4%ed%96%89%ea%b9%8c%ec%a7%80/">[1편] Swift 소개와 환경 설정 — Xcode 설치부터 첫 줄 실행까지</a> first appeared on <a href="https://nangchang.nes.or.kr">Naver Ending Study</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://nangchang.nes.or.kr/1%ed%8e%b8-swift-%ec%86%8c%ea%b0%9c%ec%99%80-%ed%99%98%ea%b2%bd-%ec%84%a4%ec%a0%95-xcode-%ec%84%a4%ec%b9%98%eb%b6%80%ed%84%b0-%ec%b2%ab-%ec%a4%84-%ec%8b%a4%ed%96%89%ea%b9%8c%ec%a7%80/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
