먼저 1주차에 공부해야 할 부분들은 아래와 같았다
- HTTP / REST API
- URL Session in SwiftUI
- Async / Await
- Codable / JSONDecoder
- WWDC20 - Data Essetial 정리
아직 마지막 영상 정리는 못했고, 나머지 공부한 부분들을 공유하고자 한다!
HTTP / REST API
이전에 웹을 공부하면서도 질리도록 들은 API 이지만
일상 게시판에 잘 찾아보면 알 수 있듯, 항상 껍데기만 만들어왔기 때문에
아직도 API가 뭔지 잘 모르는 상태였다
그래서 다시 정리함!
API
Application Programming Interface
즉, Application에서 어떤 프로그램이 제공하는 기능을 사용할 수 있게 만든 것이다
이번에 새로 시작하는 Challenge를 하면서도 서버를 도입하면서 API를 사용할 것 같은데
이참에 정리하게 돼서 오히려 좋아..
API는 서버에서 제공하고자 하는 데이터나 기능을 원할때 가져다 쓸 수 있도록 제작해둔 것이다
단, 권한이 있는 클라이언트(User)이어야만 제공되는 API도 있다
HTTP
우리가 브라우저에 접속할 때마다 자주 보는 알파벳들..
그래서 HTTP가 뭔데?
Hyper Text Transfer Protocol
인터넷에서 리소스를 주고 받을 수 있도록 해주는 Protocol(규칙)이다
HTTP를 따르는 통신 방식을 HTTP Method라고 하는데
상당히 다양한 Method들이 있다
그 중 일단은 자주 쓰일 POST와 GET만 보자..!
- POST - 서버로 데이터를 전송
- GET - 특정 리소스를 서버로부터 가져옴
영어 그대로 POST(= 게시하다)는 서버에 데이터를 게시한다고 생각하면 될 것 같고
GET(= 가져오다)도 서버로부터 데이터를 가져온다고 보면 될 듯 하다
추가로 다른 Method들은 아래와 같이 다양하기 때문에
궁금하다면 스스로 찾ㅇ...
HTTP 통신 과정
위 그림과 같이, HTTP 통신 과정은 두가지로 나뉜다.
HTTP Request
웹 브라우저의 URL을 통해 어느 웹사이트의 어느 경로의 페이지를 요청할지 나타내는 것
사실 HTTP Request에 관해 작성하려면 끝도 없기 때문에
일단 대략적인 개념만 보고 넘어가자...
사실 저도 아직은 잘 몰ㄹ...
HTTP Response
HTTP Request를 통해 요청된 정보에 대해 웹서버가 클라이언트에 보내는 응답형식과 결과
HTTP Response는 Request처럼 넘어가기 전에
Status Line에 대해서만 알아보자
Response가 오면 제대로 된 응답이 됐는지
상태를 보여주는 세 자리수 숫자이고 아래와 같이 나뉜다
- 1xx : Informational
- 요청을 클라이언트에서 성공적으로 수신했음
- 서버 끝에서 처리 중임
- 2xx : Success
- 서버가 요청을 성공적으로 받았고 처리도 함
- 3xx : Redirection
- 자동으로 다른 URL로 리디렉션 함
- 4xx : Client Error
- 서버가 해결할 수 없는 클라이언트 측 에러
- 5xx : Server Error
- 서버가 클라이언트의 요청을 처리하지 못함
HTTP Request Parameter
HTTP를 통해 어떤 요청을 보낼 때 필요한 Parameter 들이 있다
형태는 아래와 같다
<Scheme>://<id>:<pw>@<Hostname>:<Port>/(path)?<Query String>$<Query String>..#<Fragment>
눈치 빠른 사람들은 위에 형태가 URL 형태랑 비슷함을 알 수 있을거다!
요소 하나하나 어떤 역할이냐 하면...
- <Scheme>
- 컴퓨터 간 정보를 주고받을 때 통신 방법에 대한 규칙으로, HTTP, HTTPS 등이 들어감
- <id>:<pw>
- 특정 서버는 서버의 데이터에 접근할 때 아이디와 패스워드를 요구함
- 하지만 서버의 데이터가 공개된 데이터인 경우 입력하지 않아도 됨😉
- <Hostname>:<Port>
- 해당 웹 서버로 접근하기 위해 서버의 IP(또는 Hostname)에 접근
- 각 서버는 매우 많은 포트가 존재하고, 특정 웹페이지에 접근하기 위해서는 지정된 포트를 입력해야 함..!
- 보통 HTTP는 80번 포트 / HTTPS는 443번 포트
- (path)
- 호스트 서버에서 제공하는 자원의 실질적인 경로
- <Query String>$<Query String>
- 위에 전체 형태에서 ?에서 #사이의 구간을 parameter라고 함
- 이 구간에서는 Query String이라는 질의문을 수행함
- 여기서 Query String 이란 DB에 직접 SQL문을 날려 결과값을 받는 것!
- <Fragment>
- 페이지의 특정 요소 지정 가능함
REST API
REST란?
REpresentational State Transfer
웹 서비스의 구조를 만드는 데 활용되는 패턴
- HTTP를 잘 활용하기 위한 원칙
- CRUD 메소드, HTTP 메소드 중 POST, GET, PUT, DELETE 만 사용
- URI로 자원을 표현
- URI - 특정 리소스를 식별하는 통합 자원 식별자
- URL을 포함하는 개념
- URI는 식별, URL은 위치를 알려줌
REST의 4가지 속성
REST가 되기 위해서는 몇가지 속성을 만족해야 하는데, 아래와 같다
- 서버의 모든 resource는 클라이언트가 바로 접근할 수 있는 고유 URL이 존재
- 모든 Request는 클라이언트가 요청할 때마다 필요한 정보를 주기 때문에 세션 정보를 보관할 필요 없음
- HTTP 메소드를 사용 → Resource는 HTTP Method인 GET, POST, PUT, DELETE
- 서비스 내의 하나의 Resource가 주변에 연관된 리소스들과 연결되어 표현이 돼야 함
URL Session
공식문서 정의
An object that coordinates a group of related, network data transfer tasks
용도
URL로 표기되는 곳에서 데이터를 다운로드 및 업로드 할 수 있는 API를 제공
애플에서 제공하는 앱과 서버 간의 데이터를 주고받기 위한 API라 생각하면 될 듯?!
URL Session을 사용하면 여러 Protocol과 Authentication, Cache와 Cookie 관리 등을 할 수 있음
URL Session 기본 설정으로 사용하기
- URLSessionConfiguration 생성
let config = URLSessionConfiguration.default // default, ephemeral, background 사용 가능
let session = URLSession(configuration: config)
직접 URLSession을 사용해보니, 위와 같이 default로 설정할거면 굳이 이 코드를 작성할 필요가 없었다
그렇다면 configuration으로는 어떤게 있을까
- default - 기본 통신
- ephemeral - 쿠키나 캐시를 저장하지 않도록 정책을 가져가는 통신
- background - 앱이 백그라운드에 있는 상황에서 다운로드/업로드 할 수 있도록 함
- URLComponents 생성하여 query 설정
var urlComponents = URLComponents(
string: "https://ids-identity-project.tistory.com/search/users?"
let urlIDQuery = URLQueryItem(name:"Id", value:"123")
let urlAgeQuery = URLQueryItem(name:"age", value:"20")
urlComponents?.queryItems?.append(userIDQuery)
urlComponents?.queryItems?.append(ageQuery)
이것도 이론만 공부할때는 몰랐고, 직접 해보니 알게 된 부분이다
위 코드를 보면 URLComponents()를 사용하여 변수를 만들었는데, 이렇게 하면 URL()로 만든것과 차이점이 생긴다
3번째 단계 코드를 잘 보면 첫번째 줄에 urlComponents?.url이 되어 있는걸 볼 수 있는데, 엄연히 URLComponent와 URL은 다른 것이기 때문에 URL을 사용하려면 URLComponent를 URL로 전환해주는 과정이 필요하다
그러면 굳이 왜 한번 더 전환해야 하는데 URLComponent를 사용할까?
Query문을 추가하기 쉬워지기 때문..!
결국 JSON 형태의 데이터를 가져올 때 Query문으로 어떤 데이터를 가져올지 지정을 해야할텐데 그냥 URL에서는 하기 번거로운걸 URLComponent에서는 쉽게 추가할 수 있다!!
위 코드와 같이 그냥 append만 해도 되고
필요한 Query문을 Array 형식으로 저장하고, queryItems로 넣을수도 있다
let urlNumberQuery = [URLQueryItem(name: "number", value: "\(number)")]
urlComp.queryItems = urlNumberQuery
- URLComponents와 URLSession을 이용하여 Task 생성
guard let requestURL = urlComponents?.url else { return }
let dataTask = session.dataTask(with: requestURL) { (data, response, error) in
// error가 존재하면 종료
guard error == nil else { return }
// status 코드가 200번대여야 성공적인 네트워크라 판단
let successsRange = 200..<300
guard let satusCode = (response as? HTTPURLResponse)?.statusCode,
successsRange.contains(statusCode) else { return }
// response 데이터 획득, utf8인코딩을 통해 string형태로 변환
guard let resultData = data else { return }
let resultString = String(data: resultData, encoding: .utf8)
print(resultData)
print(resultString)
}
dataTask.resume()
마지막으로 해당 Session에서 어떤 Task를 수행할지 지정하고, 해당 Task에서 수행할 코드를 작성해주면 된다
Task 생성 후 실행할 코드는 주석으로 설명을 뒀으니 자세한 설명은 생략..
사실 이 부분은 더 공부가 필요..
Async / Await
먼저 나도 synchronous와 asynchronous가 뭔지 잘 몰랐기 때문에..
간략하게 설명을 두겠다
Synchronous
→ Thread에서 주어진 task만 처리 가능
Asynchronous
→ Thread에서 주어진 task를 실행하면서 다른 task도 병행
→ 주어진 task를 완료하면 completionHandler 등이 알려줄 수 있음
Async
공식문서 정의
Schedules a work item for immediate execution, and returns immediately
"Enables a function to suspend"
함수를 유예할 수 있게 만들어줌
Await
"Marks where an async function may suspend execution"
Async 함수가 어디에서 유예할 가능성이 있는지 마크해줌
사실 async / await 모두 개념적으로는 이해가 어느정도 됐는데
실제로 코드에서 쓰라고 하면 어떻게 써야할지 모르겠다..
추가 궁금증
URLSession.dataTask(
with: URLRequest,
completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void
)
URLSession.data(with: URLRequest) async throws -> (Data, URLResponse)
공식 영상을 보면 completion handler를 async / await로(위 코드에서 아래 코드로) 간략화 시킬 수 있는걸 강조하는데
completion handler는 말 그대로 어떤걸 마치고 나서 실행시킬 코드가 아닌가..!
하지만 async/await는 비동기처리에 사용되는것 같은데 이 두가지가 어떻게 연관이 되는지...
이 부분은 추가로 공부를 해보면서 알아봐야하지 않을까 싶다🤔
JSONDecoder / Codable
JSONDecoder
공식문서 정의
An object that decodes instances of a data type from JSON objects
→ JSON 형식의 struct에는 Codable Protocol이 포함되어야지 Decoding 가능
Codable 및 JSONDecoder를 사용한 예시
struct GroceryProduct: Codable {
var name: String
var points: Int
var description: String?
}
let json = """
{
"name": "Durian",
"points": 600,
"description": "A fruit with a distinctive scent."
}
""".data(using: .utf8)!
let decoder = JSONDecoder()
let product = try decoder.decode(GroceryProduct.self, from: json)
print(product.name) // Prints "Durian"
Codable
공식문서 정의
A type that can convert itself into and out of an external representation
→ Encodable과 Decodable 모두를 아우르는 Protocol
단, Encoding과 Decoding 둘 중 하나만 필요한 경우에는 Codable보다는 하나만 사용하기..!
Codingkey
Codable 인스턴스가 Encoding 또는 Decoding 될 때
필수로 필요한 property를 enum 형식으로 지정해둠
반대로, 필수로 필요한 property가 아니라면
Codingkey에서 제외하여 필수가 아님을 표시할 수 있음
→ JSON 파일을 Decoding 할 때 key 값이 매치되지 않는 경우가 생기면,
Codingkey Protocol을 사용해 해결 가능!
코드 예시
let jsonString = """
{
"name" : "Zedd",
"age" : 100,
"**birth_date**" : "2017-01-22T23:16:50+0000"
}
"""
struct Person: Codable {
var name: String
var age: Int
var birthday: Date
}
위와 같은 경우, birthday라는 key가 없다는 오류 발생하는데,
이런 경우에 Codingkey를 enum으로 추가하면 해결..!
struct Person: Codable {
var name: String
var age: Int
var birthday: Date
enum CodingKeys: String, CodingKey {
case name
case age
case birthday = "birth_day"
}
}
추가로, 이렇게 하면 JSON 파일의 birth_day라는 key는
Codingkeys의 birthday key와 매치됨!
참고자료
URLSession
Apple Developer Documentation
JSONDecoder
https://www.youtube.com/watch?v=CimY_Sr3gWw
Codable
Apple Developer Documentation
'멀고도 험난한 개발 일지' 카테고리의 다른 글
UIKit_유투브_신동규 - #2 Pro처럼 UI 디자인하기 (0) | 2022.07.10 |
---|---|
UIKit_유투브_신동규 - #1 Pro처럼 project 시작하기 (0) | 2022.07.10 |
이런저런_구글링_2 - Alamofire, 얼탱이 없는 Moya.. (0) | 2022.06.23 |
이런저런_구글링_1 - Date to String, Design Pattern, Voice Recorder, Post Method (0) | 2022.06.11 |
Stanford SwiftUI - Lecture 1 (0) | 2022.05.22 |