don't stop believing

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

Swift/Perfect

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

Tongchun 2018. 1. 31. 18:03

빠르게 가겠습니다.

swift 버전을 확인합니다.

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

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

$ 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/

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

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

$ swift package generate-xcodeproj
$ open nGleServer008.xcodeproj

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

// 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"]),
    ]
)

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

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

$ swift build
$ cd Sources/nGleServer008/
$ touch controller.swift

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

$ swift package generate-xcodeproj
$ open nGleServer008.xcodeproj

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

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)")
}

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

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

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)")
}

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

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)
    }
}

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


$ cd Sources/nGleServer008/
$ touch Config.swift
$ cd ../..
$ mkdir Config
$ cd Config
$ touch ServerConfiguration.json
$ touch ServerConfigurationLinux.json

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

$ swift package generate-xcodeproj
$ open nGleServer008.xcodeproj/


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

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
        }
    }
}

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

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")
        }
    }
}

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

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

{
    "server" : {
        "serverPort": 1026,
        "documentRoot": "webroot"
    }
}

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

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)")
}

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


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

{
    "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
    }
}

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

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")
        }
    }
}


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


Comments