함수

SPR : 함수의 역할은 하나만 할당한다.

  • 하나의 함수에 역할이 많아지면 오류 발생 가능성이 커지고, 가독성이 떨어지며 테스트를 진행하기 어렵다.

  • 따라서 하나의 함수에 많은 로직을 넣지 말자.

// as-is
func createUser(email, password) -> Bool {
    if email.contains("@") || password.contains("@") {
        //error
        return false
    }

    var user = User()
    user.email = email
    user.password = password

    database.add(user)

    emailClient = EmailClient()
    emailClient.setConfig()
    emailClient.send(email, "회원가입이 완료되었습니다")
    
    return true
}
    
// to-be
func createUser(email: String, password: String) -> Bool { // 주요 로직 실행 함수
    validateCreateUser(email, password)

    user = BuildUser(email, password)

    saveUser(user)
    sendEmail(email)
    return true
}

// 역할 별로 함수를 작성한다.
func validateCreateUser(_ email: String, _ password: String) { }

func Builduser() { }

func saveUser(_ user: User) { }

DRY (Don’t repeat yourself) : 반복하지 말자

  • 반복되는 코드가 많아지면, 전체 코드가 많아진다.

  • 특정 부분에 변경사항이 생기면, 공통적인 모든 부분을 변경해줘야한다.

  • 이에 관심사를 잘 분리하고 의존성을 줄이기 위해 반복되는 코드를 하나의 함수로 만들어서 사용하자.

// as-is

func createUser(email: String, password: String) {
    if email.contains("@") || password.contains("@") {
        //error
        return false
    }

    // code...
}

func updateUser(email: String, password: String) {
        if email.contains("@") || password.contains("@") {
        //error
        return false
    }

    // code...
}
// to-be
func validateCreateUser(_ email: String, _ password: String) -> Bool {
    if email.contains("@") || password.contains("@") {
        //error
        return false
    }
}

func createUser(email, password) {
    guard validateCreateUser(email, password) else { ... }
    // code..
}

func updateUser(email, password) {
    guard validateCreateUser(email, password) else { ... }
    // code..
}

파라미터 수는 적게 유지하자

// as-is
func saveUser(userName: String, email: String, password: String, createAt: Int) {
    ...
}

// to-be
func saveUser(user: User) {
    ...
}

사이드 이펙트를 잘 핸들링하자

  • 사이드 이펙트란? (Side Effect)

    • 함수가 실행되었을 때, 함수 이외의 어떤 것들에 변화를 주는 것을 의미한다.

    • 이를 잘 다루지 못하면 예측하지 못하는 문제가 발생할 수 있다.

    • A, B, C, D라는 함수가 있을 때, A 실행시 B또는 C함수에도 영향을 주는 경우가 Side Effect라고 한다.

    // 사이드 이펙트가 없는 경우
    func getUser(email, password) -> User {
        let user = User(email, password)
        return user
    }
    
    // 사이드 이펙트가 있는 경우
    func updateUser(user) {
        var user = getUser(user.email, user.password)
        user.eail = "newEmail" // 인자로 받은 user 객체를 업데이트한다
        return user
    }
    
    // 사이드 이펙트가 있는 경우
    func createUser(email, password) {
        let user = User(email, password)
        dbSession() // 외부 DB 세션에 변화를 준다.
    }
  • 사이드 이펙트 핸들링하는 방법

    • 코드를 통해 사이드이펙트를 예측할 수 있도록 네이밍을 잘 해야한다.

      • update, set 등 직관적인 prefix를 붙여 암시한다.

    • 함수의 사이드 이펙트가 있는 부분과 없는 부분으로 나누어 관리한다.

      • 명령과 조회를 분리하는 CQRS 방식이 존재한다.

        • 명령(Command) : 사이드이펙트가 존재

        • 조회(Read) : 사이드 이펙트가 없다

      • 하나의 함수에 명령과 조회 모두가 실행된다면, 사이드이펙트 덩어리가 된다. 이를 함수나 클래스로 영역을 나누어 분리시킨다.

    • 순수함수 형태로 사용하여 직관적으로 표현하고, 에러를 방지한다.

      • update 함수를 남발하지 않는다.

      // as-is
      var carts = [Cart]()
      
      // 사이드이펙트 발생
      func addCart(product: Cart) {
          casts.append(product)        // 외부에 있는 carts의 변경
      }
      
      product = Product()
      addCart(product: product)
      // to-be
      var carts = [Cart]()
      
      // 사이드이펙트가 없는 순수함수
      func getAddCart(product: Cart) -> [Cart] {
          return carts + product
      }
      
      carts = getAddCart(product)                // carts를 재선언한다.

Last updated