수행 내용

  • 쥬스 메이커 프로젝트 코드 리팩터링
    • class, struct 사용 시 이유 설명할 수 있게끔 준비
    • 실험적 무조건 옵셔널 언래핑을 옵셔널 바인딩으로 처리
    • 열거형 rawValue에 접근하지 않고도 코드 가독성을 높일 수 있게끔 간접 호출용 프로퍼티 작성
    • 접근제어자 추가
    • 메서드 에러처리
    • Mark 주석 추가
    • 콜론 규칙 단일화
    • 가독성 향상을 위한 메서드, 프로퍼티 네이밍 변경 작업

학습 내용

class, struct 개인적 사용 기준 정해보기

class와 struct의 인스턴스는 각각 값타입과 참조타입으로 메모리 관리 양상에서 다른 모습을 보인다. 업계에서는 '사용 리소스가 크게 차이나지 않으므로 앱개발자가 신경쓸 정도이지는 않다'라고 이야기하고 있지만, 개인적인 기준은 정해두면 좋겠다 판단했다.

일단, Swift Programming Language Guide는 아래와 같이 안내하고 있다.

  • 다음 조건 중 하나 이상에 해당한다면 구조체를 사용하는 것을 권장합니다.
    1. 연관된 간단한 값의 집합을 캡슐화하는 것만이 목적일 때
    2. 캡슐화한 값을 참조하는 것보다 복사하는 것이 합당할 때
    3. 구조체에 저장된 프로퍼티가 값 타입이며 참조하는 것보다 복사하는 것이 합당할 때
    4. 다른 타입으로부터 상속받거나 자신을 상속할 필요가 없을 때

프로젝트 성격, 데이터 활용도에 따라 조금씩 달라질 수 있겠으나 큰 기준을 위의 조건으로 생각하면 될 것 같다. 예를 들어, 쥬스 메이커 프로젝트의 Stock 타입의 경우 과일의 재고 관리와 이를 위한 메서드의 모음이 있고 상속 받거나 상속할 소요가 없다고 판단되므로 구조체 타입이 적합할 것으로 판단된다. 새로운 메서드가 필요해도 익스텐션으로 대응이 가능하므로 전혀 문제가 없을 것이다. 하지만 쥬스메이커 타입의 경우 제조 방식(메서드)과 같은 작동 방식이 다른 유사한 자판기를 만들 소요가 있을 것으로 판단되므로 클래스로 구현하는 것이 적합해보인다.

Note: 똑똑한 스위프트의 복사 처리

스위프트의 기본 데이터 타입이 모두 구조체라서 다수의 배열 또는 딕셔너리 등의 데이터를 복사하고 이용할 때 메모리를 비효율적으로 사용한다고 오해할 수 있습니다. 그렇지만 스위프트는 꼭 필요한 경우에만 '진짜 복사'를 합니다. 컴파일러가 판단해서 꼭 복사를 할 필요가 없을 경우, 요소를 많이 갖는 큰 배열을 함수의 전달인자로 넘겨준다고 해서 꼭 모든 값을 메모리의 다른 공간에 복사해 넣지 않을 수도 있다는 뜻입니다. 스위프트가 적절히 알아서 효율적으로 처리해줄 것입니다.
-"야곰 스위프트 프로그래밍 3판"

문제점 / 고민한 점

코드 리팩터링

누가 봐도 이해되는 코드를 작성하고 싶다. 내가 작성한 코드지만 하루 아침에 프로젝트를 떠난다고 하더라도 인수인계가 필요없는 그런 코드. 정답은 스스로 찾아가겠지만 현재로써는 코드, 메서드 이름, 프로퍼티 이름이 길어지더라도 글을 읽듯이 작성하고 싶다.

JSON 형식을 활용한 쥬스 레시피 관리

모둠원 강경이 JSON 형식으로 레시피를 가져오는 방식을 고민해주어 관련 내용을 많이 학습하였다. 이 내용은 별도로 포스팅하여 정리해봐도 좋을 것 같다.

해결 방법

  • 아직 미숙한 점이 많지만 읽기 좋은 코드를 작성할 수 있게끔 많이 노력하고 있다. 아래는 오늘 리팩터링 작업한 내용의 일부이다. 실제 어떤 방식으로 기능을 구현했는지를 개별 메서드로 은닉하고 캡슐화하여 일련의 과정들이 단순한 메서드들의 호출로 이루어질 수 있게끔 만드는데 노력을 기울였다. 지금 보니 if문을 별도의 메서드로 처리해서 더 간결한 메서드를 만들어도 좋을 것 같다.
// 리팩터링 전 (쥬스메이커 쥬스 제작 메서드)
func makeJuice(juice: Juice) {
    let necessaryStock = necessaryFruit(juice)
    guard let preparedFruit = settingFruit(necessaryStock),
          !preparedFruit.isEmpty else {
        return
    }

    for (fruit, stock) in preparedFruit {
        stockOfFruit.use(type: Fruit(rawValue: fruit)!, count: stock)
    }

    print("\(juice.rawValue)가 나왔습니다! 맛있게 드세요!")
}

// 리팩터링 후
mutating func make(of orderedJuice: Juice) {
  let stockedFruitsForJuice = stockedFruits(
    for: requiredFruits(for: orderedJuice)
  )
  
  if hasEnoughIngredients(in: stockedFruitsForJuice) {
    subtractStockedFruits(from: stockedFruitsForJuice)
    printOrderCompleted(for: orderedJuice)
  } else {
    printNotEnoughIngredients()
  }
}
  • JSON 형식 인코딩, 디코딩을 지원하는 Codable 프로토콜, Decodable 프로토콜이 지원하는 메서드를 사용하는 방법과 활용 방법을 학습하였다. Swift가 기본적으로 지원하는 String, Int와 같은 타입은 Codable 프로토콜을 통해 인코딩, 디코딩이 가능하지만 사용자 정의 타입은 Codable 프로토콜을 채택해주어야 해당 타입으로 디코딩을 지원한다는 것을 확인하였다.

    2021. 3. 11. 덧붙임

    사용자 정의 타입에 Codable 프로토콜을 채택하면 컴파일은 가능하지만 JSON 디코딩 결과를 사용자 정의 타입으로 받을 수 없다는 것을 확인하였다. 이 상태에서 빌드하면 JSONDecoder().decode() 메서드의 try? 구문에서 nil을 반환한다. 디코딩 시 사용자 정의 타입으로 받는 방법이 있는지 알아보는 중.

profile
합리적인 해법 찾기를 좋아합니다.

0개의 댓글