[TIL]09.02

rbw·2022년 9월 2일
0

TIL

목록 보기
40/97
post-thumbnail

프로퍼티 래퍼에 관하여

간단히 getter, setter를 가진 프로퍼티를 쉽게 선언할 수 있도록 함 정확한 정의는, 값을 저장하는 코드와 정의하는 코드를 분리 시킬 수 있도록 하는 레이어를 추가 하는 것.

thread-safe 한지, db에 저장해야 하는지 등 작업의 반복을 줄여준다.

이러한 Property Wrapper를 사용하려면, Struct, Class, Enum을 통헤 wrappedValue로 정의 해줘야 한다.

좀 더 사용성을 향상 시킬 수 있게, init도 사용 가능. 프로퍼티 래퍼 내부에 작성 후 만드려는 프로퍼티에 값을 할당하면, 그 조건에 맞는 이니셜라이저가 호출 ! 또는 @smallNumber(para1: 4, para2: 10) var height: Int 로 작성 가능

Swift에서 프로퍼티 래퍼의 할당은 wrappedValue에 인자를 넘기는 것과 동일하게 생각한다.

Projected Value

이는 $와 같이 사용. 이 값은 사전에 젖의된 로직을 제외하고 따로 조작이 불가하다.

UserDefault와 같이 쓰면 유용하다.

Key, defaultValue(제네릭) 을 정의하여 적절히 get,set을 구현하면 간단하게 사용이 가능하다.

enum GlobalSettings {
    @UserDefault(key: "KeyKey", defaultVlue: false)
    static var someThing: Bool or 값 할당 
}

SRP(단일 책임 원칙)

하나의 함수는 하나의 역할을 해야한다. 이 법칙을 지키려면 함수를 쪼개야 하는데 이 쪼개는 범위가 모호한 경우가 많다.

여기서 Tip으로는, "함수를 조립하기 위한 매개 함수로 쓰일 수 있는 것들을 쪼개 주는 것이 좋다"

  • .filter()에 들어갈 조건 판별 함수
  • .forEach()에 들어갈 동작을 하는 side-effect 함수
  • .map()에 들어갈 project 함수
  • .sort()에 들어갈 정렬 함수

이 쪼개는 기준이 좀 어렵기는 한 거 같다. 하지만 가독성을 생각하면서 쪼갠다면 좀 더 쉬워지지 않을까 싶다. (그리고 응집도 !)

판단할 기준으로 체크할만한 항목들로

  1. 재사용이나 변경의 여지가 있는가 ?
  2. 함수명이 표현식보다 훨씬 더 가독성이 있는가?
  3. 반대로 함수표현으로 인해 전체 파이프라인을 왔다 갔다 하게되어 이해하는데에 방해가 되는가 ?
  4. 하나의 파이프 라인에 속한 함수들이 각각의 모듈로 쪼개져 있어 응집도가 떨어지는가?

다음 팁으로는 순수 함수로 작성해 보는 것이다.

순수함수와 부수효과를 분리하는 구조가 되어야 한다.

순수함수란 ?

1개의 반환값이 반드시 존재. 같은 인자를 넣었을때에는 항상 같은 값을 반환. 함수 외부의 어떠한 값을 변화 시켜서는 안된다.

OCP(개방 폐쇄 원칙)

"소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀있어야 한다."

예를 들어 트럭이라는 운송수단과 뒤에 달리는 기구를 분리/결합 할 수 있는 구조를 만들어두면, 새로운 목적이 필요한 도구를 만들때 전체를 만들지 않아도 된다.

이것의 의미는, 새로운 기능의 추가가 일어날 때에는, 기존코드의 수정 없이 추가가 되어야 하고, 내부 매커니즘이 변경되어야 할때는, 외부 코드 변화가 없어야 한다를 의미한다.

함수형 프로그래밍에서 OCP를 잘 느낄수 있는 부분은 Higher order Function(or Method)에서 두드러진다.

하나의 함수의 기능이 여러가지 옵션들로 인해 내부에서 분기가 많이 발생하고 있다면 OCP, SRP의 원칙에 맞게 함수를 매개변수로 받는 방법을 통해 공통 매커니즘의 코드와 새로운 기능에 대한 코드를 분리하여 다룰 수 있게 할 수 있다.

함수형 개발 설계에서 매우 중요하다. 만약 새로운 기능을 추가시 기존의 함수를 수정하면서 개발 하는 것은, OCP를 위반한 코드를 작성하고 있을 확률이 매우 높다.

제 생각에는 Swift에서는 프로토콜을 적극 활용하면 OCP를 지키는 코드를 작성할 수 있지 않을까라고 생각합니다.

LSP(리스코프 치환 원칙)

"프로그램의 객체는 프로그램의 정확성을 깨트리지 않으면서 하위타입의 인스턴스로 바뀌어야 한다"

간단히 말해서 상속한 상위 타입과 상속을 받은 하위타입을 치환해도 프로그램에는 문제가 없어야 한다는 의미이다.

상속을 받아 만든 하위타입의 제약조건들이, 상위 타입에서 먼저 선언한 조건들과 충돌이 날 경우 유지보수가 힘들어진다는 문제가 있기 때문에 만들어진 법칙이다.

하지만 이를 지키기는 어려운 경우가 많다.. 경우에 따라서 위배 하는 편이 더 좋은 방법일지도 모른다.

계층간에 is-a 관계(새-펭귄, 직사각형-정사각형)를 만족하더라도, 하위타입에서 가변성을 가지면서, 상위타입에서 정의한 조건과 일치하지 않는다면 상속을 받지 말아야 한다.

ISP(인터페이스 분리 원칙)

"사용자가 필요하지 않은 것들에 의존하게 되지 않도록 인터페이스를 작게 유지해라."

이 부분은 함수형 프로그래밍에서는 위반되기 쉽지 않다. 인터페이스당 함수가 1대1의 관계이기 떄문이다. Swift에서는 Protocol도 작게 유지하라는 의미로 들리기는 한다.(아닐지도 !)

DIP(의존관계 역전 원칙)

"프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다."

예를 들어 전기 기구를 사용하기 위해, 플로그를 콘센트에 꽂는 방법만 알면 되지, 전기의 배선을 붙이거나 하지는 않는다.

따라서, 추상화된 방법만 전달을 하고 있다면 구체적인 것은 사용자는 관여하지 않아도 된다.

추상화 하는 방향으로 의존하라. 상위 레벨 모듈이 하위 레벨 세우사항에 의존하면 x

잘 만들어진 구조로 컴포넌트에서 서버의 데이터를 조작하기 위해서는 적절한 수준의 추상화와 레이어가 존재하게 된다.

JS에서의 예를 설명하자면, axios 레이어를 통해 서버와의 데이터를 주고 받는다. custom hook을 통해서 컴포넌트에서는 서버에 직접호출하는 구체화보다, 그저 필요한 데이터를 요청하는 형식의 추상화된 레이어 계층인 hook을 사용할 수 있다.

이렇게 추상화된 레이어를 두는 이유는, 컴포넌트 입장에서는 데이터가 필요하지, 그게 반드시 서버의 데이터일 필요는 없기 때문이다.

이러한 레이어를 통해서, 언제든 서버가 아니라 로컬의 mock 데이터나 다른 방식으로도 사용하는 쪽의 코드를 변화 없이 변경할 수 있게 된다.


참조

https://velog.io/@teo/Javascript%EC%97%90%EC%84%9C%EB%8F%84-SOLID-%EC%9B%90%EC%B9%99%EC%9D%B4-%ED%86%B5%ED%95%A0%EA%B9%8C

https://docs.swift.org/swift-book/LanguageGuide/Properties.html

profile
hi there 👋

0개의 댓글