24.08.20 Today I Learned

2024. 8. 20. 18:12

프로젝트를 하다가 같이 공부하면 좋을 부분을 공유하게돼서 공부할 부분을 찾았다.

 

1. Import Foundation 과 Import UIKit이 뜻하는 바를 알아보기

2. 각 프레임워크가 어떤 기능을 제공하는지 알아보기

3. MVC 패턴 적용중인 프로젝트에서 view파일에 어떤 프레임 워크가 적절한지

4. final 키워드에 대해 알아보고 viewController 앞에 쓰면 어떤 점이 좋은지

5. Generic이 어떤 것인지, 왜 필요한지, 안쓰면 어떻게 되는지.

6. 캡슐화가 무엇인지.


1. Import Foundation 과 Import UIKit이 뜻하는 바를 알아보기

Import Foundation

  • Foundation은 Swift의 기본적인 데이터 구조와 유틸리티를 제공함
  • 문자열, 배열, 딕셔너리와 같은 컬렉션 타입, 날짜와 시간 관리, 파일 관리, 데이터 형식 변환, 네트워킹 등 기본적인 기능들이 포함됨
  • Apple의 모든 플랫폼에서 사용됨
  • 예를 들어, Date, URL, Data, JSONSerialization 등의 클래스나 구조체는 Foundation 프레임워크에서 제공됨

Import UIKit

  • UIKit은 주로 iOS 애플리케이션의 사용자 인터페이스를 구축하는 데 사용됨. 이 프레임워크는 화면에 뷰를 표시하고, 터치 이벤트를 처리하며, 뷰 컨트롤러를 통해 화면 간 전환을 관리하는 등 UI 관련 기능을 제공함.
  • UIKit에는 버튼, 레이블, 텍스트 필드, 테이블 뷰, 컬렉션 뷰 등 UI 요소와 애니메이션, 제스처 처리, 화면 전환 등의 기능이 포함됨.
  • 예를 들어, UIView, UIButton, UILabel, UITableViewController 등의 클래스는 UIKit에서 제공됨.

-> 프로젝트 중 MVC 패턴에서 Model을 다루는 부분에서 Import Foundation을 사용하고 UIKit을 사용하지 않았는데 그 이유였다.


2. 각 프레임워크가 어떤 기능을 제공하는지 알아보기

Foundation은 앱의 기본적인 데이터 처리와 유틸리티 기능을 제공

  • 데이터 타입 관리: 문자열(String), 배열(Array), 딕셔너리(Dictionary) 등의 기본 데이터 구조를 제공
  • 날짜와 시간: Date, Calendar, DateFormatter 등을 사용하여 날짜와 시간을 처리
  • 파일 관리: FileManager를 사용하여 파일과 디렉토리를 생성, 삭제, 이동하는 등의 작업을 할 수 있음
  • 네트워킹: URLSession을 통해 인터넷에서 데이터를 주고받는 작업을 수행할 수 있음
  • 데이터 변환: JSONSerialization 등을 통해 JSON 데이터를 Swift 객체로 변환하거나 그 반대로 변환할 수 있음

UIKit은 **사용자 인터페이스(UI)**를 만들고 사용자와의 상호작용을 처리하는 기능을 제공

  • UI 요소: 버튼(UIButton), 레이블(UILabel), 이미지 뷰(UIImageView), 텍스트 필드(UITextField) 등 화면에 표시되는 UI 요소들을 제공
  • 뷰 컨트롤러: UIViewController를 사용하여 화면 간 전환과 화면 내용을 관리
  • 제스처 및 터치 이벤트: 터치, 스와이프, 핀치 등의 제스처를 인식하고 처리할 수 있음
  • 애니메이션: 뷰의 이동, 크기 조절, 투명도 변화 등을 부드럽게 애니메이션으로 구현할 수 있음
  • 자동 레이아웃: 다양한 화면 크기에 맞게 UI 요소들의 위치와 크기를 자동으로 조정할 수 있음

3. MVC 패턴 적용중인 프로젝트에서 view파일에 어떤 프레임 워크가 적절한지

1. Model 파일:

Model은 데이터와 비즈니스 로직을 담당, 이 부분에서는 주로 Foundation 프레임워크를 사용

  • Foundation:
    • 데이터를 처리하고, 파일 시스템 접근, 네트워킹, JSON 데이터 파싱, 날짜와 시간 관리 등과 같은 기본적인 기능을 제공
    • 모델 객체는 데이터를 저장하고 관리하며, 네트워크를 통해 데이터를 가져오거나, JSON 데이터를 처리하는 등의 작업을 수행.

2. View 파일:

View는 사용자 인터페이스(UI)를 표현하고 사용자와의 상호작용을 처리, 이 부분에서는 UIKit 프레임워크를 사용

  • UIKit:
    • 화면에 표시되는 UI 요소를 정의하고, 레이아웃을 설정하며, 사용자의 입력을 받아들이는 역할
    • UIButton, UILabel, UIImageView, UITableView, UICollectionView와 같은 UI 요소들이 포함

3. Controller 파일:

Controller는 Model과 View 사이에서 중재자 역할, 여기에서는 **UIKit**을 주로 사용하지만, Foundation도 함께 사용될 수 있음

  • UIKit:
    • UIViewController를 통해 화면을 관리하고, 뷰와 사용자 상호작용을 처리
    • 뷰에서 발생하는 이벤트(예: 버튼 클릭)를 받아서 모델과 상호작용한 후, 그 결과를 다시 뷰에 전달
  • Foundation:
    • 컨트롤러가 데이터 로직을 처리할 때, 모델과 연동하여 데이터를 조작하거나 네트워크 요청을 처리하는 데 사용

요약:

  • Model 파일: Foundation
  • View 파일: UIKit
  • Controller 파일: UIKit (필요에 따라 Foundation도 사용)

4. final키워드에 대해 알아보고 사용하면 좋은점

: final 키워드는 클래스, 메서드, 프로퍼티에 적용할 수 있는 키워드로 상속과 오버라이딩을 방지하는 역할

 

1. 클래스 앞에 final을 붙이면

  • 해당 클래스를 상속할 수 없게 만듦, 즉, 이 클래스를 다른 클래스가 상속받으려 하면 컴파일 오류가 발생
final class MyClass {
    // 클래스 내용
}

class SubClass: MyClass { // 오류 발생: 'MyClass'는 'final'로 선언되었기 때문에 상속할 수 없음
}

 

2. 메서드 또는 프로퍼티 앞에 final을 붙이면:

  • 해당 메서드나 프로퍼티를 오버라이드할 수 없게 만듦, 즉, 서브클래스에서 이 메서드나 프로퍼티를 재정의하려고 하면 컴파일 오류가 발생
class MyClass {
    final func myMethod() {
        // 메서드 내용
    }
}

class SubClass: MyClass {
    override func myMethod() { // 오류 발생: 'myMethod'는 'final'로 선언되었기 때문에 오버라이드할 수 없음
    }
}

 

그럼 왜 final 키워드를 사용하는가?

  1. 안정성: 클래스나 메서드를 상속받거나 오버라이드하지 못하게 하여, 의도치 않은 동작이나 버그를 방지, 이는 특히 라이브러리나 프레임워크를 개발할 때 중요함
  2. 성능 최적화: 컴파일러가 final로 선언된 클래스나 메서드를 알면, 상속이나 오버라이딩이 불가능하다는 것을 알기 때문에, 컴파일러가 성능 최적화를 수행할 수 있음
  3. 코드 명확성: 특정 클래스나 메서드가 상속 또는 오버라이드될 필요가 없음을 명확히 표현하여, 코드의 의도를 더 잘 전달할 수 있음

따라서, final 키워드는 클래스 계층 구조와 메서드 오버라이딩을 제어하여 코드의 안정성과 성능을 높이는 데 유용함.

 

단점은 무엇일까?

 

1. 유연성 감소:

  • 상속 불가능: final 키워드를 사용하면 해당 클래스나 메서드를 상속하거나 오버라이드할 수 없게 되므로, 코드의 유연성이 줄어듦. 나중에 프로젝트가 확장되거나, 기존 클래스를 재사용해야 할 때 새로운 기능을 추가하거나 기존 기능을 변경할 수 없음.
    • 예를 들어, 라이브러리에서 제공하는 클래스가 final로 선언되어 있다면, 그 클래스를 상속받아 커스터마이징할 수 없음. 이로 인해, 새로운 기능을 추가하려면 처음부터 클래스를 작성해야 할 수도 있음.

2. 확장성 제한:

  • 확장 어려움: 프로젝트가 커지거나 요구 사항이 변경되면 기존 코드를 확장해야 할 필요가 생기는데, final 키워드가 적용된 클래스나 메서드는 확장하기 어려움. 새로운 요구사항을 수용하기 위해 다른 접근 방식을 찾아야 할 수 있음.

3. 재사용성 저하:

  • 클래스 재사용: 상속을 통해 다른 클래스에서 재사용할 수 있는 기능을 제한함, 만약 여러 클래스가 동일한 기능을 필요로 하는 경우, 그 기능을 상속을 통해 재사용하지 못하고, 중복 코드가 발생할 수 있음.

4. 라이브러리나 프레임워크 사용 시 제약:

  • 제한적인 활용: 서드파티 라이브러리나 프레임워크를 사용할 때, 그들이 final 클래스를 제공하면 사용자가 해당 클래스를 상속하거나 수정하여 맞춤화할 수 없음. 이는 특정 시나리오에서 라이브러리의 활용도를 제한할 수 있음.

따라서, final 키워드는 클래스나 메서드의 의도된 용도를 명확히 하고, 코드의 안정성을 높이는데 유용하지만, 유연성과 확장성을 제한할 수 있음. final 키워드를 사용할 때는 이점과 단점을 고려하여, 코드가 앞으로 확장되거나 재사용될 가능성이 높은지 판단한 후 사용하는 것이 좋음.


5. 제네릭이 어떤 것인지, 왜 필요한지, 안쓰면 어떻게 되는지

제네릭(Generic)이 뭐냐면, Swift에서 코드 유연성하고 재사용성 높이려고 쓰는 것. 데이터 타입에 상관없이 함수, 구조체, 클래스 등을 여러 타입에 걸쳐 재사용할 수 있게 해주는 것.

 

왜 필요하냐면:

  1. 코드 재사용성: 제네릭 쓰면 같은 로직을 여러 타입에 대해 반복해서 안 만들어도 됨. 예를 들어, 배열의 요소를 뒤집는 함수를 만든다고 할 때, Int 배열, String 배열 각각에 대해 함수를 따로 만들 필요 없음. 제네릭으로 한 번만 만들면 됨.
  2. 타입 안전성: 제네릭을 쓰면 컴파일 시점에 타입이 정해지니까, 타입 오류를 미리 잡을 수 있음. 예를 들어, 배열에 들어갈 타입을 잘못 넣는 실수를 방지할 수 있음.
  3. 유연성: 다양한 타입을 받아들일 수 있어서 코드가 더 유연해짐. 타입마다 다른 처리를 할 수 있음.

안 쓰면 어떻게 되냐면:

  1. 중복 코드: 제네릭 안 쓰면 같은 기능을 여러 타입에 대해 반복해서 만들어야 함. 코드가 중복되고, 유지보수가 어려워짐.
  2. 타입 안전성 낮아짐: 제네릭 없이 타입캐스팅을 많이 하면 런타임에 오류가 발생할 가능성이 높아짐. 컴파일 시점에 타입 검사를 제대로 못 하니까.
  3. 코드가 덜 유연해짐: 특정 타입에만 동작하는 코드가 많아지면, 코드의 유연성이 떨어짐. 새로운 타입을 추가할 때마다 코드를 수정해야 할 수도 있음.

그래서 제네릭은 코드를 더 간결하고 안전하게 만들어줌. 안 쓰면 코드가 중복되고, 오류 가능성도 커짐.

 

예시 코드

제네릭 없는 함수 만들기 (중복 코드 발생)

func swapInts(a: inout Int, b: inout Int) {
    let temp = a
    a = b
    b = temp
}

func swapStrings(a: inout String, b: inout String) {
    let temp = a
    a = b
    b = temp
}

// 더 많은 타입에 대해 같은 로직을 반복해야 함

 

제네릭 사용한 경우(중복 코드 발생X)

func swapValues<T>(a: inout T, b: inout T) {
    let temp = a
    a = b
    b = temp
}

// 이제 Int, String, 그리고 다른 타입도 모두 이 함수 하나로 처리 가능
// <T>는 제네릭 타입을 의미함. 이걸 쓰면 swapValues 함수가 어떤 타입이든 사용할 수 있게 됨. 
// 코드 재사용이 가능하고, 중복이 없어짐.

 

사용 예시

var intA = 5
var intB = 10
swapValues(a: &intA, b: &intB)
print("intA: \(intA), intB: \(intB)") // intA: 10, intB: 5

var stringA = "Hello"
var stringB = "World"
swapValues(a: &stringA, b: &stringB)
print("stringA: \(stringA), stringB: \(stringB)") // stringA: World, stringB: Hello

6. 캡슐화란?

캡슐화(Encapsulation)는 객체지향 프로그래밍의 중요한 개념 중 하나임. 간단히 말해, 객체의 데이터(속성)와 이를 다루는 메서드(기능)를 하나로 묶고, 외부에서 직접 접근하지 못하게 숨기는 것을 의미함.

캡슐화의 핵심 개념:

  1. 데이터 보호: 객체 내부의 데이터(프로퍼티)를 외부에서 직접 접근하지 못하게 하여, 잘못된 값이 들어가는 것을 방지함. 대신, 메서드를 통해서만 데이터를 조작할 수 있게 함. 이렇게 하면 객체의 상태를 일관되게 유지할 수 있음.
  2. 인터페이스 제공: 객체가 외부와 상호작용할 수 있는 메서드들을 제공함. 이 인터페이스를 통해서만 객체의 내부 데이터를 읽거나 수정할 수 있음. 내부 구현은 숨기고, 필요한 기능만 외부에 공개함.
  3. 정보 은닉: 객체의 내부 구현을 숨기고, 외부에서는 객체가 제공하는 메서드만 사용하게 함. 이렇게 하면 객체의 내부 구조가 바뀌어도 외부 코드에 영향을 주지 않음.

Swift에서 캡슐화 구현 방법:

  • 접근 제어자: private, fileprivate, internal, public, open 등의 접근 제어자를 사용하여 객체의 프로퍼티나 메서드에 대한 접근을 제한할 수 있음.

캡슐화 예시

class BankAccount {
    private var balance: Double = 0.0

    func deposit(amount: Double) {
        if amount > 0 {
            balance += amount
        }
    }

    func withdraw(amount: Double) -> Bool {
        if amount > 0 && amount <= balance {
            balance -= amount
            return true
        } else {
            return false
        }
    }

    func getBalance() -> Double {
        return balance
    }
}

let account = BankAccount()
account.deposit(amount: 100)
print(account.getBalance())  // 100.0
let success = account.withdraw(amount: 50)
print(success)               // true
print(account.getBalance())  // 50.0
  • balance 프로퍼티: private으로 선언되어 있어서 외부에서 직접 접근할 수 없음. 오직 deposit, withdraw, getBalance 메서드를 통해서만 balance를 조작하거나 조회할 수 있음.
  • 메서드를 통해 접근: deposit과 withdraw 메서드로만 잔액을 조작할 수 있기 때문에, 잘못된 방식으로 잔액을 변경하는 것을 방지할 수 있음. 예를 들어, 음수 값을 잔액에 추가하는 것을 막아줌.

이렇게 캡슐화를 하면 객체의 데이터 무결성을 보호하고, 코드의 유지보수가 쉬워짐. 내부 구현을 숨김으로써, 외부 코드가 객체의 세부사항에 의존하지 않게 되어 코드의 유연성과 안전성이 높아짐

'Today I Learned > 2024' 카테고리의 다른 글

24.08.26 Today I Learned  (0) 2024.08.26
24.08.23 Today I Learned  (0) 2024.08.25
24.08.19 Today I Learned  (0) 2024.08.19
24.08.08 Today I Learned  (0) 2024.08.08
24.08.05 Today I Learned  (0) 2024.08.05

BELATED ARTICLES

more