네트워크 통신의 이해
- iOS에서 서버와의 통신과 처리
HTTP 프로토콜에 대한 이해
- 네트워킹
- TCP / IP 프로토콜
- 여러가지 프로토콜의 결합
- 스위프트 내 프로토콜은 약속같은 느낌.
- 일상생활에서 와이파이를 잡을 수 있는 이유는 여러가지 약속(HTTP, IP, TCP…)의 조합을 통해서 사용할 수 있음.
- 앱을 만들기 위해 알아야 할 프로토콜은 HTTP
- 웹을 사용하기 위해선 (WWW를 구성하는 기술) → HTML, HTTP, URL
- TCP / IP 프로토콜
- HTTP 프로토콜
- HyperText Transfer Protocol
- 하이퍼 문서를 전송하는 것에서 시작
- 현재는 이미지 / 영상 / 음성 / 파일 / JSON 등 모든 형태의 데이터 전송가능
- 인터넷의 모든 것은 HTTP로 이루어져 있음
- TCP / IP 프로토콜 [4계층]
- 클라이언트에서
- APP - 애플리케이션: HTTP 프로토콜 (리퀘스트)
- 편지지(request)를 작성하는 것과 유사
- 각각의 어플이 자신들만의 HTTP 프로토콜 안에서 각각의 편지지를 작성한다는 뜻
- OS (아래는 모두 보완 수단)
- 트랜스포트: TCP (조각 / PORT붙임)
- 각각의 app마다 다른 port를 가짐
- port를 붙이는 일을 진행
- 편지지를 봉투에다가 넣는 행위
- 인터넷: IP
- 패킷의 이동 (네트워크 경로 결정)
- 편지지 규격에 맞게 주소를 쓰는 것
- 트랜스포트: TCP (조각 / PORT붙임)
- LAN
- 링크: 네트워크(MAC주소 추가)
- 디바이스 드라이버 / 물리적 하드웨어
- 택배 상자에 넣는 행위
- 링크: 네트워크(MAC주소 추가)
- 서버에서
- 링크: 네트워크
- 택배 상자를 벗김
- 트랜스포트, 인터넷
- 편지지 봉투를 벗김
- 애플리케이션: HTTP
- 편지지 내부를 확인
- 링크: 네트워크
| HTTP | 데이터를 어떻게 주고 받을지에 대한 약속 (요청 / 응답)
- 리퀘스트 → HTTP 요청 메시지 |
| --- | --- |
| TCP | 주고 받을 상태 확인 및 검증 / PORT (어떤 앱과 통신하는지) |
| IP | 주고 받는 주소(경로) |
- HTTP 메시지 형태 / 데이터를 어떻게 주고 받을지에 대한 약속
- HTTP 프로토콜 / 요청 메서드의 종류
- R(Read) -GET : 네이버 중고나라 게시물을 확인하고 싶을때(조회)
- C(Create) - POST: 게시물을 등록(등록)
- U(Update) - PUT: 게시물 수정(데이터 전부 대체)(데이터 대체, 없으면 생성)
- PATCH: 게시물 좋아요 누르기
- D(Delete) - DELETE: 삭제
- → 일반적으로 CRUD라고 불림
- HTTP 프로토콜 / 응답 상태 코드
- 1xx : 메시지는 볼일이 거의 없고
- 2xx : 메시지는 정상처리
- 3xx : 클라이언트가 요청 시, 새로운 주소로 안내하는 경우.
- 4xx : 클라이언트 에러, 요청을 하는 방식에 대해서 뭔가 잘못 요청한 것
- 5xx : 서버 에러(서버 다운, 정기점검)
HTTP 프로토콜
- URL query
- key = value의 형태
- ?로 시작, &로 추가 기능
- 밑의 코드에선 q=swift&hl=ko가 쿼리 파라미터
- 443 → 포트(아파트 주소)
https://www.google.com:443/search**?q=swift&hl=ko**
- 쿼리 파라미터를 통한 데이터 전송
- GET 메소드
- (예) 검색어 / 정렬기준
- 메시지 바디를 통한 데이터 전송
- POST / PUT / PATCH
- (예) 회원가입 / 게시글 작성 / 게시글 수정\
- Response(응답) 데이터의 형태 / 앱에서 처리하는 데이터의 형태
- 실제 JSON의 형태
{
"이름": "홍길동",
"나이": 25,
"성별": 남
}
REST API
- REST한 형식의 API (예전엔 SOAP)
- https://(사이트 주소)/movielists → 상영중 영화목록 요청 GET
iOS에서의 네트워킹
- URL : URL 주소가 필요
- URL Session : 객체, 애플이 통신을 자유롭게 하기 위해 만든 것. 브라우저를 키는 행위와 비슷
- dataTask : 일거리, 클로저를 통해서 데이터가 넘어오게 함
- 시작(resume) : 엔터를 치는 행위처럼 시작 버튼을 눌러줘야함
- 서버에서는 요청사항을 받아서 메세지 바디를 통해 JSON 데이터를 받게 된다.
- 이를 변형시켜서 우리가 사용하는 형태로 정제해야한다.
var myKey = "330491f5e07bfbabc52d952e3e95e9e8"
// 0. URL주소 - 문자열로
let movieURL = "http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.xml?key=330491f5e07bfbabc52d952e3e95e9e8&targetDt=20120101"
//"http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.xml?key=330491f5e07bfbabc52d952e3e95e9e8&targetDt=20120101"
// 1. URL 구조체 만들기
let url = URL(string: movieURL)!
// 2. URLSession 만들기 (네트워킹을 하는 객체 - 브라우저 같은 역할)
let session = URLSession.shared
// 3. 세션에 (일시정지 상태로)작업 부여
let task = session.dataTask(with: url) { (data, response, error) in
// 일반적으로 error 유무를 먼저 확인
if error != nil {
print(error!)
return
}
if let safeData = data {
print(String(decoding: safeData, as: UTF8.self))
}
// guard let safeData = data else {
//
// return
// }
// 데이터를 그냥 한번 출력해보기
//print(String(decoding: safeData, as: UTF8.self))
//dump(parseJSON1(safeData)!)
}.resume()
// 이런 형태로도 많이 사용
//URLSession.shared.dataTask(with: url) { d, r, e in
// if error != nil {
// print(error!)
// return
// }
//
// if let safeData = data {
// print(String(decoding: safeData, as: UTF8.self))
// }
//}
// 4.작업시작
task.resume() // 일시정지된 상태로 작업이 시작하기 때문
JSON Parsing
// 서버에서 주는 데이터의 형태 ====================================================
struct MovieData: Codable {
let boxOfficeResult: BoxOfficeResult
}
// MARK: - BoxOfficeResult
struct BoxOfficeResult: Codable {
let dailyBoxOfficeList: [DailyBoxOfficeList]
}
// MARK: - DailyBoxOfficeList
struct DailyBoxOfficeList: Codable {
let rank: String
let movieNm: String
let audiCnt: String
let audiAcc: String
let openDt: String
}
- 분석 → Parsing
- Decodable 프로토콜
- 이를 채택해야 JSONDecoder에서 코드를 분석할 수 있음.
- Encodable 프로토콜
- 구조체나, 클래스를 데이터 형태로 변형시켜주는 프로토콜
- 위 둘을 합쳐서 Codable 이라는 프로토콜로 명명이 되어 있음.
- Decodable 프로토콜
- JSONDecoder() 객체
- decode(변형하고 싶은 형태, from: 데이터) 메서드
// 예전 형태
func parseJSON2(_ movieData: Data) -> [DailyBoxOfficeList]? {
do {
var movieLists = [DailyBoxOfficeList]()
// 스위프트4 버전까지
// 딕셔너리 형태로 분석
// JSONSerialization
if let json = try JSONSerialization.jsonObject(with: movieData) as? [String: Any] {
if let boxOfficeResult = json["boxOfficeResult"] as? [String: Any] {
if let dailyBoxOfficeList = boxOfficeResult["dailyBoxOfficeList"] as? [[String: Any]] {
for item in dailyBoxOfficeList {
let rank = item["rank"] as! String
let movieNm = item["movieNm"] as! String
let audiCnt = item["audiCnt"] as! String
let audiAcc = item["audiAcc"] as! String
let openDt = item["openDt"] as! String
// 하나씩 인스턴스 만들어서 배열에 append
let movie = DailyBoxOfficeList(rank: rank, movieNm: movieNm, audiCnt: audiCnt, audiAcc: audiAcc, openDt: openDt)
movieLists.append(movie)
}
return movieLists
}
}
}
return nil
} catch {
return nil
}
}
// 현재의 형태
func parseJSON1(_ movieData: Data) -> [DailyBoxOfficeList]? {
do {
// 스위프트5
// 자동으로 원하는 클래스/구조체 형태로 분석
// JSONDecoder -> 데이터를 우리가 원하는 코드 형태로 변환
let decoder = JSONDecoder()
let decodedData = try decoder.decode(MovieData.self, from: movieData)
return decodedData.boxOfficeResult.dailyBoxOfficeList
} catch {
return nil
}
}
- 최종
import UIKit
var movieURL = "http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=(key입력)&targetDt=20120101"
// 1. URL 구조체 만들기
let url = URL(string: movieURL)!
// 2. URLSession 만들기 (네트워킹을 하는 객체 - 브라우저 같은 역할)
let session = URLSession.shared
// 3. 세션에 (일시정지 상태로) 작업 부여
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
print(error!)
return
}
guard let safeData = data else {
return
}
var movieArray = parseJSON1(safeData)
dump(movieArray!)
}
// 4. 작업 시작
task.resume()
func parseJSON1(_ movieData: Data) -> [DailyBoxOfficeList]? {
do {
// JSONDecoder
let decoder = JSONDecoder()
let decodedData = try decoder.decode(MovieData.self, from: movieData)
return decodedData.boxOfficeResult.dailyBoxOfficeList
} catch {
return nil
}
}
// MARK: - Welcome
struct MovieData: Codable {
let boxOfficeResult: BoxOfficeResult
}
// MARK: - BoxOfficeResult
struct BoxOfficeResult: Codable {
let dailyBoxOfficeList: [DailyBoxOfficeList]
}
// MARK: - DailyBoxOfficeList
struct DailyBoxOfficeList: Codable {
let rank: String
let movieNm: String
let audiCnt: String
let audiAcc: String
let openDt: String
}
네트워크 통신(서버와의 통신)의 기초
- 요청(Request) → 서버데이터(JSON) → 분석(Parse) → 변환(Struct / Class)
import UIKit
// 서버에서 주는 데이터 ===========================================================
struct MovieData: Codable {
let boxOfficeResult: BoxOfficeResult
}
// MARK: - BoxOfficeResult
struct BoxOfficeResult: Codable {
let dailyBoxOfficeList: [DailyBoxOfficeList]
}
// MARK: - DailyBoxOfficeList
struct DailyBoxOfficeList: Codable {
let rank: String
let movieNm: String
let audiCnt: String
let audiAcc: String
let openDt: String
}
// 내가 정제한 데이터
struct Movie {
static var movieId: Int = 0
let movieName: String
let rank: Int
let openDate: String
let todayAudience: Int
let totalAudience: Int
init(movieNm: String, rank: String, openDate: String, audiCnt: String, accAudi: String) {
self.movieName = movieNm
self.rank = Int(rank)!
self.openDate = openDate
self.todayAudience = Int(audiCnt)!
self.totalAudience = Int(accAudi)!
Movie.movieId += 1
}
}
// 서버와의 통신 시 일반적으로 Manager 구조체로 감싼다
struct MovieDataManager {
let movieURL = "http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?"
let myKey = "7a526456eb8e084eb294715e006df16f"
func fetchMovie(date: String, completion: @escaping ([Movie]?) -> Void) {
let urlString = "\(movieURL)&key=\(myKey)&targetDt=\(date)"
performRequest(with: urlString) { movies in
completion(movies)
}
}
func performRequest(with urlString: String, completion: @escaping ([Movie]?) -> Void) {
print(#function)
// 1. URL Struct
guard let url = URL(string: urlString) else { return }
// 2. URlSession
let session = URLSession.shared
// 3. 작업 부여
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
completion(nil)
return
}
guard let safeData = data else {
completion(nil)
return
}
// 데이터 분석
if let movies = self.parseJSON(safeData) {
completion(movies)
} else {
completion(nil)
}
}
// 4. Start the task
task.resume()
}
func parseJSON(_ movieData: Data) -> [Movie]? {
print(#function)
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(MovieData.self, from: movieData)
let dailyLists = decodedData.boxOfficeResult.dailyBoxOfficeList
let myMovieLists = dailyLists.map {
Movie(movieNm: $0.movieNm, rank: $0.rank, openDate: $0.openDt, audiCnt: $0.audiCnt, accAudi: $0.audiAcc)
}
return myMovieLists
} catch {
print("parsing fail")
return nil
}
}
}
var downloadedMovies = [Movie]()
let movieManager = MovieDataManager()
movieManager.fetchMovie(date: "20240201") { (movies) in
if let movies = movies {
downloadedMovies = movies
dump(downloadedMovies)
print("total movie: \(Movie.movieId)")
} else {
print("No Movie Data, Or fail to download")
}
}
반응형
'iOS > Swift' 카테고리의 다른 글
비동기 프로그래밍(About Asynchronous) [2] (1) | 2024.02.17 |
---|---|
비동기 프로그래밍(About Asynchronous) [1] (0) | 2024.02.16 |
Swift No.25 (0) | 2024.02.03 |
Swift No.24 (0) | 2024.02.02 |
Swift No.23 (1) | 2024.01.31 |