- Published on
๐ Swift - URLProtocol
- Authors
- Name
- ์ด์ฐฝ์ค
- ๋ฐฐ๊ฒฝ
- ๊ณผ์
- URLRequest to Response
- URLProtocol
- canInit
- canonicalRequest
- startLoading
- stopLoading
- XCTestCase์์ ์ฌ์ฉํด๋ณด๊ธฐ
- ๊ฒฐ๊ณผ
- ์ฐธ๊ณ ๋ฌธ์
๋ฐฐ๊ฒฝ
๋คํธ์ํน์ ํ ์คํธํ๊ธด ํด์ผํ ๊ฒ ๊ฐ์๋ฐ.. ์ด๋ป๊ฒ ํ์ง...?
์..! ํ์ต ์คํ๋ฆฐํธ ๋ URLProtocol
์ด๋๊ฑธ ์ธ๊ธํ์
จ๋ ๊ฒ ๊ฐ์๋ฐ..!?
๊ณผ์
URLRequest to Response
์ฑ์ ์ค์ ๋ก ๋คํธ์ํฌ ํต์ ์ ํ๊ธฐ ์ํด ์ ์ฌ์ง๊ณผ ๊ฐ์ ๊ณผ์ ์ ๊ฑฐ์นฉ๋๋ค.
- URLRequest๋ฅผ ์์ฑํ๊ณ
- URLRequest๋ฅผ ์ด์ฉํด URLSession Task๋ฅผ ๋ง๋ญ๋๋ค.
- ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ผ๋ฉด
- ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ํ์ฑํด์ ์ฌ์ฉํฉ๋๋ค.
์ ํฌ๋ ์ด ์ค์์ 3๋ฒ ๊ณผ์ ์ Mock ์๋ฒ๋ก ๋ฐ๊พธ๊ณ ์ถ์๊ฑฐ์ฃ ?
URLSession์ ๋ง๋ค ๋ ํ์ํ URLSessionConfiguration
์ URLProtocol
๋ค์ ์ฌ์ฉํ๋ค๊ณ ํ๋๋ฐ์,
let configuration: URLSessionConfiguration = .ephemeral
configuration.protocolClasses?.insert(MockURLProtocol.self, at: .zero)
์ฌ๊ธฐ์ ์ ํฌ๊ฐ ์ํ๋ ์ปค์คํ Protocol์ ๋ฃ๋๋ค๋ฉด?
์ ํฌ๊ฐ ์ํ๋ ๋ฐฉ์๋๋ก URLSession
์ ์ฌ์ฉํ ์ ์๋๊ฒ๋๋ค!
์ด URLProtocol์ด ๋คํธ์ํฌ ์ปค๋ฅ์ ์ ์ด๊ณ , Request๋ฅผ ์์ฑํ๊ณ , Response๋ฅผ ๋ฐ์ ์ฒ๋ฆฌํ๋ ๊ณผ์ ๊น์ง ๋ชจ๋ ๊ฑธ ์ํ๋ ๋ฐฉ์๋๋ก ๋ง๋ค์ด์ค ์ ์๊ธฐ ๋๋ฌธ์ Mocking์ ์ ์ฉํ ๊ฒ์ด์ฃ .
๊ทผ๋ฐ ๋ง๋ก๋ง ๋ค์ด๋ณด๋ฉด ํ ์คํ ์ด ์๋๋ผ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉํ ๋๋ ๊ฝค ์ ์ฉํ ์ ์๊ฒ ๋ค๋ ์๊ฐ์ด ๋ญ๋๋ค.
URLProtocol
์ด๋ค ๋์ธ์ง๋ ์์์ง๋ง ๊ทธ๋๋ ์ ์๋ ํ ๋ฒ ๋ด์ค์ผ๊ฒ ์ฃ ? ๊ณต์๋ฌธ์ ๊ณ ๊ณ
ํ๋กํ ์ฝ์ ๋ฐ๋ผ URL ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ๊ณผ์ ์ ํธ๋ค๋งํ๋ ์ถ์ ํด๋์ค
์ด๋ฆ์ Protocol์ธ๋ฐ ์ ์ ํด๋์ค๋ค์..ใ
์ฌ์ฉ๋ฒ์ URLProtocol
์ ์์๋ฐ๊ณ , override
๋ฉ์๋๋ฅผ ๋ค ๊ฐ์ง ์์ฑํด์ฃผ๋ฉด ๋ฉ๋๋ค.
canInit
์ฃผ์ด์ง URLRequest
์ URLProtocol
์ ์ ์ฉํ ์ง ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํฉ๋๋ค.
request
ํ๋ผ๋ฏธํฐ์ ์ด๋ค ์กฐ๊ฑด๋ฌธ์ ๊ฑธ์ด์ true
๋ false
๋ฅผ ๋ฐํํด์ฃผ๋ฉด ๋๋๋ฐ...
๋๋ถ๋ถ์ ๊ฒฝ์ฐ์๋ ๊ทธ๋ฅ true
๋ฅผ ๋ฐํํด์ฃผ๋ฉด ๋ฉ๋๋ค.
canonicalRequest
์ฌ๊ธฐ์๋ ์ฌ์ฉํ URLRequest
๋ฅผ ๋ฐ๊ฟ์น๊ธฐ ํ ์ ์๋๋ฐ์..
๋ง์ฐฌ๊ฐ์ง๋ก ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ๊ทธ๋ฅ request
๋ฅผ ๊ทธ๋๋ก ๋ฐํํด์ค๋๋ค.
startLoading
์ค์ ๋ก ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๋ฉ์๋์ ๋๋ค.
static var requestHandler: ((URLRequest) throws -> (HTTPURLResponse, Data))?
์ด๋ฐ requestHandler
๋ฅผ ํ๋ ๋ง๋ค์ด์
guard let handler = MockURLProtocol.requestHandler else {
XCTFail("Received unexpected request with no handler set")
return
}
do {
let (response, data) = try handler(request)
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
client?.urlProtocol(self, didLoad: data)
client?.urlProtocolDidFinishLoading(self)
} catch {
client?.urlProtocol(self, didFailWithError: error)
}
์ด๋ฐ์์ผ๋ก ์ฌ์ฉํด์ค๋๋ค.
stopLoading
์ด ๋ฉ์๋๋ Task๊ฐ ์ทจ์๋์ด์ผ ํ ๋์ ํ๋๊ฐ๋ น๋ค์ ์ ๋ ๊ณณ์ธ๋ฐ์,
MockURLProtocol์ ๊ฒฝ์ฐ์๋ ์ทจ์๋ฅผ ๋ชฉ์ ์ผ๋ก ํ์ง๋ ์๊ธฐ ๋๋ฌธ์ ๋น ์นธ์ผ๋ก ๋น์๋์์ต๋๋ค.
XCTestCase์์ ์ฌ์ฉํด๋ณด๊ธฐ
์ด๋ ๊ฒ MockURLProtocol์ ์์ฑํ๋ค๋ฉด ์ฌ์ฉํ ์ค๋น๊ฐ ๋๋๊ฒ๋๋ค!
override func setUp() {
URLProtocol.registerClass(MockURLProtocol.self)
let configuration: URLSessionConfiguration = .ephemeral
configuration.protocolClasses?.insert(MockURLProtocol.self, at: .zero)
let session = URLSession(configuration: configuration)
self.networking = MSNetworking(session: session)
}
setUp
์์ ์์ ๋ดค๋๋๋ก URLSessionConfiguration
์ MockURLProtocol
์ ๋ฃ์ด์ค๋๋ค.
๊ทธ๋ฆฌ๊ณ ํด๋น Configuration์ผ๋ก URLSession
์ ๋ง๋ค์ด์ ์ฌ์ฉํด์ฃผ๋ฉด ๋ฉ๋๋ค!
๊ฐ Test Case๋ง๋ค ๋ค๋ฅธ Response๋ฅผ ํ ์คํธํ๊ฒ ์ฃ ?
MockURLProtocol.requestHandler = { _ **in**
let response = HTTPURLResponse(url: URL(string: "https://api.codesquad.kr")!,
statusCode: 200,
httpVersion: nil,
headerFields: ["Content-Type": "application/json"])!
return (response, data)
}
MockURLProtocol
์ static
์ผ๋ก ์ ์ํด๋์๋ requestHandler
๋ฅผ ์ฌ๊ธฐ์ ์ ์ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
์ด๋ ๊ฒ ์ํ๋ response๋ฅผ ๋ง๋ค์ด์ ๋ฐํ์ ํด์ฃผ๋ฉด, MockURLProtocol
์ startLoading
๋ฉ์๋์์ ํด๋น ํธ๋ค๋ฌ๋ก ์ฃผ์ด์ง ์๋ต์ ๊ณ ์ ์ ์ผ๋ก ๋ด๋๊ฒ ๋๋ ๊ฒ์ด์ฃ .
๊ฒฐ๊ณผ
Repository
๋จ์์ Mock์ ๋ง๋ค์ด์ ๋ฐํํ๋ ๋ฐฉ๋ฒ๋ง ์ฌ์ฉํ ์ ์๋ ์ค ์์๋๋ฐ,
์ด๋ ๊ฒ ๋คํธ์ํน ์์ฒด๋ฅผ Mockingํ ์ ์๋ค๋ ์ ์ด ๊ฝค ์ ๊ธฐํ์ต๋๋ค.
Response์ statusCode๋ ํน๋ณํ ์๋ต์ ๋ํ ํ ์คํธ ์ผ์ด์ค๋ฅผ ์์ฑํ ๋ ์์ฃผ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์์ ๊ฒ ๊ฐ๋ค์.