Skip to content

๐Ÿ›’ REST API์™€์˜ ์—ฐ๋™์„ ํ†ตํ•ด ๊ฐ„๋‹จํ•œ ๋งˆ์ผ“ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด๋ณผ ์ˆ˜ ์žˆ๋Š” ์•ฑ

Notifications You must be signed in to change notification settings

leeari95/ios-open-market

ย 
ย 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

48 Commits
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

์˜คํ”ˆ ๋งˆ์ผ“

  1. ํ”„๋กœ์ ํŠธ ๊ธฐ๊ฐ„: 2022.01.03 - 2022.01.14
  2. Ground Rules
    1. ์‹œ๊ฐ„
      • ์‹œ์ž‘์‹œ๊ฐ„ 10์‹œ
      • ์ ์‹ฌ์‹œ๊ฐ„ 12์‹œ~2์‹œ
      • ์ €๋…์‹œ๊ฐ„ 6์‹œ~7์‹œ ์‚ฌ์ด๋ถ€ํ„ฐ 2์‹œ๊ฐ„
    • ์ง„ํ–‰ ๊ณ„ํš
      • ํ”„๋กœ์ ํŠธ๊ฐ€ ์ค‘์‹ฌ์ด ์•„๋‹Œ ํ•™์Šต๊ณผ ์ด์œ ์— ์ดˆ์ ์„ ๋งž์ถ”๊ธฐ
      • ์˜๋ฌธ์ ์„ ๊ทธ๋ƒฅ ๋„˜์–ด๊ฐ€์ง€ ์•Š๊ธฐ
    • ์Šคํฌ๋Ÿผ
      • 10์‹œ์— ์Šคํฌ๋Ÿผ ์‹œ์ž‘
  3. ์ปค๋ฐ‹ ๊ทœ์น™
    1. ๋‹จ์œ„
      • ๊ธฐ๋Šฅ ๋‹จ์œ„
    • ๋ฉ”์„ธ์ง€
      • ์นด๋ฅด๋งˆ ์Šคํƒ€์ผ

๋ชฉ์ฐจ

ํ‚ค์›Œ๋“œ

  • ์˜์กด์„ฑ ์ฃผ์ž…(DI)
  • URLSession
    • URLProtocol
    • URLRequest
  • API
  • HTTP TCP/IP
    • MIME-Type
      • multipart/form-data
      • application/json
  • Result
  • Codable CodingKey
  • Async Test

STEP 1 : ๋„คํŠธ์›Œํ‚น ํƒ€์ž… ๊ตฌํ˜„

  • ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ๋‹ด๋‹นํ•œ ํƒ€์ž…์„ ์„ค๊ณ„ํ•ฉ๋‹ˆ๋‹ค.
  • Mock ๋ฐ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋‹จ์œ„ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

1-1 ๊ณ ๋ฏผํ–ˆ๋˜ ๊ฒƒ

1. ๋‹จ์ผ ์ฑ…์ž„ ์›์น™(Single responsibility principle)

  • ํ•œ ํƒ€์ž…์ด ํ•˜๋‚˜์˜ ์—ญํ• ๋งŒ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„์— ๋งŽ์€ ๊ณ ๋ฏผ์„ ํ•ด๋ณด์•˜๋‹ค.

2. CodingKeys ํ™œ์šฉ

์‹ค์ œ ๋„คํŠธ์›Œํฌ์—์„œ ๋‚ด๋ ค์˜ค๋Š” ๋ณ€์ˆ˜๋ช…์ด ์Šค๋„ค์ดํฌ ์ผ€์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ณ€์ˆ˜๋Š” 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"
}

3. NetworkManager์™€ Network

  • 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

4. Name Space

  • ํ•˜๋“œ์ฝ”๋”ฉ์„ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด enum ํƒ€์ž…์„ ๋งŒ๋“ค์–ด Address์™€ HTTPMethod์˜ ๊ฐ’๋“ค์„ ๋ถ„๋ฅ˜ํ•ด์ฃผ์—ˆ๋‹ค.

5. Request, Response

  • Requestํ•  ๋•Œ, ๊ทธ๋ฆฌ๊ณ  Responseํ•˜๋Š” ํƒ€์ž…์ด ์„ธ๋ถ€์ ์œผ๋กœ ๋‹ฌ๋ผ ProductModification, ProductRegistration ๋“ฑ... ๊ฐ ํƒ€์ž…์„ ๋ชจ๋‘ ๊ตฌํ˜„ํ•˜์˜€๋‹ค.

6. Overloading function

  • ์ƒํ’ˆ ์‚ญ์ œ, ๋“ฑ๋ก, ์กฐํšŒ ๋“ฑ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์š”์ฒญ์„ request ๋ฉ”์†Œ๋“œ ํ•˜๋‚˜๋ฅผ ์˜ค๋ฒ„๋กœ๋”ฉ์„ ํ™œ์šฉํ•˜์—ฌ ์ž‘์„ฑํ•˜์˜€๋‹ค.

7. Test Doubles

  • ํ…Œ์ŠคํŠธ ์ž‘์„ฑ์„ ์œ„ํ•ด ์˜์กด์„ฑ ์ฃผ์ž…์„ ํ™œ์šฉํ•˜์—ฌ Mock, Stub ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ํ™œ์šฉํ•˜์˜€๋‹ค.
  • URLProtocol์„ ์ƒ์†๋ฐ›์€ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๊ณ  ์žฌ์ •์˜๋ฅผ ํ•ด์ฃผ์—ˆ๋‹ค.
    • ์ด ๋ฐฉ๋ฒ•์€ URLSession์˜ dataTask๋ฅผ ์ง์ ‘ Stub์œผ๋กœ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์—ˆ์ง€๋งŒ, URLSessionDataTask๋ฅผ ์ฑ„ํƒํ•œ ํƒ€์ž…์— init()์„ ์ •์˜ํ•˜๋‹ˆ deprecated ๊ฒฝ๊ณ ๊ฐ€ ๋– ์„œ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์‚ญ์ œ ํ›„ URLProtocol์„ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๋กœ์ง์„ ๋ณ€๊ฒฝํ•˜์˜€๋‹ค.

1-2 ์˜๋ฌธ์ 

  • ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋™๊ธฐ ๋ฉ”์„œ๋“œ๋Š” ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ ํ…Œ์ŠคํŠธ๋กœ ์ง„ํ–‰ํ•ด์•ผํ• ๊นŒ?
  • URLProtocol๊ณผ URLSession์˜ ๊ด€๊ณ„๊ฐ€ ์ •ํ™•ํ•˜๊ฒŒ ์ดํ•ด๋˜์ง€ ์•Š๋Š”๋‹ค...
  • Health Checker์˜ ํ•„์š”์„ฑ์„ ๋ชจ๋ฅด๊ฒ ๋‹ค..
  • ํ…Œ์ŠคํŠธ ์‹œ Request์˜ ๋ฐ”๋””๋„ ์ฒดํฌ๋ฅผ ํ•ด์•ผํ• ๊นŒ?

1-3 Trouble Shooting

1. URLSessionDataTask๋ฅผ ์ฑ„ํƒํ•œ ํƒ€์ž…์— init()์— deprecated ๊ฒฝ๊ณ ..?

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

  • ์ด์œ  URLSessionDataTask init()์ด 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์„ ๊ฐ™์ด ๋„ฃ๋Š” ๋ฐฉ๋ฒ•

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๋ฅผ ๋„ฃ์–ด์ค€๋‹ค.
  • ํ•ด๊ฒฐ ์œ„์—์„œ ์ •๋ฆฌํ•œ ์–‘์‹๋Œ€๋กœ ๋ฐ”๋””๋ฅผ ์ถ”๊ฐ€ํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์˜€๋‹ค.

1-4 ๋ฐฐ์šด ๊ฐœ๋…

  • multipart/form-data
  • API๋ฌธ์„œ ์ฝ๋Š” ๋ฐฉ๋ฒ•
  • ํŒŒ์‹ฑํ•œ JSON ๋ฐ์ดํ„ฐ์™€ ๋งคํ•‘ํ•  ๋ชจ๋ธ ์„ค๊ณ„
    • CodingKeysย ํ”„๋กœํ† ์ฝœ์˜ ํ™œ์šฉ
  • URL Session์„ ํ™œ์šฉํ•œ ์„œ๋ฒ„์™€์˜ ํ†ต์‹ 
    • URLRequest๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•
    • Testableํ•œ ๋„คํŠธ์›Œํฌ ์ฝ”๋“œ ์ž‘์„ฑํ•˜๊ธฐ
      • ๋„คํŠธ์›Œํฌ ์ƒํ™ฉ๊ณผ ๋ฌด๊ด€ํ•œ ๋„คํŠธ์›Œํ‚น ๋ฐ์ดํ„ฐ ํƒ€์ž…์˜ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ(Unit Test)

top

About

๐Ÿ›’ REST API์™€์˜ ์—ฐ๋™์„ ํ†ตํ•ด ๊ฐ„๋‹จํ•œ ๋งˆ์ผ“ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด๋ณผ ์ˆ˜ ์žˆ๋Š” ์•ฑ

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Swift 100.0%