본 게시글은 Vapor 4.0.0, Fluent 4.0.0, Fluent-mysql-driver 4.0.0 버전을 바탕으로 작성되었습니다.
또한 최소 타겟 버전 iOS 16.1, Swift 5.0 이상, xcode 14.1 버전 이상에서 정상 작동되는 것을 확인하였습니다.
Vapor
를 왜 써야 하는지 고민고민.Firebase
는 문서형 db인데, 이걸 관계형처럼 써보겠다고 DocumentReference
타입 경로를 만들면서 씨름했다.static
하게 쓰게 되었다.nil
값을 가질 수 있고, 필드도 자유롭게 추가할 수 있는 점은 매우매우 좋다.DocumentReference
타입의 "참조경로"가 나를 너무 괴롭혔기 때문.. 다른 값들은 nil
일 수 있는데, 이 녀석만큼은 nil
이면 안 되는 예외기도 했고.POST
구현과 Fluent ORM Framework를 활용한 db 연동 성공public func configure(_ app: Application) throws {
/// test DBTable을 db로 사용, 당연히 테스트라서 이름과 패스워드는 valse, (검열)로 설정하고 따로 보안조치 하지 않음.
/// db의 이름은 db의 이름을 써주면 된다(db테이블 이름이 아님!).
app.databases.use(.mysql(hostname: "localhost", username: "valse", password: "********", database: "test"), as: .mysql)
app.migrations.add(CreateTodo())
// register routes
try routes(app)
}
id
를 필수로 가져야 하며, 기본적으로 UUID
타입을 생성할 수 있으나 커스텀하여 Int
로도 사용이 가능한 것으로 보인다.genereatedBy
아규먼트를 지정하지 않아서 그런듯final class TestTable: Model, Content {
static let schema = "test_table"
@ID(custom: "id")
var id: Int?
@Field(key: "name")
var name: String
@Field(key: "job")
var job: String
@Field(key: "age")
var age: Int
init() { }
init(id: Int?, name: String, job: String, age: Int) {
self.id = id
self.name = name
self.job = job
self.age = age
}
}
public func boot(routes: Vapor.RoutesBuilder) throws {
/// "api" 엔드포인트를 갖는 route를 묶고 각 요청에 대해 아래의 메소드로 대체한다.
let testTables = routes.grouped("api")
testTables.get(use: index)
testTables.post(use: create)
/// 상단의 엔드포인트에 아이디가 포함되어 있고 delete 요청의 경우엔 delete 메소드로 대체한다.
testTables.group(":id") { eachInfo in
eachInfo.delete(use: delete)
}
}
// MARK: Create New item in db(?)
/// testable이 라우터에 의해 post 될 때, 이 메소드를 사용한다.
/// HTTP body로 전달된 application/json을 decode하고 db에 등록한다.
/// 저장된 구조체를 리턴해서 db에 저장된 내용을 확인할 수 있다.
public func create(req: Request) async throws -> TestTable {
dump("++++ CREATE ++++")
let testTable = try req.content.decode(TestTable.self)
dump("---- STEP 1 ----")
try await testTable.save(on: req.db)
dump("---- STEP 2 ----")
return testTable
}
-- 모델 작업 완료 --
Post
는 HTTP 통신 중, Content Body, Method, Header 지정이 필요하다.public func createUserInfo(with newUser: UserInfo) async -> Void {
do {
let encodedUserData = try encoder.encode(newUser)
let postEndPoint = URL(string: "\(url)/create")!
var request = URLRequest(url: postEndPoint)
request.httpBody = encodedUserData
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
await runCreateSession(withRequest: request, withNewUserData: encodedUserData)
} catch {
dump("\(newUser) - CANT BE ENCODED : \(error.localizedDescription)")
}
}
URLSession
객체를 만들고 비동기로 .upload(for:from:)
메소드를 호출한다.uploadData
를 받아온다.dump()
로 확인한다.private func runCreateSession(withRequest: URLRequest, withNewUserData: Data) async -> Void {
do {
let uploadData = try await URLSession(configuration: .default).upload(for: withRequest, from: withNewUserData)
let answer = try JSONDecoder().decode(UserInfo.self, from: uploadData.0)
dump("++++ SUCCESS, \(answer.name) \(answer.job)")
} catch {
dump("+++ \(error.localizedDescription), \(withNewUserData)")
}
}
POST
구현 메소드를 연결한다.Button {
Task {
await vaporManager.createUserInfo(with: UserInfo(name: "국영", job: "영화배우", age: 22))
}
} label: {
Text("Submit")
}
POST
이전의 dbPOST
이후의 dbUPDATE
랑 REMOVE
도 해야지..?