Notice
Recent Posts
Recent Comments
Link
관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 2. 서버 - RESTful API 실습 (Foundation framework사용) 본문

iOS 실전 (swift)/서버

[iOS - swift] 2. 서버 - RESTful API 실습 (Foundation framework사용)

jake-kim 2020. 4. 24. 02:08

* 통신은 주로 Alamofire프레임워크를 사용하지만, 기본기를 위해서 Foundation 프레임워크로 구현 해보기

1. ATS설정과 API문서 

1) HTTP통신인 경우 ATS(App Transport Security)설정

 - 허용되는 사이트를 설정

App Transport Security Settings 추가 -> 하위 탭에서 Allow Arbitrary Loads 추가 -> YES로 변경


- 특정 사이트만 허용하는 방법

(1)App Transport Security Settings -> Exception Domains 하위 항목에 아이템 생성 후 Dictionary로 설정

 

(2)New Item이라고 있던 곳을 "www"나 서브 도메인 제외하고 주소를 입력

(3) 하위 항목에 다음 세 가지 아이템 추가 후, 옵션이름을 적고 Boolean타입으로 변환 -> YES로 설정

 NSExceptionAllowsInsecureHTTP : 해당 도메인에 대해 HTTP접속을 허용할지 여부 결정

 NSExceptionAllowsInsecureHTTPLoads : 해당 도메인에 대한 HTTP통신을 허용할지 여부 결정

 NSIncludeSubdomains : ATS구성이 입력한 도메인의 서브 도메인에도 적용 (기본값은 NO)


 

2) API문서의 형태

항목
Description 서버로 부터 현재 시간을 가져오는 것
URL http://swiftapi.rubypaper.co.kr:2029/practice/currentTime
Method GET
REQ 없음
RES 날짜 형태의 문자열(예시, 2020-04-24 12:18:48)

(위 API문서를 읽고 전체적인 내용 파악)

 

3) 간단한 API테스트 도구 : "포스트맨(Postman)"

https://www.postman.com

 

Postman | The Collaboration Platform for API Development

Simplify workflows and create better APIs – faster – with Postman, a collaboration platform for API development.

www.postman.com

 

postman 다운 -> 가입 -> open -> '+'버튼을 누르면 밑과 같은 화면 등장

2. RESTful API

1) GET 방식

항목
Description 서버로 부터 현재 시간을 가져오는 것
URL http://swiftapi.rubypaper.co.kr:2029/practice/currentTime
Method GET
REQ 없음
RES 날짜 형태의 문자열(예시, 2020-04-24 12:18:48)
1
2
3
4
5
6
7
8
9
10
11
12
        do {
            // METHOD : GET
            let url = URL(string: "http://swiftapi.rubypaper.co.kr:2029/practice/currentTime")
            
            // API 호출
            let response = try String(contentsOf: url!)
                    
            print(response) // 2020-04-24 00:39:01
            
        } catch let error as NSError {
            print(error.localizedDescription)
        }
 
 

* API호출 방법 (반환 타입 설정)

 (1) Base64인코딩 형식let response = try Data(contentsOf: url!)

   * Base64 인코딩 : binary data -> ASCII format

       이미지 데이터에 많이 사용 (binary데이터는 이동하다가 서버에서 1byte로 묶여서 데이터에 손실이 올 수 있기 때문)

 

 (2) 문자열 반환let response = try String(contentsOf: url!)

 

 (3) UTF-8인코딩 : let response = try NSString(contentsOf: url!, encoding: String.Encoding.utf8.rawValue) 

     * 한글과 같은 2byte를 사용하는 언어를 utf-8로 인코딩

 

 

2) POST 방식

(String 방식)

항목
Description 클라이언트가 보낸 요청을 그대로 JSON형식으로 변경하여 응답
URL http://swiftapi.rubypaper.co.kr:2029/practice/echo
Method POST
Content Type x-www-form-urlencoded
REQ 사용자 정의 형식
RES

JSON 객체 형식

 result : 성공 시 "SUCCESS", 실패 시 "FAIL"

 기타 : 사용자 요청 내용을 그대로 회신

 

TextView에 POST한 결과 출력

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
    @IBAction func post(_ sender: Any) {
        
        // 바디에 넣을 데이터 생성
        let bodyParam = "userId=\(self.id.text!)&name=\(self.name.text!)"
        let body = bodyParam.data(using: .utf8)
 
        // URL 객체 정의
        let url = URL(string: "http://swiftapi.rubypaper.co.kr:2029/practice/echo")
 
        // 1) REQ - 라인 설정
        var req = URLRequest(url: url!)
        req.httpMethod = "POST"
                
        // 2) REQ - 헤더 설정
        req.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type"// POST
        req.setValue(String(body!.count), forHTTPHeaderField: "Content-Length"// 서버에 길이 알림
 
        // 3) REQ - 바디 설정
        req.httpBody = body
 
        // 서버에 전송
        URLSession.shared.dataTask(with: req) {(data, response, error) in
            
            // error 체크
            if let e = error {
                print(e.localizedDescription)
                return
            }
            
            print("response=\(response)")
            
            DispatchQueue.main.async {
                do{
                    let json = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary
                    guard let jsonObj = json else {return}
                    
                    let result = jsonObj["result"asString
                    let id = jsonObj["userId"asString
                    let name = jsonObj["name"asString
                    
                    if result == "SUCCESS" {
                        // data 확인
                        print("결과==>\(String(data: data!, encoding: .utf8)!)")
                        
                        // responseView는 @IBOulet변수
                        self.responseView.text = "아이디 = \(String(id!))\n"
                                                + "이름 = \(String(name!))\n"
                        
                    }
                    
                } catch let e as NSError {print(e.localizedDescription)}
            }
            
            
        }.resume() // resume()을 해야 전송이 됨 : URLSession.shared.dataTask(...)객체
    }
 
 

* 소스코드 43번 줄 결과

cf) JSON형식으로 통신하려면? 

 API문서에서 Content Type이 application/json명시되어 있다면,

   - 위 코드에서 4번줄 bodyParam을 딕셔너리 형태로 구현 (let bodyParam = ["userId" : id, "name" : name] )

   - 헤더 설정에서 15번줄을 "application/json"타입으로 수정

   - 5번줄 body는 JSONSerialization.data(withJSONObject:bodyParam, options:[])

 

*소스 코드 및 API출처 : 루비페이퍼, 꼼꼼한 재은씨의 스위프트 실전편

Comments