일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 날짜처리
- strikeThrough
- 스파르타코딩클럽
- 코드스니펫
- userdefaults
- Swift
- Storyboard Reference
- 오토레이아웃
- pull down button
- 메모장만들기
- pop up button
- DART
- UISlider
- 내일배움캠프
- ios
- 내배캠
- Xcode
- attributedText
- 커맨드라인툴
- ListTile
- 플러터
- Wil
- Storyboard
- todolist
- URLSession
- 개발자
- 앱개발
- Flutter
- 알고리즘
- 계산기만들기
- Today
- Total
이리메라 갖다가
[Swift] 메모 앱 만들기 심화 (3) : URL Session 으로 API 통신하여 고양이 사진 가져오기 본문
URL로 되어 있는 외부 API를 사용해서 메모 앱에 추가하려고 한다.
The Cat API(https://thecatapi.com)에서 제공하는 API를 활용하였고, 자세한 가이드는 공식 홈페이지에서 참조하였다.
시작에 앞서, 해당 API를 호출하면 다음과 같은 결과를 받을 수 있다.
[
{
"id": "e4f",
"url": "https://cdn2.thecatapi.com/images/e4f.jpg",
"width": 500,
"height": 375
}
]
해당 결과값은 JSON 형태로 제공되므로, 데이터를 받아올 때 변환해줘야 한다.
무료로 제공하는 갯수는 10개이며, 그 이상의 데이터 수를 얻기 위해서는 가입해서 API KEY를 받아 설정하면 된다.
그리고 고려해야 할 점은 ViewController의 Life Cycle을 고려하여 호출되는 시점을 정해야 한다.
URLSession를 통해 API 데이터 가져오기
- URL 생성
- URLSession 인스턴스 생성
- Data Task 생성
- Data Task 실행
- 데이터 처리
- 에러 처리
- 세션 종료
간단한 예제코드
// 1. URL 생성
let apiUrl = URL(string: "https://api.example.com/data")
// 2. URLSession 인스턴스 생성
let session = URLSession.shared
// 3. Data Task 생성
let dataTask = session.dataTask(with: apiUrl!) { (data, response, error) in
if let error = error {
print("Error: \(error)")
return
}
// 4. 데이터 처리
if let data = data {
// 데이터 파싱 및 활용
print("Received data: \(data)")
}
}
// 5. Data Task 실행
dataTask.resume()
1. URL 생성
먼저 가져올 데이터를 제공하는 API의 URL을 생성한다. 이 때 필요한 파라미터나 경로 등을 포함할 수 있다.
let url = URL(string: "https://api.thecatapi.com/v1/images/search")!
// 만약 api key 나 품종 등 추가 설정이 존재할 경우 커스터마이징
guard let url = URL(String: "https://api.thecatapi.com/v1/images/search") else { return }
- 나는 해당 url이 nil이 될 가능성이 없다고 판단하여 강제 옵셔널 해제를 하였지만, 혹시 모를 nil에 대응하기 위해 옵셔널 바인딩 하는 것을 추천
- guard let 구문을 활용하여 nil 일 경우 에러 메세지를 보여줄 수 있음
2. URLSession 인스턴스 생성
URLSession 클래스의 인스턴스를 생성하여 네트워크 작업을 수행할 수 있다.
let session = URLSession(configuration: .default)
- configuration 특징 : 캐시 및 쿠키 사용, 인증과 관련된 기본 설정, Connection Pooling*, 백그라운드 업로드 및 다운로드, 캐시 정책 설정, 타임아웃 설정
* 여러 네트워크 작업 간에 연결을 재사용하여 성능을 최적화 함 - default : 기본적인 설정을 사용하는 configuration으로 일반적인 네트워크 작업에 적합함
- ephemeral : 일시적인 데이터 요청에 적합. 캐시 및 쿠키를 저장하지 않고 임시적인 세션을 사용하여 데이터 요청을 처리
- background : 백그라운드에서 데이터 업로드 및 다운로드를 처리. 앱이 백그라운드로 들어갔을 때 네트워크 작업을 계속할 수 있음
- custom : 사용자 정의 설정을 통해 네트워크 작업에 특정한 옵션을 정확하게 조정할 수 있음
(추가) 데이터 받을 객체 생성
Data Task를 생성하기 전에 해당 url의 데이터를 받을 객체를 생성해야 한다.
해당 url은 id, url, width, height 로 구성되어 있으며 나는 url만 필요해서 아래와 같이 간단하게 모델을 생성했다.
struct RandomImage: Codable {
let url: String
}
3. Data Task 생성
URLSession 인스턴스를 사용하여 데이터를 가져오기 위한 Data Task를 생성한다. 생성된 Task는 백그라운드에서 실행되며 네트워크 요청을 처리한다.
session.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
return
}
// Data Task 실행 코드
}
- session.dataTask(with:completionHandler:) : URLSession에서 데이터를 가져오는 작업을 생성. with - 가져올 데이터의 URL, Handler - 네트워크 작업이 완료되었을 때 실행할 클로저를 전달
- data : 서버로부터 받아온 데이터
- response : 서버의 응답에 대한 정보가 담겨있는 객체
- error : 네트워크 작업 중 발생한 에러. nil인 경우 네트워크 작업이 성공적으로 완료된 것을 의미
- 상기 코드는 data가 nil이 아니고 error가 nil인 경우에만 실행되는 코드로 데이터가 성공적으로 받아와짐을 의미
4. Data Task 실행
생성한 Data Task를 실행하여 데이터를 가져온다. 이 때 다양한 옵션을 설정하여 요청을 커스터마이즈 할 수 있다.
session.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
return
}
...
}.resume()
- 생성된 Data Task를 불러와 마지막에 .resume()을 붙여 실행할 수도 있고, 상기 코드처럼 바로 실행할 수도 있음
- resume()을 호출함으로써 데이터 작업이 서버로 요청을 보내고, 서버 응답을 기다리며 데이터를 받아오는 등의 동작이 시작됨
- resume()을 호출하면 앞서 정의한 클로저가 실행되며, 클로저 내부에서 데이터를 받아와서 처리
- 비동기적으로 네트워크 작업을 수행하면 앱이 멈추지 않고 다른 작업을 수행할 수 있게 됨
5. 데이터 처리
Data Task가 완료되면 서버로부터 받은 데이터 또는 에러 정보를 처리한다. 성공적으로 데이터를 가져온 경우 원하는 형식으로 파싱하고 활용할 수 있다.
let decoder = JSONDecoder() // (1)
guard let randomCatImage = try? decoder.decode([RandomImage].self, from: data) else { return } // (2)
if let imageUrl = URL(string: randomCatImage.first?.url ?? "") { // (3)
URLSession.shared.dataTask(with: imageUrl) { imageData, _, _ in // (4)
if let imageData = imageData, let image = UIImage(data: imageData) { // (5)
DispatchQueue.main.async { // (6)
self.randomImageView.image = image
}
}
}.resume()
}
- 상기 코드는 JSON 데이터를 파싱하여 이미지 url을 가져오고, 해당 url을 통해 이미지 데이터를 받아와 imageView에 나타냄
- (1) : JSON 데이터를 디코딩하기 위한 JSONDecoder 인스턴스 생성
- (2) : 서버에서 받아온 JSON 데이터(data)를 앞서 정의한 RandomImage 타입으로 디코딩하고 randomCatImage 변수에 저장. 만약 실패하면 함수 실행 종료
- (3) : randomCatImage에 저장된 첫 번째 이미지 URL을 가져옴. nil이 아닐 경우에만 실행
- (4) : (3)에서 가져온 이미지 url을 이용하여 새로운 dataTask를 생성
- (5) : imageData가 nil이 아닐경우 UIImage로 변환
- (6) : 이미지뷰 업데이트는 메인 스레드에서 실행
6. 에러 처리
Data Task 실행 중에 에러가 발생할 경우를 대비라여 적정한 에러 처리를 구현한다. 네트워크 연결 오류, 서버 응답 오류 등을 처리할 수 있다.
URLSession.shared.dataTask(with: imageUrl) { data, response, error in
if let error = error {
print("Error: \(error)")
}
if let httpResponse = response as? HTTPURLResponse {
print("Status Code: \(httpResponse.statusCode)")
}
if let imageData = data, let image = UIImage(data: imageData) {
DispatchQueue.main.async {
self.randomImageView.image = image
}
}
}
- error : 에러 정보 프린트
- response : HTTP 응답에 대한 정보 프린트
7. 세션 종료
필요한 모든 데이터를 가져온 뒤에는 URLSession을 종료하여 리소스 관리와 메모리 누수를 방지한다.
session.finishTasksAndInvalidate()
- invalidateAndCancel : 세션을 무효화하고 진행 중인 작업을 취소. 현재 세션과 해당 세션 내 모든 작업 중단
- finishTasksAndInvalidate : 현재 진행 중인 작업을 완료한 후 세션을 무효화. 현재 작업들이 완료될 때까지 기다린 후 세션 종료
- 아무 코드도 작성하지 않으면 앱 수명동안 유지되며, 작업이나 Task를 계속해서 추가할 수 있음
최종코드
import UIKit
import SkeletonView
class PetViewController: UIViewController {
var imageList: [RandomImage] = []
@IBOutlet weak var randomImageView: UIImageView!
@IBOutlet weak var randomButton: UIButton!
@IBOutlet weak var catImageView: UIImageView!
@IBAction func randomButton(_ sender: Any) {
getRandomCatImage()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
randomImageView.showAnimatedGradientSkeleton()
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
self?.randomImageView.hideSkeleton()
}
}
override func viewDidLoad() {
super.viewDidLoad()
getRandomCatImage()
}
func getRandomCatImage() {
guard let url = URL(string: "https://api.thecatapi.com/v1/images/search") else { return }
let session = URLSession(configuration: .ephemeral)
let task = session.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
let decoder = JSONDecoder()
guard let randomCatImage = try? decoder.decode([RandomImage].self, from: data) else { return }
if let imageUrl = URL(string: randomCatImage.first?.url ?? "") {
let imageTask = URLSession.shared.dataTask(with: imageUrl) { data, response, error in
if let error = error {
print("Error: \(error)")
}
if let httpResponse = response as? HTTPURLResponse {
print("Status Code: \(httpResponse.statusCode)")
}
if let imageData = data, let image = UIImage(data: imageData) {
DispatchQueue.main.async {
self.randomImageView.image = image
session.finishTasksAndInvalidate()
print("세션 종료")
}
}
}
imageTask.resume()
}
}
task.resume()
}
}
🤳🏻 적용화면
세션이 종료되는 시점을 확인할 수 있으며, 해당 페이지로 접근 또는 꾹꾹이라는 버튼을 누르지 않는 이상 이미지를 로드할 일은 없으니 이미지뷰에 데이터를 불러오자마자 세션을 종료하도록 설정했다.
실행하고보니 문제아닌 문제로 느끼는건 바로 로딩 속도... 너무 느리다... 한국패치 plz 🤮
'TIL' 카테고리의 다른 글
[Swift] 메모 앱 만들기 심화 (5) : MVC 구조 (0) | 2023.08.29 |
---|---|
[Swift] 메모 앱 만들기 심화 (4) : TableView 오류 해결하기 (4) | 2023.08.28 |
[Swift] 메모 앱 만들기 심화 (2) : TableView Section/Header/Footer (3) | 2023.08.24 |
[Swift] 메모 앱 만들기 심화 (1) : UserDefaults, URLSession 적용하기 (2) | 2023.08.23 |
[Swift] URLSession 데이터 불러오기, UserDefaults CRUD (4) | 2023.08.22 |