📌 REST API란
출처: https://www.ibm.com/kr-ko/topics/rest-apis
🔷 API란
API(Application Programming Interface)는
애플리케이션이나 디바이스가 서로 간에 연결하여 통신할 수 있는 방법을 정의하는 규칙 세트이다.
각 기업에서는 API를 통해 자사 애플리케이션의 데이터 및 기능을 외부 써드파티 개발자, 비즈니스 파트너, 사내 부서에 공개할 수 있다.
그러면 문서화된 단일 인터페이스를 통해 여러 서비스와 제품끼리 서로 통신하고 상호 데이터와 기능을 활용하는 것이 가능해진다.
- 클라이언트 : 액세스를 수행하는 애플리케이션이나 서비스
- 서버 : 리소스가 포함된 애플리케이션이나 서비스
🔷 REST API란
- REST(REpresentational State Transfer) 아키텍쳐 스타일의 디자인 원칙을 준수하는 API이다.
- 2000년, 컴퓨터 과학자인 Roy Fielding 박사에 의해 정의되었다.
- 개발자에게 비교적 높은 수준의 유연성과 자유를 제공한다. (거의 모든 프로그래밍 언어로 개발 가능, 다양한 데이터 포맷 지원)
(<-> SOAP, XML-RPC와 같은 일부 API는 개발자에게 엄격한 프레임워크를 부과한다)
🔷 REST 디자인 원칙
1. 균일한 인터페이스.
요청이 어디에서 오는지와 무관하게, 동일한 리소스에 대한 모든 API 요청은 동일하게 보여야 합니다. REST API는 사용자의 이름이나 이메일 주소 등의 동일한 데이터 조각이 오직 하나의 URI(Uniform Resource Identifier)에 속함을 보장해야 합니다. 리소스가 너무 클 필요는 없지만, 이는 클라이언트가 필요로 하는 모든 정보를 포함해야 합니다.
2. 클라이언트-서버 디커플링
REST API 디자인에서 클라이언트와 서버 애플리케이션은 서로 간에 완전히 독립적이어야 합니다. 클라이언트 애플리케이션이 알아야 하는 유일한 정보는 요청된 리소스의 URI이며, 이는 다른 방법으로 서버 애플리케이션과 상호작용할 수 없습니다. 이와 유사하게, 서버 애플리케이션은 HTTP를 통해 요청된 데이터에 전달하는 것 말고는 클라이언트 애플리케이션을 수정하지 않아야 합니다.
3. Stateless
REST API는 stateless입니다. 이는 각 요청에서 이의 처리에 필요한 모든 정보를 포함해야 함을 의미합니다. 즉, REST API는 서버측 세션을 필요로 하지 않습니다. 서버 애플리케이션은 클라이언트 요청과 관련된 데이터를 저장할 수 없습니다.
4. 캐싱 가능성
가능하면 리소스를 클라이언트 또는 서버측에서 캐싱할 수 있어야 합니다. 또한 서버 응답에는 전달된 리소스에 대해 캐싱이 허용되는지 여부에 대한 정보도 포함되어야 합니다. 이의 목적은 서버측의 확장성 증가와 함께 클라이언트측의 성능 향상을 동시에 얻는 것입니다.
5. 계층 구조 아키텍처
REST API에서는 호출과 응답이 서로 다른 계층을 통과합니다. 경험에 따르면 클라이언트와 서버 애플리케이션이 서로 간에 직접 연결된다고 가정하지 않는 것이 좋습니다. 통신 루프에는 다수의 서로 다른 중개자가 있을 수 있습니다. REST API는 엔드 애플리케이션 또는 중개자와 통신하는지 여부를 클라이언트나 서버가 알 수 없도록 설계되어야 합니다.
6. 코드 온디맨드(옵션)
REST API는 일반적으로 정적 리소스를 전송하지만, 특정한 경우에는 응답에 실행 코드(예: Java 애플릿)를 포함할 수도 있습니다. 이 경우에 코드는 요청 시에만 실행되어야 합니다.
🔷 REST API 작동 방식
REST API는 HTTP 요청을 통해 통신함으로써 리소스 내에서 레코드(CRUD라고 함)의 작성, 읽기, 업데이트 및 삭제 등의 표준 데이터베이스 기능을 수행한다.
ex) GET 요청을 사용하여 레코드를 검색, POST 요청으로 레코드를 작성, PUT 요청으로 레코드를 업데이트, DELETE 요청으로 레코드를 삭제
특정 순간 또는 타임스탬프의 리소스 상태를 리소스 표현이라 한다.
이러한 정보는 JSON(JavaScript Object Notation), HTML, XLT, Python, PHP 또는 일반 텍스트를 포함하여 실제로 거의 모든 형식으로 클라이언트에 전달될 수 있다. JSON은 사람과 기계가 모두 읽을 수 있고 프로그래밍 언어에 구애받지 않기 때문에 자주 사용된다.
요청 헤더와 매개변수 역시 메타데이터, 권한 부여, URI(Uniform Resource Identifier), 캐싱, 쿠키 등의 중요한 식별자 정보를 포함하므로 REST API 호출에서 중요하다. 요청 헤더와 응답 헤더는 일반적인 HTTP 상태 코드와 함께 잘 디자인된 REST API 내에서 사용된다.
📌 Swift에서 REST API 호출하기
참고: https://matteomanferdini.com/swift-rest-api/
0️⃣ [Codable]로 struct 만들기
구조체 안에서는 다음 작업을 해야 한다.
1. (필수) 프로퍼티 선언
2. (조건) 키와 프로퍼티 이름이 다를 경우 CodingKey 구현
3. (선택) Encodable/Decodable 메소드 커스텀
import Foundation
struct Product: Codable {
// 1. 프로퍼티 선언 (필수)
let id: Int
let title: String
let description: String
let price: Int
let discountPercentage: Float
// 2. CodingKey 구현 (선택)
// BUT, 외부 데이터 키와 프로퍼티 이름이 다를 경우는 CodingKey 구현 필수
enum CodingKeys: String, CodingKey {
case id
case title
case description
case price
case discountPercentage = "discount_percentage" // 키 이름이 다른 것을 CodingKeys로 맵핑
}
// 3. Encoding, Decoding 메소드 커스텀 (선택)
// Encodable 프로토콜을 준수하기 위한 커스텀 인코딩 로직
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(title, forKey: .title)
// 다른 프로퍼티들도 인코딩 가능
}
// Decoding - 디코딩
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
title = try container.decode(String.self, forKey: .title)
description = try container.decode(String.self, forKey: .title)
price = try container.decode(Int.self, forKey: .title)
discountPercentage = try container.decode(Float.self, forKey: .title)
// 모든 프로퍼티 초기화 필수
}
}
1️⃣ URLSession 인스턴스 생성
이제 본격적으로 코딩 시작!
먼저 URLSession 인스턴스를 생성한다.
[Swift|TIL] 240415 - URLSession; configuration과 shared session 에서 잠깐 다뤘는데,
URLSession 인스턴스를 생성하는 방법은 여러가지다.
이번에는 configuration을 default로 설정!
// 1. URLSession 인스턴스 생성
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration)
2️⃣ URL 프로퍼티 생성
URL 프로퍼티를 생성한다.
// 2. URL 생성
let productID = 2
let url = URL(string: "https://dummyjson.com/products/\(productID)")!
3️⃣ URLSessionDataTask로 비동기적으로 데이터 요청
1️⃣에서 만들어 둔 URLSession에 접근하여 dataTask로 비동기적으로 데이터를 요청한다.
이때, 클로저 내부에서 guard문을 적절히 활용하여 오류를 처리한다.
// 3. URLSessionDataTask로 비동기적으로 데이터 요청
let task = session.dataTask(with: url) { data, response, error in
// 3-1. 성공한 응답 걸러내기
// status code 200~ : 성공 / 300~: 리다이렉션 메시지 / 400~: 클라이언트 에러 / 500~: 서버 에러
guard let httpResponse = response as? HTTPURLResponse,
(200..<300).contains(httpResponse.statusCode) else {
print("error: \(error)")
return
}
// 3-2. 데이터 옵셔널 해제
guard let data = data else { return }
// 3-3. 디코딩
do {
let decoder = JSONDecoder()
let product = try decoder.decode(Product.self, from: data)
print(product.title)
} catch let error as NSError {
print("error: \(error)")
}
}
4️⃣ 네트워크 요청 시작
네트워크 요청을 시작한다.
// 4. 네트워크 요청 시작
task.resume()