iOS App์—์„œ ์„œ๋ฒ„์™€ ํ†ต์‹ ํ•˜๊ธฐ ์œ„ํ•ด Apple์—์„œ๋Š” URLSession์ด๋ผ๋Š” ๊ธฐ๋ณธ API๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค. third-party library๋กœ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” Alamofire, kingfisher ๋“ฑ์˜ ๊ธฐ๋ฐ˜์ด ๋˜๋Š” API๋กœ ์„œ๋ฒ„ ํ†ต์‹ ์„ ์œ„ํ•ด ํ•„์ˆ˜์ ์œผ๋กœ ์•Œ์•„์•ผ ํ•œ๋‹ค. Apple ๋ฌธ์„œ๋ฅผ ์ฝ์œผ๋ฉด์„œ ์ดํ•ดํ•ด๋ณด์ž.

Concept

์ผ๋‹จ ์–ด๋–ค ํ๋ฆ„์œผ๋กœ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š”์ง€ ๊ฐœ๊ด„์ ์œผ๋กœ ์ดํ•ดํ•ด๋ณด์ž.

  1. Session configuration ๊ฒฐ์ •
  2. Session ์ƒ์„ฑ
  3. URL ์ƒ์„ฑ
  4. Request ๊ฐ์ฒด ์ƒ์„ฑ
  5. ์‚ฌ์šฉํ•  Task ๊ฒฐ์ •
  6. Completion handler Or Delegate ์‚ฌ์šฉ ์—ฌ๋ถ€ ๊ฒฐ์ •
  7. Task ์‹คํ–‰
  8. Completion handler Or Delegate ์‹คํ–‰

URLSessionConfiguration

let `default` = URLSessionConfiguration.default
let ephemeral = URLSessionConfiguration.ephemeral
let background = URLSessionConfiguration.background(withIdentifier: "configurationIdentifier")

URLSessionConfiguration ๊ฐ์ฒด๋Š” URLSession ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋กœ๋“œํ•˜๊ฑฐ๋‚˜ ๋‹ค์šด๋กœ๋“œํ•  ๋•Œ ์‚ฌ์šฉํ•  ์ •์ฑ…๊ณผ ํ–‰๋™์„ ์ •์˜ํ•œ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— task๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ธฐ ์ „์—, ์ด ์„ค์ • ์ž‘์—…์ด ์ตœ์šฐ์„ ์œผ๋กœ ์ง„ํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒƒ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • timeout ๊ฐ’
  • caching ์ •์ฑ…
  • connection ์š”๊ตฌ ์‚ฌํ•ญ
    • cellular ํ—ˆ์šฉ ์—ฌ๋ถ€
    • connectivity

URLSessionConfiguration ๊ฐ์ฒด๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ์ค‘์š”ํ•˜๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์ด ์„ค์ •์„ ๊ธฐ๋ฐ˜์œผ๋กœ URLSession object๊ฐ€ ์ƒ์„ฑ๋˜๊ฒŒ ๋˜๋Š”๋ฐ, URLSession Object๊ฐ€ ์ƒ์„ฑ๋œ ์ดํ›„์— configuration์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ผ๋‹จ URLSession ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ instance๋กœ ์„ค์ •๋˜๋ฉด, URLSessionConfiguration instance์— ๋ฐœ์ƒํ•˜๋Š” ๋ชจ๋“  ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์ƒ์„ฑ๋œ URLSession์— ๋ฐ˜์˜๋˜์ง€ ์•Š๋Š”๋‹ค. ์ด๋ฅผ ๋ฐ˜์˜ํ•˜์—ฌ ์ž‘์—…ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ƒˆ๋กœ์šด URLSession ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•๋ฐ–์— ์—†๋‹ค.

Default

class var `default`: URLSessionConfiguration { get }

๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” configuration์ด๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŠน์ง•์„ ๊ฐ–๋Š”๋‹ค.

  • disk-based cache
    • ๊ฒฐ๊ณผ๊ฐ€ ํŒŒ์ผ๋กœ ๋‹ค์šด๋กœ๋“œ ๋˜๋Š” ๊ฒฝ์šฐ๋Š” ์ œ์™ธ
  • user์˜ keychain์— ์ธ์ฆ์„œ๋“ค์„ ์ €์žฅ
  • cookie ์ €์žฅ

Ephemeral

class var ephemeral: URLSessionConfiguration { get }

ephemeral configuration์˜ ๊ฒฝ์šฐ default์™€ ๋น„์Šทํ•˜๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ ๋งŒ ๋‹ค๋ฅด๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

  • cache๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š์Œ
  • ์ธ์ฆ์„œ๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š์Œ
  • session๊ณผ ๊ณผ๋ จ๋œ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋„ disk์— ์ €์žฅํ•˜์ง€ ์•Š์Œ
    • ram์— ์ €์žฅํ•จ

๋Œ€๋ถ€๋ถ„์˜ ์ •๋ณด๋ฅผ ram์— ์ €์žฅํ•˜๊ฑฐ๋‚˜, ์ €์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. URL content๊ฐ€ file์ธ ๊ฒฝ์šฐ์—๋งŒ disk์— ์ €์žฅํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ephemeral configuration์„ ์‚ฌ์šฉํ•˜๋Š” ์ตœ๋Œ€ ์žฅ์ ์€ ์—ญ์‹œ privacy์ด๋‹ค. ์ž ์žฌ์ ์œผ๋กœ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ disk์— ์ €์žฅํ•˜์ง€ ์•Š์Œ์œผ๋กœ์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฐ€๋กœ์ฑ„์ ธ์„œ ์‚ฌ์šฉ๋  ๊ฐ€๋Šฅ์„ฑ์„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค. ์‹ค์ œ๋กœ ํ•ด๋‹น ์„ธ์…˜์ด private browsing mode ํ˜น์€ ๋น„์Šทํ•œ ์ƒํ™ฉ์—์„œ ๋งŽ์ด ์‚ฌ์šฉ๋œ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์ผ๋‹จ ephemeral configuration์ด disk์— cacheํ•˜์ง€ ์•Š๊ณ  ram์— ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, cache size๋Š” ram size์— ๋”ฐ๋ผ ์ œํ•œ๋œ๋‹ค. ์ฆ‰, ์•ž์— ์ €์žฅ๋œ cache๊ฐ€ ๋” ๋นจ๋ฆฌ ์ œ๊ฑฐ๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค๋Š” ๋ง์ด๋‹ค. ๋˜ํ•œ user๊ฐ€ app์„ ๋‹ซ๊ฑฐ๋‚˜ ์žฌ์‹คํ–‰ํ•˜๋ฉด ์ด cache ์ •๋ณด๋Š” ๋‹ค ๋‚ ์•„๊ฐ„๋‹ค. ๊ฒฐ๊ตญ app์˜ ํŠน์„ฑ์— ๋”ฐ๋ผ perfomance๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Œ์„ ์ธ์ง€ํ•˜๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค.

์ถ”๊ฐ€์ ์œผ๋กœ ์ด configuration์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ session์„ invalidateํ•œ๋‹ค๋ฉด, ๋‚ด๋ถ€ session data๋Š” ์ž๋™์ ์œผ๋กœ ์‚ญ์ œ๋œ๋‹ค. ์•„ ๊ทธ๋Ÿฐ๋ฐ memory cache์˜ ๊ฒฝ์šฐ์—๋Š” app์ด suspend ์ƒํ™ฉ(home์œผ๋กœ ๋„˜์–ด๊ฐ„ ์ƒํ™ฉ์—์„œ background์—์„œ ์ž‘์—…์„ ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ)์ธ ๊ฒฝ์šฐ ์ž๋™์ ์œผ๋กœ ์‚ญ์ œ๋˜์ง€๋Š” ์•Š๋Š”๋‹ค. ๋‹ค๋งŒ app์ด terminated ๋˜๊ฑฐ๋‚˜ memory๊ฐ€ ๋ถ€์กฑํ•œ ๊ฒฝ์šฐ(๋ถ€์กฑํ•˜๋ฉด suspend ์ƒํƒœ app์ด ์ œ๊ฑฐ๋˜๋‹ˆ๊นŒ) ์‚ญ์ œ๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค. (๋ชจํ˜ธํ•˜๊ฒŒ ๋งํ•ด๋’€๋„ค)

Background

class func background(withIdentifier identifier: String) -> URLSessionConfiguration

ํ•ด๋‹น configuration์€ ์ถ”๊ฐ€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€์ง€๋Š”๋ฐ, ์–˜๋Š” nil์ด๊ฑฐ๋‚˜ empty string์ด๋ฉด ์•ˆ๋œ๋‹ค.

์ด๋…€์„์€ app์ด background์—์„œ ์ž‘๋™ํ•˜๋Š” ๋™์•ˆ data๋ฅผ ๋ณด๋‚ด๊ธฐ ์œ„ํ•ด ์ ์ ˆํ•œ configuration์ด๋‹ค. ์ด configuration์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ URLSession์€ ์‹œ์Šคํ…œ์œผ๋กœ ์ „์†ก ์ œ์–ด๊ถŒ์„ ๋„˜๊ธฐ๊ณ , ์ด ์„ธ์…˜์€ ๋ณ„๋„์˜ process์—์„œ ์ „์†ก์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. ์‹ฌ์ง€์–ด iOS์˜ ๊ฒฝ์šฐ, ์ด configuration์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ URLSession์€ app์ด suspend ํ˜น์€ terminated๋œ ์ƒํƒœ์—์„œ๋„ ์ „์†ก์„ ๊ณ„์†ํ•  ์ˆ˜ ์žˆ๋‹ค.(system์œผ๋กœ ๋„˜๊ฒจ์„œ ๊ทธ๋Ÿฐ ๋“ฏ)

๋งŒ์•ฝ์— iOS app์ด system์— ์˜ํ•ด terminated๋˜๊ฑฐ๋‚˜ ์žฌ์‹คํ–‰๋œ ๊ฒฝ์šฐ, app์ด ์ด session์„ ์ฐพ์•„์•ผ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ ์ƒˆ๋กœ์šด configuration๊ณผ session object๋ฅผ ๋งŒ๋“ค๊ณ , ์ข…๋ฃŒ์‹œ์ ์˜ ์ „์†ก progress๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด ์„ค์ •ํ•œ ๊ฐ™์€ identifier๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋Š” system์— ์˜ํ•ด ์ •์ƒ์ ์œผ๋กœ ์ข…๋ฃŒ๋œ ์‹œ์ ์—๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค. ๋งŒ์•ฝ์— user๊ฐ€ app์„ ๋ฉ€ํ‹ฐ ํƒœ์Šคํ‚นํ™”๋ฉด์œผ๋กœ๋ถ€ํ„ฐ ์ข…๋ฃŒํ•œ ๊ฒฝ์šฐ, system์€ ๋ชจ๋“  background ์ „์†ก์„ ์ข…๋ฃŒํ•œ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ system์€ user๊ฐ€ app์„ ๊ฐ•์ œ์ข…๋ฃŒํ•œ ๊ฒฝ์šฐ ์ž๋™์ ์œผ๋กœ app์„ ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋‹ค์‹œ transfer๊ฐ€ ์‹œ์ž‘๋˜๋ ค๋ฉด user๊ฐ€ ๋ช…์‹œ์ ์œผ๋กœ app์„ ๋‹ค์‹œ ์ผœ์•ผ ํ•œ๋‹ค.

isDiscredit property๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ตœ์ ์˜ ์„ฑ๋Šฅ์„ ์œ„ํ•ด system ์žฌ๋Ÿ‰์— ๋”ฐ๋ผ ์ „์†ก์„ ์˜ˆ์•ฝํ•˜๋„๋ก background configuration์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ๋งŒ์•ฝ์— ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ• ์ผ์ด ์žˆ๋‹ค๋ฉด ์ด value๋ฅผ true๋กœ ๋ฐ”๊ฟ”์ฃผ์ž. ์ถ”๊ฐ€์ ์œผ๋กœ ์˜ˆ์‹œ๊ฐ€ ๊ถ๊ธˆํ•˜๋‹ค๋ฉด Downloading Files in the Background ์—ฌ๊ธธ ๋ณด์ž. (์ด๊ฑด ๋ชป๋ณด๊ฒ ๋‹ค ํž˜๋“ค๋‹ค)

URLSession

let defaultSession = URLSession(configuration: .default)
let ephemeralSession = URLSession(configuration: .ephemeral)
let backgroundSession = URLSession(configuration: .background(withIdentifier: "backgroundIdentifier"))
 
let defaultSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil) // with delegate

URLSession ๊ฐ์ฒด ํ˜น์€ ๊ด€๋ จ๋œ ๊ฐ์ฒด๋Š” ์ง€์ •๋œ URL๋“ค๋กœ ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์šด๋กœ๋“œ ํ•˜๊ฑฐ๋‚˜ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š” API๋ฅผ ์ œ๊ณตํ•œ๋‹ค. App์ด runํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ suspended ์ƒํƒœ์ธ ๊ฒฝ์šฐ Background download๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. URLSessionDelegate ํ˜น์€ URLSessionTaskDelegate๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด authentication ์ง€์›์ด๋‚˜ redirection, task completion๊ณผ ๊ฐ™์€ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

App์ด ํ•˜๋‚˜์ด์ƒ์˜ URLSession instance๋ฅผ ๋งŒ๋“ค๊ณ , ๊ทธ ๊ฐ๊ฐ์˜ instance๋Š” ๊ด€๋ จ๋œ data transfer task๋ฅผ ์กฐ์ •ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด web browser๋ฅผ ๋งŒ๋“ ๋‹ค๋ฉด, Tab ํ˜น์€ window๋งˆ๋‹ค ํ•˜๋‚˜์˜ ์„ธ์…˜์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ํ˜น์€ interactiveํ•œ ์‚ฌ์šฉ์„ ์œ„ํ•œ Session, background download๋ฅผ ์œ„ํ•œ Session ์‹์œผ๋กœ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๊ฐ Session์—์„œ App์€ ํŠน์ • URL์— ๋Œ€ํ•œ ์š”์ฒญ์„ ๋‚˜ํƒ€๋‚ด๋Š” task๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. (ํ•„์š”ํ•œ ๊ฒฝ์šฐ HTTP redirect๋ฅผ ๋”ฐ๋ฆ„)

URLSession์„ ๋งŒ๋“ค๊ฒŒ ๋˜๋ฉด configuration ๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ์•ž์—์„œ ์„ค๋ช…ํ–ˆ๋“ฏ, ๋‹จ์ผ ํ˜ธ์ŠคํŠธ์— ์—ฐ๊ฒฐํ•  ์ตœ๋Œ€ ๊ณต์‹œ ์—ฐ๊ฒฐ ์ˆ˜, cellular network ์‚ฌ์šฉ ์—ฌ๋ถ€๋“ฑ๊ณผ ๊ฐ™์€ ๋™์ž‘์„ ์ •์˜ํ•˜๋Š” URLSessionConfiguration ๊ฐ์ฒด๋ฅผ ๊ฐ€์ง€๊ณ  ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ๊ธฐ๋ณธ์ ์ธ request๋ฅผ ์œ„ํ•œ shared singleton ๊ฐ์ฒด๋ฅผ ์ œ๊ณตํ•œ๋‹ค. ์ด๋…€์„์€ configuration object๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๋‹ค. ์ด๋…€์„์€ custimziation์ด ๋ถˆ๊ฐ€ํ•˜์ง€๋งŒ, ์ œํ•œ๋œ requirement๋ผ๋ฉด ์ข‹์€ ์‹œ์ž‘์ ์ด ๋  ์ˆ˜ ์žˆ๋‹ค. ์ถ”๊ฐ€ ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค๋ฉด URLSessionConfiguration ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜์ž.

async/await ๊ตฌ๋ฌธ ์—ญ์‹œ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ์ด ๋ถ€๋ถ„์€ ์ถ”๊ฐ€ ํฌ์ŠคํŒ…์œผ๋กœ ์ž‘์„ฑํ•  ์˜ˆ์ •์ด๋‹ค.

URLSession์€ ์ž์ฒด์ ์œผ๋กœ data, file, ftp, http, https URL Scheme์„ ์ง€์›ํ•œ๋‹ค. ๋˜ํ•œ HTTP/1.1, HTTP/2, HTTP/3 ํ”„๋กœํ† ์ฝœ ์—ญ์‹œ ์ง€์›ํ•œ๋‹ค. ์ถ”๊ฐ€์ ์œผ๋กœ custom networking protocl์„ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋„ ์ง€์›ํ•˜๋Š”๋ฐ, URLProtocol์„ ์ฐธ๊ณ ํ•˜์ž.

iOS 9.0 ๋ฐ macOS 10.11 ์ด์ƒ์€ URLSession์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ ๋ชจ๋“  HTTP ์—ฐ๊ฒฐ์— ์•ฑ ์ „์†ก ๋ณด์•ˆ(App Transport Security: ATS)์„ ์‚ฌ์šฉํ•œ๋‹ค. ๋ณด์•ˆ์— ์ทจ์•ฝํ•œ ๋„คํŠธ์›Œํฌ์˜ ์—ฐ๊ฒฐ์„ ์ฐจ๋‹จํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ๋ณดํ†ต ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐํ•˜๊ณ  http๋กœ ๋œ url์— ์ ‘์†ํ•˜๋Š” ๊ฒฝ์šฐ ๋ณด๊ฒŒ๋˜๋Š” ์—๋Ÿฌ์™€ ๊ด€๋ จ์žˆ๋‹ค. App Transport Security์„ ์ฐธ๊ณ ํ•˜์ž.

URLSession API๋Š” thread safeํ•˜๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์—, ์ž์œ ๋กญ๊ฒŒ session๊ณผ task๋ฅผ ์–ด๋Š thread์—์„œ๋“  ๋งŒ๋“ค์–ด๋„ ๋œ๋‹ค. delegate method๊ฐ€ completion handler๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด, ์˜ฌ๋ฐ”๋ฅธ delegate queue์— ์ž๋™์œผ๋กœ ์˜ˆ์•ฝ๋œ๋‹ค.

๋งˆ๋ฌด๋ฆฌ

์–ด๋–ป๊ฒŒ Session์„ ๋งŒ๋“ค๊ณ , Session์„ ๋งŒ๋“œ๋Š”๋ฐ ํ•„์š”ํ•œ ์„ค์ •์ด ์–ด๋–ค ๊ฒƒ๋“ค์ด ์žˆ๋Š”์ง€ ์•Œ์•„๋ณด์•˜๋‹ค. ๋‹ค์Œ์€ URL๊ณผ Request ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž. ๋!

Reference