본문 바로가기
Server/Vapor - ServerSide

Vapor ) Vapor과 PostgreSQL을 이용해서 READ를 구현하자!

by 후르륵짭짭 2020. 10. 16.
728x90
반응형

안녕하세요 후르륵짭잡 입니다!

이번에는 Vapor를 이용해서 PostgreSQL에 저장, 수정, 삭제, 읽기를 구현할 것 입니다.

일단,,,, Vapor에 대한 자세한 설명이 없어서,,, 정말 고생 많이 했습니다.

Vapor에도 고차함수가 있는데,,, 기본적인 쓰임은 비슷하나,,,, 용도의 정확한 의미를 몰라서,,, 많이 헤맸습니다.

나중에 Vapor에 익숙해지면 깊게 다시 다뤄볼 생각 입니다.

(주의 사항 : 시행착오를 기록한 내용이니, 두서가 별로 없고 자세한 내용은 기대하지 마세요 ㅎㅎㅎㅎㅎ)

 

** Model 생성 **

import Foundation
import Vapor
import FluentSQLiteDriver

final class user : Model, Content {
    
    static let schema : String = "user"
    
    @ID(key: .id)
    var id : UUID?
    
    @Field(key: "status")
    var status: String
    
    init(){}
    
    init(id : UUID? = nil , status : String){
        self.id = id
        self.status = status
    }
    
}

일단 기본적인 Model을 생성해주세요! 

Model은 데이터 페이스랑 Xcode를 연결하는 역할을 한다고 생각하면 됩니다.

따라서 CRUD를 구현할 때, 반드시 필요합니다.

 

** Migration 생성 **

import Foundation
import Fluent
import FluentPostgresDriver

struct CreateUserModel : Migration {
    
    func prepare(on database: Database) -> EventLoopFuture<Void> {
        
        database.schema("user")
            .id()
            .field("status", .string)
            .create()
    }
    
    func revert(on database: Database) -> EventLoopFuture<Void> {
        database.schema("user").delete()
    }
    
}

Migraion은 테이블 생성이라 생각하면 됩니다.

schema에 해당하는 이름으로 테이블을 만들고 field를 그 구성요소를 만들게 됩니다.

따라서 Model에도 Field를 만드는데, Model에서 만든 Field는 데이터 베이스와 연결을 위한 용이고 

Migraion은 초기 생성에 사용된다 생각하면 됩니다.

( 하지만, 다른 블로거들을 본다면 Migraion에 굉장히 많은 코드가 들어가는데, 그 부분에 대해서 쫌 더 알 필요가 있을 것 같습니다.

추후 공부해서 올리도록 하겠습니다. )

 

** READ 구현 **

자료가 별로 없어서 고생좀 했습니다 ㅎㅎㅎㅎ

일단 READ 구현에는 두가지 방법이 있습니다.

UUID로 찾는 방법

특정 Field로 찾는 방법

이 두가지를 모두 다뤄보도록 하겠습니다.

 

ID 값으로 찾는 방법 :

import Vapor
import Leaf
import Fluent

func routes(_ app: Application) throws {

//UUID로 찾기
    app.get("find",":id"){ req -> EventLoopFuture<user> in
            
        let id = req.parameters.get("id" , as : UUID.self)
        
        let test = user.find(id, on: req.db)
        let temp = test.unwrap(or: Abort(.notFound)).map({ (result) -> user in
           
            return result
        })
        
        return temp
    }
    
    }

id로 찾는 방식은 위 Model에서 id를 자동으로 생성하도록 했습니다.

만약 이 id 값을 알고 있다면, localhost:8080/find/<ID>값으로 보내줘서 

req.parameters.get(id)를 적으면 UUID에 해당 값이 있는지 확인하고 존재한다면

id를 반환하고 없다면 nil을 반환하게 됩니다.

그리고 이 값을 Model.find(<UUID 형식의 ID>, on : request.db) 이렇게 작성 해줍니다.

request.db는 Vapor DOC에 적혀 있기를,,,

The easiest way to connect to your database is simply using the incoming Request. This will use the model's defaultDatabase property to automatically fetch a pooled connection to the database.


데이터베이스에 연결하는 가장 쉬운 방법은 들어오는 요청을 사용하는 것입니다. 모델의 defaultDatabase 속성을 사용하여 데이터베이스에 대한 풀링 된 연결을 자동으로 가져옵니다.

이렇게 적혀 있습니다.

즉, 그냥 request.db 해주면 현재 Model의 schema와 일치하는 DB를 찾아서 연결 시켜주는 것 같습니다.

(아직 완벽하게 이해 한 것이 아니라 ㅠㅠ 부족한 점이 많아 죄송합니다)

 

자 여기 까지가,,, 이제 찾는 거 였습니다. 

이제 그 값이 존재 할수도 있고 없을 수도 있습니다.

(test 값을 보면 EventLoopFuture<user?> 로 되어 있습니다. 옵셔널 타입인데,, 일반적인 옵셔널 타입이 아닙니다 ㅎㅎㅎㅎ)

따라서 unwrap(or: Abort(.notFound)) 를 해줍니다.

(unwrap는 Enum의 ERROR 타입을 해결해주는 방식으로 여러 타입이 있습니다.)

그리고 젤 중요한 map으로 모든 요소를 탐색해줍니다.

operations performed in map should not block, or they will block the entire event loop. map is intended for use when you have a data-driven function that performs a simple data transformation that cannot error.

내용을 보시면 "no Block, entire evnet loop , performs a simple data transformation that cannot error"로 적혀 있는 것으로 보아

오류가 발생하지 않는 데이터의 반복문으로 생각 할 수 있을 것 같습니다.

이렇게 하고 map의 Return 값으로 Model을 넣어주면 됩니다!

 

특정 Field 값으로 찾기 :

 //UUID 없이 찾기
    app.get("findItem"){ req -> EventLoopFuture<user> in
            
        let test = user.query(on: req.db).filter(\.$status == "test").first().unwrap(or: Abort(.notFound)).map { (result) -> user in
            return result
        }
        
        return test
    }

위에서는 URI에 localhost:8080/find/<ID> 값을 넣어줘서 보내줬지만,

일반적으로 사용할 때는 Field 값을 사용합니다. 

그래서 query문을 사용해서 탐색을 합니다!

Xcode와 데이터 베이스 연결은 Model에서 합니다. 

따라서 Model의 내부 함수인 query를 사용합니다. 

(이때 반드시 필요한것이 import Fluent를 잊지 말아야합니다!)

 

그리고 filter를 사용합니다!!

그런데,, 우리가 일반적으로 사용하는 filter라 사용법이 다릅니다 ㅎㅎㅎㅎ

(\.$값 == "값")

이런씩의 표기법을 사용합니다! 

어쨋든,, ㅋㅋ 이렇게 filter를 해주고 first()로 첫번 째 요소가 있는지 탐색하고 map으로 반환해줍니다!

(갑자기 Multiple 데이터에는 어떻게 반환 할 수 있을지 의구심이 드네요,,,,)

 

** Multiple Data 받아오기 **

 app.get("getMutiple"){ req -> EventLoopFuture<[user]> in
            
        return user.query(on: req.db).filter(\.$status == "test").all()
    }

위에서 많은 기술에 대해 배웠습니다.

Multiple 데이터를 쿼리로 부터 가져오기 위해서는 위에 처럼 

fileter를 해주시고 all() 를 사용해서 가져오면 됩니다!

 

다음에는 Update & Save & Delete에 대해 연구 해보도록 하겠습니다!

 

참고사이트 :

docs.vapor.codes/4.0/fluent/query/

 

Vapor: Fluent → Query

Query Fluent's query API allows you to create, read, update, and delete models from the database. It supports filtering results, joins, chunking, aggregates, and more. // An example of Fluent's query API. let planets = Planet.query(on: database) .filter(\.

docs.vapor.codes

docs.vapor.codes/3.0/fluent/querying/

 

Querying - Vapor Docs

Fluent Queries Once you have a model you can start querying your database to create, read, update, and delete data. Connection The first thing you need to query your database, is a connection to it. Luckily, they are easy to get. Request The easiest way to

docs.vapor.codes

theswiftdev.com/get-started-with-the-fluent-orm-framework-in-vapor-4/

 

Get started with the Fluent ORM framework in Vapor 4 - The.Swift.Dev.

Learn how to use the Fluent ORM framework. Migrations, schemas, relations powered by PostgreSQL, written in Swift.

theswiftdev.com

theswiftdev.com/a-generic-crud-solution-for-vapor-4/

 

A generic CRUD solution for Vapor 4 - The.Swift.Dev.

Learn how to build a controller component that can serve models as JSON objects through a RESTful API written in Swift.

theswiftdev.com

stackoverflow.com/questions/59108951/vapor-filters-are-ambiguous-in-context

 

Vapor filters are ambiguous in context

why this works when directly in router.swift router.delete("users", User.parameter, "books", Book.parameter, "favourite") { req -> Future in try req.parameters.next(User.s...

stackoverflow.com

github.com/vapor/fluent-kit/releases/tag/1.0.0-beta.5

 

Release Beta 5 · vapor/fluent-kit

Important, breaking changes highlighted with ⚠️. Adds top-level FieldKey type (#165) Adds a new top-level FieldKey type to FluentKit. This type represents possible field key values but in a slight...

github.com

 

728x90
반응형

댓글