[iOS] 면접질문 공부 🤓... ing

someng·2022년 11월 4일
0

면접 준비

목록 보기
1/2

🌳 iOS

📌 iOS 앱 생명주기에 대해 설명하시오.

  • Not Running: 앱이 실행되지 않은 상태
  • Inactive: 앱이 실행중인 상태. 그러나 아무런 이벤트를 받지 않는 상태
  • Active: 앱이 실행중이며, 이벤트가 발생한 상태
  • Background: 앱이 백그라운드에 있는 상태. 그러나 실행되는 코드가 있는 상태
  • Suspended: 앱이 백그라운드에 있고, 실행되는 코드가 없는 상태

📌 실제 디바이스가 없을 경우 개발 환경에서 할 수 있는 것과 없는 것을 설명하시오.

하드웨어

  • 가속도 센서, 가압계 센서, 주변광 센서,GPS 센서 기능을 이용 불가
  • 마우스로 시뮬레이터의 터치를 하기 때문에 두 손가락으로 하는 줌인, 줌아웃 등의 기능을 테스트 불가
  • 카메라 기능 x
  • 마이크 기능 x
  • 전화기능 x

API

  • Apple의 푸시 알림 받기와 보내기 x
  • 사진, 연락처, 캘린더에 액세스하기 위해 개인 정보 보호 알림을 지원하지 않음
  • Handoff 기능(애플 기기끼리 웹, 클립보드 등 전환) 지원 x
  • MessageUI(메일, 메세지 보내는 기능) 지원 x

그 외

  • 맥의 성능이 아이폰의 성능보다 훨씬 뛰어나 CPU나 메모리 부담이 얼마나 되는지 알 수 없다.
  • 네트워크 속도 테스트 불가
  • Face ID는 직접 얼굴 인식은 안되지만 인식되는지 여부는 확인 가능

📌 앱의 콘텐츠나 데이터 자체를 저장/보관하는 특별한 객체를 무엇이라고 하는가?

1. UserDefaults

: key-value 쌍으로 디바이스에 데이터를 저장하는 기능을 제공하는 인터페이스

  • 대용량의 데이터보다 자동 로그인 여부, 아이디, 환경 설정에서의 설정 데이터 값과 같은 단일 데이터 등을 보관한다.
  • Swift에서 제공하는 기본 데이터 타입이면 별도의 작업 없이 저장
    • float, double, integer 및 boolean과 같은 공통 유형에 액세스하기위한 메소드를 제공할 뿐만아니라,
    • NSData, NSString, NSNumber, NSDate, NSArray 또는 NSDictionary 유형의 객체를 저장할 수도 있음!
    • (다른 객체 유형의 경우 NSKeyedArchiver를 사용하여 데이터를 저장하고 검색해야함)

2. CoreData

: Apple에서 제공하는 데이터관리 프레임워크

  • 데이터를 유지 및 캐싱할 수 있을 뿐 아니라 Cloudkit을 사용해 여러 장치에 데이터를 동기화하는 것도 가능하다.
  • SQLite에 데이터를 저장하고 관리하는 프레임워크
  • SQLite와 같이 테이블을 이용하지 않고 객체를 생성하여 데이터를 운영하기에 더 많은 저장공간과 메모리를 필요로 한다.
  • 더욱 빠르게 데이터를 가져온다.
  • Data Model을 생성한 후 Entity를 생성한다.

3. SQLite

: 서버가 아니라 응응프로그램에 넣어 사용하는 비교적 가벼운 데이터베이스

  • Swift에는 특별한 설치없이 바로 사용할 수 있다.
  • C언어로 작성되어 있기에 매우 가벼운 것이 특징
  • 전체 데이터베이스를 디스크 파일 1개에 저장하고, 설정 자체가 매우 간편하기에 관리하기가 수월하다.
  • SQLite는 iOS, Android, Linux, Window 등과 같이 다양한 운영체제에서 사용된다.
  • 수많은 프로세스와 스레드의 접근으로부터 안전하다.
  • 일반적인 RDBMS에 비해 대규모 작업에는 적합하지 않지만, 중소 규모라면 속도에 손색이 없다.

4. Realm

: 오픈 소스 데이터베이스 관리시스템(DBMS)으로, 모바일 환경을 주요 타깃으로 삼은 데이터베이스

  • SQLite, Core Data보다 속도가 빠르고 성능면에서 더 우수하다.
  • 많은 작업들을 처리하기 위해 코드가 많이 필요하지 않으며, 메인 스레드에서 데이터의 읽기, 쓰기 작업을 모두 할 수 있어 편리하다.
  • 대용량의 데이터에 대해 무료로 사용할 수 있으며, 용량이 적고 큼에 상관없이 속도와 성능이 유지된다.
  • 기존의 정형화된 데이터베이스와 다르게 NoSQL 데이터베이스를 지향하며, 데이터 모델 구조 자체가 객체 컨테이너로 구성되어 있다.
  • 서버에서 Realm을 사용하면 Realm 모바일 플랫폼을 구축할 수 있다. 이 경우 Real-time data sync 즉, 라이브 오브젝트를 통한 서버와 모바일 디바이스 간의 실시간 동기화가 가능해진다.

📌 앱 화면의 콘텐츠를 표시하는 로직과 관리를 담당하는 객체를 무엇이라고 하는가?

UIViewController: UIKit 앱의 뷰 계층 구조를 관리하는 객체

🌱 ViewController의 핵심 역할

  • 모델 데이터를 기반으로 변경된 사항에 맞춰 View Content를 업데이트
  • View를 통한 사용자의 입력에 즉각적인 대응
  • View의 크기 조정 및 전체 레이아웃 관리
  • 다른 ViewController 와 협업

🌱 ViewController의 유형

Contents View Controller : 화면을 구성하는 View를 직접 구현하고 관련된 이벤트를 처리하는 View Controller

Container View Controller : 하나 이상의 ViewController를 child Viewcontroller로 관리하는 View Controller.

  • Child ViewController에 속한 View들을 관리하지 않는다.
  • 자신의 Root View 관리, 크기와 위치만을 관리한다.

📌 App thinning에 대해서 설명하시오.

: 애플리케이션이 디바이스에 설치될 때, 앱 스토어와 운영체제가 디바이스의 특성에 맞게 설치되도록 하는 설치 최적화 기술.
최소한의 디스크 사용빠른 다운로드를 제공할 수 있다.
App thinning은 슬라이싱(slicing), 비트코드(bitcode), 그리고 주문형 리소스(on-demand resource)가 있다.

✅ 슬라이싱(slicing)

앱이 지원하는 여러 디바이스에 대해 각각 조각 애플리케이션 번들을 생성하고, 해당 디바이스에 가장 적합한 조각을 전달하는 기술이다.
개발자가 App store connect에 업로드하면, 앱 스토어에서 디바이스 특성에 따라 다양한 버전의 조각들을 생성한다. 그리고 사용자가 그 조각 중에서 가장 알맞은 조각(app variant)을 다운로드 받는 것이다.

✅ 비트코드(bitcode)

기계언어로 번역되기 이전 단계의 중간표현(Intermediate Representation)을 말한다.
현재 iOS에서는 옵션이나 기본 설정으로 되어 있으며, 개발자가 프로젝트 옵션에서 선택할 수 있다. 비트코드를 사용하여 업로드를 하면 애플이 애플리케이션을 재컴파일하여 앱 바이너리(app binary)를 생성한다. 비트코드를 사용하지 않으면, 모든 경우의 디바이스 경우에 따라 바이너리로 생성하여 합쳐져서 fat binary라는 것이 업로드되지만, 비트코드를 사용하면 필요 경우에 따라 재컴파일하게 되므로 여기에서 최적화할 수 있다.

✅ 주문형 리소스(on-demand resource)

쉽게 말해서, 필요할 때 다운로드 받는 것이다.
예를 들어 사용자가 게임을 할 때, 현재 레벨보다 상위레벨은 필요하지 않으므로 갖고 있을 필요가 없다. 사용자의 레벨이 필요할 때 다운로드 받는 것이다! 인앱 구매도 사용자의 선택에 따라 다운로드를 받는 또다른 예다.

📌 앱이 시작할 때 main.c 에 있는 UIApplicationMain 함수에 의해서 생성되는 객체는 무엇인가?

→ UIApplication 객체가 생성된다.

UIApplication 객체란?

: iOS에서 실행중인 앱의 제어와 조정을 담당하는 중앙지점.
시스템과 앱의 여러 객체들간의 대화를 가능하게 해준다.

🌱 object-c

  1. 앱이 실행되면서 맨 처음 main()함수가 실행된다
  2. main()함수는 UIApplicationMain()함수를 호출한다
  3. UIApplicationMain()함수가 UIApplication 객체를 생성한다.
  4. UIApplication객체는 info.plist 파일로부터 앱 구성에 필요한 정보들을 로드한다.
  5. 이벤트 루프 및 초기 설정을 진행한다.
  6. 실행 완료 직전에 App Delegate의 application(_:didFinishLaunchingWithOptions:)메소드가 호출된다.

🌱 swift

Swift는 main 함수가 없지만 @main 이라는 어노테이션 표기가 있다. 이 표기를 통해서 object-c의 1-5 과정이 대체된다.

1~5. @main 어노테이션을 찾고 그 클래스를 실행한다
6. App Delegate클래스의 application(:didFinishLaunchingWithOptions:)메소드를 호출한다 (앱이 실행될때 처리할 내용이 있으면 여기에 작성)
7. 이벤트 루프 실행, 작성한 코드들 실행
8. 앱이 종료될때 앱에 대한 메모리 제거를 위해서 aplicationWillTerminate(:)메소드를 호출한다 (앱이 종료될때 처리할 내용이 있으면 여기에 작성)

📌 @Main에 대해서 설명하시오.

: 프로그램 실행 시작 시 진입점으로 타입을 지정하기 위한 Swift 언어의 기능

  • 사용자는 탑 레벨의 코드를 작성하는 대신 싱글타입에 @main 속성을 사용할 수 있다.

    탑 레벨 코드(Top Level Code)란?

    Swift 소스 파일 안에서 탑 레벨 코드는 0개 이상의 선언이나 정의, 그리고 표현식으로 구성이 된다.
    기본적으로 소스파일의 탑 레벨에서 선언된 것들은 같은 모듈 내의 모든 소스파일에서 접근이 가능하다.

    탑 레벨 코드에는 선언문실행문 두 가지가 있다.

    🌱 선언문 (top-level declarations)

    • 모든 Swift Source File에서 사용이 가능하다.
    • 변수, 함수 등의 선언문은 전역으로 얼마든지 선언이 가능하며 같은 모듈 내에서 자유롭게 접근 가능하다.

    🌱 실행문 (excutable top-level code)

    • 선언을 제외한 모든 구문을 의미한다.
    • 실행문은 Top-Level Entry Point에서만 사용이 가능하다.
  • 라이브러리와 프레임워크는 프로토콜과 클래스 상속을 통해 앱의 진입점을 커스터마이징할 수 있다.

  • Swift 5.3 버전 이전에는 @main 대신 @UIApplicationMain이라는 키워드를 사용하였다.

    왜 키워드가 @UIApplicationMain에서 @main으로 바꼈나?

    • 구조체에서 @main 대신 @UIApplicationMain을 쓴다면 class 선언에만 사용이 가능하다는 오류가 발생한다.
    • 타입 기반의 Swift 코드에서 이상적인 진입점을 알려주며,
      main() 함수는 일반 정적 메서드이므로 프로토콜에서 확장 메서드 또는 기본 클래스로 제공 할 수 있다.
      → 자유롭게 Entry Point를 지정할 수 있다.

📌 앱이 Foreground에 있을 때와 Background에 있을 때 어떤 제약사항이 있나?

🌱 Foreground

  • 메모리 및 기타 시스템 리소스에 높은 우선순위를 가지며, 시스템은 이러한 리소스를 사용할 수 있도록 필요에 따라 background 앱을 종료한다.

🌱 Background

  • 가능한 적은 메모리 공간을 사용해야한다 (시스템 리소스 해제, 메모리에서 해제 후 데이터를 디스크에 작성)
  • 사용자 이벤트를 받기 어렵다.
  • 공유 시스템 리소스를 해제하고, 이미지 객체 참조 등 메모리 제한이 있다.

📌 상태 변화에 따라 다른 동작을 처리하기 위한 AppDelegate 메서드들을 설명하시오.

//애플리케이션이 실행된 직후 사용자의 화면에 보여지기 직전에 호출 
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool	

//애플리케이션이 최초 실행될 때 호출되는 메소드 
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool		

//애플리케이션이 InActive 상태로 전환되기 직전에 호출  task 일시정지, 타이머 비활성화, 일시정지(게임)
func applicationWillResignActive(_ application: UIApplication)	

//애플리케이션이 백그라운드 상태로 전환된 직후 호출
func applicationDidEnterBackground(_ application: UIApplication)	

//애플리케이션이 Active 상태가 되기 직전, 화면에 보여지기 직전에 호출 
func applicationWillEnterForeground(_ application: UIApplication)	

//애플리케이션이 Active 상태로 전환된 직후 호출
func applicationDidBecomeActive(_ application: UIApplication)

//애플리케이션이 종료되기 직전에 호출 
func applicationWillTerminate(_ application: UIApplication)

📌 앱이 In-Active 상태가 되는 시나리오를 설명하시오.

✅ 시나리오

  • 다른 상태로 넘어가기 전에 앱은 반드시 이 상태를 거친다.
  • 전화나 메시지 같은 interrupt 발생 시
  • 미리 알림 같은 특정 알림창이 화면을 덮어서 앱이 실질적으로 event를 받지 못하는 상태 등이 여기에 해당
  • 사용자가 앱을 실행한다: Not running » In-Active » Active
  • 앱 실행 도중 홈버튼을 누른다: Active » In-Active » Background
  • 앱을 다시 켠다: Background » In-Active » Active
  • 앱이 백그라운에 있다가 Suspended 상태로 전이: Active » In-Active » Background » Suspended

🌳 Autolayout

📌 Autolayout Constraint의 Priority의 개념이 무엇이고, 어떤상황에 사용하나요?

: 제약들간의 우선순위

다수의 뷰들에 여러 제약이 걸려있을 때, 보통은 제약간의 충돌이 일어나지 않게끔 제약들을 설계하는게 일반적이지만,
상황에 따라서는 뷰들의 크기가 유동적으로 변하는 경우가 있는데, 이럴때 어떤 제약들이 서로간에 충돌이 일어나는 경우가 있을 수 있다.
이럴때에는 어떤 제약의 우선순위를 더 우위에 둘것이냐를 결정함으로써 이러한 충돌을 해결할 수 있다.

📌 Autolayout을 코드로 작성하는 방법은 무엇인가? (3가지)

1. NSLayoutAnchor

: 제약을 주고 싶은 item의 anchor property에 접근해서 제약을 정의하는 방식

  • 간결한 작성 방식으로 인한 뛰어난 가독성
  • type safety 지원
  • NSLayoutConstraint.Attribute (ex. left, right, leading, trailing) 서브 클래스는 추가적으로 Type 검사를 제공하여 유효하지 않은 제약 조건을 생성하는 것을 방지한다.
let margins = view.layoutMarginsGuide

subview.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
subview.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true

2. NSLayoutConstraint class직접 이용

: NSLayoutConstraint class의 convenience method를 통해 직접 생성하는 방식

  • layout에 관여하지 않는 parameter도 모두 값을 지정해주어야 함.
    → boilerplate 코드가 많아지고, 가독성이 떨어짐
  • layout anchor 방식과는 다르게, 특정 제약사항의 조건들을 확인시켜 주지 않아 오류에 취약함 (runtime이 되어서야 알 수 있음)

👍🏻 편리한 방식

3. Visual Format Language

  • 시각적으로 정의하기 때문에 표현성이 좋음
  • 굉장히 간결한 표현 방식이라 한 번에 여러 개의 제약을 정의할 수 있음
  • 단점: 표현성을 강조한 언어라 모든 제약사항을 정의할 수 없음

ex) myView를 superview의 leading과 trailing constraint에 constant 값 0으로 딱 붙이겠다

📌 hugging, resistance에 대해서 설명하시오.

뷰에 제약사항을 걸어줄 때 공간이 남게 되면 hugging이 높은 뷰는 자신의 크기를 유지하고, 낮은 뷰는 남은 공간만큼 뷰의 크기가 커지게 된다.
반대로, 공간이 부족하게 되면 resistance가 높은 뷰는 자신의 크기를 유지하고, 낮은 뷰는 부족한 공간만큼 크기가 작아지게 된다.

📌 Intrinsic Size에 대해서 설명하시오.

: 콘텐츠의 본질적인 크기

  • ex) UIButtonIntrinsic Size = Title Size + margin
  • 모든 view가 Intrinsic Size를 갖는 것은 아니다.
  • 각각의 view마다 Intrinsic Size가 적용되는 방식이 다르다.

📌 Storyboard를 이용했을 때의 장단점을 설명하시오.

🌱 장점

  • 앱의 흐름을 직관적으로 볼 수 있다.
  • 드래그 앤드롭으로 UI를 구성할 수 있다.
  • View의 초기화가 필요없어 코드 베이스보다 UI 개발 속도가 빠르다.

🌱 단점

  • Storyboard에 많은 컴포넌트들이 있으면 로딩시간이 오래걸린다.
  • Storyboard 파일의 포맷이 XML이기 때문에 협업 시에 Merge Conflict가 발생하고, 해결하기 어렵다.
    (깃 충돌 방지를 위해 1View 1StoryBoard를 사용하면, 쉽게 앱의 흐름을 파악하는 장점이 사라진다 ㅠㅠ)
  • 협업 시 다른 사람이 만든 View의 수정을 하기가 어렵다.
  • Pull Request를 할 때 View의 수정사항을 자세히 써주지 않으면, 어떠한 점이 변경되었는지 파악하기 힘들다.
  • View의 재사용성이 떨어진다.
  • Storyboard의 identifier를 항상 지정해줘야 하므로, 번거로울 수 있다.

📌 Safearea에 대해서 설명하시오.

: 시스템에 의해 가려질 수 있는 부분의 마진을 자체적으로 가지는 것

아이폰X 등장 이후 4개의 라운드 코너, 노치 디자인으로 인해 TV OS에서만 사용되던 Safearea가 iOS에 처음 도입되었다.

  • Portrait 모드: 상단 statusbar, navigation bar 하단 home indicator를 제외한 모든 부분이 Safearea이다.

  • Landscape 모드: 상하좌우 모두 Safearea inset margin이 생긴다.

📌 Left Constraint 와 Leading Constraint 의 차이점을 설명하시오.

Left Constraint: 어떤 객체의 왼쪽
Leading Constraint: 어떤 객체의 앞쪽 가장자리

Left/Right Constraint는 절대적이며 항상 화면 또는 컨트롤의 왼쪽 / 오른쪽을 참조한다. Leading/Trailing Constraintdevice locale의 영향을 받는다. (장치별 국가 설정)

따라서 읽기 방향이 오른쪽에서 왼쪽인 locale(예: 히브리어, 아랍어)에서는 leading이 오른쪽이 되고 trailing이 왼쪽이 된다.

👍🏻 Left/Right 보다 Leading/Trailing을 사용하게 되면 코드를 조금 더 유연하게 작성할 수 있다.


🌳 Swift

📌 struct와 class와 enum의 차이를 설명하시오.

struct & enum

  • value type (대입 연산 시 값 자체가 복제되어 할당됨(공유가 불가능))
  • 생성된 인스턴스는 stack 메모리 영역에 저장
  • 상속 불가능

class

  • reference type (대입 연산 시 레퍼런스가 복사되어 할당됨. (공유 가능))
  • 객체화 시 heap 메모리영역에 저장되며 ARC 로 객체의 메모리해제가 관리된다.
    (*참조하는 값이 존재하는 위치는 stack 메모리 영역에 저장하고, 참조되는 값은 heap 메모리 영역에 저장)
  • 상속 가능

📌 타입 캐스팅을 할 때 사용하는 키워드인 as, as?, as! 이 셋의 차이는 무엇인가요?

  • as: 컴파일러가 타입 변환의 성공을 보장. 컴파일타임에 가능/불가능 여부를 알 수 있음
  • as?: 타입변환에 실패하는 경우 nil 을 리턴. 컴파일타임에 가능/불가능 여부를 알 수 없음
  • as!: 타입변환에 실패하는 경우 실행시간 (Runtime) 오류를 발생시킴.

📌 UIStackView의 장점을 설명하시오.

여러 뷰를 가로방향 또는 세로방향으로 배치할 때, 복잡한 constraint 설정 없이, 또는 constraint 만으로 설정하기 어려운 뷰의 배치등을 구현할 때 쓰일 수 있는 뷰.

arrangedSubview 로 하위 뷰들이 관리되며, 이 하위뷰들에 Axis(가로 세로 방향), Alignment(세로방향 정렬), Distribution(가로방향 정렬), Spacing(하위 뷰들간의 간격) 의 규칙을 적용할 수 있다.

📌 UICollectionViewLayout클래스에 prepare 메소드는 어떤 역할을 하나요?

  • layout 관련 연산이 일어날 때마다 가장 먼저 호출된다. 이 메소드에서 셀의 위치/크기 등을 계산하기 위한 사전처리를 할 수 있다.
  • UICollectionViewLayout 를 상속받아 Custom 한 CollectionView Layout 을 구성하고자 할때, 데이터소스를 참조하여 셀의 위치 및 크기를 미리 계산하여 캐싱해두고, CollectionView 로부터 셀의 위치 및 크기 요청이 들어올때, 미리 계산하여 캐싱해둔 데이터를 전달해주는 방식으로 커스텀 layout 을 구성하는 방식이 있다.

📌 Frame과 Bounds의 차이점을 설명하시오.

Frame : SuperView(상위뷰) 좌표시스템 내에서의 view 의 위치(origin) 과 크기(size)

  • Frame은 단순히 UIView와 동일한 위치나 크기가 아니라, view가 회전되었을 때도 감쌀 수 있는 사각형을 의미한다.

Bounds : view 자기 자신의 좌표시스템에서의 위치와 크기.

  • 자기 자신의 좌표시스템을 가리키기 때문에 기본적으로 origin 은 x:0, y:0 을 가리킨다.
  • Bounds 의 origin 을 변경한다는 것은 곧, subview 들이 화면상에서 drawing 되는 위치가 변경됨을 의미한다
  • 이게 subview 들의 frame 값을 변화시키는게 아니다. 부모뷰 좌표축이 변하면서 subview 가 그려져야하는 위치가 달라졌기 떄문이다.

참고 링크

📌 ViewController의 생명주기를 설명하시오.

  • loadView: 컨트롤러가 관리하는 뷰를 만든다.
  • viewDidLoad: 컨트롤러의 뷰가 메모리에 올라간 뒤에 호출된다. 리소스를 초기화하거나 초기 화면을 구성하는 용도로 주로 사용된다. 화면이 처음 만들어질 때 한 번만 실행된다.
  • viewWillAppear: 화면에 뷰가 표시될때마다 호출된다. 이 단계는 뷰는 정의된 바운드를 가지고 있지만 화면회전은 적용되지않는다.
  • viewWillLayoutSubviews: 뷰컨트롤러에게 그 자식뷰의 레이아웃을 조정하는 것에 대한 것을 알려주기위해 호출된다. 이 메소드는 frame이 바뀔때마다 호출된다.
  • viewDidLayoutSubviews: 뷰가 그 자식 뷰의 레이아웃에 영향을 준 것을 뷰컨트롤러에게 알려주기 위해 호출된다. 뷰가 그 자식 View의 레이아웃을 바꾸고난 뒤에 추가적인 변경을 하고 싶을때 사용하는 이벤트 함수
  • viewDidAppear: 뷰가 나타났다는 것을 컨트롤러에게 알리는 역할을 한다. 호출되는 시점으로는 뷰가 화면에 나타난 직후에 실행된다.
  • viewWillDisappear: 뷰가 사라지기 직전에 호출되는 함수이다. 뷰가 삭제 되려고하고있는 것을 ViewController에게 알린다.
  • viewDidDisappear: ViewController에게 View가 제거되었음을 알린다. 호출시점은 viewWillDisAppear 다음에 호출된다.

📌 고차함수 (map, reduce, filter) 에 대해 설명하시오.

고차함수: 매개변수로 함수를 받는 함수

✅ Map

  • 데이터를 변형하고자 할 때 사용
  • 기존 컨테이너의 값들은 변경되지 않고 새로운 컨테이너를 생성하여 반환한다.
  • 장점
    • 코드 재사용 용이
    • 컴파일러 최적화 측면에서 성능이 좋다
    • 다중 스레드 환경에서, 하나의 컨테이너에 여러 스레드들이 동시에 변경을 하려고 할 때 예측하지 못한 결과 발생을 방지
let numbers = [0, 1, 2, 3, 4]

var doubledNumbers = [Int]()
var strings = [String]()

// map
doubledNumbers = numbers.map{ $0 * 2 }
strings = numbers.map{ "\($0)" }

✅ Filter

  • 컨테이너 내부의 값들을 걸러서 추출하고자 할 때 사용
  • filter의 매개변수로 전달되는 함수의 반환 타입은 Bool이다. true라면 값을 포함하고 false면 배제하여, map과 마찬가지로 새로운 컨테이너를 생성하여 반환한다.
let numbers = [0, 1, 2, 3, 4, 5]

let evens = numbers.filter{ $0 % 2 == 0 } // [0, 2, 4]
let odds = numbers.map{ $0 + 3 }.filter{ $0 % 2 != 0 } // [3, 5, 7]

✅ Reduce

  • 컨테이너 내부를 하나로 합쳐주는 기능
  • 정수 배열이라면 전달받은 함수의 연산 결과로 합쳐주고, 문자열 배열이라면 문자열을 하나로 합쳐준다. 첫 번째 매개변수를 통해 초깃값을 지정할 수 있다. 이 초깃값이 최초의 $0 으로 사용된다.
let numbers = [1, 2, 3]
var sum = numbers.reduce(10) { $0 + $1 } // 16

📌 class의 성능을 향상 시킬수 있는 방법들을 나열해보시오.

  • dynamic dispatch보다 static dipatch를 지향한다.

    Dispatch란?

    Dispatch: 어떤 메서드를 호출할 것인지를 결정하여, 그것을 실행하는 메커니즘

    • Static Dispatch (Direct Call)
      : "컴파일 타임"에 호출될 함수를 결정하여, 런타임 때 그대로 실행한다 (성능상 이점)
    • Dynamic Dispatch (Indirect Call)
      : "런타임"에 호출될 함수를 결정한다 (성능상 손해)

    ...

    class는 상속이 가능하기 때문에 하위클래스에서 메서드가 오버라이드 될 가능성이 존재한다. 그렇기 때문에 Dynamic Dispatch 방식으로 메서드를 호출한다. Dynamic Dispatch는 런타임시에 일어나기 때문에 성능상 손해를 보게 된다.

    하지만 final 키워드를 붙여주게 되면 상속의 가능성을 배제할 수 있기 때문에 Static Dispatch로 함수를 호출하게 된다. 결과적으로 컴파일 타임에 함수를 호출하게 되고, 성능은 올라가게 된다.

    또한 private 키워드를 사용할 경우 참조할 수 있는 곳이 현재 파일로 제한 되기 때문에 컴파일러는 자동적으로 private 키워드가 참조될 수 있는 곳에서 오버라이딩이 되는지 안되는지 판단 가능하다.

    쉽게 말해 private 키워드가 있는 객체는 final 키워드가 없더라도 오버라이딩이 되지 않고 있다면, 자동적으로 Static Dispatch로 동작한다.

  • heap보다는 stack 메모리 할당을 지향한다.

    ✅ Stack

    Stack은 LIFO(Last In First Out)의 단순한 구조로 메모리 할당과 해제가 편리하다. 시간복잡도는 O(1)로 속도가 매우 빠르다. Stack Pointer를 사용하여 할당과 해제를 처리한다.

    ✅ Heap

    HeapStack보다 좀 더 복잡하다. 다이나믹한 할당 방법을 사용하는데, Heap 영역에서 사용하지 않은 블록을 찾아 메모리 할당을 처리한다. 할당을 해제하기 위해, 해당 메모리를 적절한 위치로 다시 삽입한다. 여러 스레드가 동시에 Heap에 접근할 수 있기에 Locking 또는 다른 동기화 메커니즘으로 무결성을 보호해야 한다.

    [String: Any]

    딕셔너리에 String 타입의 키를 사용하는 것은 성능에 좋지 않다. String 타입은 value 타입이지만, Heap에 Character 타입으로 문자들을 간접적으로 저장하기 때문이다. 사용하게 되면 heap allocation이 발생한다.

    이를 해결하는 방법으로는, struct을 만들어서 key로 사용하는 것이다. 이때 Hashable이라는 프로토콜을 채택해야 하는데, 커스텀 객체를 collection에 사용하기 위해 필요하다.

    struct Attributes: Hashable {
        var color: Color
        var orientation: Orientation
        var tail: Tail	
    }
    let key = Attributes(color: color, orientation: orientation, tail: tail)
  • reference counting을 적게 만든다.

struct label {
    var text: String
    var font: UIFont
    func draw() {}
}

String 타입은 Character들을 Heap에 저장하며, UIFont 등은 클래스로 만들어진 객체이므로 래퍼런스 카운트가 필요하다.
→ uuid를 String 타입이 아닌 UUID로 변경하여 reference counting을 줄인다. UUID는 struct 타입이다. String 대신 enum을 사용하여 해결할 수도 있다.

📌 Copy On Write는 어떤 방식으로 동작하는지 설명하시오.

value 타입의 데이터는 값을 참조하지 않고 복사한다. 하지만 이렇게 매번 복사를 할 경우 값이 변경될 필요가 없음에도 매번 새로운 메모리 공간을 할당하여 복사를 수행하게 된다. 따라서 메모리가 낭비되고, 오버헤드까지 발생한다. 그래서 사용되는 것이 Copy on write이다.

Copy On Write는 A라는 변수에 B라는 변수를 할당해주었을 때, 새로 메모리에 할당하는 것이 아니라, B의 메모리를 A가 공유하는 형태로 구성된다. 그러다가 A가 값이 수정될 때 새로 메모리에 할당이 되는 식으로 동작한다.

📌 Convenience init에 대해 설명하시오.

초기화의 종류에는 크게 2가지로 Designated, Convenience가 있다.

✅ (Designated) init

: 클래스의 모든 프로퍼티를 초기화

✅ Convenience init

: 보조 이니셜라이저로, 클래스의 원래 이니셜라이저인 init을 도와주는 역할

  • convenience init은 같은 클래스에서 다른 이니셜라이저를 호출해야한다는 규칙이 있다.
  • init파라미터 중 일부를 기본값을 설정해서, convenience init안에서 init을 호출하여 초기화 진행할 수 있다.
    즉, 파라미터로 넘겨주지 않은 값은 그냥 임의로 지정해주고, 파라미터로 넘어간 것들만 넣어주면 된다.
class Person {
    var name: String
    var age: Int
    var gender: String

    init(name: String, age: Int, gender: String) {
        self.name = name
        self.age = age
        self.gender = gender
    }
		
	convenience init(age: Int, gender: String) {
        self.init(name: "zedd", age: age, gender: gender)
    }
}

📌 AnyObject에 대해 설명하시오.

Swift는 Type에 민감한 언어기 때문에 특정하지 않은 타입에 대해 동작하도록 특별한 타입 두가지를 제공한다.

원래는 타입 캐스팅을 수행할 때, 일반적으로 상속 관계에 있는 클래스끼리만 캐스팅이 가능하지만, Any, AnyObject 타입을 사용할 경우, 상속 관계에 있지 않아도 타입 캐스팅이 가능하다.

✅ Any

: 함수타입을 포함하여 모든 타입의 인스턴스를 나타낼 수 있다.
value 타입(구조체, 열거형), reference 타입(클래스, 클로저)이건 상관 없이 저장이 가능하다. 한마디로, Any는 모든 타입을 포함할 수 있는 범용 타입!

✅ AnyObject

: 모든 클래스 타입의 인스턴스를 나타낼 수 있는 프로토콜
AnyObject로 선언 시, "클래스 타입"만 저장할 수 있다.
(따라서 클래스 타입이 아닌 구조체, 열거형, Reference Type인 클로저는 AnyObject에 해당하지 않는다)

✅ Any, AnyObject 타입 캐스팅의 단점

매번 타입 체크 및 형변환을 해야하기때문에 필요에 의한 것이 아니라면 사용하지 않는 것이 좋다. 그럼에도 불구하고 사용한다면, switch문을 활용하여 각 타입마다 as 를 통해 캐스팅이 성공하면 case문이 실행하게 만들어준다.

var name: Any = "Somang"

if var name = name as? String {
    name.append("다운캐스팅")
}

📌 Optional 이란 무엇인지 설명하시오.

: 값이 있을 수도 없을 수도 있는 enum 타입.
(some: 값이 있고 래핑된 상태, none: 값이 없는 nil 상태)

  • Swift의 안정성을 보장해주는 기능.
  • 변수 안에 값이 확실히 있다는 것을 보장할 수 없을 때 ?를 붙여 Optional 을 사용한다.
var test: Int?

Optional 변수의 값 가져오는 방법

✅ Forced Unwrapping (강제 언래핑)

: 옵셔널에 값이 있다고 가정하고 값에 바로 접근할 수 있도록 강제하는 방법

  • 느낌표(!) 키워드를 붙여 사용한다.
  • 그러나 값이 존재하지 않는 옵셔널 값에 접근하려 시도하면 런타임 에러가 발생한다. 느낌표를 사용하여 강제 언래핑 하기 전에 항상 옵셔널 값이 nil 이 아니라는 것을 확실히 해야 한다.
var optionalEmail: String?
print(optionalEmail!) 

✅ Optional Binding

: 옵셔널의 값이 존재하는 지를 검사한 뒤, 존재한다면 그 값을 다른 변수에 대입시켜준다.

  • if let if var 구문을 사용한다.
  • 옵셔널을 바인딩할 때 , 를 사용해서 조건도 함께 지정할 수 있다.
var optionalAge: Int? = 22
if let age = optionalAge, age >= 20 {
	print(age)
}

✅ Optional Chaining

: 옵셔널의 속성에 접근할 때, 옵셔널 바인딩 과정을 ? 키워드로 줄여주는 역할을 한다.

let isEmptyArray = array?.isEmpty == true

📌 Struct 가 무엇이고 어떻게 사용하는지 설명하시오.

Struct: 인스턴스의 값(property)을 저장하거나 기능(method)를 제공하고, 이를 캡슐화할 수 있도록 Swift가 제공하는 타입

  • struct 키워드로 정의한다.
  • 인스턴스를 생성해서 속성 및 함수에 접근할 수 있다.
struct [구조체 이름] { 
    [프로퍼티와 메서드들]
}

🌱 Struct 특징

  • value type : 주소값을 넘겨주는 reference type과는 다르게 현재 값을 복사하는 값의 복사가 일어난다.
  • 레퍼런스 카운트 ARC 영향을 받지 않는다.
  • 코드의 유연성에 도움이 된다. 객체를 분리해 하나의 타입으로 개발하고싶을 때 사용한다.
  • 상속이 불가능하다.

📌 Subscripts에 대해 설명하시오.

🌱 Subscript란?

  • Subscript: 클래스, 구조체, 열거형에서 정의해서 사용하는 스크립트
    즉, collection, list, sequence 등 집합 내 속한 요소에 쉽게 접근할 수 있게 해주는 문법
  • 추가적인 메소드없이 특정 값을 할당(set)하거나 가져(get)올 수 있다.
  • ex) 배열의 특정 요소를 접근하는 Array[index] 문법
  • 오버로드를 통해 다양하게 입맛대로 정의할 수 있다.

🌱 Subscript 문법

  • 읽고 쓰기 가능
subscript(index: Int) -> Int {
    get {
        // 적절한 반환 값
    }
    set(newValue) {
        // 적절한 set 액션
    }
}
  • 읽기 전용
subscript(index: Int) -> Int {
    // 적절한 반환 값
}

🌱 사용 예시

struct TimesTable {
    let multiplier: Int
    subscript(index: Int) -> Int {
        return multiplier * index
    }
}

// TimesTable구조체의 multiplier를 3으로 설정 
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// "six times three is 18" 출력

📌 String은 왜 subscript로 접근이 안되는지 설명하시오.

다른 언어들에서는 Int 자료형 index로 문자열에 접근이 가능하지만,
Swift에서는 String에서 Int 형식으로 인덱스 참조가 불가능하다. Int로 서브스크립트를 사용할 수 없고 String.index로 사용해야 한다.
Swift에서 Character는 1개 이상의 Unicode Scalar로 이루어져 있어 크기가 가변적이기 때문이다.


🌳 ARC

📌 ARC에 대해 설명하시오.

  • ARC (Automatic Reference Counting)란, 자동 레퍼런스 카운팅으로서 자동으로 메모리를 관리해주는 방식을 말한다. 참조 카운팅이 0이 될 때만 메모리에서 해제한다는 뜻이다.

  • 클래스의 새로운 인스턴스를 만들 때 ARC는 인스턴스의 정보를 저장하기 위해 메모리를 할당한다. 또한 ARC는 인스턴스가 더 이상 사용되지 (참조 카운팅 0) 않는다고 판단하면 메모리를 해제한다.

  • 레퍼런스 프로퍼티에 인스턴스를 할당하면 ARC는 참조되는 프로퍼티의 개수를 카운트하여 참조하는 모든 변수가 인스턴스를 해제하기 전에 ARC는 인스턴스를 메모리에서 해제하지 않는다.

📌 Retain Count 방식에 대해 설명하시오.

✅ Retain Counting이란?

  • ARC는 retain, release의 호출을 통해 메모리 관리를 수행하는 것이고,
    이러한 메모리 관리 방법을 Reference Counting(Retain Counting)이라고 한다.
  • 컴파일 타임에 자동으로 retain, release를 적절한 위치에 삽입하는 방식이다.

🎈 retain

객체의 reference count(retain count)를 증가시킨다.
객체가 메모리에서 해제되지 않도록 이 함수를 호출하여 카운트를 증가시키는 것

🎈 release

객체의 reference count(retain count)를 감소시킨다.
객체를 더이상 필요로 하지 않을 때 이 함수를 호출하여 카운트를 감소시키는 것

1) compile time에는 코드를 분석하고 예측하여 적절한 위치에 retain, release를 삽입해준다.

2) run time에 삽입된 코드가 실행되면서 retain, release에 의해 reference count를 증감하고,
count가 0이 되었을 때 메모리에서 제거한다.
!

📌 Weak와 Strong, Unowned에 대해 설명하세요.

✅ Strong(강한 참조)

  • strong객체를 소유하여 레퍼런스 카운트가 증가하는 프로퍼티이다. 값 지정 시점에 retain이 되고 참조가 종료되는 시점에 release가 된다.
  • 레퍼런스 객체가 할당되는 순간, 해당 객체의 레퍼런스 카운트를 증가 시킨다. 레퍼런스 카운트를 증가시켜 ARC로 인한 메모리 해제를 피하고 객체를 안전하게 사용하고자 할 때 쓰인다.

✅ Weak(약한 참조)

  • weak객체를 소유하지 않고 주소값만 가지고 있는 포인터 개념이다. 자신이 참조는 하지만, weak 메모리를 해제할 수 있는 권한은 다른 클래스에 있다. 값 지정 시 retain이 발생하지 않는다. 따라서 릴리즈도 발생하지 않는다. 그래서 언제 어떻게 메모리가 해제될 지 알 수가 없다. 다만 메모리가 해제될 경우 자동으로 레퍼런스가 nil로 초기화를 해 준다. 그렇기 때문에 weak 속성을 사용하는 객체는 항상 옵셔널타입이여야 한다.
  • 객체가 할당될 때 레퍼런스 카운트를 증가시키지 않는다. 객체가 ARC에 의해 해제가 되면 nil 값이 할당된다. 대표적으로 retain cycle에 의해 메모리가 누수되는 문제를 막기 위해 사용되며, iOS 프레임워크에서 대표적인 예로는 Delegate패턴이 있다.

✅ Unowned(미소유 참조)

  • weak와 매우 비슷한 역할을 한다. 차이점으로는 Unowned로 선언된 변수는 nil이 될 수가 없다. 그러므로 Unowned 변수는 옵셔널로 선언되어서는 안된다. 해제된 메모리 영역을 접근하지 않는다는 확실한 경우에만 사용해야한다.
  • 객체가 할당될 때 레퍼런스 카운트를 증가시키지 않는다. 그러나, 객체가 ARC에 의해 메모리해제가 되더라도, 해당 객체 값을 존재하는 것으로 인지하며, 해당 객체에 액세스 할 경우 런타임 오류를 발생시킨다. 객체의 라이프사이클이 명확하고 개발자에 의해 제어 가능이 명확한 경우 , weak Optional 타입 대신 사용하여 좀더 간결한 코딩이 가능하다.

📌 클로저 블럭내에서 [weak self] in 코드를 넣는 방법으로 순환 참조를 해결하는데 이때 [weak self] in 코드가 하는 역할과 그 이유는?

[weak self]의 역할로는 ARC가 프로퍼티의 갯수를 카운팅 하지 않도록 만들며, 카운팅이 되지 않기에 순환참조가 일어나지 않도록 만드는 역할을 한다. 그 이유로는 weak 참조는 ARC에 의해 참조되는 인스턴스가 메모리에서 해제 될 때 프로퍼티의 값을 nil로 만들기 때문에 순환 참조가 발생하지 않는다.

(* 순환 참조: 두 가지 이상의 객체가 서로에 대한 Strong Reference(강한 참조) 상태를 가지고 있을 때 발생하며, 순환 참조가 발생하게 되면 서로에 대한 참조가 해제되지 않기 때문에 메모리에서 유지되며 이로 인해 메모리 릭이 발생하게 된다.)

📌 강한 순환 참조 (Strong Reference Cycle) 는 어떤 경우에 발생하는지 설명하시오.

두 개 이상의 객체가 서로 상호 소유를 할 경우에 강한 순환 참조가 발생한다.

하나의 인스턴스를 참조하는 변수가 nil이 되어서 메모리 해제가 되어야하는 시점에서도 인스턴스를 참조하고 있는 인스턴스가 있어서 reference count는 여전히 1이기 때문에 메모리 해제가 안되어서 메모리 릭이 발생한다.

class ClassA {
	var objB: ClassB!
	deinit { print("A 객체 해제") }
}

class ClassB {
	var objA: ClassA!
	deinit { print("B 객체 해제") }
}

var a: ClassA! = ClassA()    // -> A 객체 R.C = 1
var b: ClassB! = ClassB()    // -> B 객체 R.C = 1

// 서로 객체 소유
**a.objB = b**    // -> B 객체 R.C = 2
**b.objA = a**    // -> A 객체 R.C = 2

a = nil    // -> A 객체 R.C = 1
b = nil    // -> B 객체 R.C = 1

🌱 강한 순환 참조를 방지하는 방법

: weak, unowned 참조를 사용!
(*unowned 참조는 non-optional 이기 때문에 런타임 에러 발생 가능)


🌳 기타

📌 스레드와 멀티 스레드에 대해 설명하시오.

✅ 스레드

: 하나의 프로세스 내에서 실행되는 작업흐름의 단위.
프로세스가 시작하는 동시에 동작하는 스레드를 메인 스레드라 하고, 이외의 추가로 생성되는 스레드를 서브 스레드라 부른다.

✅ 멀티 스레드

: 여러 개의 스레드가 동시에 진행되는 것.
하나의 프로세스 내에서 여러 개의 스레드가 존재하고, 스레드들이 프로세스의 자원을 공유하되, 실행은 독립적으로 이루어지는 구조

📌 동기, 비동기에 대해 설명하시오

✅ 동기 (Synchronize)

: 주어진 명령을 차례대로 처리하되 하나의 업무가 완료될 때 까지는 다른 업무로 넘어가지 않는 방식.

중간에 대기하는 시간때문에 효율은 떨어지지만, 일관된 업무 보장과 동시다발적 업무가 발생하지 않으므로 대응이 불필요하여 업무구성이 단순화됨

✅ 비동기 (Asynchronize)

: 주어진 명령을 차례대로 처리하되, 시간이 걸리는 업무는 진행해둔 채 기다리는 동안 다른 업무를 처리하는 방식.

일관적인 업무 흐름이 깨지고, 응답에 대한 대응이 필요하다.

📌 Concurrency한 작업을 하기 위해 iOS에서 지원해주는 API

⓵ GCD

: Grand Central Dispatch(GCD)멀티코어와 멀티 프로세싱 환경에서 최적화된 프로그래밍을 할 수 있도록 애플이 지원하는 저수준 API.

기본적으로 스레드 풀의 관리를 프로그래머가 아닌 운영체제에서 관리하기 때문에 프로그래머가 태스크(작업)을 비동기적으로 쉽게 사용할 수 있다. 프로그래머가 실행할 태스크(작업)을 생성하고 Dispatch Queue에 추가하면 GCD 는 태스크(작업)에 맞는 스레드를 자동으로 생성해서 실행하고 작업이 종료되면 해당 스레드를 제거한다.

Dispatch Queue (디스패치 대기열)

  • GCD 기술의 일부
  • 작업을 연속적 혹은 동시에 진행하기는 하지만, 언제나 먼저 들어오면 먼저 나가는 순서로 실행된다.

Serial Dispatch Queue: 한 번에 하나의 작업만을 실행하며, 해당 작업이 대기열에서 제외되고 새로운 작업이 시작되기 전까지 기다린다.
Concurrent Dispatch Queue: 이미 시작된 작업이 완료될 때까지 기다리지 않고 가능한 많은 작업을 진행한다.

② NSOperation

: 어떤 하나의 작업을 나타낸다. NSOperation은 모델링 상태, 우선순위, 의존성 그리고 관리를 지원하는 유용하고 Thread safe한 추상 클래스이다.

예를 들어 네트워크 요청, 이미지 리사이즈, 텍스트 처리, 또는 기타 다양한 반복처리 등 오래 걸리는 작업을 처리해야하는 NSOperation 이 있다고 할 때, 이 특정 작업이 담겨있는 객체는 감독 없이 많은 일을 할 수 없다. 이러한 작업을 진행하는 감독을 NSOperationQueue 가 담당한다.

📌 RESTful API

API란?

Application Programming Interface(API)는 다른 소프트웨어 시스템과 통신하기 위해 따라야 하는 규칙을 정의한다.

웹 API는 클라이언트와 웹 리소스 사이의 게이트웨이라고 생각할 수 있다.

REST(Representational State Transfer) : API 작동 방식에 대한 조건을 부과하는 소프트웨어 아키텍처.
RESTful API : 두 컴퓨터 시스템이 인터넷을 통해 정보를 안전하게 교환하기 위해 사용하는 인터페이스
RESTful 웹 서비스 : REST 아키텍처를 구현하는 웹 서비스

🌱 REST의 장점

  • 대규모의 고성능 통신을 안정적으로 지원할 수 있다.
  • 쉽게 구현하고 수정할 수 있어 모든 API 시스템을 파악하고 여러 플랫폼에서 사용할 수 있다.

🌱 RESTful API의 장점

  • 확장성: REST가 클라이언트-서버 상호 작용을 최적화하기 때문에 효율적으로 크기 조정할 수 있다
  • 유연성: 완전한 클라이언트-서버 분리를 지원
  • 독립성: REST API는 사용되는 기술과 독립적이다. API 설계에 영향을 주지 않고 다양한 프로그래밍 언어로 클라이언트 및 서버 애플리케이션을 모두 작성할 수 있다. 또한 통신에 영향을 주지 않고 양쪽의 기본 기술을 변경할 수 있다.

🌱 RESTful API 작동 방식

RESTful API의 기본 기능은 인터넷 브라우징과 동일하다!

  • 클라이언트는 리소스가 필요할 때 API를 사용하여 서버에 접속한다.
  • API 개발자는 서버 애플리케이션 API 문서에서 클라이언트가 REST API를 어떻게 사용해야 하는지 설명한다.

모든 REST API 호출에 대한 일반 단계

  1. 클라이언트가 서버에 요청을 전송한다. 클라이언트가 API 문서에 따라 서버가 이해하는 방식으로 요청 형식을 지정한다.
  2. 서버가 클라이언트를 인증하고 해당 요청을 수행할 수 있는 권한이 클라이언트에 있는지 확인한다.
  3. 서버가 요청을 수신하고 내부적으로 처리한다.
  4. 서버가 클라이언트에 응답을 반환한다. 응답에는 요청이 성공했는지 여부를 클라이언트에 알려주는 정보가 포함된다. 응답에는 클라이언트가 요청한 모든 정보도 포함된다.

🌱 RESTful API 클라이언트 요청 구성요소

  • 고유 리소스 식별자: 일반적으로 URL 사용
  • 메서드: HTTP 메서드(GET, POST, PUT, DELETE) 종종 사용
  • HTTP 헤더: 클라이언트와 서버 간에 교환되는 메타데이터 (ex: 요청 및 응답의 형식을 나타내고 요청 상태 등에 대한 정보를 제공)
  • 데이터
  • 파라미터: URL 세부정보를 지정하는 경로 파라미터, 리소스에 대한 추가 정보를 요청하는 쿼리 파라미터, 클라이언트를 빠르게 인증하는 쿠키 파라미터

📌 PHP

  • 웹 페이지 프로그래밍 언어에는 JavaScriptPHP 등이 있다. (JavaScript클라이언트 측 스크립트이고, PHP서버 측 스크립트)
  • PHP 프로그램은 내려 받기 전에 웹 서버에서 실행된다.
  • 웹브라우저에 내려받은 코드는 PHP가 포함되지않은 HTML코드이다.

🌱 PHP 장점

  • 프로그램 코드가 보여지지 않기에 자바스크립트에 비해 보안성이 뛰어나다.
  • 웹 브라우저의 버전과 종류에 영향을 받지 않는다.

🌱 PHP 단점

  • 실시간으로 처리하는 경우 적합하지 않다.

📌 SQL vs NoSQL

🟢 SQL 장점

  • 명확하게 정의된 스키마, 데이터 무결성 보장
  • 관계는 각 데이터를 중복없이 한번만 저장

🔴 SQL 단점

  • 덜 유연함. 데이터 스키마를 사전에 계획하고 알려야 함. (나중에 수정하기 힘듬)
  • 관계를 맺고 있어서 조인문이 많은 복잡한 쿼리가 만들어질 수 있음
  • 대체로 수직적 확장만 가능함

🟢 NoSQL 장점

  • 스키마가 없어서 유연함. 언제든지 저장된 데이터를 조정하고 새로운 필드 추가 가능
  • 데이터는 애플리케이션이 필요로 하는 형식으로 저장됨. 데이터 읽어오는 속도 빨라짐
  • 수직 및 수평 확장이 가능해서 애플리케이션이 발생시키는 모든 읽기/쓰기 요청 처리 가능

🔴 NoSQL 단점

  • 유연성으로 인해 데이터 구조 결정을 미루게 될 수 있음
  • 데이터 중복을 계속 업데이트 해야 함
  • 데이터가 여러 컬렉션에 중복되어 있기 때문에 수정 시 모든 컬렉션에서 수행해야 함 (SQL에서는 중복 데이터가 없으므로 한번만 수행이 가능)

🌱 SQL 데이터베이스 사용이 더 좋을 때

  • 관계를 맺고 있는 데이터가 자주 변경되는 애플리케이션의 경우
    (NoSQL에서는 여러 컬렉션을 모두 수정해야 하기 때문에 비효율적)
  • 변경될 여지가 없고, 명확한 스키마가 사용자와 데이터에게 중요한 경우

🌱 NoSQL 데이터베이스 사용이 더 좋을 때

  • 정확한 데이터 구조를 알 수 없거나 변경/확장 될 수 있는 경우
  • 읽기를 자주 하지만, 데이터 변경은 자주 없는 경우
  • 데이터베이스를 수평으로 확장해야 하는 경우 (막대한 양의 데이터를 다뤄야 하는 경우)
profile
👩🏻‍💻 iOS Developer

0개의 댓글