don't stop believing

json 파일로 config를 관리해 봅시다. 본문

Swift/Perfect

json 파일로 config를 관리해 봅시다.

Tongchun 2018. 1. 31. 18:03

빠르게 가겠습니다.

swift 버전을 확인합니다.

1
2
3
$ swift -version
Apple Swift version 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2)
Target: x86_64-apple-macosx10.9
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

프로젝트 폴더로 이동해 swift package init으로 초기화 합니다.

1
2
3
4
5
6
7
8
9
$ cd ~/Documents/Test_Perfect/nGleServer008
$ swift package init --type executable
Creating executable package: nGleServer008
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/nGleServer008/main.swift
Creating Tests/
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

swift package 기본 폴더들이 생성된 것을 볼 수 있습니다.

xcodeproj 파일을 생성하고 열어줍니다.

1
2
$ swift package generate-xcodeproj
$ open nGleServer008.xcodeproj
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

xcode가 실행되면 Package.swift 파일에 Config를 사용할 만한 Package들을 잔득 넣습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "nGleServer008",
dependencies: [
.package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", from: "3.0.0"),
.package(url: "https://github.com/PerfectlySoft/Perfect-RequestLogger.git", from: "3.0.0"),
.package(url: "https://github.com/PerfectlySoft/Perfect-Mustache.git", from: "3.0.0"),
.package(url: "https://github.com/SwiftORM/Postgres-StORM.git", from: "3.0.0"),
.package(url: "https://github.com/PerfectlySoft/Perfect-Session-Redis.git", from: "3.0.0"),
],
targets: [
.target(
name: "nGleServer008",
dependencies: ["PerfectHTTPServer",
"PostgresStORM",
"PerfectMustache",
"PerfectSessionRedis"]),
]
)
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

저장하고 swift build를 해줍니다.

그리고 빌드가 완료되면 Source/nGleServer008 폴더로 이동해 controller.swift 파일도 만들어 줍니다.

1
2
3
$ swift build
$ cd Sources/nGleServer008/
$ touch controller.swift
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

xcodeproj를 다시 생성해주고 열어 봅니다.

1
2
$ swift package generate-xcodeproj
$ open nGleServer008.xcodeproj
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

xcode가 실행되면 main.swift파일로 이동해 아래와 같이 설정해 봅니다.

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import PerfectLib
import PerfectHTTP
import PerfectHTTPServer
import PerfectMustache
import PerfectSession
import PerfectSessionRedis
import PerfectRedis
import StORM
import PostgresStORM
RedisSessionConnector.host = "192.168.1.12"
RedisSessionConnector.password = "12345678"
RedisSessionConnector.port = redisDefaultPort
SessionConfig.name = "MacaronRedisDrivers"
SessionConfig.idle = 60
// Optional
SessionConfig.cookieDomain = "localhost"
SessionConfig.IPAddressLock = true
SessionConfig.userAgentLock = true
SessionConfig.CSRF.checkState = true
SessionConfig.CORS.enabled = true
SessionConfig.CORS.acceptableHostnames.append("http://www.ngle.co.kr")
SessionConfig.CORS.maxAge = 60
let sessionDriver = SessionRedisDriver()
let server = HTTPServer()
server.setRequestFilters([sessionDriver.requestFilter])
server.setResponseFilters([sessionDriver.responseFilter])
// PostgreSQL
PostgresConnector.host = "192.168.1.12"
PostgresConnector.username = "macaron"
PostgresConnector.password = "12345678"
PostgresConnector.database = "macaron"
PostgresConnector.port = 5432
// PostgreSQL table setup
let setupWords = Words()
try? setupWords.setup()
try StudyController().putWordMaxCount()
let setupPatterns = Patterns()
try? setupPatterns.setup()
try StudyController().putPatternMaxCount()
let setupDialogues = Dialogues()
try? setupDialogues.setup()
try StudyController().putDialogueMaxCount()
let setupEBS = EBS()
try? setupEBS.setup()
try StudyController().putEBSMaxCount()
server.serverPort = 1026
server.documentRoot = "webroot"
let controller = Controller()
server.addRoutes(Routes(controller.routes))
let RC = RedisController()
do {
try server.start()
} catch PerfectError.networkError(let err, let msg) {
print("Network error thrown: \(err) \(msg)")
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

실제 서버를 실행하려면 postgreSQL과 redis는 설치되어 있어야 합니다. 만약 없다면 기본 서버 정보만으로 진행할 수 있습니다.

기본 설정은 아래와 같습니다. 이 Post를 따라한다면 기본 설정으로 하시기 바랍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import PerfectLib
import PerfectHTTP
import PerfectHTTPServer
let server = HTTPServer()
server.serverPort = 1026
server.documentRoot = "webroot"
let controller = Controller()
server.addRoutes(Routes(controller.routes))
do {
try server.start()
} catch PerfectError.networkError(let err, let msg) {
print("Network error thrown: \(err) \(msg)")
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

그 다음 controller.swift에서 아래와 같이 작성합니다.

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
import Foundation
import PerfectLib
import PerfectHTTP
import PerfectHTTPServer
final class Controller {
var routes: [Route] {
return [
Route(method: .get, uri: "/heartbeat", handler: heartbeat)
]
}
func heartbeat(request: HTTPRequest, response: HTTPResponse) {
let data = ["message": "macaron api is alive."] as [String: Any]
do {
try response.setBody(json: ["result": 0, "data": data, "ts": Date().ticks])
.setHeader(.contentType, value: "application/json")
.completed()
} catch {
response.setBody(string: "Error handling request: \(error)")
.completed(status: .internalServerError)
}
}
}
extension Date {
var ticks: UInt64 {
return UInt64((self.timeIntervalSince1970 + 62_135_596_800) * 10_000_000)
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

xcode의 Edit Scheme...에서 Run의 Options에 Working Directory를 프로젝트 폴더로 잡아줍니다.


그리고 실행을 하면 아래와 같이 서버 실행 로그가 출력 됩니다.


브라우저를 열고 http://localhost:1026/heartheat을 호출하면 아래처럼 json 데이터가 리턴됩니다.

이제 json 파일을 이용한 configuration을 시작해 봅시다.

프로젝트 폴더에서 Source/nGleServer008로 이동 후 Config.swift 파일을 생성합니다. 

그리고 config 파일로 사용할 json 파일도 만들어 보겠습니다. 프로젝트 폴더 root에 Config라고 폴더를 만듭니다. 그리고 Config 폴더 안에 아래 두 json 파일을 만들어 줍니다.

ServerConfiguration.json

ServerConfigurationLinux.json


1
2
3
4
5
6
7
$ cd Sources/nGleServer008/
$ touch Config.swift
$ cd ../..
$ mkdir Config
$ cd Config
$ touch ServerConfiguration.json
$ touch ServerConfigurationLinux.json
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이제 다시 xcodeproj를 다시 생성해 줍니다.

1
2
$ swift package generate-xcodeproj
$ open nGleServer008.xcodeproj/
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX


Config.swift 파일에 아래와 같이 작성합니다.

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
import Foundation
import PerfectLib
public struct JSONConfig {
public enum ConfigError: Error {
case FileNotWritten
case FileDoesNotExist
}
let name: String
public init?(name:String) {
self.name = name
let thisFile = File(name)
if thisFile.exists == false {
print("Settings file does not exist")
}
}
public func getValues() -> Dictionary?{
let thisFile = File(name)
do {
try thisFile.open(.read, permissions: .readUser)
defer { thisFile.close() }
let txt = try thisFile.readString()
let dict = try txt.jsonDecode() as! Dictionary
return dict
} catch {
print(error)
return .none
}
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

그리고 바로 밑에 json 파일에서 데이터를 불러오는 class를 만들어 줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Config {
func set() {
#if os(Linux)
let configPath = "/Home/macaron/Config/ServerConfigurationLinux.json"
#else
let configPath = "./Config/ServerConfiguration.json"
#endif
if let configData = JSONConfig(name: configPath) {
if let conf = configData.getValues() {
let serverConf = conf["server"] as![String: Any]
print("\(serverConf)")
// Required Values
server.serverPort = UInt16(serverConf["serverPort"] as! Int)
server.documentRoot = serverConf["documentRoot"] as! String
}
} else {
print("Unable to get Configuration")
}
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이제 json 파일에 config 정보를 추가할 차례입니다.

ServerConfiguration.json과 ServerConfigurationLinux.json 두 파일에 동일하게 아래와 같이 추가해 줍니다.

1
2
3
4
5
6
{
"server" : {
"serverPort": 1026,
"documentRoot": "webroot"
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

마지막으로 main.swift 파일에서 설정부분을 주석처리하고 Config.set()을 추가합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import PerfectLib
import PerfectHTTP
import PerfectHTTPServer
let server = HTTPServer()
let config = Config()
config.set()
// server.serverPort = 1026
// server.documentRoot = "webroot"
let controller = Controller()
server.addRoutes(Routes(controller.routes))
do {
try server.start()
} catch PerfectError.networkError(let err, let msg) {
print("Network error thrown: \(err) \(msg)")
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

xcode를 실행하면 아래와 같이 정상적으로 서버 포트를 불러 오는 걸 확인할 수 있습니다.


session, redis, postgresql 등 설정이 많은 경우 아래와 같이 json 파일로 설정할 수 있습니다.

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
{
"server": {
"serverPort": 10260,
"documentRoot": "webroot"
},
"RedisSessionConnector": {
"host": "192.168.1.12",
"password": "12345678",
"port": 6379
},
"SessionConfig": {
"name": "MacaronRedisDrivers",
"idle": 60,
"cookieDomain": "localhost",
"IPAddressLock": true,
"userAgentLock": true
},
"PostgresConnector": {
"host": "192.168.1.12",
"username": "macaron",
"password": "12345678",
"database": "macaron",
"port": 5432
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

configuration.swift 파일의 Config 클레스에는 아래와 같이 json 데이터를 불러와 설정해 주고 Log.info()로 확인해 줄 수 있습니다.

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
class Config {
func set() {
#if os(Linux)
let configPath = "/home/macaron/macaron-api/Config/ServerConfigurationLinux.json"
#else
let configPath = "./Config/ServerConfiguration.json"
#endif
if let configData = JSONConfig(name: configPath) {
if let conf = configData.getValues() {
Log.info(message: "Macaron API Server Configuration Info \n")
// Server Info
let serverConf = conf["server"] as![String: Any]
server.serverPort = UInt16(serverConf["serverPort"] as! Int)
Log.info(message: "server.serverPort = \(String(describing: serverConf["serverPort"]!))")
server.documentRoot = serverConf["documentRoot"] as! String
Log.info(message: "server.documentRoot = \(String(describing: serverConf["documentRoot"]!)) \n")
// RedisSessionConnector Info
let redisSessionConf = conf["RedisSessionConnector"] as![String: Any]
RedisSessionConnector.host = redisSessionConf["host"] as! String
Log.info(message: "RedisSessionConnector.host = \(String(describing: redisSessionConf["host"]!))")
RedisSessionConnector.password = redisSessionConf["password"] as! String
Log.info(message: "RedisSessionConnector.password = \(String(describing: redisSessionConf["password"]!))")
RedisSessionConnector.port = redisSessionConf["port"] as! Int
Log.info(message: "RedisSessionConnector.port = \(String(describing: redisSessionConf["port"]!)) \n")
// SessionConfig Info
let SessionConf = conf["SessionConfig"] as![String: Any]
SessionConfig.name = SessionConf["name"] as! String
Log.info(message: "SessionConfig.name = \(String(describing: SessionConf["name"]!))")
SessionConfig.idle = SessionConf["idle"] as! Int
Log.info(message: "SessionConfig.idle = \(String(describing: SessionConf["idle"]!))")
SessionConfig.cookieDomain = SessionConf["cookieDomain"] as! String
Log.info(message: "SessionConfig.cookieDomain = \(String(describing: SessionConf["cookieDomain"]!))")
SessionConfig.IPAddressLock = SessionConf["IPAddressLock"] as! Bool
Log.info(message: "SessionConfig.IPAddressLock = \(String(describing: SessionConf["IPAddressLock"]!))")
SessionConfig.userAgentLock = SessionConf["userAgentLock"] as! Bool
Log.info(message: "SessionConfig.userAgentLock = \(String(describing: SessionConf["userAgentLock"]!))")
SessionConfig.CSRF.checkState = true
Log.info(message: "SessionConfig.CSRF.checkState = true")
SessionConfig.CORS.enabled = true
Log.info(message: "SessionConfig.CORS.enabled = true")
SessionConfig.CORS.acceptableHostnames.append("http://www.test-cors.org")
Log.info(message: "SessionConfig.CORS.acceptableHostnames.append(\"http://www.test-cors.org\")")
SessionConfig.CORS.maxAge = 60
Log.info(message: "SessionConfig.CORS.maxAge = 60 \n")
// PostgreSQL
let PostgresConnConf = conf["PostgresConnector"] as![String: Any]
PostgresConnector.host = PostgresConnConf["host"] as! String
Log.info(message: "PostgresConnector.host = \(String(describing: PostgresConnConf["host"]!))")
PostgresConnector.username = PostgresConnConf["username"] as! String
Log.info(message: "PostgresConnector.username = \(String(describing: PostgresConnConf["username"]!))")
PostgresConnector.password = PostgresConnConf["password"] as! String
Log.info(message: "PostgresConnector.password = \(String(describing: PostgresConnConf["password"]!))")
PostgresConnector.database = PostgresConnConf["database"] as! String
Log.info(message: "PostgresConnector.database = \(String(describing: PostgresConnConf["database"]!))")
PostgresConnector.port = PostgresConnConf["port"] as! Int
Log.info(message: "PostgresConnector.port = \(String(describing: PostgresConnConf["port"]!)) \n")
}
} else {
print("Unable to get Configuration")
}
}
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX


여기까지 json 파일을 이용한 서버 configuration 이었습니다.