Skip to main content

· 3 min read

Interface Segregation Principle

  • ISP(Interface Segregation Principle) - 인터페이스 분리 원칙
    • 인터페이스를 일반화하여 구현하지 않는 인터페이스를 채택하는 것보다 구체적인 인터페이스를 채택하는 것이 더 좋다는 원칙
    • 인터페이스를 설계할 때, 굳이 사용하지 않는 인터페이스는 채택하여 구현하지 말고 오히려 한 가지의 기능만을 가지더라도 정말 사용하는 기능만을 가지는 인터페이스로 분리하라는 원칙

BadCase

protocol Vehicle {
var wheels: Int { get set }

func moveWithWheel()

func stir()
}

class Tesla: Vehicle {
var wheels: Int = 4

func moveWithWheel() {
print("Move forward with \(wheels) wheels")
}

func stir() {
print("Can't stir")
}
}

class K1: Vehicle {
var wheels: Int = 0

func moveWithWheel() {
print("Move forward with \(wheels) wheels")
}

func stir() {
print("Can stir")
}
}
  • 포괄적인 인터페이스를 만드는 것은 좋지 않다. 기능별로 분리하여 한 가지 역할만 할 수 있도록 처리하는 것이 바람직하다.
  • 여러가지 일을하는 protocol은 분리하여 하나의 기능만 가능하도록 처리

NiceCase

protocol Boat {
func stir()
}

protocol Car {
var wheels: Int { get set }

func move()
}

class Benz: Car {
var wheels: Int = 4

func move() {
print("Move forward with \(wheels) wheels")
}
}

class DuckBoat: Boat {
func stir() {
print("Stiring with hands")
}
}
  • move, stir 등 여러 작업을 하고 있던 포괄적인 Vehicle protocol을 Boat, Car로 분리하여 각각의 역할만 수행하도록 변경
  • 해당 protocol을 Implement 하는 class는 해당하는 역할만 구현하면 된다

참조

· 2 min read

JSX와 Babel, JSX 다루기

JXS

const element = <h1>Hello, World!</h1>

문자도 HTML도 아닌 Javascript 확장 문법

  • JSX를 사용하기 위해서는 Babel이 필요하다

Babel

JavaScript Complier 컴파일러: 언어 해석기, 특정 언어를 다른 프로그래밍 언어로 옮기는 프로그램 정확히 Babel은 Javascript 로 결과물을 만들어주는 컴파일러이다 소스 대 소스 컴파일러(Transpiler)라고 불린다 새로운 ESNext 문법을 기존의 브라우저에서 사용하기 위해서는 babel이 필수이다

  • JSX 문법을 기존 html에 작성하여 브라우저로 읽어도 원하는 동작을 하지 않는데, 이유는 바벨이 없어서 문법을 브라우저가 이해하지 못하기 때문이다

UNPKG

fast, global content delivery network for everthing on npm

React 코드를 JSX로 대체하기

<script type="text/babel">
const rootElement = document.getElementById('root')
const element = <h1 className="title">Hello World!</h1>
ReactDOM.render(element, rootElement)
</script>
  • 바벨을 적용하여 브라우저에서 JSX를 사용 가능하도록 적용

Spread 연산자

const props = {className: titleClassName, children: text}
const element = <h1 {...props} />

props로 생성된 객체를 h1 태그 안에 각각의 요로소 key value 형태로 넣어주는 연산자

const element = <h1 className={props.className} children={props.children} />

위와 같이 표현할 수도 있다

정리

JXS 는 React.createElement 표현식 Babel은 Javascript compiler JSX다루기 spread 연산자

· 3 min read

LSP(Liskov Substitution Principle) - 리스코프 치환 원칙

  • 부모(super class)로 동작하는 곳에서 자식(sub class)를 넣어주어도 대체가 가능해야 한다는 원칙
  • 자식 클래스를 구현할 때, 기본적으로 부모 클래스의 기능이나 능력들을 물려받는데 여기서 자식 클래스가 동작할 때, 부모 클래스의 기능들을 제한하면 안된다는 뜻
  • 상속을 할 때 완전한 상속이 아닌 경우는 Interface로 구현하는 것이 맞다(Refactoring 책에서도 나옴)
    • 자바의 stack이 부모의 기능을 모두 사용하지도 않으면서 array 상속받아 사용
  • 부모 클래스의 타입에 자식 클래스의 인스턴스를 넣어도 똑같이 동작하여야 한다

BadCase

class RectangleBad {
var width: Float = 0
var height: Float = 0

var area: Float {
return width * height
}
}

class SquareBad: RectangleBad {
override var width: Float {
didSet {
height = width
}
}
}

func printArea(of rectangle: RectangleBad) {
rectangle.height = 3
rectangle.width = 6
print(rectangle.area)
}

let rectangle = RectangleBad()
printArea(of: rectangle)

let square = SquareBad()
printArea(of: square)
  • Rectangle(부모) 클래스를 상속받은 Square(자식) 클래스에서 부모의 모든 자원을 활용하지 못하는 형태라 LSP에 어긋나게 된다.
  • 부모의 역할을 자신이 온전하게 대체하지 못함

NiceCase

protocol Shape  {
var area: Float { get }
}

class RectangleNice: Shape {
private let width: Float
private let height: Float

var area: Float {
return width * height
}

init(width: Float, height: Float) {
self.width = width
self.height = height
}
}

class SquareNice: Shape {
private let length: Float

var area: Float {
return self.length * self.length
}

init(length: Float) {
self.length = length
}
}

let rectangleNice = RectangleNice(width: 3, height: 6)
let squareNice = SquareNice(length: 6)
  • Protocol을 사용하여 area 함수를 구현하게 만들어 Rectangle, Square 각각이 area 함수를 구현한다
  • 구현부는 채택하는 하위 클래스로 넘기면 LSP의 원칙에 어긋나지 않는다

참조

· 2 min read

OCP(Open Closed Principle)

  1. OCP(Open Closed Principle) - 개방, 폐쇄 원칙
  • 확장에는 열려있으나 변경에는 닫혀있어야 한다는 원칙

  • 어떤 기능을 추가할 때, 기존의 코드는 만지지 않고 새로 동작하는 기능에 대해서만 코드가 작성 되어야한다

  • protocol을 활용하면 된다

  • 예제 코드

// OCP BadCase
class StudentBad {
func printName() {
print("Student")
}
}

class TeacherBad {
func printName() {
print("Teacher")
}
}

class ClassRoomBad {
private var students: [StudentBad] = [StudentBad(), StudentBad(), StudentBad()]
private var teachers: [TeacherBad] = [TeacherBad(), TeacherBad(), TeacherBad()]

func printAllName() {
students.forEach {
$0.printName()
}

teachers.forEach {
$0.printName()
}
}
}
  • 새로운 직업 이 추가될 경우 새로운 직업에 대한 class를 정의하고 ClassRoom의 코드에도 수정이 필요하기 때문에 OCP 원칙에 어긋나게 된다
// OCP NiceCase
protocol Person {
func printName()
}

class StudentNice: Person {
func printName() {
print("Student")
}
}

class TeacherNice: Person {
func printName() {
print("Teacher")
}
}

class ClassRoomNice {
private var persons: [Person] = [ StudentNice(), TeacherNice(), StudentNice(), TeacherNice()]

func printAllNames() {
persons.forEach {
$0.printName()
}
}
}
  • protocol을 사용하여 공통 타입를 만들어 Person으로 접근하면 새로운 직업이 추가 되어도 해당 직업의 대한 class만 구현해주면 된다

    참조

  • https://dongminyoon.tistory.com/49

· 3 min read

SOLID란

  • 객체지향 설계에 더 좋은 아키텍쳐를 설계하기 위해 지켜야하는 원칙들의 5가지를 앞의 약어만 따서 정리한 단어
  • 디자인 패턴 중, VIPER, MVVM들도 모두 이런 원칙에 입각하여 만들어진 패턴이다
  1. SRP(Single Responsibility Principle) - 단일 책임 원칙
  • 클래스나 함수를 설계할 때, 각 단위들은 단 하나의 책임만을 가져야한다는 원칙

  • 클래스나 함수가 새롭게 변해야 한다면 하나의 역할을 가진 상태에서 새로운 것으로 변해야한다는 것(즉, 하나의 일만하고 변해도 하나의 일만 해야 한다)

  • 예제 코드

// SRP( Single Responsibility Priciple) - 단일 책임 원칙
// API, Database, Decoding (Bad Case)
class LoginServiceBad {
func login(id: String, pw: String) {
let userData = requestlogin()
let user = decodeUserInfom(data: userData)
saveUserOnDatabase(user: user)
}

private func requestlogin() -> Data {
return Data()
}

private func decodeUserInfom(data: Data) -> User {
return User(name: "testUser", age: 10)
}

private func saveUserOnDatabase(user: User) {
print("User data \(user.name) are saved!!!")
}

}

class User {
var name: String
var age: Int

init(name: String, age: Int) {
self.name = name
self.age = age
}

private func toString() {
print("My Name is: \(self.name), and \(self.age) years old")
}
}
  • LoginService 클래스가 API request, Data decoding, Database control 3가지의 기능을 모두 구현하고 있어 SRP 원칙에 어긋나게 된다
// use Protocols to

protocol APIHanlderProtocol {
func requestLogin() -> Data
}

protocol DecodingHandlerProtocol {
func decode<T>(from data: Data) -> T
}

protocol DatabaseHandlerProtocol {
func saveOnDatabase<T>(inform: T)
}

class LoginServiceNice {
let apiHandler: APIHanlderProtocol
let decodingHandler: DecodingHandlerProtocol
let databaseHandler: DatabaseHandlerProtocol

init(
apiHandler: APIHanlderProtocol,
decodingHandler: DecodingHandlerProtocol,
databaseHandler: DatabaseHandlerProtocol
) {
self.apiHandler = apiHandler
self.decodingHandler = decodingHandler
self.databaseHandler = databaseHandler
}

func login() {
let loginData = apiHandler.requestLogin()
let user: User = decodingHandler.decode(from: loginData)
databaseHandler.saveOnDatabase(inform: user)
}
}

class RequestManager: APIHanlderProtocol {
func requestLogin() -> Data {
return Data()
}
}

class DecodingManager: DecodingHandlerProtocol {
func decode<T>(from data: Data) -> T {
return User(name: "testUserName", age: 20) as! T
}
}

class DatabaseManager: DatabaseHandlerProtocol {
func saveOnDatabase<T>(inform: T) {
print("Database completed Save process")
}
}


let loginServiceNice = LoginServiceNice(
apiHandler: RequestManager(),
decodingHandler: DecodingManager(),
databaseHandler: DatabaseManager()
)

loginServiceNice.login()

  • LoginSerice 클래스는 정말 로그인 관련된 내용만 구현하게 하고 API, Decoding, Database 는 각각의 핸들러를 만들어 외부에서 주입 받아 SRP 원칙을 따르도록 변경하여 처리한다

참조

· 4 min read

UIWindow

공식 문서

  1. UIWindow The backdrop for your app's user interface and object that dispatches events to your views

  2. Declaration

@MainActor class UIWindow: UIView
  1. Overview Windows work with your view controllers to handler events and to perform many other tasks that are funtdamental to your app's operation. UIKit handles most window-related interactions, working with other objects as needed to implement many app behaviors

You use windows only when you need to do the following:

  • Provide a main window to display your app's content
  • Create additional windows (as needed) to display additional content

Storyboards require the presence of a window property on the app delegate object, which the Xcode templates automatically provide. If your app doesn't use storyboards, you must create the window yourself.

  • This is why we make a connection between window and ViewController on the SceneDelegate

You also use UIWindow objects for a handful of other tasks:

  • Setting the z-axis level of your window, which affects the visibility of the window relative to other windows
  • Showing windows and making them the target of keyboard events
  • Converting coordinate values to and from the window's coordinate system
  • Changing the root view controller of a window
  • Changing the screen on which the window is displayed

Windows don't have any visual appearance of their own. instead, a window hosts one or more views, which are managed by the window's root view controller.

한글 자료

  1. 요약 윈도우는 뷰들을 담는 컨테이너이며, 이벤트를 전달해주는 매개체이다. iOS 앱은 콘텐츠를 화면에 보여주기 위해 최소 1개 이상의 윈도우를 가지고 있다. 시스템 윈도우에서 생성된 이벤트들은 Key Window로 전달된다.

  2. 내용 UIWindow도 UIView를 상속하고 있다.

open class UIWIndow: UIView

UIView는 UIResponder를 상속하고 있다.

open class UIView : UIResponder

이벤트를 처리하는 리스폰더 체인(Responder Chain)에는 UIWindow가 항상 포함된다. 이 부분에서 '뷰에 이벤트를 전달하는'이라는 역할이 수행되는 것이다.

@MainActor라는 어트리뷰트는 Swift 5.5부터 도입되는 개념으로, 메인 쓰레드에서 실행되는 클래스임을 가르킨다. UIWindow는 뷰들 가장 뒤에서 배경막 역할을 하고 있다.

정리

  1. UIWindow는 이벤트 처리 및 User Interface를 담당하는 배경막 역할을 하는 객체이다
  2. UIWindow는 UIView를 상속하고 있지만, 직접 뷰를 보여주는 역할을 하지는 않는다. 대신 Root ViewController를 설정하여 컨텐츠를 보여준다
  3. iOS앱은 최소한 1개 이상의 윈도우를 가지고 있어야 한다
  4. Xcode에서 storyboard를 사용하면 Xcode가 자동으로 window를 연결시켜준다
  5. iOS 13 미만에서는 AppDelegate에서 window 연결을 하고, iOS 13부터는 SceneDelegate에서 window연걸을 한다

참조

· 4 min read

React

  • 사용자 인터페이스를 만들기 위한 JavaScript 라이브러리
  • Virtual DOM / JSX / Flux / Functional Programming

Dom 다루기 Element 생성하기

Dom

  1. 웹 페이지는 어떻게 만들어 질까?
  • DOM(Document Object Model)은 웹 페이지에 대한 인터페이스
  • 웹 브라우저가 원본 HTML 문서를 읽어들인 후, 스타일을 입히고 대화형 페이지로 만들어 뷰 포트에 표시하기까지의 과정을 "Critical Rendering Path"라고 한다
  • 이 과정 여러 단계로 나누어져 있지만, 이 단계들을 대략 두 단계로 나눌 수 있다
  • 첫 번째 단계에서 브라우저가 읽어들인 문서를 파싱하여 최종적으로 어떤 내용을 페이지에 렌더링할지 결정
  • 두 번째 단계에서 브라우저가는 해당 렌더링을 수행
  • 첫 번쨰 과정을 걸치면 "렌더 트리"가 생성된다
  • 렌더 트리는 웹 페이지에 표시될 HTML 요소들과 이와 관련된 스타일 요소들로 구성된다
  • 브라우저는 렌더 트리를 생성하기 위해 다음과 같이 두 모델이 필요하다
    • DOM(Document Object Model) - HTML 요소들의 구조화된 표현
    • CSSOM(Cascading Style Sheets Object Model) - 요소들과 연관된 스타일 정보의 구조화된 표현

Vanilla JS

  • 순수 자바스크립트
  • 특정 라이브러리나 프레임워크를 사용하지 않은 그 자체의 자바스크립트

CodeSandBox

  • 프로트엔드 코드를 작성하고 이것저것 시도해볼 수 있는 모래상자(놀이도구)
  • React 등 다양한 환경에 대한 기본 설정이 되어있음
  • https://codesandbox.io/

CDN

  • Content Delivery Network
  • 웹에서 사용되는 다양한 컨텐츠(리소스)를 저장하여 제공하는 시스템

Element 생성하기

  1. vanilla JS
const rootElement = document.getElementById("root")
const element = document.createElement('h1')
element.textContent = "Hello, World!"
rootElement.appendChild(element)
  1. React
const rootElement = document.getElementById("root")
const element = React.createElement('h1', { children: "Hello World!"})
ReactDOM.render(element, rootElement);

참조

  • 패스트 캠퍼스 온라인 강좌(한 번에 끝내는 React의 모든 것 초격자 패키지 Online)
  • Dom은 정확히 무엇일까? https://wit.nts-corp.com/2019/02/14/5522
  • MDN
    • Mozilla Develop Network
    • 웹 표준과 모질라 프로젝트에 대한 개발 문서들이 담긴 모질라의 공식 웹 사이트

· One min read

Apple Watch App, iOS App Connectivity

3 ways for communication between iOS Apps and WatchApps

  1. updateApplicationContext(_:)
  2. transferUserInfo(_:)
  3. sendMessage(_:replyHandler:errorHandler:)

프로젝트 구성

  1. iOS bundle name을 WatchApp Info.plist WKCompanionAppBundleIdentifier key로 등록한다
  2. WatchApp Extension Info.plist에 NSExtension > NSExtensionAttributes > WKAppBundleIdentifier에 key 값에 WatchApp Bundle ID를 등록한다

· 2 min read

동기, 비동기 정리

  1. 동기(sync) vs 비동기(async)
  • 동기: 다른 쓰레드로 작업을 보내고, 그 작업이 끝날 떄까지 기다렸다가, 작업이 끝나면 그 결과를 가지고 다른 작업을 시작하는 형태
  • 비동기: 다른 쓰레드로 작업을 보내고, 그 작업의 결과를 기다리지 않고, 바로 다른 작업을 시작하는 형태
  • 중요 키워드: 결과값을 기다리는 것(Sync), 결과값을 기다리지 않고 다른 작업을 수행(Async)
  1. 직렬(Serial) vs 동시(Concurrent)
  • 직렬: Main Thread에서 다른 쓰레드로 보내 일을 처리하지만, 다른 쓰레드 한 곳에서 모든 일을 serial하게 처리 하는 방식
  • 동시: Main Thread에서 다른 쓰레드로 일들을 하나 하나 보내어 여러 쓰레드에서 동시에 일을 처리하는 방식
  • 중요 키워드: 한 Thread에서 처리, 여러 Thread에서 동시 처리
  • 왜 직렬(Serial) 처리가 필요한가: 특정 일들은 순서에 맞춰어 Serial하게 처리가 되어야 한다.

· 2 min read

FSCalendar 사용법

  1. FSCalendar 란
  1. 설치
  • pod 'FSCalendar'
  1. FSCalendar 사용법
  • 생성

    • private var viewFSCalendar: FSCalendar = { let calendar = FSCalendar() calendar.translatesAutoresizingMaskIntoConstraints = false calendar.appearance.titleDefaultColor = .blue // set weekday color calendar.appearance.titleWeekendColor = .red // set weekend color calendar.appearance.headerTitleColor = .purple // set title color calendar.appearance.weekdayTextColor = .orange // set weekdayText(like 월,화,수,목,금,토,일) calendar.appearance.headerDateFormat = "YYYY년 M월" // set title DateFormat calendar.locale = Locale(identifier: "ko_KR") // set weekdayText language calendar.appearance.headerMinimumDissolvedAlpha = 0 // set prev, next text alpha calendar.scope = .month // Calendar 디스플레이 방법 디폴트 Month return calendar }()
  • 제스처 및 델리게이트 설정

    • let upGesture: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(testUpGestureAction(sender:)))
    • upGesture.direction = .up
    • let downGesture: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(testUpGestureAction(sender:)))
    • downGesture.direction = .down
    • viewFSCalendar.addGestureRecognizer(upGesture)
    • viewFSCalendar.addGestureRecognizer(downGesture)
    • viewFSCalendar.delegate = self
    • viewFSCalendar.dataSource = self
    • delegate, dataSource
  • 이벤트 처리

    • let formatter = DateFormatter()
    • formatter.locale = Locale(identifier: "ko_KR")
    • formatter.dateFormat = "yyyy-MM-dd"
    • let xmas = formatter.date(from: "2021-12-25")
    • let myBirthday = formatter.date(from: "2021-11-22")
    • events = [xmas!, myBirthday!] // add events (Date Type) on events [Date] NSArray
    • delegate, dataSource
      • extension ViewController: FSCalendarDelegate, FSCalendarDataSource { func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { // can write did select event on this func print("didSelected") let dateFormatter = DateFormatter() dateFormatter.dateFormat = "YYYY년 M월 d일" let currentDate = dateFormatter.string(from: date) print(currentDate) } func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int { if self.events.contains(date) { // draw points on event day return 1 } else { return 0 } } func calendar(_ calendar: FSCalendar, boundingRectWillChange bounds: CGRect, animated: Bool) { print("test") } func calendarCurrentPageDidChange(_ calendar: FSCalendar) { print("changed") }

}