IOS

Kyu hyunSung·2025년 4월 18일

IOS

목록 보기
2/9
post-thumbnail

가장 참고할만한 튜토리얼 가이드라인

공식문서 만으로 iOS 개발 배우기

0. Architecture

IOS는 Apple 독점 운영체제, MacOS를 기반으로 한다.

Dawin 이라는 애플 자사 제품용으로 개발한 UNIX 운영체제를 밑바탕에 두고 활용함 (그 밑바닥에는 C, C++, 어셈블리, Objective-C가 깔려있는듯.)

참고 글 : MacOS architecture

당연하게도 버젼별로 기기다름

참고 : Xcode

대충 기반되어있는 IOS 아키텍처 구조는

계층(Level)설명
Application사용자가 실행하는 앱. 하위 API와 프레임워크를 사용하여 기능 구현.
Cocoa / Cocoa TouchUI 구성 및 사용자 인터페이스 관련 기능 제공 (UIKit, MapKit 등).
Media멀티미디어 처리 계층 (AVFoundation, Core Animation, OpenGL 등).
Core Services네트워크, DB, 위치 등 핵심 서비스 제공 (Core Data, Foundation 등).
Core OS시스템 커널, 드라이버, 보안, 스레드 관리 등 운영체제 핵심 기능.
Hardware실제 기기(CPU, GPU, 저장장치 등)

이렇게 각각 레이어에서 단계별로 통신하는 느낌임

참고로 운영체제 구조 자체는 공개안함 클로즈드소스임.

출처 : [iOS] Architecture of iOS Operating System

더 자세한건 여기 인도형님 글 참고하셈

1. Xcode Interface

대충 설명하면

  • Navigator
    – 프로젝트 파일, 테스트 등 프로젝트에 포함되는 입력
    – 디버깅 정보
    – 버전 컨트롤 등
  • Editor
    – 소스코드
    – 스토리보드를 이용한 UI 에디팅
  • Inspector
    – Editor에서 선택된 오브젝트에 대한 속성 등
  • Output
    – 실행 시 발생하는 출력
    – 브레이크 포인트에서 각종 변수 등의 값

이렇게 됨 대가리박아가면서 만들면 뭐가뭔지 알거임

공식문서 추천

Develop apps for Apple platforms

developer.apple.com

2. MVC 패턴

IOS에서는 StoryBoard는 MVC, SwiftUI는 MVVM 이나 MVI을 주로 쓴다곤 함

하지만 학교에서 배운 기본적인 MVC 패턴위주로 정리할꺼임

  • Model-View-Controller

    MVC(모델-뷰-컨트롤러)는 사용자 인터페이스, 데이터 및 논리 제어를 구현하는데 널리 사용되는 소프트웨어 디자인 패턴이다. 소프트웨어의 비즈니스 로직과 화면을 구분하는데 중점을 두고 있으며, 이러한 관심사 분리 는 더나은 업무의 분리와 향상된 관리를 제공한다.

  • MVC에서 각 객체는 모델, 뷰, 컨트롤러 레이어 중의 하나
  • Model Layer
    • 데이터를 저장하지만 사용자 인터페이스에 대하여 알지 못함
    • simpleApp앱에서 모델은 문제와 정답의 목록을 가지는 문자열 리스트로 구성
    • 대개 모델 레이어는 현실 세계의 사물을 대상으로 함
  • View Layer
    • 사용자에게 보이는 모든 객체를 포함
    • 버턴, 텍스트필드, 슬라이더 등이 있음
    • 대부분 이미 만들어져 있는 것을 그대로 사용함
  • Controller Layer
    • 응용 프로그램을 관리
    • 사용자에게 보이는 뷰를 구성
    • 뷰와 모델을 연결하고 동기화 함

2-1. 예제

님들이 StoryBoard로 프로젝트 만들면 대충 저런 트리가 나올거임
사실 이거보다 더 양이많음 중요한 부분만 추려와봄

.
├── IosStoryBoard/                  # 앱의 메인 소스 코드 디렉토리
│   ├── AppDelegate.swift           # 앱 생명주기 관리
│   ├── SceneDelegate.swift         # 씬(UI 인스턴스) 생명주기 관리 (iOS 13+)
│   ├── ViewController.swift        # 화면 컨트롤러 (Controller)
│   ├── Base.lproj/                 # 기본 인터페이스 파일 디렉토리
│   │   ├── Main.storyboard         # 메인 UI 인터페이스 (View)
│   │   └── LaunchScreen.storyboard # 시작 화면 (View)
│   ├── Assets.xcassets/            # 앱 리소스 (이미지, 색상 등)
│   └── Info.plist                  # 앱 설정 정보
└── IosStoryBoard.xcodeproj/        # Xcode 프로젝트 파일
    └── project.pbxproj             # 프로젝트 설정 파일

아래는 대략적인 그림

Xcode에서 자체적으로 MVC패턴을 만들어주기 때문에 가볍게 분석해볼거임

  • Model
    일반적으로 개발자가 추가로 생성하는 데이터 모델 클래스들이 이 영역에 포함됨
import Foundation

// var landmarks: [Landmark] = load("landmarkData.json")

func load<T: Decodable>(_ filename: String) -> T {
    let data: Data

    guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
        else {
            fatalError("Couldn't find \(filename) in main bundle.")
    }

    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
    }

    do {
        let decoder = JSONDecoder()
        return try decoder.decode(T.self, from: data)
    } catch {
        fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
    }
}

후술 하겠지만 이런것처럼 데이터 처리할때 주로 만들어짐

  • Controller
    – ViewController.swift: 화면과 사용자 상호작용을 관리하는 주요 컨트롤러
    – AppDelegate.swift: 앱의 생명주기를 관리
    – SceneDelegate.swift: iOS 13부터 도입된 멀티 윈도우 지원을 위한 씬 생명주기 관리

  • View
    – Main.storyboard: 앱의 주요 UI 인터페이스를 설계하는 파일
    – LaunchScreen.storyboard: 앱 시작 시 표시되는 화면
    – Assets.xcassets: 앱에서 사용하는 이미지, 색상 등의 리소스

그래서 대강 전체적으로 보면

이런 모습이됨

참고 : claude가 알려준 IOS에서 쓰는 패턴들

아키텍처 패턴스토리보드/UIKitSwiftUI주요 특징
MVC✅ 매우 일반적⚠️ 가능하나 권장되지 않음• UIKit의 기본 패턴
• 컨트롤러에 코드가 집중되는 경향
MVVM✅ 일반적✅ 매우 일반적• 뷰 모델을 통한 데이터 바인딩
• SwiftUI의 @ObservableObject와 자연스럽게 통합
MVP✅ 사용됨⚠️ 드물게 사용• 프레젠터를 통한 뷰 업데이트
• ViewController를 View로 취급
VIPER✅ 복잡한 앱에서 사용⚠️ 거의 사용되지 않음• 높은 모듈화
• 5개 구성요소로 책임 분리
Clean Architecture✅ 대규모 앱에서 사용✅ 사용됨• 계층적 의존성 구조
• 유스케이스 중심 설계
Coordinator✅ 화면 전환에 자주 사용⚠️ SwiftUI 네비게이션에 적용 가능• 화면 전환 로직 분리
• 화면 흐름의 중앙 관리
Redux/Flux⚠️ 일부 사용✅ 자주 사용• 중앙 집중식 상태 관리
• 단방향 데이터 흐름
MVI⚠️ 드물게 사용✅ 사용 증가 추세• Intent를 통한 사용자 액션 처리
• 단방향 데이터 흐름
TCA (The Composable Architecture)⚠️ 거의 사용되지 않음✅ 인기 증가 중• 상태, 액션, 환경, 리듀서로 구성
• 높은 테스트 용이성

3. SimpleApp

이런 앱을 만들어볼거임

  • StoryBoard: 안드로이드에서 layout의 xml과 동일
    – StoryBoard를 이용하여 뷰를 디자인함
    – 하나의 스토리보드에는 여러 개의 ViewController에 해당하는 View가 들어감

Xcode는 안드로이드처럼 .xml파일을 계속 만드는게아님

하나의 파일 (.storyboard) 안에 여러개의 화면을 만드는거임

안드로이드 = 1화면 1 xml
Xcode = 1개 스토리보드, n개 View Controller Scene

예시로 들자면

안드로이드는 이런구조

  • MainActivity.kt + activity_main.xml: 앱의 메인 컨테이너

    여러 Fragment를 관리:
    HomeFragment.kt + fragment_home.xml
    FeedFragment.kt + fragment_feed.xml
    SearchFragment.kt + fragment_search.xml
    NotificationFragment.kt + fragment_notification.xml
    bottom_navigation.xml: 하단 탐색바 레이아웃

저렇게 XML 각각 만드는거에 비해

Xcode에서는

  • Main.storyboard: 앱의 메인 흐름

    – MainTabBarController: 앱의 메인 탭바를 관리하는 컨트롤러
    – HomeViewController: 홈 화면을 담당하는 컨트롤러

  • Authentication.storyboard: 로그인/회원가입 화면
  • Profile.storyboard: 사용자 프로필 관련 화면
  • Settings.storyboard: 설정 관련 화면
  • Onboarding.storyboard: 앱 초기 온보딩 화면

큰 틀에서 여러개 만드는 느낌

코드로 보면

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="tne-QT-ifu">
            <objects>
                <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
            </objects>
        </scene>
    </scenes>
</document>

이렇게 하나의 스토리보드에서 여러개 ViewController가 있다는걸 알 수 있음

그래서 어떻게 UI를 만드나요

xml로 코드짜서 만들진않음 복잡해서 코드로 짜고싶으면
SwiftUI로.

Object Library에서 Button, Textfield 등등으로 화면을 구성함

– Main.storyboard를 선택하라
– 우측상단의 Object Library(+)를 클릭하라
– Label 컨트롤을 선택하여 View로 드래그&드롭하라
– 동일한 방법으로 Button, Label, Button을 View로 드래그&드롭하라
– 적절히 정렬하라
– Document Outline을 우측 그림과 같은지 확인하라.
– 좌측 상단의 삼각형을 클릭하여 실행하여 보라

Inspector 에서 다양한 기능 수행가능함

📄 - 파일 인스펙터: 파일 정보, 타겟 설정, 참조 관리
⏱️ - 히스토리 인스펙터: 파일 변경 이력 추적 및 이전 버전 복원
❓ - 도움말 인스펙터: 선택된 항목에 대한 도움말 및 문서 표시
📰 - 퀵헬프 인스펙터: 빠른 도움말 및 API 문서 액세스
≡ - 아이덴티티 인스펙터: UI 요소의 ID, 클래스, 사용자 정의 속성 설정
📐 - 사이즈 인스펙터: UI 요소의 크기, 위치, 여백, 제약조건 설정
🔄 - 연결 인스펙터: 액션, 아웃렛 연결 및 세그웨이 설정

대충 이정도만 알아보고 하나씩 써보셈

View와 UIViewController 연결

  • UIViewController에서 Label에 데이터를 write하려고 하면 Label을 액세스할 수 있어야 한다. (참조)
    • 안드로이드에서 label = findViewById(…)와 동일
  • 사용자의 View를 통하여 버턴을 클릭하면 그 내용은 UIViewController에 알려져 야한다.
    • 안드로이드에서는 button.setOnClickListener(…)와 동일

한 마디로,

IBOutlet = findViewById()

  • iOS: 스토리보드의 UI 요소(예: Label)를 코드에서 참조하기 위해 IBOutlet으로 연결.
  • 안드로이드: XML 레이아웃의 UI 요소를 코드에서 참조하기 위해 findViewById()를 사용.

IBAction = setOnClickListener()

  • iOS: 버튼 클릭 같은 사용자 이벤트를 처리하기 위해 IBAction 메서드를 연결.
  • 안드로이드: 버튼 클릭을 처리하기 위해 setOnClickListener()를 설정.

정도 됨 아래는 부가설명

연결 방법

  1. StoryBoard와 UIViewController을 동시에 오픈한다.

저거 눌러서 두개 여셈

아니면

동시 오픈 방법

  1. Main.storyboard를 오픈한다
  2. 이 상태에서 option 및 ViewController.swift를 클릭 한다
  1. StoryBoard에서 원하는 컨트롤을 “Control+드래그”하여 UIViewController의 클래스내에 드롭한다

  1. Outlet/Action을 선택한다.

  1. 적절한 이름을 정한다.

혹은 원래 있던 거면 이렇게 연결할 수도 있긴함

Outlet 연결 ,Action 연결

  • Outlet: 객체의 레퍼런스를 의미한다.
    – 레이블 Question → Outlet: questionLabel
    – 레이블 Answer → Outlet: answerLabel

  • Action: 객체의 listener를 의미한다.
    – 버턴 Next Question → Action: nextQuestion
    – 버턴 Show Question → Action: showAnswer



  • 결과

  • Connection Inspector

주의 : 연결되어있는 Outlet 을 안끊어주면 코드에서 지워도 계속 연결되어 있음

코드 분석

  • 모델이 너무 간단하기에 Controller Layer에 포함
    var questions = [
        "대한민국의 수도는 무엇인가요?",
        "한국 청년들에게 가장 인기있는 대학은 무슨 대학인가요?",
        "7+21은 얼마인가요?"
    ]

    var answers = [
        "서울",
        "한셔대학교",
        "28"
    ]

    var currentIndex = 0
  • viewDidLoad() 함수
    – 이 함수는 프로그램이 완전히 실행된 직후 가장 처음 호출되는 Callback 함수이다.
    – 이 함수에서 이 View에 대한 초기화를 한다
    (주의: 생성자는 아님)
 override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        questionLabel.text = questions[currentIndex]
	// questionLabel의 Text 안에다 questions 배열의 현재 인덱스 값이 들어감 
    }

안드로이드로 치면 onCreate() 함수임

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)

이부분은 생명주기랑 연결도 되어있어서 나중에 설명함

  • nextQuestion/showAnswer 함수 구현
    – 버튼 동작 코드 구현
@IBAction func nextQuestion(_ sender: UIButton) {
    currentIndex = (currentIndex+1)%questions.count
    questionLabel.text = questions[currentIndex]
}

@IBAction func showAnswer(_ sender: UIButton) {
    answerLabel.text = answers[currentIndex]
}
  • 결과

처음 버튼을 누르면 Model에서 설정한 문제가 나오고,
두번째 버튼을 누르면 그에대한 대답이 나온다

만약 글씨가 짤리거나 그러면

아래 참고

아래는 전체 코드

import UIKit

class ViewController: UIViewController {

   @IBOutlet weak var qustionLabel: UILabel!
   @IBOutlet weak var answerLabel: UILabel!

   var questions = [
       "대한민국의 수도는 무엇인가요?",
       "한국 청년들에게 가장 인기있는 대학은 무슨 대학인가요?",
       "7+21은 얼마인가요?"
   ]

   var answers = [
       "서울",
       "한셔대학교",
       "28"
   ]

   var currentIndex = 0

   override func viewDidLoad() {
       super.viewDidLoad()
       // Do any additional setup after loading the view.
       qustionLabel.text = questions[currentIndex]
   }

   @IBAction func nextQuetion(_ sender: UIButton) {

       currentIndex = (currentIndex+1)%questions.count
       qustionLabel.text = questions[currentIndex]
   }
   
   @IBAction func showQuestion(_ sender: UIButton) {
       answerLabel.text = answers[currentIndex]
   }
   
}

+a) 모델 레이어 만들기

  1. QuestionAnswer.swift - 모델
  • 모델(Model): 데이터 구조를 정의하는 부분
  • 데이터 구조를 정의하는 부분
  • 이 구조체는 하나의 질문과 답변 쌍을 표현
struct QuestionAnswer {
    let question: String
    let answer: String
}
  1. QuestionBank.swift - 모델 컨테이너
  • 모델 컨테이너(Model Container): 여러 모델 객체를 관리하고 관련 비즈니스 로직을 처리하는 클래스
  • 모델 컨테이너는 여러 모델 인스턴스를 저장하고 관리
  • 데이터 조작 로직을 뷰 컨트롤러에서 분리하여 코드 재사용성을 높임
class QuestionBank {
    // 모든 질문-답변 항목을 저장하는 배열. private(set)으로 외부에서 읽기만 가능하게 설정
    private(set) var items: [QuestionAnswer]
    
    // 현재 선택된 질문-답변의 인덱스. private(set)으로 외부에서 읽기만 가능하게 설정
    private(set) var currentIndex: Int = 0
    
    /**
     * 초기화 메서드
     * @param items 초기 질문-답변 목록. 기본값은 빈 배열
     */
    init(items: [QuestionAnswer] = []) {
        self.items = items
    }
    
    /**
     * 계산 프로퍼티: 현재 선택된 질문-답변 항목을 반환
     * - 배열이 비어있을 경우 nil 반환
     */
    var currentItem: QuestionAnswer? {
        guard !items.isEmpty else { return nil }
        return items[currentIndex]
    }
    
    /**
     * 다음 질문-답변으로 이동하는 메서드
     * - 마지막 항목에서 호출 시 첫 번째 항목으로 순환
     */
    func moveToNext() {
        guard !items.isEmpty else { return }
        currentIndex = (currentIndex + 1) % items.count
    }
    
    /**
     * 이전 질문-답변으로 이동하는 메서드
     * - 첫 번째 항목에서 호출 시 마지막 항목으로 순환
     */
    func moveToPrevious() {
        guard !items.isEmpty else { return }
        currentIndex = (currentIndex - 1 + items.count) % items.count
    }
}
  1. ViewController.swift - 컨트롤러
  • MVC 패턴에서 컨트롤러는 사용자 입력을 처리하고 모델을 업데이트하며 뷰에 데이터를 표시하는 역할을 담당합니다
  • 이 클래스는 UIKit 프레임워크의 UIViewController를 상속받아iOS 앱의 화면을 관리합니다
class ViewController: UIViewController {
    
    // UI 요소와 연결된 아웃렛 변수들
    @IBOutlet weak var questionLabel: UILabel! // 질문을 표시할 레이블
    @IBOutlet weak var answerLabel: UILabel!   // 답변을 표시할 레이블
    
    /**
     * 질문-답변 데이터를 관리하는 모델 컨테이너 인스턴스
     * - 샘플 데이터로 초기화됨
     */
    let questionBank = QuestionBank(items: [
        QuestionAnswer(question: "대한민국의 수도는 무엇인가요?", answer: "서울"),
        QuestionAnswer(question: "한국 청년들에게 가장 인기있는 대학은 무슨 대학인가요?", answer: "한셔대학교"),
        QuestionAnswer(question: "7+21은 얼마인가요?", answer: "28")
    ])
    
    /**
     * 뷰 컨트롤러의 뷰가 메모리에 로드된 후 호출되는 메서드
     * - 초기 UI 설정을 담당
     */
    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI() // UI 초기화
    }
    
    /**
     * UI 업데이트 메서드
     * - 현재 선택된 질문-답변에 따라 레이블 텍스트를 업데이트
     * - 데이터가 없는 경우 기본 메시지 표시
     */
    func updateUI() {
        if let currentItem = questionBank.currentItem {
            questionLabel.text = currentItem.question
            answerLabel.text = currentItem.answer
        } else {
            questionLabel.text = "질문이 없습니다"
            answerLabel.text = ""
        }
    }
    
    /**
     * 다음 버튼 탭 이벤트 처리 메서드
     * - Interface Builder에서 연결된 액션 메서드
     * @param sender 이벤트를 발생시킨 버튼
     */
    @IBAction func nextButtonTapped(_ sender: UIButton) {
        questionBank.moveToNext() // 모델 업데이트
        updateUI() // UI 업데이트
    }
    
    /**
     * 이전 버튼 탭 이벤트 처리 메서드
     * - Interface Builder에서 연결된 액션 메서드
     * @param sender 이벤트를 발생시킨 버튼
     */
    @IBAction func previousButtonTapped(_ sender: UIButton) {
        questionBank.moveToPrevious() // 모델 업데이트
        updateUI() // UI 업데이트
    }
}

4. LauncherScreen

안드로이드의 Splash 화면이랑 유사하다 생각하셈

App Icons & Launch Images → Launch Screen File에서 LaunchScreen 선택

원하는 .png 파일 Navigator에 복사.

LaunchScreen.storyboard를 오픈

Object Library에서 Image 컨 트롤을 drag하여 LaunchScreen.storyboard 중앙 에 적절히 놓기

Image컨트롤을 선택하여 속성 창에서 appleicon.jpg를 선택

Navigator에서 AppDelegate.swift를 선택하여 오픈하여 아래 코드를 삽입

참고로 AppDelegate는 iOS 앱의 생명주기를 관리하는 중요한 클래스임.
그래서 아래와같이 쓰레드를 정지시키는 코드를 쓰는거고

Thread.sleep(forTimeInterval: 2.0)
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        Thread.sleep(forTimeInterval: 2.0)// <- 여기다 삽입
        return true
    }

님이 forTimeInterval에 지정한 시간만큼 로고가 뜸

참고 iOS LaunchScreen vs 안드로이드 스플래시 화면

  • 코드 실행: iOS 불가능 / 안드로이드 가능
  • 표시 기간 제어: iOS 자동 / 안드로이드 수동 가능
  • 애니메이션: iOS 불가능 / 안드로이드 가능
  • 구현 방식: iOS storyboard / 안드로이드 Activity 또는 API

???: 아니 Thread.sleep(forTimeInterval: 2.0)
이코드는 뭔가요

시간 제어가 아니라
앱의 생명주기 중, 초기화 과정을 인위적으로 지연시키는 방법임

결론적으론 잘 안쓴다는거 타이머나 비동기처리 많이씀

5. App icon

처음에 이렇게 뜰거임 이게 기본 배경

iOS는 기기별 아이콘의 크기를 정해 놓음

여러 기기에 적용하기 위해서는 적용을 원하는 기기에 해당하는 아이콘을 전부 준비하여야 함

Figma에서 아이콘을 만들던 긴빠이치던 알아서하고

여기서 이미지넣으면 알아서 Icon으로 바꿔줌

본인은 컴포즈로 했음

다운받아서 압축파일 열면 저렇게 나온다
IOS ㄱㄱ

여러가지 상황(설정,알림때 앱모양 등등..)을 대비하여 넣어야하지만

기본으로 60x60 2배3배 정도만 넣어주고 실행시키고 꺼보셈

완성

profile
디지털 치매 예방

0개의 댓글