iOS 26 Foundation Models 프레임워크 완벽 가이드: 온디바이스 LLM과 @Generable로 Apple Intelligence 활용하기

iOS 26 Foundation Models 프레임워크 완벽 가이드. SystemLanguageModel과 @Generable 매크로, 스트리밍, 도구 호출, 오류 처리까지 온디바이스 LLM을 다루는 모든 실전 패턴.

iOS 26 Foundation Models 가이드 (2026)

업데이트: 2026년 6월 1일

iOS 26의 Foundation Models 프레임워크는 Apple Intelligence의 온디바이스 대규모 언어 모델(LLM)에 직접 접근할 수 있게 해 주는 새로운 Swift API로, 네트워크 호출 없이 텍스트 생성·요약·분류·구조화된 출력을 단 몇 줄의 Swift 코드로 구현할 수 있습니다. 핵심은 SystemLanguageModel, LanguageModelSession, 그리고 매크로 기반 가이드 생성(@Generable, @Guide)이며, Apple Silicon이 탑재된 디바이스에서 약 3B 파라미터 모델이 완전히 로컬에서 돕니다. 솔직히 처음 베타에서 이걸 만져 봤을 때 가장 인상적이었던 건 빌드 사이즈가 늘지 않는다는 점이었어요. 이 가이드에서는 가용성 확인부터 도구 호출, 스트리밍, 구조화된 출력까지 실전 예제 코드로 처음부터 다뤄 봅니다.

  • Foundation Models 프레임워크는 iOS 26·iPadOS 26·macOS 26부터 사용 가능하며, Apple Intelligence가 활성화된 디바이스에서 약 3B 파라미터 온디바이스 LLM에 접근합니다.
  • SystemLanguageModel.default.availability로 모델 사용 가능 여부를 반드시 먼저 확인해야 합니다. .unavailable(.deviceNotEligible), .appleIntelligenceNotEnabled 등 네 가지 사유가 존재합니다.
  • @Generable 매크로를 Swift 구조체에 붙이면 모델 출력이 해당 타입의 인스턴스로 자동 디코딩되며, @Guide로 각 프로퍼티에 자연어 힌트를 줄 수 있습니다.
  • LanguageModelSessionstreamResponse(to:)로 토큰을 점진적으로 받아 UI를 실시간 갱신할 수 있습니다.
  • Tool 프로토콜을 구현하면 모델이 앱 내 함수(검색·캘린더 조회 등)를 함수 호출 형태로 사용할 수 있습니다.
  • 모든 처리가 로컬에서 일어나므로 사용자 데이터가 디바이스를 떠나지 않으며, 토큰당 비용도 없습니다.

Foundation Models 프레임워크란 무엇인가

Foundation Models 프레임워크는 Apple이 iOS 26·iPadOS 26·macOS 26·visionOS 26에서 도입한 Swift API로, 운영체제에 내장된 Apple Intelligence 기반의 온디바이스 언어 모델에 앱이 직접 질의할 수 있게 해 줍니다. 기존에는 OpenAI, Anthropic, Google 같은 외부 API를 호출하거나 Core ML로 자체 LLM을 번들링해야 했죠. 이제 약 3B 파라미터 모델이 시스템 수준에서 제공되므로 앱 번들 크기가 늘지 않고 사용자 데이터도 디바이스를 떠나지 않습니다.

프레임워크의 진입점은 단 두 가지입니다. SystemLanguageModel은 모델 자체(가용성, 언어 지원 정보)를 표현하고, LanguageModelSession은 하나의 대화 상태(시스템 지시문, 누적 컨텍스트, 도구 등록)를 표현합니다. 응답은 일반 문자열로도, @Generable이 붙은 사용자 정의 타입으로도 받을 수 있으며 토큰 단위 스트리밍도 지원됩니다.

요약, 재작성, 분류, 태그 추출, 자연어 명령 해석, 구조화된 데이터 추출처럼 비교적 작은 컨텍스트에서 동작하는 작업이 가장 적합한 활용 분야입니다. 반대로 코딩 어시스턴트나 긴 문서 분석 같은 대규모 작업은 여전히 클라우드 LLM이 우세하고요. 자세한 모델 명세는 Apple의 Foundation Models 공식 문서에 정리되어 있습니다.

Foundation Models가 사용 가능한지 어떻게 확인하나

모든 호출에 앞서 반드시 가용성 검사를 수행해야 합니다. 사용자가 Apple Intelligence를 끄거나, 호환되지 않는 디바이스를 사용하거나, 시스템이 모델을 아직 다운로드 중일 수 있기 때문이죠. SystemLanguageModel.default.availability는 다음 네 가지 값 중 하나를 반환합니다.

  • .available: 즉시 사용 가능
  • .unavailable(.deviceNotEligible): A17 Pro/M1 미만 등 하드웨어 미지원
  • .unavailable(.appleIntelligenceNotEnabled): 설정에서 비활성화됨
  • .unavailable(.modelNotReady): 모델 파일을 백그라운드에서 다운로드 중

이 분기를 한 곳에서 처리하는 헬퍼를 두면 호출부가 훨씬 깔끔해집니다.

import FoundationModels
import SwiftUI

@Observable
final class IntelligenceGate {
    enum State { case ready, unsupported, disabled, downloading }
    private(set) var state: State = .unsupported

    func refresh() {
        switch SystemLanguageModel.default.availability {
        case .available:
            state = .ready
        case .unavailable(.deviceNotEligible):
            state = .unsupported
        case .unavailable(.appleIntelligenceNotEnabled):
            state = .disabled
        case .unavailable(.modelNotReady):
            state = .downloading
        @unknown default:
            state = .unsupported
        }
    }
}

SwiftUI 뷰에서는 .task에서 refresh()를 호출하고, .downloading 상태에서는 사용자에게 진행 중임을 알리는 안내문을 보여 주세요. .disabled 상태에서는 설정 앱의 Apple Intelligence 페이지로 유도하는 안내 텍스트가 좋습니다. 가용성 검사는 비싸지 않으니 화면이 활성화될 때마다 다시 호출해도 무방합니다.

첫 LanguageModelSession 만들기

LanguageModelSession은 하나의 논리적 대화를 캡슐화합니다. 인스턴스를 만들 때 instructions(시스템 프롬프트)와 도구 목록을 지정하고, 이후 respond(to:) 또는 streamResponse(to:)로 사용자 프롬프트를 보냅니다. 세션은 자체적으로 컨텍스트를 누적하니까 멀티턴 대화도 별도 작업 없이 동작합니다.

import FoundationModels

let session = LanguageModelSession(
    instructions: """
    당신은 한국어 뉴스 요약 어시스턴트입니다.
    출력은 항상 3개의 불릿으로 제한하고, 각 불릿은 80자 이내여야 합니다.
    """
)

func summarize(_ article: String) async throws -> String {
    let response = try await session.respond(to: article)
    return response.content
}

세션은 액터로 격리돼 있으므로 동시 호출은 직렬화됩니다. 동일한 세션에 두 개의 respond를 병렬로 보낼 경우 두 번째 호출은 첫 번째가 끝날 때까지 대기합니다. 진정한 병렬 처리가 필요하면 세션을 두 개 만드세요. 액터 격리와 호출 패턴은 Swift 6.2 Approachable Concurrency 가이드에서 더 자세히 다룹니다.

@Generable 매크로로 구조화된 출력 받기

Foundation Models의 가장 강력한 기능은 가이드 생성(Guided Generation)입니다. @Generable을 Swift 타입에 붙이고 응답 타입을 지정하면, 모델은 해당 스키마에 맞는 JSON을 생성하고 프레임워크가 이를 곧장 Swift 인스턴스로 디코딩합니다. 별도 JSON 파싱이나 후처리가 필요 없어요.

import FoundationModels

@Generable
struct RecipeCard: Equatable {
    @Guide(description: "요리의 한국어 이름")
    let title: String

    @Guide(description: "1~5 사이의 난이도", .range(1...5))
    let difficulty: Int

    @Guide(description: "필요한 재료 목록")
    let ingredients: [String]

    @Guide(description: "조리 단계, 각 단계는 한 문장")
    let steps: [String]
}

let session = LanguageModelSession(
    instructions: "사용자 요청에 맞는 한국 가정식 레시피를 생성합니다."
)

let card = try await session.respond(
    to: "냉장고에 두부와 김치가 있어요. 간단한 한 그릇 요리 알려 주세요.",
    generating: RecipeCard.self
).content

print(card.title)        // 예: "김치두부덮밥"
print(card.difficulty)   // 예: 2

@Guide는 단순한 자연어 설명뿐 아니라 .range(_:), .count(_:), .pattern(_:) 같은 제약을 받습니다. 이 제약은 디코딩 시점이 아니라 생성 시점에 모델에 전달되어 모델이 잘못된 값을 만들 확률 자체를 낮추는 점이 중요합니다. 중첩된 @Generable 타입과 옵셔널 필드도 지원되므로, 도메인 모델을 그대로 사용할 수 있어요. SwiftData의 @Model 타입과 결합하면 LLM이 생성한 구조체를 곧장 데이터베이스에 저장하는 파이프라인도 가능합니다. 자세한 패턴은 SwiftData 완벽 가이드를 참고하세요.

스트리밍 응답으로 실시간 UI 업데이트하기

요약처럼 200~500토큰을 생성하는 작업도 디바이스에서는 1~3초가 걸립니다. 사용자를 기다리게 하지 않으려면 streamResponse(to:)를 사용해 토큰이 생성되는 대로 받아 화면에 점진적으로 표시해야 합니다. 반환 타입은 AsyncThrowingStream이라 일반 for try await 루프로 소비합니다.

import SwiftUI
import FoundationModels

struct StreamingSummaryView: View {
    @State private var output = ""
    @State private var isStreaming = false
    let article: String

    var body: some View {
        ScrollView {
            Text(output)
                .textSelection(.enabled)
                .padding()
        }
        .task(id: article) {
            await stream()
        }
    }

    private func stream() async {
        output = ""
        isStreaming = true
        defer { isStreaming = false }

        let session = LanguageModelSession(
            instructions: "기사를 3문장으로 한국어 요약하세요."
        )
        do {
            for try await partial in session.streamResponse(to: article) {
                output = partial.content
            }
        } catch {
            output = "요약 실패: \(error.localizedDescription)"
        }
    }
}

주의할 점이 하나 있는데요, partial.content가 매 이벤트마다 누적된 전체 문자열이라는 것입니다. 토큰 델타가 아니므로 output += partial.content처럼 추가하면 안 되고, 그대로 대입해야 합니다(저도 처음 베타에서 이 부분에 한참을 헤맸어요). @Generable 타입을 스트리밍할 때는 부분 디코딩(partial.content가 점차 채워지는 구조체)이 가능해, 폼 미리보기 같은 점진적 렌더링에 자연스럽게 어울립니다.

도구 호출(Tool Calling)을 어떻게 구현하나

모델이 앱의 내부 상태(연락처, 캘린더, 데이터베이스, 네트워크 검색 등)에 접근해야 한다면 도구(Tool)를 등록합니다. Tool 프로토콜을 채택한 타입은 이름, 설명, 인자 스키마, 실행 함수를 정의하고, 모델이 필요하다고 판단하면 그 도구를 함수 호출 형태로 부릅니다. 결과는 자동으로 세션 컨텍스트에 주입되어 모델이 최종 응답을 만들 때 활용됩니다.

import FoundationModels

struct WeatherTool: Tool {
    let name = "currentWeather"
    let description = "지정한 도시의 현재 기온과 날씨 상태를 반환합니다."

    @Generable
    struct Arguments {
        @Guide(description: "한국어 도시 이름, 예: 서울")
        let city: String
    }

    func call(arguments: Arguments) async throws -> ToolOutput {
        let temp = try await WeatherService.shared.temperature(for: arguments.city)
        return ToolOutput("\(arguments.city): \(temp)°C, 맑음")
    }
}

let session = LanguageModelSession(
    tools: [WeatherTool()],
    instructions: "사용자가 날씨를 물으면 currentWeather 도구를 사용하세요."
)

let answer = try await session.respond(
    to: "오늘 서울이랑 부산 기온 차이 알려 줘."
).content

이 예제에서 모델은 자체적으로 도구를 두 번(서울, 부산) 호출한 뒤 결과를 비교해 최종 응답을 만듭니다. 도구는 async throws이므로 네트워크나 데이터베이스 호출도 자연스럽게 통합되고요. WWDC25 세션 286 "Meet the Foundation Models framework"에서 도구 호출 흐름을 시각적으로 확인할 수 있습니다.

오류 처리와 가드레일 대응

Foundation Models 호출은 일반 네트워크 호출과 다른 종류의 실패가 발생합니다. LanguageModelSession.Error는 적어도 다음 케이스를 포함합니다.

  • .guardrailViolation: Apple의 안전 정책에 의해 응답이 차단된 경우. 사용자 프롬프트뿐 아니라 모델이 생성하려던 출력이 차단되는 경우도 포함됩니다.
  • .unsupportedLanguage: 지원되지 않는 언어로 응답을 시도한 경우. 한국어, 영어, 일본어, 중국어 등 1차 지원 언어 외 입력에서 종종 발생합니다.
  • .exceededContextWindow: 누적 컨텍스트가 모델 한도를 넘어선 경우.
  • .assetUnavailable: 추론 직전에 모델 파일이 회수된 경우(저장공간 부족 등). 가용성 검사를 다시 돌려야 합니다.
do {
    let result = try await session.respond(to: prompt)
    return .success(result.content)
} catch LanguageModelSession.Error.guardrailViolation {
    return .blocked("요청을 처리할 수 없는 내용이 포함되어 있어요.")
} catch LanguageModelSession.Error.exceededContextWindow {
    // 세션을 잘라내거나 재생성
    session = LanguageModelSession(instructions: baseInstructions)
    return .retry
} catch {
    return .failure(error)
}

특히 .guardrailViolation은 콘텐츠 모더레이션 메시지가 아니라 일반 오류로 던져집니다. 사용자에게는 모델 오류를 그대로 노출하지 말고, 차단·재시도·다른 표현으로 시도하기 같은 선택지를 제공하는 UI 패턴이 권장됩니다. 또한 모든 오류 분기에서 빈 응답이 화면에 남지 않도록 상태를 명시적으로 초기화하세요.

프롬프트와 instructions 설계 패턴

온디바이스 3B 모델은 GPT-4나 Claude Opus급 모델보다 훨씬 작습니다. 그래서 프롬프트 설계의 영향력이 큽니다. 실무에서 검증된 몇 가지 패턴이 있어요.

1. instructions는 명령형, 프롬프트는 데이터

"어떻게 행동할지"는 instructions에, "무엇을 처리할지"는 respond(to:) 인자에 넣습니다. 사용자 입력을 instructions에 섞으면 프롬프트 인젝션 위험이 커지고, 같은 instructions를 캐싱·재사용하기도 어려워지거든요.

2. 출력 형식을 코드로 강제

"JSON으로 답하세요" 같은 자연어 지시 대신 @Generable을 쓰면 정확도가 극적으로 올라갑니다. 자연어 형식 지시는 모델이 종종 어깁니다.

3. Few-shot은 짧고 강하게

예시 1~2개로도 톤, 길이, 형식이 크게 달라집니다. 작은 모델일수록 예시의 표면적 특징을 강하게 모방하니까, 예시의 길이와 문장 구조를 원하는 출력과 정확히 일치시키세요.

4. 모르면 "모른다"고 말하게 하라

환각을 줄이는 가장 효과적인 instructions 줄은 "확실하지 않으면 '모릅니다'라고 답하세요"입니다. 지난 프로젝트에서 이 한 줄만 추가했더니 사실관계 오답률이 체감 수준으로 떨어졌습니다.

성능과 메모리 최적화

모델은 시스템이 관리하므로 앱 메모리에 통째로 적재되지는 않지만, 추론 중에는 약 100~300MB의 추가 메모리가 점유됩니다. 모바일에서 다음 사항을 점검하세요.

  • 세션 재사용: 매 호출마다 새 LanguageModelSession을 만들면 instructions 처리 비용이 반복됩니다. 화면당 하나의 세션을 들고 멀티턴으로 활용하세요.
  • 출력 길이 제한: 가능한 한 @Generable로 구조화하고, 자유 텍스트라면 instructions에서 "100자 이내" 같은 제약을 명시하세요. 토큰이 절반이 되면 추론 시간도 거의 절반이 됩니다.
  • 백그라운드 회피: 백그라운드 상태에서는 시스템이 모델 자원을 회수할 수 있으니 장시간 추론은 포그라운드에서만 시도하세요.
  • 오류 메시지 사용자화: LanguageModelSession.Error.guardrailViolation이나 .unsupportedLanguage는 그대로 노출하기보다 한국어 안내문으로 재포장하세요.

벤치마크 측정 시에는 signpost로 첫 토큰 지연(time-to-first-token)과 토큰/초를 따로 기록하는 것이 권장됩니다. Apple Machine Learning 페이지에 공개된 자료 기준으로 iPhone 15 Pro에서 약 30 토큰/초, M2 iPad Pro에서 약 50 토큰/초가 일반적이에요. iOS 26의 시각적 변화에 맞춘 결과 UI 디자인은 iOS 26 Liquid Glass 가이드에서 참고하세요.

자주 묻는 질문

어떤 디바이스에서 Foundation Models 프레임워크를 사용할 수 있나요?

A17 Pro 이상의 iPhone(iPhone 15 Pro·15 Pro Max 및 iPhone 16 전 모델), M1 이상의 iPad와 Mac, 그리고 Vision Pro에서 사용 가능합니다. 사용자가 설정 → Apple Intelligence에서 기능을 활성화해야 하며, 첫 사용 전에 약 4GB 분량의 모델 파일이 백그라운드에서 다운로드됩니다.

Foundation Models와 Core ML은 무엇이 다른가요?

Core ML은 임의의 머신러닝 모델을 앱 번들에 직접 포함해 실행하는 범용 런타임이고, Foundation Models는 OS가 제공하는 단일 LLM에 대한 고수준 Swift API입니다. 텍스트 생성·요약·구조화 같은 자연어 작업이라면 Foundation Models가, 비전·오디오·맞춤 모델이 필요하다면 Core ML이 적합합니다. 두 프레임워크는 같은 앱에서 함께 사용할 수 있습니다.

인터넷 연결이 필요한가요?

아니요. 모델 파일이 한 번 다운로드된 뒤에는 모든 추론이 완전히 오프라인에서 수행됩니다. 사용자의 프롬프트와 생성된 응답이 디바이스를 떠나지 않으므로, 의료·금융 등 민감한 정보 처리에도 적합합니다.

토큰 사용 비용이 발생하나요?

없습니다. Foundation Models 프레임워크는 토큰당 과금이나 API 키가 필요 없는 무료 시스템 API입니다. 다만 추론 중에는 배터리와 발열에 영향이 있으니, 백그라운드 일괄 처리보다는 사용자 상호작용 중심으로 호출하는 것이 좋습니다.

한국어 응답 품질은 어느 정도인가요?

iOS 26 기준 한국어는 1차 지원 언어에 포함되어 있어 영어와 거의 동일한 품질의 응답을 생성합니다. 다만 매우 전문적인 용어나 최신 고유명사는 부정확할 수 있으니, 사실관계가 중요한 영역에서는 도구 호출로 외부 데이터를 주입하거나 사용자가 결과를 확인할 수 있는 UI를 함께 제공해야 합니다.

Editorial Team
저자 소개 Editorial Team

Our team of expert writers and editors.