- ํ๋ก์ ํธ ๊ธฐ๊ฐ: 2022.01.03 - 2022.01.14
- Ground Rules
- ์๊ฐ
- ์์์๊ฐ 10์
- ์ ์ฌ์๊ฐ 12์~2์
- ์ ๋ ์๊ฐ 6์~7์ ์ฌ์ด๋ถํฐ 2์๊ฐ
- ์งํ ๊ณํ
- ํ๋ก์ ํธ๊ฐ ์ค์ฌ์ด ์๋ ํ์ต๊ณผ ์ด์ ์ ์ด์ ์ ๋ง์ถ๊ธฐ
- ์๋ฌธ์ ์ ๊ทธ๋ฅ ๋์ด๊ฐ์ง ์๊ธฐ
- ์คํฌ๋ผ
- 10์์ ์คํฌ๋ผ ์์
- ์๊ฐ
- ์ปค๋ฐ ๊ท์น
- ๋จ์
- ๊ธฐ๋ฅ ๋จ์
- ๋ฉ์ธ์ง
- ์นด๋ฅด๋ง ์คํ์ผ
- ๋จ์
์์กด์ฑ ์ฃผ์ (DI)
URLSession
URLProtocol
URLRequest
API
HTTP
TCP/IP
MIME-Type
multipart/form-data
application/json
Result
Codable
CodingKey
Async Test
- ๋คํธ์ํฌ ํต์ ์ ๋ด๋นํ ํ์ ์ ์ค๊ณํฉ๋๋ค.
- Mock ๋ฐ์ดํฐ๋ฅผ ํ์ฉํ์ฌ ๋จ์ํ ์คํธ๋ฅผ ์ํํฉ๋๋ค.
- ํ ํ์ ์ด ํ๋์ ์ญํ ๋ง ํ ์ ์๋๋ก ์ค๊ณ์ ๋ง์ ๊ณ ๋ฏผ์ ํด๋ณด์๋ค.
์ค์ ๋คํธ์ํฌ์์ ๋ด๋ ค์ค๋ ๋ณ์๋ช
์ด ์ค๋ค์ดํฌ ์ผ์ด์ค๋ฅผ ์ฌ์ฉํ๋ ๋ณ์๋ Codingkey
๋ฅผ ์ด์ฉํ์ฌ parsingํ๋ key๋ฅผ ๋ฐ๊ฟ์ฃผ์์ผ๋ฉฐ ์ค๋ค์ดํฌ์ผ์ด์ค๋ฅผ ์ฌ์ฉํ์ง ์๋, ์ฆ ํ์
์ ๋ณ์๋ช
๊ณผ ์ผ์นํ๋ฉด rawValue๋ฅผ ๋ช
์ํ ํ์๊ฐ ์์ด ๊ฐ๋
์ฑ์ ์ํด ํ ์ค๋ก case๋ฅผ ํฉ์ณ์ฃผ์๋ค.
enum CodingKeys: String, CodingKey {
case id, stock, name, thumbnail, currency, price, images, vendors
case vendorID = "vendor_id"
case bargainPrice = "bargain_price"
case discountedPrice = "discounted_price"
case createdAt = "created_at"
case issuedAt = "issued_at"
}
- Networkํ๋ ๊ณผ์ ์์ ์ญํ ๋ง๋ค ๊ฐ์ฒด๋ฅผ ๊ตฌ๋ถํ์ฌ ๊ตฌํํ์๋ค.
-
Network
: dataTask()๋ฅผ ํตํด SessionDataTask๋ฅผ ์๋ฒ๋ก ์ ์กํด ์ง์ ๋คํธ์ํนํ๋ ๊ฐ์ฒดfunc execute(request: URLRequest, completion: @escaping (Result<Data?, Error>) -> Void) { session.dataTask(with: request) { data, response, error in ...
-
NetworkManager
: Network์ excute๋ฅผ ํตํด data๋ฅผ ๋ฐ์ decodingํ๋ fetch()๋ฅผ ๊ฐ์ง ๊ฐ์ฒดfunc fetch<T: Decodable>(request: URLRequest, decodingType: T.Type, completion: @escaping (Result<T, Error>) -> Void) { network.execute(request: request) { result in
-
- ํ๋์ฝ๋ฉ์ ๊ฐ์ ํ๊ธฐ ์ํด enum ํ์ ์ ๋ง๋ค์ด Address์ HTTPMethod์ ๊ฐ๋ค์ ๋ถ๋ฅํด์ฃผ์๋ค.
- Requestํ ๋, ๊ทธ๋ฆฌ๊ณ Responseํ๋ ํ์ ์ด ์ธ๋ถ์ ์ผ๋ก ๋ฌ๋ผ ProductModification, ProductRegistration ๋ฑ... ๊ฐ ํ์ ์ ๋ชจ๋ ๊ตฌํํ์๋ค.
- ์ํ ์ญ์ , ๋ฑ๋ก, ์กฐํ ๋ฑ ์ฌ๋ฌ๊ฐ์ง ์์ฒญ์ request ๋ฉ์๋ ํ๋๋ฅผ ์ค๋ฒ๋ก๋ฉ์ ํ์ฉํ์ฌ ์์ฑํ์๋ค.
- ํ ์คํธ ์์ฑ์ ์ํด ์์กด์ฑ ์ฃผ์ ์ ํ์ฉํ์ฌ Mock, Stub ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ํ์ฉํ์๋ค.
- URLProtocol์ ์์๋ฐ์ ํด๋์ค๋ฅผ ๋ง๋ค๊ณ ์ฌ์ ์๋ฅผ ํด์ฃผ์๋ค.
- ์ด ๋ฐฉ๋ฒ์ URLSession์ dataTask๋ฅผ ์ง์ Stub์ผ๋ก ๋ง๋๋ ๋ฐฉ๋ฒ๋ ์์์ง๋ง, URLSessionDataTask๋ฅผ ์ฑํํ ํ์ ์ init()์ ์ ์ํ๋ deprecated ๊ฒฝ๊ณ ๊ฐ ๋ ์ ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ญ์ ํ URLProtocol์ ํ์ฉํ๋ ๋ฐฉ๋ฒ์ผ๋ก ๋ก์ง์ ๋ณ๊ฒฝํ์๋ค.
- ๋น๋๊ธฐ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๋๊ธฐ ๋ฉ์๋๋ ๋น๋๊ธฐ ๋ฉ์๋ ํ ์คํธ๋ก ์งํํด์ผํ ๊น?
- URLProtocol๊ณผ URLSession์ ๊ด๊ณ๊ฐ ์ ํํ๊ฒ ์ดํด๋์ง ์๋๋ค...
- Health Checker์ ํ์์ฑ์ ๋ชจ๋ฅด๊ฒ ๋ค..
- ํ ์คํธ ์ Request์ ๋ฐ๋๋ ์ฒดํฌ๋ฅผ ํด์ผํ ๊น?
1. URLSessionDataTask๋ฅผ ์ฑํํ ํ์ ์ init()์ deprecated ๊ฒฝ๊ณ ..?
์ํฉ
URLSessionDataTask์ ๋์ฒดํ ๊ฐ์ฒด๋กยStubURLSessionDataTask
ย ๋ฅผ ๊ตฌํํ๋ค๊ฐ ๊ฒฝ๊ณ ๋ฅผ ๋ง์ฃผํ๊ฒ ๋์๋ค.
class StubURLSessionDataTask: URLSessionDataTask {
var dummyData: DummyData?
// init ๋ถ๋ถ์์ ์๋ฌ๊ฐ ๋ฌ๋ค.
init(dummy: DummyData?, completionHandler: DataTaskCompletionHandler?) {
self.dummyData = dummy
self.dummyData?.completionHandler = completionHandler
}
override func resume() {
dummyData?.completion()
}
}
'init()' was deprecated in iOS 13.0: Please use -[NSURLSession dataTaskWithRequest:] or other NSURLSession methods to create instances
์ด์
URLSessionDataTaskinit()
์ด IOS13 ์ดํ์ deprecatede๋์๊ธฐ ๋๋ฌธ์ด๋ค. ํด๋น ๊ฒฝ๊ณ ๋ฅผ ์์ ๊ณ ์ถ์ด์ ๊ตฌ๊ธ๋ง์ ํ๋ค๊ฐURLProtocol
์ ๋ฐ๊ฒฌํ๊ฒ ๋์๋ค.ํด๊ฒฐ
URLProtocol์ ์์๋ฐ์ MockURLProtocol์ ๋ง๋ค์ด์ URLSession configuration์ ๊ตฌ์ฑํ๋ ๋ฐฉ๋ฒ์ผ๋ก ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ ๊ธฐ์กด์ ๋ง๋ค์๋ StubURLSessionDataTask, DummyData, MockSession ํ์ ์ ๋์ด์ ์ฌ์ฉํ์ง ์๊ฒ๋์ด ๋ชจ๋ ์ญ์ ํด์ฃผ์๋ค.URLProtocol
์ด๋?- URL ๋ฐ์ดํฐ ๋ก๋ฉ์ ๋ค๋ฃจ๋ ์ถ์ํด๋์ค
- URLProtocol์ URLProtocolClient ํ๋กํ ์ฝ์ ํตํด ๋คํธ์ํฌ ์งํ ์ํฉ์ ์ ๋ฌํ๋ค.
- ํ ์คํธ ๋ฒ๋ค์์ MockURLProtocol ํด๋์ค๋ฅผ ๋ง๋ค๊ณ ๋ฉ์๋๋ฅผ ์ฌ์ ์ ํด์ค๋ค.
- ๋ก๋๋ฅผ ํ ๋ ์ค์ ํ ํ ์ ๋ฌํ Data, Error, Response๋ฅผ ๋์
๋๋ฆฌ๋ก ์ค์ ํด์ค๋ค.
- ์ด ๊ฐ์ URLProtocol์ ์ฐ๊ฒฐํ์ฌ ์ค์ ๊ฐ์ ์ธํ ํด์ฃผ๊ธฐ ์ํ ๊ฐ์ด ๋๋ค.
- Unit Test๋ฅผ ์ํด ์์๋ฐ์์ ์ค๋ฒ๋ผ์ด๋ ํจ์ผ๋ก์จ ์ปค์คํ
ํ์ฌ Mock ๊ฐ์ฒด๋ฅผ ์๋กญ๊ฒ ๋ง๋ค ์ ์๋ค.
- ๊ธฐ์กด์ฒ๋ผ ์ธ๋ถ ๋คํธ์ํฌ์ ์์ฒญ์ ์ง์ ๋ณด๋ด๋ ๋์์ด ์๋๋ผ, ์์ฒญ์ ๊ฐ๋ก์ฑ์ ์ํ๋ ์๋ต์ ๋ฐํํ๊ฒ ๋ ์ปค์คํ ํ๋ ์์ ์ด๋ค.
- ์ฆ ์๋ ๊ฐ์ด ์น ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ๊ณผ์ ์ด ์๋๊ณ , ๋ด๊ฐ ์ค์ ํ ๊ฐ(data, response)์ ๊ทธ๋๋ก ๋ฐํํ๊ฒ ๋ง๋ค์ด ์ฃผ๋ ๊ณผ์ ์ธ ๊ฒ์ด๋ค.
2. multipart form-data ์์ ์ด๋ฏธ์ง์ JSON์ ๊ฐ์ด ๋ฃ๋ ๋ฐฉ๋ฒ
-
์ํฉ
JSON์ ์ธ์ฝ๋ฉํด์ ๋ฐ๋์ ์ถ๊ฐํด์ฃผ๋ฉด ๋์ง๋ง,multipart form-data
์ ๊ฒฝ์ฐ ์์์ด ๋ฌ๋๋ค. -
์ด์
์๋ ์์์ ๋ง์ถฐ์ JSON๊ณผ ์ด๋ฏธ์งํ์ผ์ ๋ณํํด์ ๋ฐ๋์ ๋ฃ์ด์ฃผ๊ธฐ ์ํด์multipart form-data
์ผ๋กbody
์ ํ์ผ์ ์ค์ด๋ณด๋ ์์ ์ ์ฐพ์๋ณด์๋ค.POST /test.html HTTP/1.1 // \r\n Host: example.org // \r\n Content-Type: multipart/form-data;boundary="boundary" // \r\n // \r\n --boundary // \r\n Content-Disposition: form-data; name="field1" // \r\n // \r\n value1 // \r\n --boundary // \r\n Content-Disposition: form-data; name="field2"; filename="example.txt" // \r\n // \r\n value2 // \r\n --boundary-- // \r\n
- HTTP ํต์ ๊ท๊ฒฉ์ ํ์ธํด์ JSONํ์ผ๊ณผ Imageํ์ผ์ ๋ฐ๋์ ์ถ๊ฐํ๊ฒ ์ฝ๋๋ฅผ ์ง์ผํ๋ค.
- Content-Type์ด multipart form-data๋ก ์ง์ ๋์ด ์์ด์ผํ๋ค.
- ์ ์ก๋๋ ํ์ผ ๋ฐ์ดํฐ์ ๊ตฌ๋ถ์๋ก boundary์ ์ง์ ๋์ด์๋ ๋ฌธ์์ด์ ์ด์ฉํ๋ค.
- ๋ง์ง๋ง์๋ boundary ์์์
--
๋ฅผ ๋ถ์ฌ์ ๋ฐ๋์ ๋์ ์๋ฆฐ๋ค. - header์ header๋ฅผ ๊ตฌ๋ถํ๊ธฐ ์ํด ๊ฐํ๋ฌธ์๋ฅผ ์ถ๊ฐํ๋ค.
\r\n
- header์ body๋ฅผ ๊ตฌ๋ถํ๊ธฐ ์ํด ๊ฐํ๋ฌธ์ 2๊ฐ๋ฅผ ์ถ๊ฐํ๋ค.
\r\n\r\n
- body์ ํฌํจ๋์ด์๋ file data๋ฅผ ๊ตฌ๋ถํ๊ธฐ ์ํด boundary๋ฅผ ๋ฃ์ด์ค๋ค.
- HTTP ํต์ ๊ท๊ฒฉ์ ํ์ธํด์ JSONํ์ผ๊ณผ Imageํ์ผ์ ๋ฐ๋์ ์ถ๊ฐํ๊ฒ ์ฝ๋๋ฅผ ์ง์ผํ๋ค.
-
ํด๊ฒฐ
์์์ ์ ๋ฆฌํ ์์๋๋ก ๋ฐ๋๋ฅผ ์ถ๊ฐํ๋๋ก ์ฝ๋๋ฅผ ์์ฑํ์๋ค.
multipart/form-data
- API๋ฌธ์ ์ฝ๋ ๋ฐฉ๋ฒ
- ํ์ฑํ JSON ๋ฐ์ดํฐ์ ๋งคํํ ๋ชจ๋ธ ์ค๊ณ
CodingKeys
ย ํ๋กํ ์ฝ์ ํ์ฉ
- URL Session์ ํ์ฉํ ์๋ฒ์์ ํต์
URLRequest
๋ฅผ ์ค์ ํ๋ ๋ฐฉ๋ฒ- Testableํ ๋คํธ์ํฌ ์ฝ๋ ์์ฑํ๊ธฐ
- ๋คํธ์ํฌ ์ํฉ๊ณผ ๋ฌด๊ดํ ๋คํธ์ํน ๋ฐ์ดํฐ ํ์ ์ ๋จ์ ํ ์คํธ(Unit Test)