Firebase ๋ณด์•ˆ๊ทœ์น™ ์ˆ˜์ • : ํŒŒ์ด์–ด๋ฒ ์ด์Šค์— ์•ˆ์ „ํ•˜์ง€ ์•Š์€ ๊ทœ์น™์ด ์žˆ์Šต๋‹ˆ๋‹ค ๋ฉ”์ผ ์˜ฌ ๊ฒฝ์šฐ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

    ํŒŒ๋ฒ  ๋ณด์•ˆ๊ทœ์น™

    ๋ณด์•ˆ ๊ทœ์น™ ์‹œ์ž‘ํ•˜๊ธฐ  |  Firestore  |  Google Cloud

     

    ๋ณด์•ˆ ๊ทœ์น™ ์‹œ์ž‘ํ•˜๊ธฐ  |  Firestore  |  Google Cloud

    ์˜๊ฒฌ ๋ณด๋‚ด๊ธฐ ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”. ๋ณด์•ˆ ๊ทœ์น™ ์‹œ์ž‘ํ•˜๊ธฐ Firestore ๋ณด์•ˆ ๊ทœ์น™์„ ์‚ฌ์šฉํ•˜๋ฉด ์ธํ”„๋ผ๋ฅผ ๊ด€๋ฆฌํ•˜๊ฑฐ๋‚˜ ์„œ๋ฒ„ ์ธก ์ธ์ฆ ๋ฐ ์Šน์ธ

    cloud.google.com

    ์ •๋ง ์ง•๊ธ€์ง•๊ธ€ํ•˜๊ฒŒ ์ด๋ฉ”์ผ์„ ๋ณด๋‚ด ๊ดด๋กญํžˆ๋˜ firebase ๋ณด์•ˆ๊ทœ์น™์„ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค (๊ณ„์† ๊ฒฝ๊ณ ํ•˜๋‹ˆ๊นŒ ๋ถˆ์•ˆํ–ˆ์Šด ๐Ÿฅฒ ๊ฒฝ๊ณ  ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๊ตฌ๊ธ€์”จ)

    1. ๋ณด์•ˆ๊ทœ์น™ ๋ฒ„์ „ ์ž‘์„ฑ

    rules_version = '2';

    ๊ธฐ์กด์˜ rules_version = '1' ์€ ์™€์ผ๋“œ ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํ•œ ๊ฐœ ํ˜น์€ ๋‘ ๊ฐœ ์ด์ƒ์˜ path๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์–ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ž์œ ๋กญ๊ฒŒ ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” zero path๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ๋ฒ„์ „ 2๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค

    2. ์„œ๋น„์Šค ๋ฐ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์„ ์–ธ

    service cloud.firestore {
    match /databases/{database}/documents
    
    • service cloud.firestore { : ๊ทœ์น™์˜ ๋ฒ”์œ„๋ฅผ cloud.firestore๋กœ ์ง€์ •ํ•ด cloud firestore ๋ณด์•ˆ๊ทœ์น™๊ณผ ๋‹ค๋ฅธ ์ œํ’ˆ์˜ ๊ทœ์น™๊ฐ„์˜ ์ถฉ๋Œ ๋ฐฉ์ง€ํ•œ๋‹ค.
    • ๊ตฌ์ฒดํ™”์‹œํ‚ฌ path์˜ pattern์„ ๋งค์น˜ํ•œ๋‹ค. ํ”„๋กœ์ ํŠธ์˜ ๋ชจ๋“  cloud firestore ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค๊ฐ€ ์ผ์น˜ํ•˜๋„๋ก ์ง€์ •ํ•œ๋‹ค.

    3. ๋ณด์•ˆ๊ทœ์น™ ์ž‘์„ฑ

    ๊ธฐ๋ณธ๊ตฌ์กฐ

    service cloud.firestore {
    
      match /databases/{database}/documents {
          match /stories/{storyid} {
          allow write: if request.auth.uid == resource.data.author;
            }
        }
    }
    

    1) ๊ทœ์น™์„ ์ ์šฉํ•  ๋ฌธ์„œ๋ฅผ ์ง€์ •ํ•œ๋‹ค.

    • cities collection ๋‚ด seoul์ด๋ผ๋Š” ํŠน์ • ๋ฌธ์„œ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™ ์ ์šฉ
    match /cities/ Seoul {
    
    • cities collection ๋‚ด์˜ ๋ชจ๋“  ๋ฌธ์„œ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™ ์ ์šฉ (ํ•˜์œ„ ์ปฌ๋ ‰์…˜์€ ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.)
    match /cities/{city} {
    
    • cities collection ๋‚ด ๋ชจ๋“  ํ•˜์œ„ ๋ฒ”์ฃผ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™ ์ ์šฉ
    match /cities/{document=**}
    
    • { } ๋‚ด์˜ ์ด๋ฆ„์€ ์ปฌ๋ ‰์…˜ ์ด๋ฆ„๊ณผ ๋‹ค๋ฅด๊ฒŒ ์ž„์˜๋กœ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.

    2) ์ง€์ • ๋ฌธ์„œ์— ๋Œ€ํ•œ ๋ณด์•ˆ ๊ทœ์น™์„ ์ •์˜ํ•œ๋‹ค.

    a. ํ–‰๋™(action) ์ •์˜

    • ์ฝ๊ธฐ
    allow read: if <condition>;
    
    allow get: if <condition>;
    allow list: if <condition>;
    

    read : ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๋Š” ํ–‰๋™ ( get + list )

    get : ๋‹จ์ผ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๋Š” ํ–‰๋™

    Firestore.instance
                 .collection(‘users’)
                    .document(uid)
                    .get()
    
    

    list : ์ฟผ๋ฆฌ ๋ฌธ์„œ ํ˜น์€ collection๋‚ด ๋ชจ๋“  ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๋Š” ํ–‰๋™

    Firestore.instance
            .collection(‘users’)
            .get()
    
    
    • ์“ฐ๊ธฐ
    allow write: if <condition>;
    
    allow create: if <condition>;
    allow update: if <condition>;
    allow delete: if <condition>;
    

    write : ๋ฌธ์„œ๋ฅผ ์“ฐ๋Š” ํ–‰๋™ ( create + update + delete )

    create : ๋ฌธ์„œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ–‰๋™

    update : ๋ฌธ์„œ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ํ–‰๋™

    delete : ๋ฌธ์„œ๋ฅผ ์‚ญ์ œํ•˜๋Š” ํ–‰๋™

    b. ๋ณด์•ˆ ์กฐ๊ฑด ์ •์˜

    ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ๋ณด์•ˆ์กฐ๊ฑด


    • ๋ชจ๋“ ์‚ฌ๋žŒ๋“ค์ด ์ฝ๊ณ  ์“ฐ๊ธฐ ๊ฐ€๋Šฅ
    allow read, write: if true
    
    • ์ฝ˜ํ…์ธ  ์†Œ์œ ์ž๋งŒ ์ฝ๊ณ  ์“ฐ๊ธฐ ๊ฐ€๋Šฅ
    allow read, write: if request.auth.uid == request.resource.data.author_uid
    
    • ๋ชจ๋“  ์ ‘๊ทผ์„ ์ฐจ๋‹จ
    allow read, write: if false
    
    
    • ๊ทธ ์™ธ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด ํ™œ์šฉ (request.auth)

    request.auth๋ž€?

    request.auth.uid  : ์‚ฌ์šฉ์ž uid ์ •๋ณด

    Google.com์„

    https://firebase.google.com/docs/reference/rules/rules.firestore.Request

    ์˜ˆ์‹œ )

    allow read : if request.auth.token.email != null && request.auth.token.email_verified
    

    ๋‹จ, ์ด๋ฉ”์ผ ์ธ์ฆ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์ด๋ฉ”์ผ ์ธ์ฆ ๊ด€๋ จ ๋ณด์•ˆ๊ทœ์น™์„ ์ ์œผ๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

    ๋˜ํ•œ ํœด๋Œ€ํฐ ์ธ์ฆ์„œ๋น„์Šค์— ๋Œ€ํ•œ ๋ณด์•ˆ๊ทœ์น™ ์‚ฌ์šฉ ์‹œ Authentication์— ์ €์žฅ๋œ ํ˜•ํƒœ์ธ phone_number == '821045241423' ํ˜•ํƒœ๋งŒ ํ—ˆ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค. '01045241423' ํ˜•ํƒœ๋Š” ๊ฐ™์ง€ ์•Š๋‹ค๊ณ  ์ธ์‹ํ•˜๋‹ˆ ์œ ์˜ํ•˜์ž.

    • ์‚ฌ์šฉ์ž ์ธ์ฆ ํด๋ ˆ์ž„ ์ƒ์„ฑ

    ์„œ๋ฒ„ ์ธก ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋˜๋Š” cloud function์„ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ์‚ฌ์šฉ์ž์— ๋Œ€ํ•ด ์„ค์ •ํ•˜๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ๋ณ€์ˆ˜์ด๋‹ค.

    match / {everythingInMyDatabase == **}{
    allow read, write : if request.auth.token.super_admin == true;
    }
    
    match /reviews / {reviewID}{
    allow read, write : if request.auth.token.role == "moderator";
    }
    

    ๋ฌธ์„œ์˜ ์—‘์„ธ์Šค์— ๋Œ€ํ•œ ๋ณด์•ˆ์กฐ๊ฑด

    ๋ถˆํ•„์š”ํ•œ ๋‚ด์šฉ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋˜๋Š” ๊ฒƒ์„ ๋ง‰๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

    ๋ฐ์ดํ„ฐ์˜ ์ข…๋ฅ˜ - request data : the data requests that's coming in

    • target documents : you're looking to either read or update ( resource.data )
    • some other document : some other data located in some other part

    • ์‚ฌ์šฉ์ž๊ฐ€ ์ž‘์„ฑํ•œ ๋ฌธ์„œ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์“ธ ๊ฒฝ์šฐ (request.resource.data)

    request.resource.data๋ž€?

    //์ž‘์„ฑํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฌธ์„œ์˜ score ํ•„๋“œ๊ฐ’์ด 50์ผ ๋•Œ ์“ฐ๊ธฐ ํ—ˆ์šฉ
    allow write : if request.resource.data.score == 50
    allow write : if request.resource.data["score"] == 50
    
    //score ํ•„๋“œ์˜ ๊ฐ’์ด ์ˆซ์ž์ž„์„ ํ™•์‹ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ null value๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค
    allow write : if request.resource.data.score is number
    
    //score ํ•„๋“œ ๊ฐ’์ด 1๋ณด๋‹ค ํฌ๊ณ  5๋ณด๋‹ค ์ž‘์€ ๋ฌธ์„œ๋งŒ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
    allow write : if request.resource.data.score >= 1 && request.resource.data.score <= 5
    
    //score ํ•„๋“œ๊ฐ’์ด 1๋ณด๋‹ค ํฌ๊ฑฐ๋‚˜ 5๋ณด๋‹ค ์ž‘์€ ๋ฌธ์„œ๋ฅผ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
    allow write : if request.resource.data.score >= 1
    allow write : if request.resource.data.score <= 5
    
    //์ž‘์„ฑํ•˜๊ณ ์žํ•˜๋Š” ๋ฌธ์„œ์˜ category์˜ ๋‚ด์šฉ์ด [widgets, things]๋‚ด์— ์žˆ์„ ๊ฒฝ์šฐ๋งŒ ํ—ˆ์šฉ
    allow write : if request.resource.data.category in ['widgets', 'things']
    
    //headline์ด string type์ด๊ณ  200 character ๋ฏธ๋งŒ์ผ ๋•Œ๋งŒ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
    allow write : if request.resource.data.headline is string && request.resource.data.headline.size() <200
    
    //firestore์— ์ถ”๊ฐ€๋˜๊ณ ์ž ํ•˜๋Š” ๋ฌธ์„œ์˜ reviewID ํ•„๋“œ๊ฐ’๊ณผ request.auth.uid ๊ฐ™์„ ๋•Œ๋งŒ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•œ๋‹ค.
    allow write : if request.resource.data.reviewID == request.auth.uid;
    
    • ์ด๋ฏธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์กด์žฌํ•˜๋Š” ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์“ธ ๊ฒฝ์šฐ(resource.data)

    resource.data๋ž€?

    //์ง€์ •ํ•œ ๋ฌธ์„œ์˜ reviewerID ํ•„๋“œ์™€ request.auth.uid์ด ๊ฐ™์„ ๋•Œ๋งŒ ๋ฌธ์„œ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
    allow update: if resource.data.reviewerID == request.auth.uid
    
    //score์˜ ํ•„๋“œ๊ฐ’์„ ๋ณ€ํ™”์‹œํ‚ค์ง€ ์•Š๊ธฐ ์œ„ํ•จ.
    allow update: resource.data.score == resource.data.score;
    
    // ์ง€์ •๋ฌธ์„œ์˜ x ํ•„๋“œ์˜ ๊ฐ’์ด 5 ์ด์ƒ์ธ ๋ฐ์ดํ„ฐ๋งŒ ์‚ฌ์šฉ์ž๊ฐ€ ์ฝ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ
     allow read: if resource.data.x > 5;
    
    // ์ง€์ •๋ฌธ์„œ์˜ published ํ•„๋“œ๊ฐ’์ด true์ธ ๋ฐ์ดํ„ฐ๋งŒ ์‚ฌ์šฉ์ž๊ฐ€ ์ฝ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ
    allow read: if resource.data.published == true ;
    
    allow read: if resource.data.name == 'John Doe'
    
    • ๋‹ค๋ฅธ ๋ฌธ์„œ์— ๋Œ€ํ•œ ์—‘์„ธ์Šค (์ง€์ •ํ•œ ๋ฌธ์„œ ์™ธ์— ๋‹ค๋ฅธ ๋ฌธ์„œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•  ๊ฒฝ์šฐ)

    get : ์ง€์ • ๋ฌธ์„œ ์™ธ์— ๋‹ค๋ฅธ ๋ฌธ์„œ๋ฅผ ๊ฐ€์ ธ์™€์„œ ๋น„๊ต

    exist ์˜ˆ์‹œ )

    //users collection๋‚ด์— ํŠน์ • uid์˜ ๋ฌธ์„œ๊ฐ€ ์กด์žฌํ•˜๋Š”๊ฐ€
    
    if exists(/databases/$(database)/documents/users/$(request.auth.uid))
    if exists(/databases/$(database)/documents/Users/$('2s6xdCuygpMv0ralWTnxbRZcYo73'));
    
    //users subcollection๋‚ด์— request.auth.uid์ธ ๋ฌธ์„œ๊ฐ€ ์กด์žฌํ•˜๋Š”๊ฐ€
    
    if exists(/databases/$(database)/documents/leagues/$(league)/users/$(request.auth.uid))}
    
    //Users ์ปฌ๋ ‰์…˜ ๋‚ด ํŠน์ •๋ฌธ์„œ์˜ 'name' ํ•„๋“œ๋ฅผ writeํ•˜๋ ค๊ณ  ํ•œ๋‹ค.
    //์ƒˆ๋กœ์šด name์˜ ํ•„๋“œ๊ฐ’์ด  Kits์˜ document id์— ์กด์žฌํ•  ๋•Œ๋งŒ ํ—ˆ์šฉํ•œ๋‹ค.
    
     match /Users/{user} {
    
    		allow write :
            if exists(/databases/$(database)/documents/Kits/$(request.resource.data.name));
    
    }
    
    //Users ์ปฌ๋ ‰์…˜ ๋‚ด ํŠน์ • ๋ฌธ์„œ์˜ name ํ•„๋“œ๋ฅผ read ํ˜น์€ writeํ•˜๋ ค๊ณ  ํ•œ๋‹ค.
    //writeํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฌธ์„œ์˜ birth ํ•„๋“œ๊ฐ’์ด Kits์˜ document id๋กœ ์กด์žฌํ•  ๋•Œ ํ—ˆ์šฉํ•œ๋‹ค.
    
    match /Users/{user} {
    
    	allow read, write :
        if exists(/databases/$(database)/documents/Kits/$(resource.data.birth));
    
        }
    
    • request.resource.data๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— writeํ•˜๊ธฐ๋ฅผ ์‹œ๋„ํ•˜๋Š” ๋ฐ์ดํ„ฐ์ž„์œผ๋กœ read์—๋Š” ์ ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ๋ฐ˜๋ฉด resource.data๋Š” ํ˜„์žฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์กด์žฌํ•˜๋Š” ๋ฐ์ดํ„ฐ์ž„์œผ๋กœ read์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

    get ์˜ˆ์‹œ)

    //request.auth.uid์ธ ๋ฌธ์„œ์˜ admin ํ•„๋“œ๊ฐ€ true์ธ๊ฐ€
    if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true
    
    //map ํ˜•ํƒœ์˜ ํ•„๋“œ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ
    //(roles / request.auth.uid / role / isadmin : true) ์ผ๋•Œ ์Šน์ธ
    
    match /posts/{docId} {
        allow read, write: if hasRole("isAdmin")
    }
    function hasRole(userRole) {
      return get(/databases/$(database)/documents/roles/$(request.auth.uid)).data.role[userRole]
    }
    
    //map ํ˜•ํƒœ์˜ ํ•„๋“œ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ
    //(restaurants/restaurantID/ private_data (sub collection) / private / roles /request.auth.uid
    //== editor or owner ) ์ผ ๋•Œ ์Šน์ธ
    
    if get(/databases/$(database)/documents/restaurants/$(restaurantID)
    /private_data/private).data.roles[request.auth.uid] == ["editor" ,"owner"]
    
    • $(database)๋Š” Cloud Firestore Security Rules ์™€์ผ๋“œ ์นด๋“œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ์ •์˜ํ•˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค. match /databases/{database}/documents
    • exist๋Š” ๋‹ค๋ฅธ ๋ฌธ์„œ์— ํŠน์ • ๋ฌธ์„œ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€๋ฅผ ํŒ๋ณ„ํ•œ๋‹ค. ํŠน์ • ํ•„๋“œ๊ฐ’์ด ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” get์„ ์‚ฌ์šฉํ•œ๋‹ค.
    • ์ฟผ๋ฆฌํ•œ ๋‚ด์šฉ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๊ฒฝ์šฐ ( request.query )

    limit - query limit clause.

    document fusersQuery = Firestore.instance.collection('users').orderBy('lastLaunch').limit(20)
    fusersQuery.getDocuments()
    
    allow list; if request.query.limit <= 20 && request.query.orderBy.lasetLaunch == "ASC";
    
    • ์‚ฌ์šฉ์ž๊ฐ€ ์š”์ฒญํ•œ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋ณด์•ˆ๊ทœ์น™์„ ์ ์šฉํ•  ๊ฒฝ์šฐ (request.time)
    //์š”์ฒญํ•œ ์‹œ๊ฐ„์ด 2019๋…„ 3์›” 24์ผ ์ดํ›„ ์ผ๋•Œ๋งŒ ์ฝ๊ธฐ ํ—ˆ์šฉ
    allow read: if request.time.toMillis() >=
                 timestamp.date(2020,1,1).toMillis();
    
    //๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ publication timestamp ์™€ ์š”์ฒญ๋œ ์‹œ๊ฐ„ ๋น„๊ต
    // 1 millisecond = 0.001 seconds.
    
    allow read: if request.time.toMillis() >=
                resource.data.publicAt.seconds * 1000;
    
    • time์— ๋Œ€ํ•œ ์กฐ๊ฑด์„ ์ƒ์„ฑํ•  ๊ฒฝ์šฐ (duration)

    duration.time(hours, mins, secs, nanos)

     //๋งˆ์ง€๋ง‰์œผ๋กœ ์ „์†ก๋œ ์•Œ๋žŒ์‹œ๊ฐ„ ์ดํ›„ 1์‹œ๊ฐ„์ด ์ง€๋‚œ ๋ฌธ์„œ๋งŒ readํ•  ์ˆ˜ ์žˆ๋‹ค.
    
    allow read: if request.time > (resource.data.lastSendNotification + duration.time(1, 0, 0, 0));
    

    duration.value(๊ฐ„๊ฒฉ, ๋‹จ์œ„)

    w Weeks

    d Days
    h Hours
    m Minutes
    s Seconds
    ms Milliseconds
    ns Nanoseconds
    match /notification/{notificationId} {
       allow create: if
          request.time.toMillis() > getRequiredTimeInMillis();
    
       function getRequiredTimeInMillis() {
         return (getLastSendNotificationTimestamp().seconds +
                       duration.value(2, 'h').seconds()) * 1000;
       }
    
       function getLastSendNotificationTimestamp() {
         return get(/databases/$(database)/documents/
            user/$(request.auth.uid)).data.lastSendNotification;
       }
    }
    
    //๋งˆ์ง€๋ง‰์œผ๋กœ ์ „์†ก๋œ ์•Œ๋žŒ์‹œ๊ฐ„ ์ดํ›„ 2์‹œ๊ฐ„์ด ์ง€๋‚˜์•ผ ๋ฌธ์„œ๋ฅผ createํ•  ์ˆ˜ ์žˆ๋‹ค.
    

    https://firebase.google.com/docs/reference/rules/rules.duration_

    • ํ•จ์ˆ˜ ์‚ฌ์šฉ

    ๋ฐ˜๋ณต์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ทœ์น™์˜ ๊ฒฝ์šฐ ํ•จ์ˆ˜๋กœ ๋งŒ๋“ค์–ด๋†“์œผ๋ฉด ํŽธ๋ฆฌํ•˜๋‹ค.

    service cloud.firestore {
    
      match /databases/{database}/documents {
    
     //์ง€์ •๋ฌธ์„œ๋ฒ”์œ„ ๋ฐ”๊นฅ ์ชฝ์— ์žˆ๋Š” ํ•จ์ˆ˜
     	 function outerauthorOrPublished(storyid) {
            return resource.data.published == true || request.auth.uid == 	resource.data.author;
          }
         match /stories/{storyid} {
    
         //์ง€์ •๋ฌธ์„œ๋ฒ”์œ„ ๋‚ด์— ์žˆ๋Š” ํ•จ์ˆ˜
             function innerauthorOrPublished() {
               return resource.data.published == true || request.auth.uid == 	esource.data.author;
             }
    
             allow list: if request.query.limit <= 10 &&
                             innerauthorOrPublished();
    
             allow get: if outerauthorOrPublished(storyid);
             allow write: if request.auth.uid == resource.data.author;
            }
          }
        }
    
    • ๋ฐ˜๋“œ์‹œ ์ฃผ์˜ํ•ด์•ผํ•  ์ 

    ๋ณด์•ˆ๊ทœ์น™์€ ํ•„ํ„ฐ๊ฐ€ ์•„๋‹ˆ๋‹ค. ์‹ค์ œ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ€์ •๋œ ๋ฐ์ดํ„ฐ์— ๋ณด์•ˆ๊ทœ์น™์„ ์ ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฟผ๋ฆฌํ•œ ๋ฌธ์„œ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™์„ ์ ์šฉํ•  ๊ฒฝ์šฐ ์ฝ”๋“œ ์ƒ์—์„œ์˜ ์ฟผ๋ฆฌ ๋‚ด์šฉ๊ณผ ๋ณด์•ˆ๊ทœ์น™์—์„œ์˜ ์ฟผ๋ฆฌ ๋‚ด์šฉ์ด ๋™์ผํ•ด์•ผํ•œ๋‹ค. ์ฝ”๋“œ๊ฐ€ ์•„๋‹Œ ๋ณด์•ˆ๊ทœ์น™์—๋งŒ ์ฟผ๋ฆฌ๋ฅผ ์ ์šฉํ•˜๋ฉด ์กฐ๊ฑด์— ๋งž์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜ฌ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ณด์•ˆ๊ทœ์น™์ด ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๊ฑฐ๋ถ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

    allow read, write: if request.auth.uid == resource.data.author;
    

    ์˜ฌ๋ฐ”๋ฅธ ์˜ˆ์‹œ)

    FirebaseUser user = await _firebaseAuth.currentUser();
    Firestore.instance.collection("stories").where("author", isEqualTo: user.uid)
             .getDocuments()
    

    ์ž˜๋ชป๋œ ์˜ˆ์‹œ) ํ•ด๋‹น ์ฟผ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†๋‹ค.

    ํŒŒ๋ฒ  ๋ณด์•ˆ๊ทœ์น™

    ๋ณด์•ˆ ๊ทœ์น™ ์‹œ์ž‘ํ•˜๊ธฐ  |  Firestore  |  Google Cloud

    ์ •๋ง ์ง•๊ธ€์ง•๊ธ€ํ•˜๊ฒŒ ์ด๋ฉ”์ผ์„ ๋ณด๋‚ด ๊ดด๋กญํžˆ๋˜ firebase ๋ณด์•ˆ๊ทœ์น™์„ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค (๊ณ„์† ๊ฒฝ๊ณ ํ•˜๋‹ˆ๊นŒ ๋ถˆ์•ˆํ–ˆ์Šด ๐Ÿฅฒ)

    ์šฐ๋ฆฌ๊ฑฐ์ธ๋ฐ ์‚ฌ์‹ค ์šฐ๋ฆฐ ์ฝ”๋“œ์ƒ์—์„œ auth๋กœ ํ•œ๋ฒˆ ๊ฑฐ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๊ตณ์ด ์ด˜์ด˜ํ•˜๊ฒŒ ์•ˆ์งฐ์–ด์š”์˜ค

    ์•„๋ž˜ ๋‚ด์šฉ ์–ด๋ ต์ง€ ์•Š๊ณ  ์ •๋ง ์œ ์šฉํ•˜๋‹ˆ๊นŒ ์ด๋ฒˆ๊ธฐํšŒ์— ๋‹ค๋“ค ๊ผญ ๋งˆ์Šคํ„ฐํ•˜์‹œ๊ธธ ,,, ํ™”์ดํƒฑ ~!!

    1. ๋ณด์•ˆ๊ทœ์น™ ๋ฒ„์ „ ์ž‘์„ฑ

    rules_version = '2';

    ๊ธฐ์กด์˜ rules_version = '1' ์€ ์™€์ผ๋“œ ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํ•œ ๊ฐœ ํ˜น์€ ๋‘ ๊ฐœ ์ด์ƒ์˜ path๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์–ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ž์œ ๋กญ๊ฒŒ ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” zero path๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ๋ฒ„์ „ 2๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค

    2. ์„œ๋น„์Šค ๋ฐ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์„ ์–ธ

    service cloud.firestore {
    match /databases/{database}/documents
    
    • service cloud.firestore { : ๊ทœ์น™์˜ ๋ฒ”์œ„๋ฅผ cloud.firestore๋กœ ์ง€์ •ํ•ด cloud firestore ๋ณด์•ˆ๊ทœ์น™๊ณผ ๋‹ค๋ฅธ ์ œํ’ˆ์˜ ๊ทœ์น™๊ฐ„์˜ ์ถฉ๋Œ ๋ฐฉ์ง€ํ•œ๋‹ค.
    • ๊ตฌ์ฒดํ™”์‹œํ‚ฌ path์˜ pattern์„ ๋งค์น˜ํ•œ๋‹ค. ํ”„๋กœ์ ํŠธ์˜ ๋ชจ๋“  cloud firestore ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค๊ฐ€ ์ผ์น˜ํ•˜๋„๋ก ์ง€์ •ํ•œ๋‹ค.

    3. ๋ณด์•ˆ๊ทœ์น™ ์ž‘์„ฑ

    ๊ธฐ๋ณธ๊ตฌ์กฐ

    service cloud.firestore {
    
      match /databases/{database}/documents {
          match /stories/{storyid} {
          allow write: if request.auth.uid == resource.data.author;
            }
        }
    }
    

    1) ๊ทœ์น™์„ ์ ์šฉํ•  ๋ฌธ์„œ๋ฅผ ์ง€์ •ํ•œ๋‹ค.

    • cities collection ๋‚ด seoul์ด๋ผ๋Š” ํŠน์ • ๋ฌธ์„œ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™ ์ ์šฉ
    match /cities/ Seoul {
    
    • cities collection ๋‚ด์˜ ๋ชจ๋“  ๋ฌธ์„œ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™ ์ ์šฉ (ํ•˜์œ„ ์ปฌ๋ ‰์…˜์€ ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.)
    match /cities/{city} {
    
    • cities collection ๋‚ด ๋ชจ๋“  ํ•˜์œ„ ๋ฒ”์ฃผ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™ ์ ์šฉ
    match /cities/{document=**}
    
    • { } ๋‚ด์˜ ์ด๋ฆ„์€ ์ปฌ๋ ‰์…˜ ์ด๋ฆ„๊ณผ ๋‹ค๋ฅด๊ฒŒ ์ž„์˜๋กœ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.

    2) ์ง€์ • ๋ฌธ์„œ์— ๋Œ€ํ•œ ๋ณด์•ˆ ๊ทœ์น™์„ ์ •์˜ํ•œ๋‹ค.

    a. ํ–‰๋™(action) ์ •์˜

    • ์ฝ๊ธฐ
    allow read: if <condition>;
    
    allow get: if <condition>;
    allow list: if <condition>;
    

    read : ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๋Š” ํ–‰๋™ ( get + list )

    get : ๋‹จ์ผ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๋Š” ํ–‰๋™

    Firestore.instance
                 .collection(‘users’)
                    .document(uid)
                    .get()
    
    

    list : ์ฟผ๋ฆฌ ๋ฌธ์„œ ํ˜น์€ collection๋‚ด ๋ชจ๋“  ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๋Š” ํ–‰๋™

    Firestore.instance
            .collection(‘users’)
            .get()
    
    
    • ์“ฐ๊ธฐ
    allow write: if <condition>;
    
    allow create: if <condition>;
    allow update: if <condition>;
    allow delete: if <condition>;
    

    write : ๋ฌธ์„œ๋ฅผ ์“ฐ๋Š” ํ–‰๋™ ( create + update + delete )

    create : ๋ฌธ์„œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ–‰๋™

    update : ๋ฌธ์„œ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ํ–‰๋™

    delete : ๋ฌธ์„œ๋ฅผ ์‚ญ์ œํ•˜๋Š” ํ–‰๋™

    b. ๋ณด์•ˆ ์กฐ๊ฑด ์ •์˜

    ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ๋ณด์•ˆ์กฐ๊ฑด


    • ๋ชจ๋“ ์‚ฌ๋žŒ๋“ค์ด ์ฝ๊ณ  ์“ฐ๊ธฐ ๊ฐ€๋Šฅ
    allow read, write: if true
    
    • ์ฝ˜ํ…์ธ  ์†Œ์œ ์ž๋งŒ ์ฝ๊ณ  ์“ฐ๊ธฐ ๊ฐ€๋Šฅ
    allow read, write: if request.auth.uid == request.resource.data.author_uid
    
    • ๋ชจ๋“  ์ ‘๊ทผ์„ ์ฐจ๋‹จ
    allow read, write: if false
    
    
    • ๊ทธ ์™ธ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด ํ™œ์šฉ (request.auth)

    request.auth๋ž€?

    request.auth.uid  : ์‚ฌ์šฉ์ž uid ์ •๋ณด

    Google.com์„

    https://firebase.google.com/docs/reference/rules/rules.firestore.Request

    ์˜ˆ์‹œ )

    allow read : if request.auth.token.email != null && request.auth.token.email_verified
    

    ๋‹จ, ์ด๋ฉ”์ผ ์ธ์ฆ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์ด๋ฉ”์ผ ์ธ์ฆ ๊ด€๋ จ ๋ณด์•ˆ๊ทœ์น™์„ ์ ์œผ๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

    ๋˜ํ•œ ํœด๋Œ€ํฐ ์ธ์ฆ์„œ๋น„์Šค์— ๋Œ€ํ•œ ๋ณด์•ˆ๊ทœ์น™ ์‚ฌ์šฉ ์‹œ Authentication์— ์ €์žฅ๋œ ํ˜•ํƒœ์ธ phone_number == '821045241423' ํ˜•ํƒœ๋งŒ ํ—ˆ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค. '01045241423' ํ˜•ํƒœ๋Š” ๊ฐ™์ง€ ์•Š๋‹ค๊ณ  ์ธ์‹ํ•˜๋‹ˆ ์œ ์˜ํ•˜์ž.

    • ์‚ฌ์šฉ์ž ์ธ์ฆ ํด๋ ˆ์ž„ ์ƒ์„ฑ

    ์„œ๋ฒ„ ์ธก ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋˜๋Š” cloud function์„ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ์‚ฌ์šฉ์ž์— ๋Œ€ํ•ด ์„ค์ •ํ•˜๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ๋ณ€์ˆ˜์ด๋‹ค.

    match / {everythingInMyDatabase == **}{
    allow read, write : if request.auth.token.super_admin == true;
    }
    
    match /reviews / {reviewID}{
    allow read, write : if request.auth.token.role == "moderator";
    }
    

    ๋ฌธ์„œ์˜ ์—‘์„ธ์Šค์— ๋Œ€ํ•œ ๋ณด์•ˆ์กฐ๊ฑด

    ๋ถˆํ•„์š”ํ•œ ๋‚ด์šฉ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋˜๋Š” ๊ฒƒ์„ ๋ง‰๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

    ๋ฐ์ดํ„ฐ์˜ ์ข…๋ฅ˜ - request data : the data requests that's coming in

    • target documents : you're looking to either read or update ( resource.data )
    • some other document : some other data located in some other part

    • ์‚ฌ์šฉ์ž๊ฐ€ ์ž‘์„ฑํ•œ ๋ฌธ์„œ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์“ธ ๊ฒฝ์šฐ (request.resource.data)

    request.resource.data๋ž€?

    //์ž‘์„ฑํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฌธ์„œ์˜ score ํ•„๋“œ๊ฐ’์ด 50์ผ ๋•Œ ์“ฐ๊ธฐ ํ—ˆ์šฉ
    allow write : if request.resource.data.score == 50
    allow write : if request.resource.data["score"] == 50
    
    //score ํ•„๋“œ์˜ ๊ฐ’์ด ์ˆซ์ž์ž„์„ ํ™•์‹ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ null value๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค
    allow write : if request.resource.data.score is number
    
    //score ํ•„๋“œ ๊ฐ’์ด 1๋ณด๋‹ค ํฌ๊ณ  5๋ณด๋‹ค ์ž‘์€ ๋ฌธ์„œ๋งŒ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
    allow write : if request.resource.data.score >= 1 && request.resource.data.score <= 5
    
    //score ํ•„๋“œ๊ฐ’์ด 1๋ณด๋‹ค ํฌ๊ฑฐ๋‚˜ 5๋ณด๋‹ค ์ž‘์€ ๋ฌธ์„œ๋ฅผ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
    allow write : if request.resource.data.score >= 1
    allow write : if request.resource.data.score <= 5
    
    //์ž‘์„ฑํ•˜๊ณ ์žํ•˜๋Š” ๋ฌธ์„œ์˜ category์˜ ๋‚ด์šฉ์ด [widgets, things]๋‚ด์— ์žˆ์„ ๊ฒฝ์šฐ๋งŒ ํ—ˆ์šฉ
    allow write : if request.resource.data.category in ['widgets', 'things']
    
    //headline์ด string type์ด๊ณ  200 character ๋ฏธ๋งŒ์ผ ๋•Œ๋งŒ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
    allow write : if request.resource.data.headline is string && request.resource.data.headline.size() <200
    
    //firestore์— ์ถ”๊ฐ€๋˜๊ณ ์ž ํ•˜๋Š” ๋ฌธ์„œ์˜ reviewID ํ•„๋“œ๊ฐ’๊ณผ request.auth.uid ๊ฐ™์„ ๋•Œ๋งŒ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•œ๋‹ค.
    allow write : if request.resource.data.reviewID == request.auth.uid;
    
    • ์ด๋ฏธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์กด์žฌํ•˜๋Š” ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์“ธ ๊ฒฝ์šฐ(resource.data)

    resource.data๋ž€?

    //์ง€์ •ํ•œ ๋ฌธ์„œ์˜ reviewerID ํ•„๋“œ์™€ request.auth.uid์ด ๊ฐ™์„ ๋•Œ๋งŒ ๋ฌธ์„œ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
    allow update: if resource.data.reviewerID == request.auth.uid
    
    //score์˜ ํ•„๋“œ๊ฐ’์„ ๋ณ€ํ™”์‹œํ‚ค์ง€ ์•Š๊ธฐ ์œ„ํ•จ.
    allow update: resource.data.score == resource.data.score;
    
    // ์ง€์ •๋ฌธ์„œ์˜ x ํ•„๋“œ์˜ ๊ฐ’์ด 5 ์ด์ƒ์ธ ๋ฐ์ดํ„ฐ๋งŒ ์‚ฌ์šฉ์ž๊ฐ€ ์ฝ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ
     allow read: if resource.data.x > 5;
    
    // ์ง€์ •๋ฌธ์„œ์˜ published ํ•„๋“œ๊ฐ’์ด true์ธ ๋ฐ์ดํ„ฐ๋งŒ ์‚ฌ์šฉ์ž๊ฐ€ ์ฝ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ
    allow read: if resource.data.published == true ;
    
    allow read: if resource.data.name == 'John Doe'
    
    • ๋‹ค๋ฅธ ๋ฌธ์„œ์— ๋Œ€ํ•œ ์—‘์„ธ์Šค (์ง€์ •ํ•œ ๋ฌธ์„œ ์™ธ์— ๋‹ค๋ฅธ ๋ฌธ์„œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•  ๊ฒฝ์šฐ)

    get : ์ง€์ • ๋ฌธ์„œ ์™ธ์— ๋‹ค๋ฅธ ๋ฌธ์„œ๋ฅผ ๊ฐ€์ ธ์™€์„œ ๋น„๊ต

    exist ์˜ˆ์‹œ )

    //users collection๋‚ด์— ํŠน์ • uid์˜ ๋ฌธ์„œ๊ฐ€ ์กด์žฌํ•˜๋Š”๊ฐ€
    
    if exists(/databases/$(database)/documents/users/$(request.auth.uid))
    if exists(/databases/$(database)/documents/Users/$('2s6xdCuygpMv0ralWTnxbRZcYo73'));
    
    //users subcollection๋‚ด์— request.auth.uid์ธ ๋ฌธ์„œ๊ฐ€ ์กด์žฌํ•˜๋Š”๊ฐ€
    
    if exists(/databases/$(database)/documents/leagues/$(league)/users/$(request.auth.uid))}
    
    //Users ์ปฌ๋ ‰์…˜ ๋‚ด ํŠน์ •๋ฌธ์„œ์˜ 'name' ํ•„๋“œ๋ฅผ writeํ•˜๋ ค๊ณ  ํ•œ๋‹ค.
    //์ƒˆ๋กœ์šด name์˜ ํ•„๋“œ๊ฐ’์ด  Kits์˜ document id์— ์กด์žฌํ•  ๋•Œ๋งŒ ํ—ˆ์šฉํ•œ๋‹ค.
    
     match /Users/{user} {
    
    		allow write :
            if exists(/databases/$(database)/documents/Kits/$(request.resource.data.name));
    
    }
    
    //Users ์ปฌ๋ ‰์…˜ ๋‚ด ํŠน์ • ๋ฌธ์„œ์˜ name ํ•„๋“œ๋ฅผ read ํ˜น์€ writeํ•˜๋ ค๊ณ  ํ•œ๋‹ค.
    //writeํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฌธ์„œ์˜ birth ํ•„๋“œ๊ฐ’์ด Kits์˜ document id๋กœ ์กด์žฌํ•  ๋•Œ ํ—ˆ์šฉํ•œ๋‹ค.
    
    match /Users/{user} {
    
    	allow read, write :
        if exists(/databases/$(database)/documents/Kits/$(resource.data.birth));
    
        }
    
    • request.resource.data๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— writeํ•˜๊ธฐ๋ฅผ ์‹œ๋„ํ•˜๋Š” ๋ฐ์ดํ„ฐ์ž„์œผ๋กœ read์—๋Š” ์ ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ๋ฐ˜๋ฉด resource.data๋Š” ํ˜„์žฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์กด์žฌํ•˜๋Š” ๋ฐ์ดํ„ฐ์ž„์œผ๋กœ read์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

    get ์˜ˆ์‹œ)

    //request.auth.uid์ธ ๋ฌธ์„œ์˜ admin ํ•„๋“œ๊ฐ€ true์ธ๊ฐ€
    if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true
    
    //map ํ˜•ํƒœ์˜ ํ•„๋“œ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ
    //(roles / request.auth.uid / role / isadmin : true) ์ผ๋•Œ ์Šน์ธ
    
    match /posts/{docId} {
        allow read, write: if hasRole("isAdmin")
    }
    function hasRole(userRole) {
      return get(/databases/$(database)/documents/roles/$(request.auth.uid)).data.role[userRole]
    }
    
    //map ํ˜•ํƒœ์˜ ํ•„๋“œ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ
    //(restaurants/restaurantID/ private_data (sub collection) / private / roles /request.auth.uid
    //== editor or owner ) ์ผ ๋•Œ ์Šน์ธ
    
    if get(/databases/$(database)/documents/restaurants/$(restaurantID)
    /private_data/private).data.roles[request.auth.uid] == ["editor" ,"owner"]
    
    • $(database)๋Š” Cloud Firestore Security Rules ์™€์ผ๋“œ ์นด๋“œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ์ •์˜ํ•˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค. match /databases/{database}/documents
    • exist๋Š” ๋‹ค๋ฅธ ๋ฌธ์„œ์— ํŠน์ • ๋ฌธ์„œ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€๋ฅผ ํŒ๋ณ„ํ•œ๋‹ค. ํŠน์ • ํ•„๋“œ๊ฐ’์ด ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” get์„ ์‚ฌ์šฉํ•œ๋‹ค.
    • ์ฟผ๋ฆฌํ•œ ๋‚ด์šฉ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๊ฒฝ์šฐ ( request.query )

    limit - query limit clause.

    document fusersQuery = Firestore.instance.collection('users').orderBy('lastLaunch').limit(20)
    fusersQuery.getDocuments()
    
    allow list; if request.query.limit <= 20 && request.query.orderBy.lasetLaunch == "ASC";
    
    • ์‚ฌ์šฉ์ž๊ฐ€ ์š”์ฒญํ•œ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋ณด์•ˆ๊ทœ์น™์„ ์ ์šฉํ•  ๊ฒฝ์šฐ (request.time)
    //์š”์ฒญํ•œ ์‹œ๊ฐ„์ด 2019๋…„ 3์›” 24์ผ ์ดํ›„ ์ผ๋•Œ๋งŒ ์ฝ๊ธฐ ํ—ˆ์šฉ
    allow read: if request.time.toMillis() >=
                 timestamp.date(2020,1,1).toMillis();
    
    //๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ publication timestamp ์™€ ์š”์ฒญ๋œ ์‹œ๊ฐ„ ๋น„๊ต
    // 1 millisecond = 0.001 seconds.
    
    allow read: if request.time.toMillis() >=
                resource.data.publicAt.seconds * 1000;
    
    • time์— ๋Œ€ํ•œ ์กฐ๊ฑด์„ ์ƒ์„ฑํ•  ๊ฒฝ์šฐ (duration)

    duration.time(hours, mins, secs, nanos)

     //๋งˆ์ง€๋ง‰์œผ๋กœ ์ „์†ก๋œ ์•Œ๋žŒ์‹œ๊ฐ„ ์ดํ›„ 1์‹œ๊ฐ„์ด ์ง€๋‚œ ๋ฌธ์„œ๋งŒ readํ•  ์ˆ˜ ์žˆ๋‹ค.
    
    allow read: if request.time > (resource.data.lastSendNotification + duration.time(1, 0, 0, 0));
    

    duration.value(๊ฐ„๊ฒฉ, ๋‹จ์œ„)

    w Weeks

    d Days
    h Hours
    m Minutes
    s Seconds
    ms Milliseconds
    ns Nanoseconds
    match /notification/{notificationId} {
       allow create: if
          request.time.toMillis() > getRequiredTimeInMillis();
    
       function getRequiredTimeInMillis() {
         return (getLastSendNotificationTimestamp().seconds +
                       duration.value(2, 'h').seconds()) * 1000;
       }
    
       function getLastSendNotificationTimestamp() {
         return get(/databases/$(database)/documents/
            user/$(request.auth.uid)).data.lastSendNotification;
       }
    }
    
    //๋งˆ์ง€๋ง‰์œผ๋กœ ์ „์†ก๋œ ์•Œ๋žŒ์‹œ๊ฐ„ ์ดํ›„ 2์‹œ๊ฐ„์ด ์ง€๋‚˜์•ผ ๋ฌธ์„œ๋ฅผ createํ•  ์ˆ˜ ์žˆ๋‹ค.
    

    https://firebase.google.com/docs/reference/rules/rules.duration_

    • ํ•จ์ˆ˜ ์‚ฌ์šฉ

    ๋ฐ˜๋ณต์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ทœ์น™์˜ ๊ฒฝ์šฐ ํ•จ์ˆ˜๋กœ ๋งŒ๋“ค์–ด๋†“์œผ๋ฉด ํŽธ๋ฆฌํ•˜๋‹ค.

    service cloud.firestore {
    
      match /databases/{database}/documents {
    
     //์ง€์ •๋ฌธ์„œ๋ฒ”์œ„ ๋ฐ”๊นฅ ์ชฝ์— ์žˆ๋Š” ํ•จ์ˆ˜
     	 function outerauthorOrPublished(storyid) {
            return resource.data.published == true || request.auth.uid == 	resource.data.author;
          }
         match /stories/{storyid} {
    
         //์ง€์ •๋ฌธ์„œ๋ฒ”์œ„ ๋‚ด์— ์žˆ๋Š” ํ•จ์ˆ˜
             function innerauthorOrPublished() {
               return resource.data.published == true || request.auth.uid == 	esource.data.author;
             }
    
             allow list: if request.query.limit <= 10 &&
                             innerauthorOrPublished();
    
             allow get: if outerauthorOrPublished(storyid);
             allow write: if request.auth.uid == resource.data.author;
            }
          }
        }
    
    • ๋ฐ˜๋“œ์‹œ ์ฃผ์˜ํ•ด์•ผํ•  ์ 

    ๋ณด์•ˆ๊ทœ์น™์€ ํ•„ํ„ฐ๊ฐ€ ์•„๋‹ˆ๋‹ค. ์‹ค์ œ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ€์ •๋œ ๋ฐ์ดํ„ฐ์— ๋ณด์•ˆ๊ทœ์น™์„ ์ ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฟผ๋ฆฌํ•œ ๋ฌธ์„œ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™์„ ์ ์šฉํ•  ๊ฒฝ์šฐ ์ฝ”๋“œ ์ƒ์—์„œ์˜ ์ฟผ๋ฆฌ ๋‚ด์šฉ๊ณผ ๋ณด์•ˆ๊ทœ์น™์—์„œ์˜ ์ฟผ๋ฆฌ ๋‚ด์šฉ์ด ๋™์ผํ•ด์•ผํ•œ๋‹ค. ์ฝ”๋“œ๊ฐ€ ์•„๋‹Œ ๋ณด์•ˆ๊ทœ์น™์—๋งŒ ์ฟผ๋ฆฌ๋ฅผ ์ ์šฉํ•˜๋ฉด ์กฐ๊ฑด์— ๋งž์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜ฌ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ณด์•ˆ๊ทœ์น™์ด ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๊ฑฐ๋ถ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

    allow read, write: if request.auth.uid == resource.data.author;
    

    ์˜ฌ๋ฐ”๋ฅธ ์˜ˆ์‹œ)

    FirebaseUser user = await _firebaseAuth.currentUser();
    Firestore.instance.collection("stories").where("author", isEqualTo: user.uid)
             .getDocuments()
    

    ์ž˜๋ชป๋œ ์˜ˆ์‹œ) ํ•ด๋‹น ์ฟผ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†๋‹ค.

    ๋ฐ˜์‘ํ˜•

    ๋Œ“๊ธ€