번호 : 050, 작성일자: 2024-12-23

2024.12.16 ~ 2024.12.22

💡 주간회고록 블로그 정리


2024년이 다 다가와서 주간회고록 블로그의 문서를 한 번 정리했다.

문서번호와 Format이 다른 부분들을 수정하고 내가 Ignite 저장소에 기여한 ContentPreView Style 설정하는 코드 PR이 승인이되서 코드를 적용해봤다.

⏰ 진행 중인 비동기 작업 정지하기


비동기 작업을 구현하기 위해서 completionHandler를 이용하거나, async & awiat 문법을 사용할 수 있습니다.

비동기 작업을 시작한 후 완료가 되기전에 작업을 취소하고 싶은 경우 어떻게 각각 구현하는 지 알아보았습니다.

  1. completionHandler
class AsyncWork {
    var item: DispatchWorkItem?
    
    func startWork() {
      if item == nil {
        item = DispatchWorkItem {
        self.item = nil
      }
    
      DispatchQueue.main.asyncAfter(deadline: .now() + 6, execute: item!)
    }
    
    func stopWork() {
        item.cancel()
        itme.nil
    }
}

let work = AsyncWork()
work.startWork()

// 6초가 지나도 실행되지 않음
work.stopWork()
  1. async & awiat
class AsyncWork {
    var item: Task?
    
    func startWork() {
      if item == nil {
        item = Task { [weak self] in
        guard let self else { return }

        do {
            try await Task.sleep(nanoseconds: 6_000_000_000)
            self.item = nil
        } catch {
            self.item = nil
        }
      }
     }

      func stopWork() {
        item?.cancel()
        itme.nil
      }
}

let work = AsyncWork()
work.startWork()

// 6초가 지나도 실행되지 않음
work.stopWork()

📞 전화번호 붙여넣기 유효성검사


지지난 주 UITextField에 복사-붙여넣기 작업시 붙여넣기가 동작되도록 작업을 해보았는데 작업할 때는 숫자만 추출하여 붙여넣기가 불가능한 것으로 알았는데 다른 서비스에서 가능한 것으로 보아서 한 번 살펴보았습니다.

override func paste(_ sender: Any?) 함수를 통해서 붙여넣기 시점에 처리하는 함수가 있어서 해당 함수를 활용하여 해결했습니다.

또 추가적으로 알게된 사실은 iOS 16이상부터는 붙여넣기 방지를 사용자에게 물어볼 수 있습니다.

ex) 전화번호 유효성검사 붙여넣기

//
//  PhoneNumberTextField.swift
//  SookimIosDesignSystem
//
//  Created by sookim on 12/19/24.
//

import UIKit

// 전화번호 입력 텍스트필드
protocol PhoneNumberTextFieldDelegate: AnyObject {
    func editPhoneNumberText(result: PhoneNumberTextField.ResultDescription)
}

class PhoneNumberTextField: UITextField {

    enum ResultDescription {
        case success                    // 성공
        case invalidPhoneNumber         // 잘못된 전화번호 형식
        case emptyPhoneNumber           // 빈값
        case countLimit                 // 갯수 초과
    }

    // 최대 전화번호 길이 (예: 010-1234-5678)
    private let maxPhoneNumberLength = 13

    // 결과를 전달할 클로저
    var onValidationResult: ((ResultDescription) -> Void)?
    weak var phoneNumberTextFieldDelegate: PhoneNumberTextFieldDelegate?

    override init(frame: CGRect) {
        super.init(frame: frame)

        self.translatesAutoresizingMaskIntoConstraints = false
        self.keyboardType = .numberPad
        self.delegate = self
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)

        self.translatesAutoresizingMaskIntoConstraints = false
        self.keyboardType = .numberPad
        self.delegate = self
    }

    // 붙여넣기 처리
    override func paste(_ sender: Any?) {
        if let pasteboardString = UIPasteboard.general.string {
            let filteredString = filterAndFormatPhoneNumber(pasteboardString)
            self.text = filteredString

            // 붙여넣기 후 결과 반환
            let validationResult = validatePhoneNumber(filteredString)

            onValidationResult?(validationResult)
            phoneNumberTextFieldDelegate?.editPhoneNumberText(result: validationResult)
        }
    }

}

// MARK: - UITextFieldDelegate
extension PhoneNumberTextField: UITextFieldDelegate {

    func textFieldShouldClear(_ textField: UITextField) -> Bool {
        // 지우기 버튼이 눌려 텍스트가 비워진 경우
        onValidationResult?(.emptyPhoneNumber)
        phoneNumberTextFieldDelegate?.editPhoneNumberText(result: .emptyPhoneNumber)
        return true
    }

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        guard let currentText = textField.text as NSString? else { return true }
        let newText = currentText.replacingCharacters(in: range, with: string)

        let filteredText = filterAndFormatPhoneNumber(newText)

        // 최대 길이 초과 방지
        if filteredText.count > maxPhoneNumberLength {
            onValidationResult?(.countLimit)
            phoneNumberTextFieldDelegate?.editPhoneNumberText(result: .countLimit)
            return false
        }

        // 텍스트를 업데이트
        textField.text = filteredText

        // 검증 결과 반환
        let validationResult = validatePhoneNumber(filteredText)
        onValidationResult?(validationResult)
        phoneNumberTextFieldDelegate?.editPhoneNumberText(result: validationResult)

        return false
    }
}

// MARK: - 유효성 검증 로직
extension PhoneNumberTextField {

    // 숫자만 필터링하고 전화번호 형식화
    private func filterAndFormatPhoneNumber(_ input: String) -> String {
        // 숫자만 추출
        let digitsOnly = input.compactMap { $0.isNumber ? String($0) : nil }.joined()

        // 전화번호 형식 적용
        let formattedNumber = formatAsPhoneNumber(digitsOnly)
        return formattedNumber
    }

    // 전화번호 형식으로 변환
    private func formatAsPhoneNumber(_ digits: String) -> String {
        let length = digits.count

        switch length {
        case 0...3: // 3자리 이하
            return digits
        case 4...6: // 가운데 번호가 3자리
            let firstIndex = digits.index(digits.startIndex, offsetBy: 3)
            return "\(digits[.. ResultDescription {
        if text.isEmpty {
            return .emptyPhoneNumber
        }

        if text.count > maxPhoneNumberLength {
            return .countLimit
        }

        if !text.starts(with: "010") || text.count < (maxPhoneNumberLength - 1) {
            return .invalidPhoneNumber
        }

        return .success
    }

}

🙋🏻‍♂️ 기타 사항


  1. Bitbucket에서 2024-12-10일 부터 commit들의 sign 여부를 표시하도록 기능이 추가되었다. 각 인증방식에 따라 표시가 달라진다.
    • HTTPS로 인증한 경우는 노란 경고가 표시되고 sign하라는 알림이 표시된다.
    • SSH로 인증한 경우 아무 표시가 없다.
    • GPG로 Sign 한 경우 체크 표시가 표시된다.
  2. Atlasian 관련 제품을 모바일로 로그인하면 유료결제가 될 수 도 있으므로 주의해야 한다.
  3. 타 서비스 제공업체와 처음 회의를 진행했다. 도입을 했을 때 관련 문의사항을 진행하고 어떤 식으로 커스텀해주는 지 등등 어떤 식으로 진행되는 지 경험을 해보았다.
  4. CodeFactory Dart 기초강의를 3강까지 학습했다. 마무리 하고 저장소 정리해야겠다.