POP에서는 모든 코드가 프로토콜로 시작을 해야 할까?

POP를 준수하면서 프로젝트를 진행하던 중 API에서 데이터를 받아오는 작업을 해야 할때, 평소 같으면 해당 작업을 담당하는 클래스를 하나 만들어서 작업을 하겠지만, POP를 이용한 프로젝트를 하던 참이였길래, 별 생각 없이 프로토콜을 우선적으로 구현하고 있었습니다. 그런데 해당 프로젝트에서는 코드를 재사용할 필요가 없을때에도 프로토콜로 추상화를 해야하나 이 생각이 들었습니다.

final class NetworkManager{

 static let shared = NetworkManager()

    func fetchImageList(url: String, completion : @escaping ([ContentResponse]?) -> ()){
        guard let url = URL(string: url) else { return }
        URLSession.shared.dataTask(with: url) { data, response, error in
            if error != nil {
                completion(nil)
            } else if let data = data {
                guard let imageList = try? JSONDecoder().decode([ContentResponse].self, from: data) else { return }
                completion(imageList)
            }
        }.resume()
    }
    
    func fetchImage(url: String, completion: @escaping (UIImage)->Void){
            guard let url = URL(string: url) else { return }
            URLSession.shared.dataTask(with: url) { data, response, error in
                guard let data = data, error == nil, let image = UIImage(data: data) else { return }
                completion(image)
            }.resume()
        }
}

서버 API에서 이미지 리스트를 받고, 이미지 URL로 이미지를 받아오는 역할을 가진 NetworkManager가 있습니다. 이걸 이제 프로토콜로 기능별로 분리를 해서 프로토콜 지향적으로 구현해 보겠습니다.

protocol FetchImageListable {
    func fetchImageList(url: String, completion : @escaping ([ContentResponse]?) -> ())
}

extension FetchImageListable {
    func fetchImageList(url: String, completion : @escaping ([ContentResponse]?) -> ()) {
        guard let url = URL(string: url) else { return }
        URLSession.shared.dataTask(with: url) { data, response, error in
            if error != nil {
                completion(nil)
            } else if let data = data {
                guard let imageList = try? JSONDecoder().decode([ContentResponse].self, from: data) else { return }
                completion(imageList)
            }
        }.resume()
    }
}

protocol FetchImagable {
    func fetchImage(url: String, completion: @escaping (UIImage)->Void)
}

extension FetchImagable {
    func fetchImage(url: String, completion: @escaping (UIImage)->Void) {
        guard let url = URL(string: url) else { return }
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data, error == nil, let image = UIImage(data: data) else { return }
            completion(image)
        }.resume()
    }
}


final class NetworkManager: FetchImagable, FetchImageListable {
    static let shared = NetworkManager()
    
}

프로토콜로 서버에서 이미지 리스트를 가져오는 기능, 이미지 url로 이미지를 가져오는 기능을 분리시켜서 NetworkManager가 각 프로토콜을 채택을 하게 해서 기능을 사용할 수 있게 되었습니다. 해당 기능을 다른 곳에서 사용을 해야 할때 해당 프로토콜을 채택하기만 한다면 해당 기능을 사용 할 수 있게 되었지만, 지금 진행하고 있는 프로젝트에서는 NetworkManager에서만 해당 기능을 사용합니다. 그래서 공통적인 코드만 프로토콜로 추상화 하고 그 외의 기능들은 굳이 프로토콜로 추상화 하지 않아도 되지 않아도 될것 같네요.