목차
향수 관련 앱을 만들기 위해 향수의 정보 데이터가 필요했다.
팀 프로젝트 때 Sephora에서 제공해주는 향수 데이터를 가져다 썼었다.
그 과정에서 우리 팀은 '데이터호출 / 데이터가공' 으로 역할을 나누었다.
호출을 계속 시도하던 중 데이터가 형태가 일치하지 않는 것을 알고 가공으로 넘어가게 됐다.
호출을 하다말아서 아쉬움이 남았고 내껄로 만들자 해서!
그 과정을 공부해보았고 기록하고자 한다.
API: RapidAPI
데이터: Sephora
가공: Xcode, Swift
서버: Firebase Firestore
과정
이번 글에선 1️⃣데이터 호출에 대해 다뤄볼 것이다.
1️⃣데이터 호출 -> 2️⃣호출한 데이터를 가지고 한번 더 호출 -> 3️⃣데이터 파싱 -> 4️⃣Firebase 올리기
본론에 들어가기 앞서서 어떤 데이터를 쓰는지 설명이 필요할 것 같다.
RapidAPI를 이용하여 Sephora 데이터 호출하려고 한다.
Sephora, RapidAPI가 뭔가요 ?
Sephora는 외국의 올리브영 같은 사이트이다.
이 사이트에서 제공해주는 향수 데이터를 사용할 것이다.
RapidAPI는 API를 이용하게 해주는 서비스이다.
xcode에서 전처리한 이유?
많은 향수데이터를 다루려면 자동화가 필수임
데이터를 받아오고 가공하고 업로드까지 자동화하기 위해.
그럼 왜 Firebase에 향수 데이터를 저장했을까?
- API에서 데이터 호출 시간 지연
- 프로젝트에 맞게끔 데이터를 가공하기 위해
데이터 호출
본격적으로 API를 이용하여 데이터를 호출해보자.
API란 ?
클라이언트 - API - 데이터
클라이언트가 데이터를 얻기위한 수단 (interface)
ex)
배달 시키는 상황을 생각해보자.
배민을 켜서 가게에 대한 정보를 얻고 주문까지 완료한다.
나(클라이언트) -> 인터페이스(배민) -> 데이터(가게)
RapidAPI는 가입 후 과금이 필요한데 나는 공부용이라 무료 요금제를 이용했다.
RapidAPI에서 Sephora를 검색한다.
그럼 아래처럼 Sephora의 데이터를 호출할 수 있는 페이지가 나온다.
왼쪽에서 어떤 데이터를 GET해올지 선택할 수 있는데 나는 product/list에서 향수들의 리스트를, product/detail에서 향수의 세부정보를 받아올 예정이다.
먼저 product/list에서 향수 리스트와 몇몇 정보를 받아올 것이다.
X-RapidAPI-Key 부분은 personal key이기 때문에 가려놨다.
Sephora에서는 향수뿐만 아니라 다양한 상품들의 데이터를 제공하는데 이를 카테고리 id로 구분할 수 있다.
내가 사용할 향수 카테고리의 id는 'cat60148'이다. 오른쪽의 Code snippets 부분을 보면 코드가 적혀있다.
Code snippets의 코드는 personal key(X-RapidAPI-Key)로 URL(상수 request)의 데이터를 받아온다.
언어에 따라 설정하면 되고 나는 Swift로 바꿔놓은 상태이다. URL을 보면 카테고리id가 들어가있는게 볼 수 있다.
카테고리 id를 입력하고, 파란 버튼(Test endpoint)을 눌러보면 향수 카테고리에 속한 제품들이 json 형태로 출력된다.
살펴보면 products라는 배열에 향수의 대략적인 정보들이 Dictionary형태로 들어가있다.
여기서 향수들의 brandName, displayName, productId, image450을 저장할 것이고,
그 이후에는 productId를 이용하여 product/detail에서 향수의 세부정보를 뽑아올 예정이다
우선 위에서 설명한 부분을 코드로 설명해보려한다.
위의 code snippets를 클래스에 넣어서 함수로 만들었다.
class APIStore {
let headers = [
"X-RapidAPI-Key": "Personal Key를 입력하세요",
"X-RapidAPI-Host": "sephora.p.rapidapi.com"
]
func fetchList() {
let request = NSMutableURLRequest(url: NSURL(string: "https://sephora.p.rapidapi.com/products/list?categoryId=cat60148&pageSize=60¤tPage=1")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
print(data)
}
})
dataTask.resume()
}
}
dataTask의 data를 프린트해보면 옵셔널로 출력된다.
요 data는 현재 json형태이다. 옵셔널 바인딩 해준 뒤, 데이터를 디코딩을 해줄 것이다.
위의 데이터를 우리가 보기 쉽게 만들어보면 이런 식의 json 형태의 데이터이다.
{
products: [
{
brandName: "CHANEL",
displaName: "CHANCE EAU TENDRE Eau de Toilette",
...
},
{
brandName: "Dior",
displaName: "Miss Dior Blooming Bouquet",
...
},
...
]
}
그래서 swift의 JSONDecoder를 사용하여 디코딩 해준다.
디코딩을 수월하게 해줄 Model이다.
struct FetchList: Codable {
let products: [Product]
}
struct Product: Codable, Identifiable {
var id: String { self.productId }
let brandName: String
let displayName: String
let productId: String
let image450: String
}
guard let을 이용하여 data를 바인딩해준다.
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
guard let data = data else {return}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {return}
do {
let list = try JSONDecoder().decode(FetchList.self, from: data)
print(list.products)
} catch (let err) {
print(err)
}
}
})
요렇게 디코딩을 해주고 list의 프로퍼티인 products를 출력해보면
요런 식으로 향수의 정보들이 담긴 것이 보인다.(60개)
간단하게 정리해보면 이렇다.
list.products = [
Product(brandName: "CHANEL", displayName: "CHANCE EAU TENDRE Eau de Toilette", productId: "P258612", image450: "https://www.sephora.com/productimages/sku/s1237379-main-Lhero.jpg"),
Product(brandName: "Glossier", displayName: "Glossier You Eau de Parfum", productId: "P504364", image450: "https://www.sephora.com/productimages/sku/s2649770-main-Lhero.jpg"),
Product(brandName: "Dior", displayName: "Miss Dior Blooming Bouquet", productId: "P385477", image450: "https://www.sephora.com/productimages/sku/s1599869-main-Lhero.jpg")
]
이것을 뷰에 연결해보면 아래처럼 나온다.
이후 내용은 다음 글에 이어서 올리겠습니다.
글에 나온 코드
ContentView
//
// ContentView.swift
// API
//
// Created by TAEHYOUNG KIM on 2023/03/28.
//
import SwiftUI
struct ContentView: View {
@ObservedObject var apiStore: APIStore = APIStore()
var body: some View {
NavigationStack {
VStack {
List(apiStore.products) { product in
NavigationLink {
VStack(alignment: .leading) {
Text("브랜드: \(product.brandName)")
Text("이름: \(product.displayName)")
Text("Id: \(product.productId)")
AsyncImage(url: URL(string: product.image450)) { image in
image.image?
.resizable()
.aspectRatio(contentMode: .fit)
}
}
.padding(20)
} label: {
VStack(alignment: .leading) {
Text(product.brandName)
.font(.largeTitle)
Text(product.displayName)
}
}
}
}
}
.onAppear {
apiStore.fetchList()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Model
//
// Model.swift
// API
//
// Created by TAEHYOUNG KIM on 2023/03/31.
//
import Foundation
struct FetchList: Codable {
let products: [Product]
}
struct Product: Codable, Identifiable {
var id: String { self.productId }
let brandName: String
let displayName: String
let productId: String
let image450: String
}
}
APIstore
//
// APIStore.swift
// API
//
// Created by TAEHYOUNG KIM on 2023/03/28.
//
import Foundation
class APIStore: ObservableObject {
@Published var products: [Product] = []
let headers = [
"X-RapidAPI-Key": "Personal Key를 입력하세요",
"X-RapidAPI-Host": "sephora.p.rapidapi.com"
]
func fetchList() {
let request = NSMutableURLRequest(url: NSURL(string: "https://sephora.p.rapidapi.com/products/list?categoryId=cat60148&pageSize=60¤tPage=1")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
// print(error)
} else {
guard let data = data else {return}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {return}
do {
let list = try JSONDecoder().decode(FetchList.self, from: data)
DispatchQueue.main.async {
self.products = list.products
}
} catch (let err) {
print(err)
}
}
})
dataTask.resume()
}
}
다음 글 링크: https://yahoth.tistory.com/7
API호출 후 데이터 가공해서 서버에 저장하기(2) (SwiftUI)
앞글에서 API를 이용하여 1️⃣데이터 호출에 대해 다뤘다. 이번 글에선 2️⃣먼저 호출한 데이터로 다른 데이터를 호출하는 것부터 이후 3️⃣,4️⃣까지 마무리할 것이다. 1️⃣✅ 데이터 호출
yahoth.tistory.com
'개발 공부' 카테고리의 다른 글
앱스토어 리젝 대응(크래시 로그 분석방법) (0) | 2024.08.12 |
---|---|
애플 개발자 등록하기(feat: apple developer program 등록을 완료할 수 없습니다) (0) | 2024.03.15 |
ChatGPT 4.0을 무료로 사용가능한 AI 툴 [뤼튼: wrtn] (0) | 2023.07.06 |
Github 레포에 올린 민감한 정보 예방하기 + 제거하기(.gitignore + BFG Repo-Cleaner 사용법) (0) | 2023.06.30 |
API호출 후 데이터 가공해서 서버에 저장하기(2) (SwiftUI) (0) | 2023.04.03 |