'API'에 해당되는 글 3건

  1. 2017.08.09 사진 게시판 API 만들기 [3]
  2. 2017.07.11 사진 게시판 API 만들기 [2]
  3. 2017.06.27 사진 게시판 API 만들기 [1] (6)


Perfect 미니 프로젝트 [3]


1. 사진 게시물 등록하기 
2. URL을 통해 사진 GET 


API를 하나씩 하나씩 완성해 나가도록 합시다!!

참고

2017년 7월 현재 Swift 3 / Perfect 최신버전 2.0.x 환경에서 진행함을 알려드립니다.

* Swift 최신 버전 확인(https://github.com/apple/swift/releases)

* Perfect 최신 버전 확인(https://github.com/PerfectlySoft/Perfect/releases)


지난 내용 돌아보기

2017/06/27 - [Swift/Perfect] - 사진 게시판 API 만들기 [1]

2017/07/11 - [Swift/Perfect] - 사진 게시판 API 만들기 [2]



사진 게시물 등록하기

먼저 CRUD 중, Create를 먼저 해보도록 할게요. 사진 게시물 등록을 위한 API를 구현해 봅니다. 사진 게시물 등록은 /article 경로에 POST 메서드를 통해 multipart/form-data를 전송합니다.

지난 번 정의한 API 스펙 중 사진 게시물 등록 API의 정보입니다.

요청(Request)

  • HTTP Method : POST
  • Content-Type: multipart/form-data

 매개변수

자료형 

값의 범위/기본 값 

비고 

 필수여부

image 

binary data

 

이미지 데이터 

user_name

string 

 

사용자 이름 

Y

description

string 

 

이미지 설명 

N

title

string 

 

이미지 제목 

Y


응답(Response)

Key 

자료형 

비고 

필수여부 

article_id 

string 

업로드된 게시물 고유 식별자 

 image_url

string 

이미지 주소 

user_name 

string 

사용자 이름 

description 

 string

이미지 설명 

title 

string 

이미지 제목 


우선 Sources/handlers.swift 파일로 이동하여 반복적으로 수행할 것 같은 기능이나, 상수를 정의해보려 합니다.

먼저 필수 매개변수가 모두 실려왔는지 확인하는 함수를 만듭니다. handlers.swift 파일 맨 아래쪽에 함수를 만들어봅니다.


// 부족한 매개변수가 없는지 확인하여 부족한 매개변수가 있다면 String Array로 반환
private func lakedParams(paramsNeeded: [String], paramsReceived: [(String, String)]) -> [String]? {
    
    var laked: [String] = []
    
    for param in paramsNeeded {
        if paramsReceived.filter({ (key, _) -> Bool in
            return key == param
        }).isEmpty {
            laked.append(param)
        }
    }
    
    return laked.count > 0 ? laked : nil
}

또, 나중에 해보시면 아시겠지만 JSONConvertible 타입의 데이터를 응답 객체의 body에 셋팅하려면 꼭 오류처리(try)를 해줘야 합니다. 매 번 하기 귀찮아서 그냥 함수를 하나 만들어줬습니다. lakedParams(paramsNeeded:paramsReceived:) 함수 아래에 작성해봅니다.

// 응답 보내기
private func sendCompleteResponse(_ response: HTTPResponse, status: HTTPResponseStatus = .ok, jsonBody: JSONConvertible? = nil) {
    do {
        if let json = jsonBody {
            try response.setBody(json: json)
        }
        response.completed(status: status)
    } catch {
        response.completed(status: .internalServerError)
    }
}


참, 그리고 요청에서 실려온 이미지를 저장할 디렉터리도 미리 지정해 두고 싶구요, 매개변수 이름이나 JSON 키값도 미리 지정해두고 싶습니다. ContentsType 구조체 선언 아래쪽에 아래 코드를 추가해봅니다. 사실 어디에 위치하든 크게 상관은 없지만 그래도 이게 예쁘잖아요 :D

// 이미지 저장 디렉터리
private let imageDir: Dir? = {
    let imageDir = Dir("./image")
    
    if imageDir.exists == false {
        do {
            try imageDir.create()
            print("Working Directory (\(imageDir.path)) for examples created.")
        } catch {
            print("Could not create Working Directory for examples.")
            return nil
        }
    }
    
    return imageDir
}()

// JSON 데이터의 Key 값 혹은 요청 매개변수 이름에 사용
private struct JSONKey {
    static let image = "image"
    static let userName = "user_name"
    static let description = "description"
    static let title = "title"
    static let articles = "articles"
    static let articleId = "article_id"
    static let page = "page"
    static let articlesPerPage = "articles_per_page"
    static let currentPage = "current_page"
    static let totalPage = "total_page"
    static let imageUrl = "image_url"
    static let imagePath = "image_path"
    static let message = "message"
    static let error = "error"
}


자, 밑준비를 마쳤습니다.

사용자가 게시물 작성을 요청하는 postArticleHandler(request: response:) 함수를 본격적으로 작성해봅시다. 기존에 함수 내부에 있던 코드는 지워주세요 :)


먼저, 응답은 JSON 형식이 될 것이므로 맨 윗줄에 아래 코드를 작성합니다. response 객체는 클라이언트에게 응답을 줄 때 사용할 객체입니다.

// 응답 컨텐츠 형식은 JSON response.setHeader(.contentType, value: ContentsType.json)


매개변수로 전달된 request 객체에 클라이언트의 요청 정보가 모두 실려있습니다. 먼저 모든 필수 매개변수가 전달되었는지 확인합니다.


POST 메서드로 전달된 매개변수는 request 객체의 postParams() 메서드를 통해 가져올 수 있습니다. postParams() 메서드의 반환 타입은 (String, String) 튜플의 Array 타입입니다.

필수 매개변수인 user_name, title이 모두 전달되었는지 확인하고 싶습니다(image 매개변수는 파일로 받아올 것이니 다음 차례에서 확인합니다). 

// 부족한 매개변수가 없는지 확인
    if let lakedParams = lakedParams(paramsNeeded: [JSONKey.userName, JSONKey.title], paramsReceived: request.postParams) {
        sendCompleteResponse(response, status: .badRequest, jsonBody: [JSONKey.message:"need more params \(lakedParams)"])
    }
    
    // 이미지 파일이 업로드 되었는지 확인
    guard let imageInformation: MimeReader.BodySpec = request.postFileUploads?.first,
        let imageFile = imageInformation.file else {
        sendCompleteResponse(response, status: .badRequest, jsonBody: [JSONKey.message:"need image file"])
            return
    }

자, 이렇게 모든 전달 데이터가 확인되었다면 이미지를 서버의 이미지 디렉터리에 저장할 차례입니다. 먼저 이미지 이름에 사용자 이름을 넣고 싶기 때문에 클라이언트에서 전달된 데이터를 빼냅니다.

// 사용자 이름, 제목 추출
    guard let userName = request.param(name: JSONKey.userName),
        let title = request.param(name: JSONKey.title) else {
            sendCompleteResponse(response, status: .internalServerError, jsonBody: nil)
            return
    }


그리고 이미지가 저장될 디렉터리에 접근할 수 있는지 확인합니다.

// 이미지가 저장될 디렉터리
    guard let imageDirectory = imageDir else {
        sendCompleteResponse(response, status: .internalServerError, jsonBody: nil)
        return
    }

고유한 이미지 파일 이름을 만들어주기 위해서 타임스탬프 값을 활용했습니다. 위에서 request 객체에서 꺼내온 이미지 파일을 서버의 이미지 디렉터리 내부의 경로로 복사해줍니다.

// 고유한 이미지 이름을 위해 타임스템프 값을 활용
    let timestamp: Int = icuDateToSeconds(getNow())
    
    // 사용자이름_타임스템프.jpg 형식으로 파일이름 지정
    let imageFileName: String = userName + "_" + String(timestamp) + ".jpg"
    
    // 이미지가 저장될 경로
    let imageFilePath: String = imageDirectory.path + imageFileName
    
    // 이미지 저장에 실패할 경우 실패 응답 보내기
    do {
        try imageFile.copyTo(path: imageFilePath, overWrite: false)
    } catch {
        sendCompleteResponse(response, status: .internalServerError, jsonBody: [JSONKey.error:error.localizedDescription])
        return
    }

이미지 복사도 마쳤으니 이제 데이터베이스에 정보를 저장할 차례입니다. mongoDB의 컬렉션도 불러오고, DB에 저장할 데이터를 담은 딕셔너리를 생성해줍니다.
  // mongoDB 컬렉션 가져오기
    guard let collection = DB.collection else {
        sendCompleteResponse(response, status: .internalServerError, jsonBody: [JSONKey.error:"data base initialize failed"])
        return
    }
    
    // DB에 저장할 데이터
    // 차후에 응답 데이터로도 사용합니다
    var jsonDictionary = [JSONKey.userName: userName,
                          JSONKey.description: request.param(name: JSONKey.description),
                          JSONKey.title: title,
                          JSONKey.imagePath:imageFilePath]

이제 실질적으로 DB에 저장합니다. 저장을 하면서 문서의 고유ID도 생성해줍니다. 만약 DB 저장에 실패한다면 아까 복사해온 이미지 파일은 서버 디렉터리에서 삭제해주어야 합니다.

    // DB에 저장
    do {
        let jsonString = try jsonDictionary.jsonEncodedString()
        
        let document: BSON = try BSON(json: jsonString)
        
        // 문서 사용 후에는 닫아주는게 좋겠습니다
        defer {
            document.close()
        }
        
        // 문서 고유 아이디 생성 및 아이디 부여
        let oid: BSON.OID = BSON.OID(imageFileName)
        document.append(oid: oid)
        
        // 컬렉션에 문서 저장
        let result = collection.save(document: document)
        
        // 고유 아이디를 응답 데이터에 추가
        jsonDictionary[JSONKey.articleId] = oid.description
        
        if case .success = result { } else {
            sendCompleteResponse(response, status: .internalServerError, jsonBody: [JSONKey.error:result])
            return
        }
        
    } catch {
        
        // DB 저장에 실패했으므로 이미지 파일은 삭제
        File(imageFilePath).delete()
        
        Log.error(message: error.localizedDescription)
        sendCompleteResponse(response, status: .internalServerError, jsonBody: [JSONKey.error:error.localizedDescription])
        return
    }

DB 저장을 완료하면 응답 데이터를 보내 게시물 등록 처리를 완료합니다.

    // 응답 데이터에는 이미지 경로 대신 이미지 URL 전송
    jsonDictionary[JSONKey.imagePath] = nil
    jsonDictionary[JSONKey.imageUrl] = server.serverAddress + ":\(server.serverPort)" + "/image/" + imageFileName
    
    // 모든 작업을 완료하고 JSON 응답
    sendCompleteResponse(response, status: .created, jsonBody: jsonDictionary)

자 이렇게 postArticleHandler(request:response:) 함수 작성을 마쳤습니다.

// 사진 게시물 등록
func postArticleHandler(request: HTTPRequest, response: HTTPResponse) {
    
    // 응답 컨텐츠 형식은 JSON
    response.setHeader(.contentType, value: ContentsType.json)
    
    // 부족한 매개변수가 없는지 확인
    if let lakedParams = lakedParams(paramsNeeded: [JSONKey.userName, JSONKey.title], paramsReceived: request.postParams) {
        sendCompleteResponse(response, status: .badRequest, jsonBody: [JSONKey.message:"need more params \(lakedParams)"])
    }
    
    // 이미지 파일이 업로드 되었는지 확인
    guard let imageInformation: MimeReader.BodySpec = request.postFileUploads?.first,
        let imageFile = imageInformation.file else {
        sendCompleteResponse(response, status: .badRequest, jsonBody: [JSONKey.message:"need image file"])
            return
    }
    
    // 사용자 이름, 제목 추출
    guard let userName = request.param(name: JSONKey.userName),
        let title = request.param(name: JSONKey.title) else {
            sendCompleteResponse(response, status: .internalServerError, jsonBody: nil)
            return
    }
    
    // 이미지가 저장될 디렉터리
    guard let imageDirectory = imageDir else {
        sendCompleteResponse(response, status: .internalServerError, jsonBody: nil)
        return
    }
    
    // 고유한 이미지 이름을 위해 타임스템프 값을 활용
    let timestamp: Int = icuDateToSeconds(getNow())
    
    // 사용자이름_타임스템프.jpg 형식으로 파일이름 지정
    let imageFileName: String = userName + "_" + String(timestamp) + ".jpg"
    
    // 이미지가 저장될 경로
    let imageFilePath: String = imageDirectory.path + imageFileName
    
    // 이미지 저장에 실패할 경우 실패 응답 보내기
    do {
        try imageFile.copyTo(path: imageFilePath, overWrite: false)
    } catch {
        sendCompleteResponse(response, status: .internalServerError, jsonBody: [JSONKey.error:error.localizedDescription])
        return
    }
    
    // mongoDB 컬렉션 가져오기
    guard let collection = DB.collection else {
        sendCompleteResponse(response, status: .internalServerError, jsonBody: [JSONKey.error:"data base initialize failed"])
        return
    }
    
    // DB에 저장할 데이터
    // 차후에 응답 데이터로도 사용합니다
    var jsonDictionary = [JSONKey.userName: userName,
                          JSONKey.description: request.param(name: JSONKey.description),
                          JSONKey.title: title,
                          JSONKey.imagePath:imageFilePath]
    
    // DB에 저장
    do {
        let jsonString = try jsonDictionary.jsonEncodedString()
        
        let document: BSON = try BSON(json: jsonString)
        
        // 문서 고유 아이디 생성 및 아이디 부여
        let oid: BSON.OID = BSON.OID(imageFileName)
        document.append(oid: oid)
        
        // 컬렉션에 문서 저장
        let result = collection.save(document: document)
        
        // 고유 아이디를 응답 데이터에 추가
        jsonDictionary[JSONKey.articleId] = oid.description
        
        if case .success = result { } else {
            sendCompleteResponse(response, status: .internalServerError, jsonBody: [JSONKey.error:result])
            return
        }
        
    } catch {
        
        // DB 저장에 실패했으므로 이미지 파일은 삭제
        File(imageFilePath).delete()
        
        Log.error(message: error.localizedDescription)
        sendCompleteResponse(response, status: .internalServerError, jsonBody: [JSONKey.error:error.localizedDescription])
        return
    }

    // 응답 데이터에는 이미지 경로 대신 이미지 URL 전송
    jsonDictionary[JSONKey.imagePath] = nil
    jsonDictionary[JSONKey.imageUrl] = server.serverAddress + ":\(server.serverPort)" + "/image/" + imageFileName
    
    // 모든 작업을 완료하고 JSON 응답
    sendCompleteResponse(response, status: .created, jsonBody: jsonDictionary)
}

자 이제 mongoDB도 실행된 상태에서, Perfect 서버 애플리케이션을 실행해봅니다. 그리고는 /article 경로에 새로운 게시물 등록을 요청해봅니다.

 

 


 

짜잔!! 성공했습니다! 올바른 응답이 오고 있어요!



URL을 통해 사진 GET


자, 위 이미지의 응답 데이터를 보면 image_url이 오고 있습니다. 그 URL을 통해서 이미지를 한 번 받아와볼까요?

 


아니, 응답받은 URL인데...!! 왜 다운로드가 안되는거죠!? 흐음... 라우팅을 뭔가 해줘야 할 것 같습니다.


으음... 자 그럼 요청을 핸들링 할 수 있는 함수를 하나 또 만들어줍니다. handlers.swift 파일에 아래 함수를 하나 만들어줬습니다. 요청의 주소의 마지막에 위치한 이미지파일의 이름을 가지고 이미지 디렉터리의 파일이 있는지 확인하여 파일 핸들러를 사용하여 응답합니다.

// 이미지 파일
func imageHandler(request: HTTPRequest, response: HTTPResponse) {
    response.setHeader(.contentType, value: ContentsType.formData)
    
    guard let imageDirectory = imageDir else {
        sendCompleteResponse(response, status: .internalServerError)
        return
    }
    
    guard let imageFileName = request.pathComponents.last, imageFileName.contains(".jpg") else {
        sendCompleteResponse(response, status: .badRequest)
        return
    }
    
    request.path = imageFileName
    
    let handler = StaticFileHandler(documentRoot: imageDirectory.path)
    
    handler.handleRequest(request: request, response: response)
}

main.swift 파일로 이동하여 길을 하나 더 뚫어줍니다. 일단 articleURI 상수 아래에 imageURI도 하나 선언해줍니다.

let articleURI = "/article" let imageURI = "/image" let subArticleURI = articleURI + "/*"


그리고 서버 객체를 생성하는 코드 윗 줄에 아래 코드를 작성해줍니다.

routes.add(method: .get, uri: imageURI + "/*", handler: imageHandler(request:response:))

이렇게 작성하면 /image/xxx.jpg 형식으로 요청이 들어오면 imageHandler(request:response:) 함수가 핸들링 하게 될 것입니다.


자 이제 서버를 재시작하고, 다시 요청해볼까요?



 

으헿, 잘 나왔네요!


이번에는 CRUD 중, Create 먼저 해봤습니다. 다음 번에는 Read를 함께 해봐요~!

뿅!




참고문서



 



by yagom

facebook : http://www.facebook.com/yagomSoft

facebook group : https://www.facebook.com/groups/yagom

p.s 제 포스팅을 RSS 피드로 받아보실 수 있습니다.

RSS Feed 받기   


↓↓↓ 블로거에게 공감은 큰 힘이 됩니다 ↓↓↓ 




저작자 표시 비영리 변경 금지
신고

'Swift > Perfect' 카테고리의 다른 글

사진 게시판 API 만들기 [5]  (0) 2017.09.06
사진 게시판 API 만들기 [4]  (0) 2017.08.30
사진 게시판 API 만들기 [3]  (0) 2017.08.09
사진 게시판 API 만들기 [2]  (0) 2017.07.11
사진 게시판 API 만들기 [1]  (6) 2017.06.27
Perfect 라우팅  (0) 2017.05.24
Posted by yagom


Perfect 미니 프로젝트 [2]


1. 서버 객체 생성 및 라우팅 
2. mongoDB 연결하기 




본격적으로 API 서버 만들기에 돌입합니다!!


참고

2017년 7월 현재 Swift 3 / Perfect 최신버전 2.0.x 환경에서 진행함을 알려드립니다.

* Swift 최신 버전 확인(https://github.com/apple/swift/releases)

* Perfect 최신 버전 확인(https://github.com/PerfectlySoft/Perfect/releases)


서버 객체 생성 및 라우팅

서버 객체와 라우팅에 대한 설명은 지난 글에서 했으므로, 작성한 코드만 설명합니다.

2017/05/24 - [Swift/Perfect] - Perfect 라우팅


우선, 프로젝트 폴더의 Sources폴더의 handlers.swift 파일을 생성합니다.

> vi Sources/handlers.swift


혹시 Xcode로 프로젝트를 진행하고 있다면 파일 생성시 타깃을 ImageBoard로 설정해주세요.


handlers.swift 파일에 아래 코드를 입력합니다.

import PerfectHTTP

struct ContentsType {
    static let json = "application/json; charset=utf-8"
    static let formData = "multipart/form-data"
}

// 사진 게시물 목록
func articleListHandler(request: HTTPRequest, response: HTTPResponse) {
    response.setHeader(.contentType, value: ContentsType.json)
    
    // 첫 번째 테스트를 위해 임시로 response body 생성
    do {
        try response.setBody(json: ["hello":"test"])
        response.completed()
    } catch {
        response.completed(status: .internalServerError)
    }
}

// 사진 게시물 등록
func postArticleHandler(request: HTTPRequest, response: HTTPResponse) {
    response.setHeader(.contentType, value: ContentsType.json)
    response.completed()
}

// 사진 게시물 수정
func modifyArticleHandler(request: HTTPRequest, response: HTTPResponse) {
    response.setHeader(.contentType, value: ContentsType.json)
    response.completed()
}

// 사진 게시물 삭제
func deleteArticleHandler(request: HTTPRequest, response: HTTPResponse) {
    response.setHeader(.contentType, value: ContentsType.json)
    response.completed()
}

// 사진 게시물 정보
func articleInfoHandler(request: HTTPRequest, response: HTTPResponse) {
    response.setHeader(.contentType, value: ContentsType.json)
    response.completed()
}


라우팅을 위한 핸들러 생성을 미리 해둔 것입니다.

핸들러 생성을 모두 마쳤으면 이제 서버 객체를 생성하고 구동할 차례입니다. main.swift 파일로 이동합니다.

> vi Sources/main.swift


이전 포스팅에서 모두 설명한 내용이므로 따로 코드설명은 하지 않아도 아실 것이라 생각합니다.

2017/05/17 - [Swift/Perfect] - Perfect 시작하기


import PerfectLib
import PerfectHTTP
import PerfectHTTPServer


let articleURI = "/article"
let subArticleURI = articleURI + "/*"

var routes = Routes()

routes.add(method: .post, uri: articleURI, handler: postArticleHandler(request:response:))

routes.add(method: .get, uri: articleURI, handler: articleListHandler(request:response:))

routes.add(method: .post, uri: subArticleURI, handler: modifyArticleHandler(request:response:))

routes.add(method: .delete, uri: subArticleURI, handler: deleteArticleHandler(request:response:))

routes.add(method: .get, uri: subArticleURI, handler: articleInfoHandler(request:response:))

let server = HTTPServer()

server.addRoutes(routes)
server.serverPort = 8080

do {
    try server.start()
} catch PerfectError.networkError(let error, let message) {
    Log.error(message: "Error: \(error), \(message)")
} catch {
    Log.error(message: error.localizedDescription)
}


자 이렇게 코드 작성을 마쳤으면, 동작 테스트를 해봐야죠! ImageBoard 애플리케이션을 실행 한 후 접속하여 제대로 동작하는지 확인합니다.


이렇게 응답이 온다면 제대로 동작하고 있다고 볼 수 있지요 :)

제대로 응답이 온다면 다음 스텝으로!


mongoDB 연결하기

이번엔 몽고디비를 연결해보려고 합니다. 사실 이 미니 프로젝트는 여러 데이터베이스를 사용할 필요도, 여러 콜렉션을 사용할 필요도 없습니다. 그래서 단일 데이터베이스의 단일 콜렉션을 사용하려고 합니다. 몽고  데이터베이스 이름은 "image_board", 콜렉션 이름은 "articles"라고 사용하겠습니다.


몽고디비 관련한 내용을 작성할 소스코드 파일 mongo.swift를 생성합니다.

> vi Sources/mongo.swift


mongo.swift 파일에 아래 코드를 작성합니다.

import Foundation
import MongoDB
import PerfectLib

struct DB {
    static var client: MongoClient?
    static var database: MongoDatabase?
    static var collection: MongoCollection?
    
    static func connect(uri: String, database: String, collection: String) throws {
        
        do {
            self.client = try MongoClient(uri: uri)
        } catch {
            throw error
        }
        
        self.database = DB.client?.getDatabase(name: database)
        self.collection = DB.database?.getCollection(name: collection)
    }
    
    static func disconnect() {
        self.collection?.close()
        self.database?.close()
        self.client?.close()
    }
}


그리고 main.swift 파일로 이동해서 아래 코드로 업데이트 해줍니다.

> vi Sources/main.swift


/// 더 위의 코드는 생략 do { try DB.connect(uri: "mongodb://localhost", database: "image_board", collection: "articles") try server.start() } catch PerfectError.networkError(let error, let message) { Log.error(message: "Error: \(error), \(message)") } catch { Log.error(message: "\(error)") } DB.disconnect()


이제 데이터베이스 연결까지 준비를 마쳤습니다. 


다음 번에는 실제로 데이터베이스에 쓰고 읽는 작업까지 해보려 합니다~

다음에 또 만나요! :D



참고문서



by yagom

facebook : http://www.facebook.com/yagomSoft

facebook group : https://www.facebook.com/groups/yagom

p.s 제 포스팅을 RSS 피드로 받아보실 수 있습니다.

RSS Feed 받기   


↓↓↓ 블로거에게 공감은 큰 힘이 됩니다 ↓↓↓ 




저작자 표시 비영리 변경 금지
신고

'Swift > Perfect' 카테고리의 다른 글

사진 게시판 API 만들기 [4]  (0) 2017.08.30
사진 게시판 API 만들기 [3]  (0) 2017.08.09
사진 게시판 API 만들기 [2]  (0) 2017.07.11
사진 게시판 API 만들기 [1]  (6) 2017.06.27
Perfect 라우팅  (0) 2017.05.24
Perfect 시작하기  (0) 2017.05.17
Posted by yagom


Perfect 미니 프로젝트 [1]

1. 프로젝트 개요
2. mongoDB 설치 

3. 패키지 설치 



우분투에 스위프트 설치를 시작으로 이후 두 포스팅에 걸쳐 퍼펙트에 대해 조금 알아보았습니다.



2017/04/03 - [Swift/Perfect] - 우분투(Ubuntu)에 스위프트 설치하기

2017/05/17 - [Swift/Perfect] - Perfect 시작하기

2017/05/24 - [Swift/Perfect] - Perfect 라우팅


이번 부터는 실질적으로 퍼펙트를 사용하여 작은 미니프로젝트를 해보려합니다.

아... 물론 이런 허접한 구성은 실제 서버에서 사용하면 안되지만 간단한 예제로 감만 잡아보는 겁니다. 인증도 없고, 뭐도 없고, 보안도 안되고... 뭐... 암것도 없어요. 그러나 한 번 작은 프로젝트 해보는데에 의의를 가지는 것이니 나중에 삘받으면 하나하나 덧붙여 가면 됩니다.

그저 DB 연결해보고, API로 클라이언트와 핑퐁 해보는 것이 전부이긴 하지만, 그래도 감잡기는 좋을 것 같아요 :)

이 감 말고요... 'ㅁ' 아하핳


프로젝트 개요

간단한 사진게시판을 위한 REST API 서버를 만들어보겠습니다.

API 

HTTP Method 

URI 

사진 게시물 등록 

POST 

/article 

사진 게시물 목록

GET 

사진 게시물 수정 

POST 

/article/{article_id}

사진 게시물 삭제 

DELETE

사진 게시물 정보 가져오기 

GET


사진 게시물 등록

요청(Request)

  • HTTP Method : POST
  • Content-Type: multipart/form-data

 매개변수

자료형 

값의 범위/기본 값 

비고 

 필수여부

image 

binary data

 

이미지 데이터 

user_name

string 

 

사용자 이름 

Y

description

string 

 

이미지 설명 

N

title

string 

 

이미지 제목 

Y


응답(Response)

Key 

자료형 

비고 

필수여부 

article_id 

string 

업로드된 게시물 고유 식별자 

 image_url

string 

이미지 주소 

user_name 

string 

사용자 이름 

description 

 string

이미지 설명 

title 

string 

이미지 제목 


사진 게시물 목록

요청(Request)

  • HTTP Method : GET
  • Content-Type: application/json

 매개변수

자료형 

값의 범위/기본 값 

비고 

 필수여부

page

integer

0

조회하고자 하는 페이지 번호

user_name

string 


특정 사용자의 게시물만 받아오고자 할 때

N

articles_per_page

integer

1~100 / 10 

각 페이지 당 게시물 수 

N


응답(Response)

Key 

자료형 

비고 

필수여부 

articles 

json object array (string) 

게시물 정보 배열 

Y

articles_per_page

integer

각 페이지 당 게시물 수

 Y

current_page

integer 

전송된 페이지 

total_page

integer 

전체 페이지 수 

Y

articles item object 형태

Key 

자료형 

비고 

필수여부 

image_url

string

이미지 URL

Y

user_name

string

업로드한 사용자 이름 

description

string 

이미지 설명 

title

string 

이미지 제목 

article_id

string 

게시물 고유 식별자


사진 게시물 수정

요청(Request)

  • HTTP Method : POST
  • Content-Type: multipart/form-data

 매개변수

자료형 

값의 범위/기본 값 

비고 

 필수여부

image 

binary data

이전 데이터

이미지 데이터 

N

user_name

string 


사용자 이름이 이전 사용자 이름과 일치하지 않으면 수정에 실패

Y

description

string 

이전 데이터

이미지 설명 

N

title

string 

이전 데이터

이미지 제목 

N


응답(Response)

Key 

자료형 

비고 

필수여부 

article_id 

string 

수정된 게시물 고유 식별자 

 image_url

string 

이미지 주소 

user_name 

string 

사용자 이름 

description 

 string

이미지 설명 

title 

string 

이미지 제목 


사진 게시물 삭제

요청(Request)

  • HTTP Method : DELETE
  • Content-Type: application/json

매개변수 없음


응답(Response)

Key 

자료형 

비고 

필수여부 

article_id

string

삭제된 게시물 고유 식별자

Y


사진 게시물 정보 가져오기 

요청(Request)

  • HTTP Method : GET
  • Content-Type: application/json

매개변수 없음


응답(Response)

매개변수 

자료형 

비고 

필수여부 

image_url

string

이미지 URL

Y

user_name

string

업로드한 사용자 이름 

description

string 

이미지 설명 

title

string 

이미지 제목 

article_id

string 

게시물 고유 식별자



모든 응답데이터는 JSON 형식을 사용합니다.

이 정도를 구현해 볼건데요, 데이터베이스가 필요할테니 쓸만한 데이터베이스가 무엇이 있을지 찾아봅니다.


현재(2017년 6월) 퍼펙트에서 공식적으로 제공하는 데이터베이스 커넥터 목록입니다.

  • SQLite
  • MySQL
  • MariaDB
  • PostgreSQL
  • FileMaker
  • MongoDB
  • Working with BSON
  • Apache CouchDB
  • LDAP
  • Kafka
  • Mosquitto
  • ZooKeeper
  • Hadoop

와우... 많네요!! 저는... 몽고디비를 써봐야겠습니다. 



mongoDB 설치

그러려면 먼저 mongoDB를 설치해봐야겠지요.

설치방법은 아래 링크에 친절히 나와있으니 참고하시면 되겠습니다 :)


퍼펙트에서 몽고디비를 사용하기 위해서는 mongo-c 라이브러리를 사용해야합니다. 

먼저, mongo-c 드라이버를 설치합니다. mongo-c와 관련된 설명은 Perfect의 문서를 참고하셔도 좋습니다.


macOS

HomeBrew를 먼저 설치(링크)하신 후, 터미널에서 아래 명령어를 입력합니다.

> brew install mongo-c-driver


ubuntu

아래 명령어를 입력합니다.

> sudo apt-get install libmongoc


패키지 설치

그리고 이제 새로운 프로젝트를 시작하기 위해서 프로젝트를 진행하길 원하는 위치에서 프로젝트 폴더를 생성합니다. 프로젝트 폴더 이름은 ImageBoard라고 하겠습니다.

> mkdir ImageBoard
> cd ImageBoard


그 다음 스위프트 패키지 매니저를 통해 패키지 초기화를 합니다. 스위프트 패키지 매니저는 지난 포스트[우분투(Ubuntu)에 스위프트 설치하기]에서 설치했었죠?

> swift package init --type executable 


swift package init 명령어를 실행 후 폴더에 생성된 Package.swift 파일을 열어서 Perfect 패키지 의존성을 추가합니다.

> vi Package.swift


Package.swift 파일에 아래 코드를 작성합니다.  (우리는 새로운 프로젝트를 만들면서 함께 mongo-c 패키지도 설치할거예요.)

import PackageDescription let package = Package( name: "ImageBoard", targets: [], dependencies: [ .Package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", versions: Version(0,0,0)..<Version(10,0,0)), .Package(url:"https://github.com/PerfectlySoft/Perfect.git", versions: Version(0,0,0)..<Version(10,0,0)), .Package(url:"https://github.com/PerfectlySoft/Perfect-MongoDB.git", versions: Version(0,0,0)..<Version(10,0,0)) ] )

위 패키지 설명은 Perfect HTTP Sever, Perfect, Perfect MongoDB 커넥터 패키지를 버전 0부터 10까지 중 최신 버전을 가져오도록 합니다. (현재시점으로 모두 2.x.x 버전)


소스를 저장하고 나와서 패키지를 설치합니다.

> swift package update


만약에 맥에서 Xcode 프로젝트를 생성해주고 싶다면

> swift package generate-xcodeproj

해주시면 해당 폴더에 Xcode 프로젝트가 생성됩니다. 그러면 이후 코딩을 Xcode 프로젝트로 진행할 수 있습니다.



이제 프로젝트 준비가 완료되었습니다!

다음부터는 실질적으로 코딩을 해봅니다 :D

다음에 또 만나요~!




by yagom

facebook : http://www.facebook.com/yagomSoft

facebook group : https://www.facebook.com/groups/yagom

p.s 제 포스팅을 RSS 피드로 받아보실 수 있습니다.

RSS Feed 받기   


↓↓↓ 블로거에게 공감은 큰 힘이 됩니다 ↓↓↓ 




저작자 표시 비영리 변경 금지
신고

'Swift > Perfect' 카테고리의 다른 글

사진 게시판 API 만들기 [3]  (0) 2017.08.09
사진 게시판 API 만들기 [2]  (0) 2017.07.11
사진 게시판 API 만들기 [1]  (6) 2017.06.27
Perfect 라우팅  (0) 2017.05.24
Perfect 시작하기  (0) 2017.05.17
우분투(Ubuntu)에 스위프트 설치하기  (0) 2017.04.03
Posted by yagom


티스토리 툴바