ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 기본 타입을 Extension하여 편리하게 사용하기
    Swift 2022. 8. 4. 13:51

    안녕하세요. 그린입니다🟢

    이번 포스팅에서는 기본 타입을 Extension해서 조금 유의미하게 프론트 개발에서 사용하는것을 포스팅해보려 합니다🙋🏻

     

    대게 앱 개발을 하다보면 서버에서 내려온 데이터를 다루게 되죠.

    예를들어 TimeStamp로 내려온 시간을 우리는 앱에서 커스텀하게 "2022년 8월 3일 15시 30분"과 같이 다양한 형식으로 포맷팅을 시킬 경우가 많습니다.

    이를 편리하게 익스텐션으로 구현해놓는다면 프로퍼티만 가져다 쓰게 되니 아주 편리할거라 생각해요.

    이런것처럼 오늘 주제는 작게 나눈다면 3가지가 될것 같습니다.

    아주 간단한 코드이기에 바로 해보시죠!

     

    날짜/요일/시간에 대해 Date -> String과 String -> Date 구현하기

    Date -> String

    우선 서버에서 내려온 날짜에 대한 Date 타입을 화면에 보여주기 위해 String으로 변환해야겠죠?

    이러한 변환을 공통적으로 형식만 파라미터로 보내주고 나머지는 묶어준 메서드를 이용해서 확장시켜보려합니다.

    우선 코드 먼저....!

    import Foundation
    
    extension Date {
      // MARK: - 기본 & 짧은 날짜 표시
      public var basic: String {
        return toString("yyyy년 M월 d일")
      }
      public var summary: String {
        return toString("yyyy-MM-dd")
      }
      
      // MARK: - 요일 포함 날짜 표시
      public var basicWithDay: String {
        return toString("yyyy년 M월 d일 (EEEEE)")
      }
      
      // MARK: - 시간 표시
      public var timeWithoutSecond: String {
        return toString("a h시 m분")
      }
      public var timeWithSecond: String {
        return toString("a h시 m분 s초")
      }
      public var shortTimeWithoutSecond: String {
        return toString("HH:mm")
      }
      public var shortTimeWithSecond: String {
        return toString("HH:mm:ss")
      }
      
      // MARK: - 시간 포함 짧은 날짜 표시
      public var summaryWithTimeWithoutSecond: String {
        return toString("yyyy-MM-dd HH:mm")
      }
      public var summaryWithTimeWithSecond: String {
        return toString("yyyy-MM-dd HH:mm:ss")
      }
      
      // MARK: - 요일 & 시간 포함 기본 날짜 표시
      public var basicWithDayAndTimeWithoutSecond: String {
        return toString("yyyy년 M월 d일 (EEEEE) a h시 m분")
      }
      public var basicWithDayAndTimeWithSecond: String {
        return toString("yyyy년 M월 d일 (EEEEE) a h시 m분 s초")
      }
      
      // MARK: - Date -> String
      public func toString(_ dateFormat: String) -> String {
        return DateFormatter
          .convertToKoKR(dateFormat: dateFormat)
          .string(from: self)
      }
    }
    
    extension DateFormatter {
      public static func convertToKoKR(dateFormat: String) -> DateFormatter {
        let dateFormatter = createKoKRFormatter()
        dateFormatter.dateFormat = dateFormat
        return dateFormatter
      }
    }
    
    private func createKoKRFormatter() -> DateFormatter {
      let dateFormatter = DateFormatter()
      dateFormatter.locale = Locale(identifier: "ko_KR")
      dateFormatter.timeZone = TimeZone(abbreviation: "KST")
      return dateFormatter
    }

    우선 Date 기본 타입을 익스텐션해서 어느정도 자주 사용될 날짜/요일/시간 타입에 대해 프로퍼티로 만들어주려합니다.

    그러기에 앞서 우선적으로 DateFormatter 타입을 익스텐션해서 필요한 메서드를 구현해줘야 할거에요.

    저는 우선 한국을 기준으로 createToKRFormatter 메서드에서 지역과 시간대를 한국으로 설정해줍니다.

    이 설정된 DateFormatter 타입을 가지고 converToKoKR 메서드를 구현합니다.

    해당 메서드에 인자로 들어온 문자열을 가지고 커스텀한 포맷팅을 시킨다고 보면 됩니다.

    그럼 실제로 사용하는 쪽인 toString 메서드에서 원하는 커스텀한 문자열을 가지고 DateFormatter를 뚝딱 만들어내고 이를 다시 string으로 추출해줍니다.

    그럼 실제로 사용하는 쪽에서는 위 정의된 프로퍼티 처럼 간략히 호출해서 사용할 수 있게 되는 것이죠.

     

    반대로 String -> Date를 보겠습니다.

    이건 더 간단합니다!

    import Foundation
    
    extension String {
      // MARK: - String -> Date
      public func toDate(_ dateFormat: String = "yyyy-MM-dd'T'HH:mm:ss") -> Date? {
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale(identifier: "ko_KR")
        dateFormatter.timeZone = TimeZone(identifier: "UTC")
        dateFormatter.dateFormat = dateFormat
        
        if let date = dateFormatter.date(from: self) {
          return date
        }
        return nil
      }
    }

    코드를 보면 이해가 가겠지만 설명해보자면,

    우선 인자로 dateFormat을 원하는 커스텀한 문자열로 받을 수 있습니다.

    저는 기본값을 넣어준것이구요.

    그렇다면 실제로 "2022-08-04"라는 문자열에 해당 메서드를 호출할때는 dateFormat 인자로 "yyyy-MM-dd"를 넣어줌으로 형식에 맞춰 Date로 변환해줄 수 있는것입니다.

     

    아주아주 간단하죠..!?🙌

     

    숫자 천 단위 마다 문자열에 콤마(,) 추가하기

    금액을 나타낼때 대부분 천 단위로 콤마를 추가해요.

    이건 뭐 문화나 나라마다 다르긴하지만 오늘은 한국을 예를 들겠습니다!

    너무 쉬우니 코드로 보시죠.

    import Foundation
    
    extension Int {
      public func toStringWithComma() -> String? {
        let numberFormmater = NumberFormatter()
        numberFormmater.numberStyle = .decimal
        
        if let numberToString = numberFormmater.string(from:NSNumber(value: self)) {
          return numberToString
        }
        return nil
      }
    }

    이렇게 입력받은 Int 값에 해당 메서드를 호출하게 되면 데시멀한 numberStyle이 적용되어 천 단위로 금액이 찍힐 수 있습니다.

    강제 언래핑을 해주지 않기 위해 옵셔널 바인딩으로 표현해줬습니다.

     

    휴대폰 번호 검증 및 하이픈(-) 추가하기

    많이들 회원가입 시 휴대폰 번호를 입력하고 자리수나 잘못된 휴대폰 형식일 경우도 알려줘야 합니다.

    또한 하이픈이 없을 경우 자동으로 입력 후 하이픈을 생성하여 값을 저장해야할때가 있겠죠?

    이럴때 두가지로 나눠 보시죠!

    우선 휴대폰 번호가 정상적인지 검증해봅시다.

    import Foundation
    
    extension String {
      // MARK: - 휴대폰 번호 검증
      public func validatePhone(number: String) -> Bool {
        let regex = "^01([0|1|6|7|8|9])-?([0-9]{3,4})-?([0-9]{4})$"
        return NSPredicate(format: "SELF MATCHES %@", regex)
          .evaluate(with: number)
      }
    }

    기본 문자열 타입에 익스텐션으로 구현했어요.

    validatePhone이라는 메서드를 문자열을 통해 호출하면 아래 로직을 수행합니다.

    정규식으로 검증을 하죠.

    즉 처음부터 01은 무조건 들어와야하고 그 다음 0/1/6/7/8/9 중 하나가 들어와야합니다.

    요즘은 대부분 010이지만 예전에는 016, 019, 017 등 다양한 번호가 존재해서 이도 고려해줍니다.

    그다음 하이픈(-)에 대해서는 있어도 되고 없어도 되는 조건으로 ?을 붙여 표현해줍니다.

    그리고 가운데 들어올 부분은 3~4자리의 수가 들어와야합니다.

    마지막도 마찬가지로 4자리 수가 들어와야합니다.

    이 정규식을 통해 같은지 검증을 하고 Bool값을 반환해줍니다.

     

    그럼 이어서 하이픈(-) 추가를 보시죠.

    하이픈 추가는 위 올바른 전화번호인지 검증 하고 이어져도 좋을거라 생각합니다.

    즉 10~11자리의 휴대폰 번호가 들어올거에요.

    위 검증에서 통과했다는 증거겠죠?

    그렇다면 이제 하이픈의 위치를 index를 찾아 넣어줘야합니다.

    extension String {
      // MARK: - 휴대폰 번호 하이픈 추가
      public var withHypen: String {
        var stringWithHypen: String = self
        
        stringWithHypen.insert("-", at: stringWithHypen.index(stringWithHypen.startIndex, offsetBy: 3))
        stringWithHypen.insert("-", at: stringWithHypen.index(stringWithHypen.endIndex, offsetBy: -4))
        
        return stringWithHypen
      }
    }

    휴대폰 번호 형식만을 고려한다면 이렇게 구현될 수 있을것 같습니다.

    처음부터 3자리 후 무조건 - 하이픈이 들어올거에요.

    그리고 가운데 자리 수는 3자리 혹은 4자리일테니 가변적이고 마지막 자리수는 무조건 4자리일거죠?

    그렇다면 endIndex를 이용해 뒤에서 4칸 인덱스를 움직여 - 하이픈을 넣어주면 됩니다.

     

    구현 결과

    위 모든것을 한번에 플레이 그라운드로 돌려보면 아래와 같이 정상적으로 노출 될 수 있습니다.

     

    마무리

    오늘 포스팅은 가볍지만 조금 실제 초기 개발에 도움이 될 수 있을거라 생각합니다!

    익스텐션 구현들은 저의 기준에 맞춘 커스텀일뿐 각자 잘 적용하시면 됩니다🙌

    'Swift' 카테고리의 다른 글

    정규 표현식 (Regex)  (0) 2022.09.13
    옵셔널을 다루는 방법들  (4) 2022.08.08
    WWDC 2022 - Swift 5.7  (0) 2022.06.13
    Swift5.6 - existential any  (0) 2022.05.02
    Result (With. Composable Architecture)  (0) 2022.04.25
Designed by Tistory.