don't stop believing

Web 서버 기본 구성 2 (PerfectCURL을 이용한 요청) 본문

Swift/Perfect

Web 서버 기본 구성 2 (PerfectCURL을 이용한 요청)

Tongchun 2018. 6. 19. 14:41

이번에는 API 서버와 연결해 데이터를 불러오는 부분을 구현하겠습니다.


먼저 json을 파싱하는 클래스를 하나 만들어 주겠습니다. API 서버와 연결해 데이터를 가져오면 json string을 dictionary로 변경해 주는 클래스와 함수입니다.

nGleServer002 폴더에서 nglelib.swift 파일을 하나 만들어 줍니다. 그리고 아래와 같이 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import Foundation
import SwiftyJSON
public class NgleLib {
public func jsonStringToDic(_ jsonString: String) -> [String: Any]? {
guard let dataFromString = jsonString.data(using: .utf8, allowLossyConversion: false) else {
return nil
}
var jsonDic = JSON(data: dataFromString).dictionaryObject!
let nullValueArray = jsonDic.filter { $0.value is NSNull }
for (jsonKey, _) in jsonDic {
if let _ = nullValueArray.index(forKey: jsonKey) {
jsonDic[jsonKey] = ""
}
}
return jsonDic
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX


그 다음 API 서버와 연결해 데이터를 불러오는 부분을 구현할 차례입니다.

request.swift라고 파일을 하나 만들어 주고 아래와 같이 클래스를 만들어 줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import Foundation
import PerfectCURL
import SwiftyJSON
class NgleRequest{
let ngleLib = NgleLib()
var host: String = ""
var port: Int = 80
init(host: String, port: Int) {
self.host = host
self.port = port
}
// .
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

NgleRequest 클래스를 초기화 할때 host와 post를 지정합니다.


하나의 method 호출 함수는 동기(Synchronous)와 비동기(Asynchronous) 두 가지 모두 만들어 보겠습니다.

먼저 get method의 Sync 요청입니다. NgleRequest 클래스 안에 추가합니다.

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
/// get method (Synchronous)
func getSync(_ uri: String, _ params: [String]) -> [String: Any] {
var jsonDic = [String: Any]()
var url: String = "http://\(self.host):\(self.port)\(uri)"
if params.count > 0 {
url.append("?")
url.append(params.joined(separator: "&"))
}
let request = CURLRequest(url, .failOnError)
do {
let response = try request.perform()
let responseCode = response.responseCode
print("status code: \(responseCode)")
jsonDic = response.bodyJSON
} catch let error as CURLResponse.Error {
print("Error Status Code: \(error.response.responseCode)")
} catch {
print(error.localizedDescription)
}
return jsonDic
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

getSync 라고 함수를 만듭니다. 입력 값으로는 uri와 dictionary 형태의 url 파르메터를 받습니다.

출력 값으로는 Dictionary를 리턴합니다.


getSync 함수를 handler.swift에서 호출해 보겠습니다.

Handler 클래스 안에 아래와 같이 추가합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// get method
func callCrewByGetSync(request: HTTPRequest, response: HTTPResponse) {
var values = MustacheEvaluationContext.MapType()
let nGle = NgleRequest(host: "52.79.160.182", port: 10260)
let jsonDic = nGle.getSync("/v1/crew", [])
let crewList = jsonDic["data"] ?? ["":""]
values["crews"] = crewList
let webPath = request.documentRoot + "/crewlist.mustache"
let mustachHandler = MustacheHelper(values: values)
mustacheRequest(request: request, response: response, handler: mustachHandler, templatePath: webPath)
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

NgleRequest() 클래스를 초기화 할 때 host와 port를 할당합니다.

macaron API의 모든 crew 정보를 가져오는 API를 호출하겠습니다. get Method이며 uri는 /v1/crew입니다. url parameter는 없으니 빈 배열로 넣습니다.

getSync 함수를 통해 받아온 Dictionary 데이터 중 key가 data인 것을 Mastache와 맵핑된 values 변수에 넣습니다. 

그리고 crewlist.mustach라고 화면을 출력할 파일을 하나 만들겠습니다.

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
<html>
<header>
<title>nGle Crew List</title>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
padding: 15px;
}
</style>
</header>
<body>
<h1>nGle Crew List</h1>
<table class=table>
<tr>
<td>Name</td><td>department</td><td>team</td><td>title</td><td>id</td><td>phone</td>
</tr>
{{#crews}}
<tr>
<td>{{name}}</td><td>{{department}}</td><td>{{team}}</td><td>{{title}}</td><td>{{id}}</td><td>{{phone}}</td>
</tr>
{{/crews}}
</table>
{{^crews}}
<h3>No Crew :(</h3>
{{/crews}}
</body>
</html>
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX


이제 controller.swift의 Route에 uri를 추가하고 호출해 보겠습니다.

Route(method: .get, uri: "/crew/sync", handler: sample.callCrewByGetSync)


이제 swift build 후 http://localhost:8080/crew/sync를 호출해 보겠습니다.

그럼 hander의 callCrewByGetSync 함수가 macaron API의 host와 port로 초기화 되고 getSync 함수로 /v1/crew API를 요청하게 됩니다.

동기로 받아온 데이터를 mustach를 이용해 html 형태로 화면에 출력하게 됩니다.

이제 비동기 get 호출을 만들어 보겠습니다.

request.swift 파일의 NgleRequest 클래스 안에 아래와 같이 추가합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/// get method (Asynchronous)
func getAsync(_ uri: String, _ params: [String], completion: @escaping (_ jsonData: [String: Any]) -> Void) {
var url: String = "http://\(self.host):\(self.port)\(uri)"
if params.count > 0 {
url.append("?")
url.append(params.joined(separator: "&"))
}
// async .
CURLRequest(url).perform { confirmation in
do {
let response = try confirmation()
let jsonDic: [String:Any] = response.bodyJSON
completion(jsonDic)
} catch let error as CURLResponse.Error {
print("Failed: response code \(error.response.responseCode)")
} catch {
print("Fatal error \(error)")
}
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

비동기 처리는 closure를 이용합니다.

clouser에서 함수 밖으로 데이터를 보내기 위해 Escaping clouser를 사용합니다.


Handler 클래스에 getAsync를 사용해 mustach에 뿌려주는 함수를 만들어 보겠습니다.

Handler 클래스 안에 아래와 같이 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// get method
func callCrewByGetAsync(request: HTTPRequest, response: HTTPResponse) {
var values = MustacheEvaluationContext.MapType()
let nGle = NgleRequest(host: "52.79.160.182", port: 10260)
nGle.getAsync("/v1/crew", []) { (jsonDic) in
let crewList = jsonDic["data"] ?? ["":""]
values["crews"] = crewList
let mustachHandler = MustacheHelper(values: values)
let webPath = request.documentRoot + "/crewlist.mustache"
mustacheRequest(request: request, response: response, handler: mustachHandler, templatePath: webPath)
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

비동기 처리에서는 Closure를 사용해야 하며 Closure안에서 데이터 처리와 mustach 맵핑을 해야 합니다.

macaron API 서버에서 같은 API를 호출하는 함수이니 crewlist.mustach를 그대로 사용합니다.


이제 controller.swift 파일안에 Route를 추가합니다.

Route(method: .get, uri: "/crew/async", handler: sample.callCrewByGetAsync)

이제 http://localhost:8080/crew/async를 호출해 봅니다.

동일한 화면이 나오는지 확인해 봅니다.

이번에는 post Method 호출입니다.

먼저 post Method의 동기 호출을 하겠습니다. request.swift 파일의 NgleRequest 클래스 안에 아래와 같이 작성합니다.

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
/// post method (Synchronous)
func postSync(_ uri: String, _ params: [String], _ header: [String: String], _ body: String) -> [String: Any] {
var jsonDic = [String: Any]()
var url: String = "http://\(self.host):\(self.port)\(uri)"
if params.count > 0 {
url.append("?")
url.append(params.joined(separator: "&"))
}
do {
let start = Date()
let response = try CURLRequest(url, .httpMethod(.post), .postString(body), .failOnError).perform()
let responseCode = response.responseCode
jsonDic = response.bodyJSON
let duration = Date().timeIntervalSince1970 - start.timeIntervalSince1970
print("status code: \(responseCode)")
print("Duratin: \(duration)")
} catch let error as CURLResponse.Error {
print("Error Status Code: \(error.response.responseCode)")
} catch {
print(error.localizedDescription)
}
return jsonDic
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

postSync 함수를 사용해 macaron API 서버의 로그인을 처리해 보겠습니다.

Handler 클래스에 아래와 같이 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// post method
func callLoginResultByPostSync(request: HTTPRequest, response: HTTPResponse) {
var values = MustacheEvaluationContext.MapType()
let nGle = NgleRequest(host: "52.79.160.182", port: 10260)
let bodyJson = "{ \"id\": \"tongchun\", \"password\": \"ngle3596\"}"
let jsonDic = nGle.postSync("/v1/auth/login", [], [:], bodyJson)
let loginData = jsonDic["data"] ?? ["":""]
print(loginData)
values["login"] = loginData
let webPath = request.documentRoot + "/loginresult.mustache"
let mustachHandler = MustacheHelper(values: values)
mustacheRequest(request: request, response: response, handler: mustachHandler, templatePath: webPath)
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

로그인 처리 후 결과로 받은 데이터를 화면에 보여줍니다.

loginresult.mustache 파일을 생성하고 아래와 같이 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
<header>
<title>nGle Login Result</title>
<meta charset="UTF-8">
</header>
<body>
<h1>nGle Login Result</h1>
{{#login}}
<li>ID: {{id}}</li>
<li>Access Token: {{accesstoken}}</li>
<li> : {{lastaccess}}</li>
{{/login}}
{{^login}}
<h3>No Crew :(</h3>
{{/login}}
</body>
</html>
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이레 controller.swift안에 Route를 추가합니다.

Route(method: .get, uri: "/login/sync", handler: sample.callLoginResultByPostSync)

http://localhost:8080/login/sync를 호출해 봅니다.

이번에는 post Method의 비동기 처리입니다.

앞서 동기처리하는 함수에서 header 처리를 안하고 넘어왔네요. 비동기 처리할때 header 처리를 추가해 보겠습니다.

request.swift의 NgleRequest 클래스 안에 아래와 같이 함수를 추가합니다.

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
/// post method (Asynchronous)
func postAsync(_ uri: String, _ params: [String], _ header: [String: String], _ body: String, completion: @escaping (_ jsonData: [String: Any]) -> Void) {
var url: String = "http://\(self.host):\(self.port)\(uri)"
if params.count > 0 {
url.append("?")
url.append(params.joined(separator: "&"))
}
// accessToken custom Header
let headerAccessToken = CURLRequest.Header.Name.custom(name: "AccessToken")
let tokenValue: String = header["AccessToken"] ?? ""
// async .
CURLRequest(url, .httpMethod(.post), .addHeader(headerAccessToken, tokenValue), .postString(body), .failOnError).perform { confirmation in
do {
let response = try confirmation()
let jsonDic: [String: Any] = response.bodyJSON
completion(jsonDic)
} catch let error as CURLResponse.Error {
print("Failed: response code \(error.response.responseCode)")
} catch {
print("Fatal error \(error)")
}
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

macaron API 서버 로그인 후 AccessToken을 받아오며 이후 request 호출할때마다 AccessToken을 header에 AccessToken이라는 key로 항상 넘겨줘야 합니다.

Custom Header를 만들기 위해 CURLRequest.Header.Name.custom(name: "AccessToken")를 추가합니다.

입력 값으로 받아온 Dictionary 타입의 header에서 key가 AccessToken인 값을 tokenValue 변수에 할당합니다.

그리고 CURRequest()를 호출할 때 .addHeader()로 AccessToken header를 추가합니다.


Handler에서 postAsync()를 사용해 로그인해 보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/// post method
func callLoginResultByPostAsync(request: HTTPRequest, response: HTTPResponse) {
var values = MustacheEvaluationContext.MapType()
let nGle = NgleRequest(host: "52.79.160.182", port: 10260)
let bodyJson = "{ \"id\": \"tongchun\", \"password\": \"ngle3596\"}"
let accessTokenDic: [String: String] = ["AccessToken": "8F3C1222-C7D9-48B0-9344-8F2FCB59FF1E"]
nGle.postAsync("/v1/auth/login", [], accessTokenDic, bodyJson) { (jsonDic) in
let loginData = jsonDic["data"] ?? ["":""]
print(loginData)
values["login"] = loginData
let mustachHandler = MustacheHelper(values: values)
let webPath = request.documentRoot + "/loginresult.mustache"
mustacheRequest(request: request, response: response, handler: mustachHandler, templatePath: webPath)
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이제 Route에 추가하고 호출해 보겠습니다.

Route(method: .get, uri: "/login/async", handler: sample.callLoginResultByPostAsync)



여기까지 CURLRequest을 이용한 get과 post method의 동기/비동기 처리였습니다.


Comments