본문 바로가기

Project/HiddenGem

🔨 데이터 타입 변환 및 통합하기

✅ 여러 데이터 타입을 하나의 데이터 타입으로 통합하기

 통합하는 이유는 추후체 컬렉션뷰를 사용함에 있어서 데이터 관리 용이성 확보 및, 컬렉션뷰의 품질 향상 목적

// 📍 detail 정보 
{
  "response": {
    "header": {
      "resultCode": "0000",
      "resultMsg": "OK"
    },
    "body": {
      "items": {
        "item": [
          {
            "contentid": "126128",
            "contenttypeid": "12",
            "heritage1": "0",
            "heritage2": "0",
            "heritage3": "0",
            "infocenter": "대구광역시 동구청 공원녹지과 053-662-2867",
            "opendate": "",
            "restdate": "연중무휴",
            "expguide": "",
            "expagerange": "",
            "accomcount": "",
            "useseason": "",
            "usetime": "상시 개방",
            "parking": "가능\u003Cbr\u003E요금 (최초 2시간 무료 / 이후 30분 당 400원씩 추가 요금 발생)",
            "chkbabycarriage": "없음",
            "chkpet": "",
            "chkcreditcard": "없음"
          }
        ]
      },
      "numOfRows": 1,
      "pageNo": 1,
      "totalCount": 1
    }
  }
}

 

// ✅ Common 정보 
{
  "response": {
    "header": {
      "resultCode": "0000",
      "resultMsg": "OK"
    },
    "body": {
      "items": {
        "item": [
          {
            "contentid": "126128",
            "contenttypeid": "12",
            "title": "동촌유원지",
            "createdtime": "20031105090000",
            "modifiedtime": "20250425092225",
            "tel": "",
            "telname": "",
            "homepage": "\u003Ca href=\"https://tour.daegu.go.kr/index.do?menu_id=00002942&menu_link=/front/tour/tourMapsView.do?tourId=KOATTR_115\" target=\"_blank\" title=\"새창 : 홈페이지로 이동\"\u003Ehttps://tour.daegu.go.kr\u003C/a\u003E",
            "firstimage": "http://tong.visitkorea.or.kr/cms/resource/86/3488286_image2_1.JPG",
            "firstimage2": "http://tong.visitkorea.or.kr/cms/resource/86/3488286_image3_1.JPG",
            "cpyrhtDivCd": "Type3",
            "areacode": "4",
            "sigungucode": "4",
            "lDongRegnCd": "27",
            "lDongSignguCd": "140",
            "lclsSystm1": "VE",
            "lclsSystm2": "VE03",
            "lclsSystm3": "VE030500",
            "cat1": "A02",
            "cat2": "A0202",
            "cat3": "A02020700",
            "addr1": "대구광역시 동구 효목동",
            "addr2": "산 234-29",
            "zipcode": "41179",
            "mapx": "128.6506352387",
            "mapy": "35.8826195757",
            "mlevel": "6",
            "overview": "동촌유원지는 대구시 동쪽 금호강변에 있는 44만 평의 유원지로 오래전부터 대구 시민이 즐겨 찾는 곳이다. 각종 위락시설이 잘 갖춰져 있으며, 드라이브를 즐길 수 있는 도로가 건설되어 있다. 수량이 많은 금호강에는 조교가 가설되어 있고, 우아한 다리 이름을 가진 아양교가 걸쳐 있다. 금호강(琴湖江)을 끼고 있어 예로부터 봄에는 그네뛰기, 봉숭아꽃 구경, 여름에는 수영과 보트 놀이, 가을에는 밤 줍기 등 즐길 거리가 많은 곳이다. 또한, 해맞이다리, 유선장, 체육시설, 실내 롤러스케이트장 등 다양한 즐길 거리가 있어 여행의 재미를 더해준다."
          }
        ]
      },
      "numOfRows": 1,
      "pageNo": 1,
      "totalCount": 1
    }
  }
}

 

 

// ✅ Image 정보
{
  "response": {
    "header": {
      "resultCode": "0000",
      "resultMsg": "OK"
    },
    "body": {
      "items": {
        "item": [
          {
            "contentid": "126128",
            "originimgurl": "http://tong.visitkorea.or.kr/cms/resource/80/3488280_image2_1.JPG",
            "imgname": "동촌유원지 (1)",
            "smallimageurl": "http://tong.visitkorea.or.kr/cms/resource/80/3488280_image3_1.JPG",
            "cpyrhtDivCd": "Type3",
            "serialnum": "3488280_9"
          },
          {
            "contentid": "126128",
            "originimgurl": "http://tong.visitkorea.or.kr/cms/resource/81/3488281_image2_1.jpg",
            "imgname": "동촌유원지 (2)",
            "smallimageurl": "http://tong.visitkorea.or.kr/cms/resource/81/3488281_image3_1.jpg",
            "cpyrhtDivCd": "Type3",
            "serialnum": "3488281_1"
          },
          {
            "contentid": "126128",
            "originimgurl": "http://tong.visitkorea.or.kr/cms/resource/83/3488283_image2_1.jpg",
            "imgname": "동촌유원지 (4)",
            "smallimageurl": "http://tong.visitkorea.or.kr/cms/resource/83/3488283_image3_1.jpg",
            "cpyrhtDivCd": "Type3",
            "serialnum": "3488283_2"
          },
          {
            "contentid": "126128",
            "originimgurl": "http://tong.visitkorea.or.kr/cms/resource/84/3488284_image2_1.jpg",
            "imgname": "동촌유원지 (5)",
            "smallimageurl": "http://tong.visitkorea.or.kr/cms/resource/84/3488284_image3_1.jpg",
            "cpyrhtDivCd": "Type3",
            "serialnum": "3488284_4"
          },
          {
            "contentid": "126128",
            "originimgurl": "http://tong.visitkorea.or.kr/cms/resource/85/3488285_image2_1.JPG",
            "imgname": "동촌유원지 (6)",
            "smallimageurl": "http://tong.visitkorea.or.kr/cms/resource/85/3488285_image3_1.JPG",
            "cpyrhtDivCd": "Type3",
            "serialnum": "3488285_5"
          },
          {
            "contentid": "126128",
            "originimgurl": "http://tong.visitkorea.or.kr/cms/resource/87/3488287_image2_1.jpg",
            "imgname": "동촌유원지 (8)",
            "smallimageurl": "http://tong.visitkorea.or.kr/cms/resource/87/3488287_image3_1.jpg",
            "cpyrhtDivCd": "Type3",
            "serialnum": "3488287_3"
          },
          {
            "contentid": "126128",
            "originimgurl": "http://tong.visitkorea.or.kr/cms/resource/82/3488282_image2_1.JPG",
            "imgname": "동촌유원지 (3)",
            "smallimageurl": "http://tong.visitkorea.or.kr/cms/resource/82/3488282_image3_1.JPG",
            "cpyrhtDivCd": "Type3",
            "serialnum": "3488282_12"
          }
        ]
      },
      "numOfRows": 7,
      "pageNo": 1,
      "totalCount": 7
    }
  }
}

 

✅ 통합한 데이터를 섹션과 아이템으로 구분, 추후에 컬렉션뷰에 사용하기 위함

 

✅ 통합 데이터를 관리할 목적으로 열거형과 구조체 생성 

(열거형은 추후에 컬렉션뷰의 datasource에 사용할 예정)

 

enum DetailSectionType: Int, CaseIterable {
    case common
    case intro
    case image
    
    var title: String {
        switch self {
        case .common: return "기본 정보"
        case .intro: return "소개"
        case .image: return "이미지"
        }
    }
}

enum DetailItemType: Hashable {
    case common(title: String, value: String?)
    case intro(title: String, value: String?)
    case image(title: String, value: String?)
}


struct DetailSection {
    let type: DetailSectionType
    let item: [DetailItemType]
}

 

 

✅ ViewModel에서 각각의 API메서드를 통해 받아온 데이터를 가지고, 데이터 변환할 메서드 구현 

@MainActor
class DetailViewModel: ObservableObject {
    
    // MARK: - Variable
    @Published var detailIntro: [IntroInfoItem] = []
    @Published var commonIntro: [CommonIntroItem] = []
    @Published var detailImageList: [DetailImageItem] = []
    
    @Published var detailTotalModel: [DetailSection] = []
    ...
    
    /// IntroInfoItem → DetailSection 변환하는 메서드
    func makeIntroSection() -> DetailSection? {
        guard let item = detailIntro.first else { return nil }
        
        let convertedItems = [
            ("대표 메뉴", item.firstmenu),
            ("취급 메뉴", item.treatmenu),
            ("문의 및 안내", item.infocenterfood),
            ("영업 시간", item.opentimefood),
            ("쉬는 날", item.restdatefood),
            ("주차 시설", item.parkingfood),
            ("포장 가능", item.packing)
        ].map { DetailItemType.intro(title: $0.0, value: $0.1) }
        
        return DetailSection(type: .intro, item: convertedItems)
    }
    
    
    /// CommonIntroItem → DetailSection 변환하는 메서드
    func makeCommonSection() -> DetailSection? {
        guard let item = commonIntro.first else { return nil }
        
        let convertedItems = [
            ("주소", item.addr1),
            ("소개", item.overview),
            ("대표 이미지", item.firstimage),
            ("가게 이름", item.title),
            ("카테고리", item.cat3)
        ].map { DetailItemType.common(title: $0.0, value: $0.1) }
        
        return DetailSection(type: .common, item: convertedItems)
    }
    
    /// DetailImageItem → DetailSection 변환하는 메서드
    func makeImageSection() -> DetailSection? {
        guard !detailImageList.isEmpty else { return nil }
        
        let convertedItems = detailImageList.map {
            DetailItemType.image(title: $0.imgname, value: $0.originimgurl)
        }
        
        return DetailSection(type: .image, item: convertedItems)
    }
    
    
    /// 데이터 타입을 변환하는 메서드를 통합하는 메서드
    func makeAllSections() {
        var sections: [DetailSection] = []
        
        if let common = makeCommonSection() {
            sections.append(common)
        }
        
        if let intro = makeIntroSection() {
            sections.append(intro)
        }
        
        if let image = makeImageSection() {
            sections.append(image)
        }
        
        self.detailTotalModel = sections

    }    
}

 

 

✅ ViewController 에서 해당 메서드를 호출 

extension EateryDetailViewController {
    
    func fetchDetailAllData(contentId: String, contentTypeId: String) {
        
        Task {
            async let common:() = detailVM.fetchCommonIntroInfo(contentId: contentId)
            async let intro:() = detailVM.fetchDetailInfo(contentId: contentId, contentType: contentTypeId)
            async let image:() = detailVM.fetchDetailImageList(contentId: contentId)
            
            await common
            await intro
            await image
            
            // 모든 데이터가 완료된 후 섹션 생성
            detailVM.makeAllSections()
        }
    }
    
    
    private func bindViewModel() {
        detailVM.$detailTotalModel
            .receive(on: DispatchQueue.main)
            .sink { [weak self] sections in
                print("✅ 섹션 데이터 수신 완료")
                
                for section in sections {
                    switch section.type {
                    case .common:
                        print("📘 [기본 정보]")
                        section.item.forEach { item in
                            if case let .common(title, value) = item {
                                print(" - \(title): \(value ?? "없음")")
                            }
                        }
                        
                    case .intro:
                        print("📗 [소개]")
                        section.item.forEach { item in
                            if case let .intro(title, value) = item {
                                print(" - \(title): \(value ?? "없음")")
                            }
                        }
                        
                    case .image:
                        print("📙 [이미지]")
                        section.item.forEach { item in
                            if case let .image(title, value) = item {
                                print(" - \(title): \(value ?? "없음")")
                            }
                        }
                        
                    }
                }
            }
            .store(in: &cancellables)
    }
}