iyOmSd/Title: Vapor

[Vapor] Schema CRUD 데이터베이스 테이블 수정

냄수 2021. 3. 6. 16:08
반응형

Vapor버전 4.0을 기준으로 작성된 게시글입니다!

 

데이터베이스를 사용하다보면

테이블에 새로운 컬럼을 추가할 때가 있거나 삭제할 때가 있어요

그럴때 수정을 데이터베이스에서 안하고 vapor에서 Fluent를 이용해서 코드로 할 수 있다고 하네요!

 

 

Create

이미 존재하는 이름이 있는 경우 에러를 발생 시킬 우려가 있어요

ignoreExisting을 이용해서 이미있다면 무시할 수 있도록 해주세요

database.schema("planets")
    .id()
    .field("name", .string, .required)
    .create()

planets테이블을 만들껀데

id

name

의 컬럼을 만들어 주는 거고 

이때 name은 String타입으로 무조건 값이 필요한 속성으로 만든거에요

 

Field를 만들때

required - nil 불가능

references - 외래키

identifier - primary key

조건이 있구요

 

 

Update

이미 적용된 코드가 있다면 그 코드를 포함한 아래의 코드 모두 적용되지 않아요

즉, 변경사항이 있어야만 코드가 동작한다는 소리에요

 

// 기존 planets 테이블에 'size'라는 필드하나추가하는 코드
db.schema("planets")
        .field("size", .int)
        .update()
        
        
// 실패코드
// id가 이미 있으므로 실행되지않음
db.schema("planets")
        .id()
        .field("size", .int)
        .update()

 

여기서 주의해야할 헷갈리는 개념이 있는데

updateField와 field가 있어요

updateField는 필드의 데이터타입을 변경할 때 사용

field스키마를 추가할때 사용

을 지켜줘야해요

 

Delete

스키마(테이블)을 삭제하는코드와

스키마의 필드만 삭제하는 코드로 구분해서 사용해요

// 스키마 삭제(db table삭제)
db.schema("planets").delete()

// 필드삭제(해당이름의 컬럼 삭제)
db.schema("planets")
        .deleteField("age")
        .update()

 

 


Unique

중복된 값을 허용하지 않는 필드를 만들때 사용해요

db.schema("planets")
        .updateField("name", .string)
        .unique(on: "name")
        .update()

 

JsonType

커스텀 구조체타입에 Codable 프로토콜을 준수하면 필드타입으로 사용할 수 있어요

타입을 지정할땐 dictionary타입으로만 사용하면되요

이미 있는 자료형인경우는 dictionary(of: 자료형타입)을 사용하면되구요

마찬가지로 배열도 저장할 수 있어요

db.schema("planets")
        .updateField("name", .string)
        .field("arr", .array(of: .string))
        .field("dic", .dictionary(of: .int))
        .field("type", .dictionary)
        .update()

모두 json타입으로 생성된걸 볼 수 있어요

 

Enum

열거형 타입도 디비에 저장할 수 있어요

    db.enum("planet_type")
        .case("one")
        .case("two")
        .case("three")
        .create()

one, two, three라는 케이스를 만들고

    db.enum("planet_type").read().flatMap { type in
        db.schema("planets")
            .field("enumType", type, .required)
            .update()
        
    }

planet_type에 해당하는 열거형을 read()를 이용해서 읽어오면

등록한 케이스인 one, two, three를 다 읽어오고

enumType이라는 필드에 해당 타입을 지정해주는거에요

 


이렇게 스키마를 변경할 때

마이그레이션이라는 기능을 지원해줘요

마이그레이션이란

간단하게 디비의 변경사항들을 기록해놓는 역할이라고 생각하면되요

 

변경할 사항들을 구현하고

실패시 행동할 사항을 구현해요

 

Migration 프로토콜을 채택해준 구조체를 만들어주고

struct GalaxyMigration: Migration {
    func prepare(on database: Database) -> EventLoopFuture<Void> {
        database.schema("galaxies")
            .field("age", .int)
            .update()
    }
    
    func revert(on database: Database) -> EventLoopFuture<Void> {
        database.schema("galaxies").delete()
    }
}

prepare에 변경될 사항

revert에 실패시 되돌릴 사항을 적어주고

 

코드에 방금만드 구조체를 이용해서 마이그레이션을 추가해주세요

app.migrations.add(GalaxyMigration())

 

저같은경우는

디비를 접속하는 부분에 추가를 해줬어요

private func setUpMySQL(_ app: Application) throws {
    app.databases.use(
        .mysql(
            hostname: "localhost",
            username: "root",
            password: "",
            database: "vapor",
            tlsConfiguration: .forClient(certificateVerification: .none)
    ), as: .mysql)

    app.migrations.add(GalaxyMigration())
}

 

터미널로 가서

// 마이그레이션시
vapor run migrate

// 되돌리기시 
vapor run migrate --revert

원하는 명령어를 실행하면

해당 코드가 실행된것을 볼 수 있어요

 

 

 

이때 생각하는 헷갈린점이 있어요

처음에 스키마를 생성하는 코드와

마이그레이션 코드는 무엇이다를까...?!

구조체를 이용해서 한다는거 빼고는 코드는 같아요

func prepare(on database: Database) -> EventLoopFuture<Void> {
    database.schema("galaxies")
        .field("age", .int)
        .update()
}

database.schema("galaxies")
        .field("age", .int)
        .update()

이 코드만 실행시켜도 동작은 같거든요

 

마이그레이션으로 얻을 수 있는 이득에는

생성하거나 수정하는 코드를 그냥하지않고 마이그레이션 코드에 담아서 안전하게 할 수 있는 장점이 있다고

생각되네요!

 

 

 

반응형