Repository: yorkie-team/yorkie
Pull request #484 (2023-03-06 07:29:44)

Add RemoveDocument API
<!--  Thanks for sending a pull request! -->

**What this PR does / why we need it**:

Add document deletion API.

Document deletion is crucial feature to save resources. Without deletion, accumulated documents and its relevant data will waste storage resources.

To overall document deletion process will look like this:

1. Client sends document delete API(`RemoveDocument`) with  `DocIsRemoved = true` in request `ChangePack`
2. In `PushPull` of *`RemoveDocument`(or other APIs),* check `DocIsRemoved`. And if `DocIsRemoved = true`, simply set `RemovedAt` date in `docInfo` to logically(soft) remove document.
3. Set response `ChangePack`'s `DocIsRemoved` to `true` to inform document deletion.
4. After certain period of time(`DocumentRemoveDuration`), document is physically removed from database.

To successfully implement this API, we need to seperate our tasks and implement them step by step.

- [x]  1. Implement basic document deletion API with simply setting `RemovedAt` date in `docInfo`
- [ ]  2. Determine whether to include removed documents in `ListDocuments` API used by Dashboard and CLI(Admin)
- [ ]  3. Implement housekeeping background process to physically remove document(`is_removed = true`) and its relevant data.

**Which issue(s) this PR fixes**:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Address #464

**Special notes for your reviewer**:

Discussion about document deletion API in codepair: [https://codepair.yorkie.dev/ylvpas](https://codepair.yorkie.dev/ylvpas)

Previous closed PR for implementing document deletion API: [https://github.com/yorkie-team/yorkie/pull/468](https://github.com/yorkie-team/yorkie/pull/468)

WatchDocuments API also needs to find documents by `ID`, not by `key`.

**Does this PR introduce a user-facing change?**:
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".
-->

```
adds Client.Remove(document), *RemoveDocument API*
```

**Additional documentation**:

<!--
This section can be blank if this pull request does not require a release note.

Please use the following format for linking documentation:
- [Usage]: <link>
- [Other doc]: <link>
-->
```docs

```

**Checklist**:
- [x] Added relevant tests or not required
- [x] Didn't break anything


codecov[bot]: # [Codecov](https://codecov.io/gh/yorkie-team/yorkie/pull/484?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) Report
> Merging [#484](https://codecov.io/gh/yorkie-team/yorkie/pull/484?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) (aee5cfd) into [main](https://codecov.io/gh/yorkie-team/yorkie/commit/dde4f22fdab00970a19c57ebe9a03129f3a1faa7?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) (dde4f22) will **increase** coverage by `1.84%`.
> The diff coverage is `35.18%`.

```diff
@@            Coverage Diff             @@
##             main     #484      +/-   ##
==========================================
+ Coverage   46.51%   48.35%   +1.84%     
==========================================
  Files          69       69              
  Lines        5805     5921     +116     
==========================================
+ Hits         2700     2863     +163     
+ Misses       2797     2731      -66     
- Partials      308      327      +19     
```


| [Impacted Files](https://codecov.io/gh/yorkie-team/yorkie/pull/484?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) | Coverage Δ | |
|---|---|---|
| [api/converter/from\_pb.go](https://codecov.io/gh/yorkie-team/yorkie/pull/484?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-YXBpL2NvbnZlcnRlci9mcm9tX3BiLmdv) | `54.29% <0.00%> (-0.26%)` | :arrow_down: |
| [api/converter/to\_pb.go](https://codecov.io/gh/yorkie-team/yorkie/pull/484?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-YXBpL2NvbnZlcnRlci90b19wYi5nbw==) | `63.07% <0.00%> (-0.42%)` | :arrow_down: |
| [client/client.go](https://codecov.io/gh/yorkie-team/yorkie/pull/484?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-Y2xpZW50L2NsaWVudC5nbw==) | `10.70% <0.00%> (-1.19%)` | :arrow_down: |
| [pkg/document/document.go](https://codecov.io/gh/yorkie-team/yorkie/pull/484?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2RvY3VtZW50Lmdv) | `53.62% <0.00%> (-2.44%)` | :arrow_down: |
| [pkg/document/internal\_document.go](https://codecov.io/gh/yorkie-team/yorkie/pull/484?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2ludGVybmFsX2RvY3VtZW50Lmdv) | `22.03% <ø> (ø)` | |
| [server/backend/database/client\_info.go](https://codecov.io/gh/yorkie-team/yorkie/pull/484?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-c2VydmVyL2JhY2tlbmQvZGF0YWJhc2UvY2xpZW50X2luZm8uZ28=) | `73.97% <ø> (+38.37%)` | :arrow_up: |
| [server/backend/database/doc\_info.go](https://codecov.io/gh/yorkie-team/yorkie/pull/484?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-c2VydmVyL2JhY2tlbmQvZGF0YWJhc2UvZG9jX2luZm8uZ28=) | `0.00% <0.00%> (ø)` | |
| [server/backend/database/mongo/indexes.go](https://codecov.io/gh/yorkie-team/yorkie/pull/484?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-c2VydmVyL2JhY2tlbmQvZGF0YWJhc2UvbW9uZ28vaW5kZXhlcy5nbw==) | `50.00% <ø> (ø)` | |
| [server/backend/database/mongo/client.go](https://codecov.io/gh/yorkie-team/yorkie/pull/484?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-c2VydmVyL2JhY2tlbmQvZGF0YWJhc2UvbW9uZ28vY2xpZW50Lmdv) | `20.15% <16.66%> (+11.61%)` | :arrow_up: |
| [server/rpc/yorkie\_server.go](https://codecov.io/gh/yorkie-team/yorkie/pull/484?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-c2VydmVyL3JwYy95b3JraWVfc2VydmVyLmdv) | `48.19% <57.14%> (+1.77%)` | :arrow_up: |
| ... and [7 more](https://codecov.io/gh/yorkie-team/yorkie/pull/484?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team)


Patch:
diff --git a/api/converter/from_pb.go b/api/converter/from_pb.go
index b21d6361..bfc246fc 100644
--- a/api/converter/from_pb.go
+++ b/api/converter/from_pb.go
@@ -165,6 +165,7 @@ func FromChangePack(pbPack *api.ChangePack) (*change.Pack, error) {
 		Changes:         changes,
 		Snapshot:        pbPack.Snapshot,
 		MinSyncedTicket: minSyncedTicket,
+		IsRemoved:       pbPack.IsRemoved,
 	}, nil
 }
 
diff --git a/api/converter/to_pb.go b/api/converter/to_pb.go
index 9f621f9b..b7b4582b 100644
--- a/api/converter/to_pb.go
+++ b/api/converter/to_pb.go
@@ -151,6 +151,7 @@ func ToChangePack(pack *change.Pack) (*api.ChangePack, error) {
 		Changes:         pbChanges,
 		Snapshot:        pack.Snapshot,
 		MinSyncedTicket: ToTimeTicket(pack.MinSyncedTicket),
+		IsRemoved:       pack.IsRemoved,
 	}, nil
 }
 
diff --git a/api/types/auth_webhook.go b/api/types/auth_webhook.go
index 03bd52b7..46fc678d 100644
--- a/api/types/auth_webhook.go
+++ b/api/types/auth_webhook.go
@@ -54,6 +54,7 @@ const (
 	DeactivateClient Method = "DeactivateClient"
 	AttachDocument   Method = "AttachDocument"
 	DetachDocument   Method = "DetachDocument"
+	RemoveDocument   Method = "RemoveDocument"
 	PushPull         Method = "PushPull"
 	WatchDocuments   Method = "WatchDocuments"
 )
@@ -75,6 +76,7 @@ func AuthMethods() []Method {
 		DeactivateClient,
 		AttachDocument,
 		DetachDocument,
+		RemoveDocument,
 		PushPull,
 		WatchDocuments,
 	}
diff --git a/api/yorkie/v1/resources.pb.go b/api/yorkie/v1/resources.pb.go
index 147192f2..61507489 100644
--- a/api/yorkie/v1/resources.pb.go
+++ b/api/yorkie/v1/resources.pb.go
@@ -112,12 +112,15 @@ func (DocEventType) EnumDescriptor() ([]byte, []int) {
 	return fileDescriptor_36361b2f5d0f0896, []int{1}
 }
 
+// ChangePack is a message that contains all changes that occurred in a document.
+// It is used to synchronize changes between clients and servers.
 type ChangePack struct {
 	DocumentKey          string      `protobuf:"bytes,1,opt,name=document_key,json=documentKey,proto3" json:"document_key,omitempty"`
 	Checkpoint           *Checkpoint `protobuf:"bytes,2,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"`
 	Snapshot             []byte      `protobuf:"bytes,3,opt,name=snapshot,proto3" json:"snapshot,omitempty"`
 	Changes              []*Change   `protobuf:"bytes,4,rep,name=changes,proto3" json:"changes,omitempty"`
 	MinSyncedTicket      *TimeTicket `protobuf:"bytes,5,opt,name=min_synced_ticket,json=minSyncedTicket,proto3" json:"min_synced_ticket,omitempty"`
+	IsRemoved            bool        `protobuf:"varint,6,opt,name=is_removed,json=isRemoved,proto3" json:"is_removed,omitempty"`
 	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
 	XXX_unrecognized     []byte      `json:"-"`
 	XXX_sizecache        int32       `json:"-"`
@@ -191,6 +194,13 @@ func (m *ChangePack) GetMinSyncedTicket() *TimeTicket {
 	return nil
 }
 
+func (m *ChangePack) GetIsRemoved() bool {
+	if m != nil {
+		return m.IsRemoved
+	}
+	return false
+}
+
 type Change struct {
 	Id                   *ChangeID    `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
 	Message              string       `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
@@ -2786,146 +2796,148 @@ func init() {
 func init() { proto.RegisterFile("yorkie/v1/resources.proto", fileDescriptor_36361b2f5d0f0896) }
 
 var fileDescriptor_36361b2f5d0f0896 = []byte{
-	// 2222 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x59, 0xdf, 0x8f, 0xdb, 0x58,
-	0xf5, 0x8f, 0x1d, 0xe7, 0x87, 0x4f, 0xa6, 0x9d, 0xf4, 0x4e, 0x7f, 0xa4, 0x69, 0x3b, 0x3b, 0x4d,
-	0xbf, 0xdf, 0x32, 0xdb, 0x42, 0xa6, 0x1d, 0xda, 0x05, 0xb6, 0x5a, 0x84, 0x27, 0xf1, 0x36, 0xd3,
-	0x9d, 0x66, 0x46, 0x4e, 0xa6, 0x4b, 0x2b, 0x90, 0xe5, 0xb1, 0x6f, 0x3b, 0xde, 0x49, 0x6c, 0xaf,
-	0xed, 0x64, 0x9b, 0x07, 0x5e, 0x10, 0x48, 0x3c, 0xc0, 0x3b, 0x7f, 0x00, 0x12, 0x7f, 0xc3, 0x3e,
-	0x21, 0xf1, 0x80, 0x78, 0x03, 0x04, 0x12, 0xaf, 0xd0, 0x7d, 0x40, 0x3c, 0x02, 0x12, 0x6f, 0x48,
-	0xe8, 0xde, 0x6b, 0x3b, 0x8e, 0xe3, 0x64, 0xd3, 0x30, 0x42, 0x5d, 0xde, 0x7c, 0xef, 0xfd, 0x9c,
-	0x73, 0xcf, 0xcf, 0x7b, 0x8e, 0xef, 0x85, 0xcb, 0x23, 0xdb, 0x3d, 0x31, 0xf1, 0xd6, 0xf0, 0xee,
-	0x96, 0x8b, 0x3d, 0x7b, 0xe0, 0xea, 0xd8, 0xab, 0x3b, 0xae, 0xed, 0xdb, 0x48, 0x64, 0x4b, 0xf5,
-	0xe1, 0xdd, 0xea, 0x5b, 0x2f, 0x6c, 0xfb, 0x45, 0x0f, 0x6f, 0xd1, 0x85, 0xa3, 0xc1, 0xf3, 0x2d,
-	0xdf, 0xec, 0x63, 0xcf, 0xd7, 0xfa, 0x0e, 0xc3, 0x56, 0xd7, 0x93, 0x80, 0x4f, 0x5c, 0xcd, 0x71,
-	0xb0, 0x1b, 0xf0, 0xaa, 0xfd, 0x9d, 0x03, 0x68, 0x1c, 0x6b, 0xd6, 0x0b, 0x7c, 0xa0, 0xe9, 0x27,
-	0xe8, 0x3a, 0xac, 0x18, 0xb6, 0x3e, 0xe8, 0x63, 0xcb, 0x57, 0x4f, 0xf0, 0xa8, 0xc2, 0x6d, 0x70,
-	0x9b, 0xa2, 0x52, 0x0a, 0xe7, 0x3e, 0xc0, 0x23, 0x74, 0x1f, 0x40, 0x3f, 0xc6, 0xfa, 0x89, 0x63,
-	0x9b, 0x96, 0x5f, 0xe1, 0x37, 0xb8, 0xcd, 0xd2, 0xf6, 0x85, 0x7a, 0x24, 0x52, 0xbd, 0x11, 0x2d,
-	0x2a, 0x31, 0x20, 0xaa, 0x42, 0xd1, 0xb3, 0x34, 0xc7, 0x3b, 0xb6, 0xfd, 0x4a, 0x76, 0x83, 0xdb,
-	0x5c, 0x51, 0xa2, 0x31, 0xba, 0x0d, 0x05, 0x9d, 0xca, 0xe0, 0x55, 0x84, 0x8d, 0xec, 0x66, 0x69,
-	0xfb, 0xdc, 0x04, 0x3f, 0xb2, 0xa2, 0x84, 0x08, 0x24, 0xc1, 0xb9, 0xbe, 0x69, 0xa9, 0xde, 0xc8,
-	0xd2, 0xb1, 0xa1, 0xfa, 0xa6, 0x7e, 0x82, 0xfd, 0x4a, 0x6e, 0x4a, 0x8c, 0xae, 0xd9, 0xc7, 0x5d,
-	0xba, 0xa8, 0xac, 0xf6, 0x4d, 0xab, 0x43, 0xe1, 0x6c, 0xa2, 0xf6, 0x3d, 0xc8, 0x33, 0xae, 0xe8,
-	0x06, 0xf0, 0xa6, 0x41, 0xb5, 0x2c, 0x6d, 0xaf, 0x4d, 0x6d, 0xba, 0xdb, 0x54, 0x78, 0xd3, 0x40,
-	0x15, 0x28, 0xf4, 0xb1, 0xe7, 0x69, 0x2f, 0x30, 0x55, 0x57, 0x54, 0xc2, 0x21, 0xba, 0x07, 0x60,
-	0x3b, 0xd8, 0xd5, 0x7c, 0xd3, 0xb6, 0xbc, 0x4a, 0x96, 0xca, 0x7e, 0x3e, 0xc6, 0x66, 0x3f, 0x5c,
-	0x54, 0x62, 0xb8, 0xda, 0x0f, 0x39, 0x28, 0x86, 0x1b, 0xa0, 0x6b, 0x00, 0x7a, 0xcf, 0x24, 0xf6,
-	0xf6, 0xf0, 0xc7, 0x54, 0x92, 0x33, 0x8a, 0xc8, 0x66, 0x3a, 0xf8, 0x63, 0x74, 0x1d, 0xc0, 0xc3,
-	0xee, 0x10, 0xbb, 0x74, 0x99, 0x6c, 0x9f, 0xdd, 0xe1, 0xef, 0x70, 0x8a, 0xc8, 0x66, 0x09, 0xe4,
-	0x2a, 0x14, 0x7a, 0x5a, 0xdf, 0xb1, 0x5d, 0x66, 0x58, 0xb6, 0x1e, 0x4e, 0xa1, 0xcb, 0x50, 0xd4,
-	0x74, 0xdf, 0x76, 0x55, 0xd3, 0xa8, 0x08, 0xd4, 0xee, 0x05, 0x3a, 0xde, 0x35, 0x6a, 0x9f, 0x9d,
-	0x07, 0x31, 0x92, 0x10, 0x7d, 0x19, 0xb2, 0x1e, 0xf6, 0x03, 0x5b, 0x54, 0xd2, 0x94, 0xa8, 0x77,
-	0xb0, 0xdf, 0xca, 0x28, 0x04, 0x46, 0xd0, 0x9a, 0x61, 0x04, 0xee, 0x4f, 0x47, 0x4b, 0x86, 0x41,
-	0xd0, 0x9a, 0x61, 0xa0, 0x2d, 0x10, 0xfa, 0xf6, 0x10, 0x53, 0xf9, 0x4a, 0xdb, 0x97, 0x53, 0xe1,
-	0x8f, 0xed, 0x21, 0x6e, 0x65, 0x14, 0x0a, 0x44, 0xf7, 0x21, 0xef, 0x62, 0x4a, 0x22, 0x50, 0x92,
-	0x2b, 0xa9, 0x24, 0x0a, 0x85, 0xb4, 0x32, 0x4a, 0x00, 0x26, 0xfb, 0x60, 0xc3, 0x0c, 0xc3, 0x21,
-	0x7d, 0x1f, 0xd9, 0x30, 0x89, 0x16, 0x14, 0x48, 0xf6, 0xf1, 0x70, 0x0f, 0xeb, 0x7e, 0x25, 0x3f,
-	0x67, 0x9f, 0x0e, 0x85, 0x90, 0x7d, 0x18, 0x18, 0x6d, 0x43, 0xce, 0xf3, 0x47, 0x3d, 0x5c, 0x29,
-	0x50, 0xaa, 0x6a, 0x3a, 0x15, 0x41, 0xb4, 0x32, 0x0a, 0x83, 0xa2, 0x07, 0x50, 0x34, 0x2d, 0xdd,
-	0xc5, 0x9a, 0x87, 0x2b, 0x45, 0x4a, 0x76, 0x2d, 0x95, 0x6c, 0x37, 0x00, 0xb5, 0x32, 0x4a, 0x44,
-	0x50, 0xfd, 0x15, 0x07, 0xd9, 0x0e, 0xf6, 0x49, 0xf0, 0x3b, 0x9a, 0x4b, 0xa2, 0x85, 0x2c, 0xf8,
-	0xd8, 0x50, 0xb5, 0xd0, 0x65, 0xb3, 0x82, 0x9f, 0xe1, 0x1b, 0x0c, 0x2e, 0xf9, 0xa8, 0x0c, 0x59,
-	0x92, 0xd9, 0x2c, 0x92, 0xc9, 0x27, 0xd1, 0x66, 0xa8, 0xf5, 0x06, 0xa1, 0x7b, 0xae, 0xc6, 0x18,
-	0x3d, 0xea, 0xec, 0xb7, 0xe5, 0x1e, 0x26, 0xb9, 0xdf, 0x31, 0xfb, 0x4e, 0x0f, 0x2b, 0x0c, 0x8a,
-	0xde, 0x81, 0x12, 0x7e, 0x89, 0xf5, 0x41, 0x20, 0x82, 0x30, 0x4f, 0x04, 0x08, 0x91, 0x92, 0x5f,
-	0xfd, 0x07, 0x07, 0x59, 0xc9, 0x30, 0x4e, 0x43, 0x91, 0xf7, 0x60, 0xd5, 0x71, 0xf1, 0x30, 0xce,
-	0x80, 0x9f, 0xc7, 0xe0, 0x0c, 0x41, 0x8f, 0xc9, 0xff, 0x9b, 0x5a, 0xff, 0x93, 0x03, 0x81, 0xc4,
-	0xf7, 0x1b, 0xa0, 0xf6, 0x3d, 0x80, 0x18, 0x65, 0x76, 0x1e, 0xa5, 0xa8, 0x47, 0x54, 0xcb, 0x2a,
-	0xfe, 0x29, 0x07, 0x79, 0x96, 0xa5, 0xa7, 0xa1, 0xfa, 0xa4, 0xec, 0xfc, 0x72, 0xb2, 0x67, 0x17,
-	0x95, 0xfd, 0x97, 0x02, 0x08, 0xe4, 0xb0, 0x38, 0x0d, 0xc9, 0x6f, 0x81, 0xf0, 0xdc, 0xb5, 0xfb,
-	0x81, 0xcc, 0x17, 0xe3, 0x54, 0xf8, 0xa5, 0xdf, 0xb6, 0x0d, 0x7c, 0x60, 0x7b, 0x0a, 0xc5, 0xa0,
-	0x9b, 0xc0, 0xfb, 0x76, 0x20, 0xe6, 0x2c, 0x24, 0xef, 0xdb, 0xe8, 0x18, 0x2e, 0x8d, 0xe5, 0x51,
-	0xfb, 0x9a, 0xa3, 0x1e, 0x8d, 0x54, 0x7a, 0xb6, 0x07, 0x55, 0x74, 0x7b, 0xe6, 0xf9, 0x57, 0x8f,
-	0x24, 0x7b, 0xac, 0x39, 0x3b, 0x23, 0x89, 0x10, 0xc9, 0x96, 0xef, 0x8e, 0x94, 0x35, 0x7d, 0x7a,
-	0x85, 0x14, 0x40, 0xdd, 0xb6, 0x7c, 0x6c, 0xb1, 0x93, 0x55, 0x54, 0xc2, 0x61, 0xd2, 0xb6, 0xf9,
-	0x05, 0x6d, 0x8b, 0x76, 0x01, 0x34, 0xdf, 0x77, 0xcd, 0xa3, 0x81, 0x8f, 0xbd, 0x4a, 0x81, 0x8a,
-	0xfb, 0xf6, 0x6c, 0x71, 0xa5, 0x08, 0xcb, 0xa4, 0x8c, 0x11, 0x57, 0xbf, 0x0b, 0x95, 0x59, 0xda,
-	0x84, 0x67, 0x1d, 0x37, 0x3e, 0xeb, 0x6e, 0x87, 0x59, 0x3f, 0x37, 0x7a, 0x18, 0xe6, 0x5d, 0xfe,
-	0xeb, 0x5c, 0xf5, 0x3d, 0x58, 0x4d, 0xec, 0x9e, 0xc2, 0xf5, 0x7c, 0x9c, 0xab, 0x18, 0x27, 0xff,
-	0x23, 0x07, 0x79, 0x56, 0x3e, 0xde, 0xd4, 0x30, 0x5a, 0x36, 0xb5, 0xff, 0xcc, 0x43, 0x8e, 0x96,
-	0xb8, 0x37, 0x55, 0xb1, 0x47, 0x13, 0x31, 0xc6, 0x52, 0xe2, 0xd6, 0xec, 0x4a, 0x3d, 0x2f, 0xc8,
-	0x92, 0x46, 0xca, 0x2d, 0x6a, 0xa4, 0xff, 0x30, 0x7a, 0x3e, 0xe5, 0xa0, 0x18, 0xf6, 0x03, 0xa7,
-	0x61, 0xe6, 0xed, 0xc9, 0xe8, 0x5f, 0xa6, 0xe6, 0x2d, 0x7a, 0x7c, 0xee, 0xe4, 0x41, 0x38, 0xb2,
-	0x8d, 0x51, 0xed, 0x6f, 0x1c, 0x9c, 0x9b, 0x62, 0x9e, 0x38, 0xca, 0xb9, 0x05, 0x8f, 0xf2, 0x3b,
-	0x50, 0x24, 0xb5, 0xe4, 0xf3, 0x8f, 0xff, 0x02, 0x85, 0xb1, 0x92, 0xc1, 0x7a, 0xc3, 0x05, 0xca,
-	0x5d, 0x00, 0x94, 0x7c, 0xb4, 0x09, 0x82, 0x3f, 0x72, 0x58, 0xf3, 0x79, 0x76, 0xa2, 0xa3, 0x7f,
-	0x42, 0x6c, 0xd2, 0x1d, 0x39, 0x58, 0xa1, 0x88, 0xb1, 0xef, 0x72, 0xb4, 0xb7, 0x66, 0x83, 0xda,
-	0xcf, 0x4a, 0x50, 0x8a, 0xe9, 0x8c, 0x9a, 0x50, 0xfa, 0xc8, 0xb3, 0x2d, 0xd5, 0x3e, 0xfa, 0x88,
-	0xf4, 0x9a, 0x4c, 0xdd, 0xeb, 0xe9, 0xd6, 0xa7, 0xdf, 0xfb, 0x14, 0xd8, 0xca, 0x28, 0x40, 0xe8,
-	0xd8, 0x08, 0x49, 0x40, 0x47, 0xaa, 0xe6, 0xba, 0xda, 0x28, 0xd0, 0x7f, 0x63, 0x0e, 0x13, 0x89,
-	0xe0, 0x5a, 0x19, 0x45, 0x24, 0x54, 0x74, 0x80, 0xbe, 0x05, 0xa2, 0xe3, 0x9a, 0x7d, 0xd3, 0x37,
-	0xa3, 0x6e, 0x7c, 0x16, 0x87, 0x83, 0x10, 0x47, 0x38, 0x44, 0x44, 0xe8, 0x2e, 0x08, 0x3e, 0x7e,
-	0x19, 0xa6, 0xc0, 0x95, 0x19, 0xc4, 0x24, 0x17, 0x49, 0x93, 0x4d, 0xa0, 0xe8, 0x5d, 0x52, 0x3e,
-	0x06, 0x96, 0x8f, 0xdd, 0xa0, 0x40, 0xac, 0xcf, 0xa0, 0x6a, 0x30, 0x54, 0x2b, 0xa3, 0x84, 0x04,
-	0xd5, 0x3f, 0x70, 0x00, 0x63, 0x83, 0xa0, 0x4d, 0xc8, 0x59, 0xb6, 0x81, 0xbd, 0x0a, 0x47, 0xd3,
-	0x19, 0xc5, 0x18, 0x29, 0xad, 0x2e, 0xc9, 0x7e, 0x85, 0x01, 0x96, 0xec, 0x15, 0xe2, 0x01, 0x96,
-	0x5d, 0x22, 0xc0, 0x84, 0xc5, 0x02, 0xac, 0xfa, 0x7b, 0x0e, 0xc4, 0xc8, 0x45, 0x73, 0xb5, 0x7a,
-	0x28, 0x7d, 0x71, 0xb4, 0xfa, 0x2b, 0x07, 0x62, 0x14, 0x36, 0x51, 0x12, 0x71, 0x8b, 0x27, 0x11,
-	0x1f, 0x4b, 0xa2, 0x25, 0x3b, 0xd5, 0xb8, 0xae, 0xc2, 0x12, 0xba, 0xe6, 0x16, 0xd4, 0xf5, 0x37,
-	0x1c, 0x08, 0x24, 0xca, 0xd1, 0xdb, 0x93, 0xce, 0x5b, 0x4b, 0xa9, 0x48, 0x5f, 0x0c, 0xef, 0xfd,
-	0x85, 0x83, 0x42, 0x90, 0x81, 0xff, 0xdb, 0xbe, 0x8b, 0x4a, 0xd3, 0x63, 0x28, 0x04, 0xa7, 0x46,
-	0x4a, 0x55, 0xbe, 0x03, 0x05, 0xcc, 0xce, 0xa5, 0x94, 0xae, 0x24, 0x76, 0x6a, 0x29, 0x21, 0xac,
-	0xa6, 0x43, 0x21, 0x48, 0x57, 0x74, 0x13, 0x04, 0x8b, 0x9c, 0x92, 0xec, 0xa4, 0x4f, 0x4b, 0x68,
-	0xba, 0xbe, 0xc4, 0x26, 0xcf, 0x60, 0x25, 0x0c, 0x2b, 0xd2, 0x59, 0x8c, 0xed, 0xcf, 0xc5, 0x9a,
-	0x07, 0x62, 0x97, 0x81, 0x63, 0x2c, 0x16, 0x69, 0x01, 0x50, 0xf2, 0x6b, 0xbf, 0xe3, 0xa1, 0x18,
-	0x32, 0x47, 0xff, 0x1f, 0xbb, 0x1a, 0xbb, 0x90, 0x12, 0xd4, 0xc1, 0xe5, 0x58, 0x6a, 0xf3, 0xb2,
-	0x64, 0xd9, 0xbd, 0x0f, 0x25, 0xd3, 0xf2, 0x54, 0xfa, 0x7b, 0x1b, 0x5c, 0x57, 0xcd, 0xdc, 0x5b,
-	0x34, 0x2d, 0xef, 0xc0, 0xc5, 0xc3, 0x5d, 0x03, 0x35, 0x26, 0x1a, 0xbd, 0x1c, 0x4d, 0xc3, 0x1b,
-	0x29, 0x54, 0x73, 0x7f, 0x23, 0x9e, 0x2c, 0xd2, 0xa9, 0x7d, 0x65, 0xb2, 0x7f, 0xba, 0x94, 0xb2,
-	0x09, 0x61, 0x12, 0x6b, 0xe1, 0x6a, 0xcf, 0x00, 0xc6, 0x52, 0x2f, 0xd9, 0xf6, 0x5c, 0x84, 0xbc,
-	0xfd, 0xfc, 0xb9, 0x87, 0x99, 0x27, 0x73, 0x4a, 0x30, 0xaa, 0xf5, 0x41, 0x38, 0xf4, 0xb0, 0x8b,
-	0xce, 0x46, 0xae, 0x12, 0xa9, 0x4f, 0xaa, 0x50, 0x1c, 0x78, 0xd8, 0xb5, 0xb4, 0x7e, 0xe8, 0x96,
-	0x68, 0x8c, 0xbe, 0x91, 0x92, 0x99, 0xd5, 0x3a, 0xbb, 0x25, 0xae, 0x87, 0xb7, 0xc4, 0x54, 0x0e,
-	0x7a, 0x8d, 0x1c, 0x13, 0xa3, 0xf6, 0x2f, 0x1e, 0x0a, 0x07, 0xae, 0x4d, 0x0b, 0x71, 0x72, 0x4b,
-	0x04, 0x42, 0x6c, 0x3b, 0xfa, 0x8d, 0xae, 0x01, 0x38, 0x83, 0xa3, 0x9e, 0xa9, 0xd3, 0xab, 0xe4,
-	0x2c, 0x5d, 0x11, 0xd9, 0xcc, 0x07, 0x78, 0x44, 0x96, 0x3d, 0xac, 0xbb, 0x98, 0xdd, 0x34, 0x0b,
-	0x6c, 0x99, 0xcd, 0x90, 0xe5, 0x4d, 0x28, 0x6b, 0x03, 0xff, 0x58, 0xfd, 0x04, 0x1f, 0x1d, 0xdb,
-	0xf6, 0x89, 0x3a, 0x70, 0x7b, 0xc1, 0xdf, 0xe7, 0x59, 0x32, 0xff, 0x21, 0x9b, 0x3e, 0x74, 0x7b,
-	0xe8, 0x0e, 0x9c, 0x9f, 0x40, 0xf6, 0xb1, 0x7f, 0x6c, 0x1b, 0x5e, 0x25, 0xbf, 0x91, 0xdd, 0x14,
-	0x15, 0x14, 0x43, 0x3f, 0x66, 0x2b, 0xe8, 0x9b, 0x70, 0x25, 0xb8, 0x74, 0x35, 0xb0, 0xa6, 0xfb,
-	0xe6, 0x50, 0xf3, 0xb1, 0xea, 0x1f, 0xbb, 0xd8, 0x3b, 0xb6, 0x7b, 0x06, 0xbd, 0xd5, 0x13, 0x95,
-	0xcb, 0x0c, 0xd2, 0x8c, 0x10, 0xdd, 0x10, 0x90, 0x30, 0x62, 0xf1, 0x35, 0x8c, 0x48, 0x48, 0x63,
-	0x99, 0x29, 0x7e, 0x3e, 0xe9, 0x38, 0x3d, 0x7f, 0x94, 0x85, 0x8b, 0x87, 0x64, 0xa4, 0x1d, 0xf5,
-	0x70, 0xe0, 0x88, 0xf7, 0x4d, 0xdc, 0x33, 0x3c, 0x74, 0x27, 0x30, 0x3f, 0x17, 0xf4, 0xf5, 0x49,
-	0x7e, 0x1d, 0xdf, 0x35, 0xad, 0x17, 0xf4, 0xec, 0x0e, 0x9c, 0xf3, 0x7e, 0x8a, 0x79, 0xf9, 0x05,
-	0xa8, 0x93, 0xc6, 0x7f, 0x3e, 0xc3, 0xf8, 0x2c, 0xb2, 0xee, 0xc5, 0x62, 0x3b, 0x5d, 0xf4, 0xba,
-	0x34, 0xe5, 0x9e, 0x54, 0x97, 0x7d, 0x67, 0xbe, 0xcb, 0x84, 0x05, 0x44, 0x9f, 0xed, 0xd0, 0x6a,
-	0x1d, 0xd0, 0xb4, 0x1c, 0xec, 0xe2, 0x9f, 0xa9, 0xc3, 0xd1, 0x58, 0x0a, 0x87, 0xb5, 0xef, 0xf3,
-	0xb0, 0xda, 0x0c, 0x1e, 0x45, 0x3a, 0x83, 0x7e, 0x5f, 0x73, 0x47, 0x53, 0x29, 0x31, 0x7d, 0xd1,
-	0x9a, 0x7c, 0x03, 0x11, 0x63, 0x6f, 0x20, 0x93, 0x21, 0x25, 0xbc, 0x4e, 0x48, 0x3d, 0x80, 0x92,
-	0xa6, 0xeb, 0xd8, 0xf3, 0xe2, 0x55, 0x70, 0x1e, 0x2d, 0x84, 0xf0, 0xa9, 0x78, 0xcc, 0xbf, 0x4e,
-	0x3c, 0xfe, 0x98, 0x83, 0xe2, 0x81, 0x8b, 0x3d, 0x6c, 0xe9, 0xb4, 0x0f, 0xd0, 0x7b, 0xb6, 0x7e,
-	0x42, 0x0d, 0x90, 0x53, 0xd8, 0x80, 0xfc, 0x2d, 0x10, 0xa7, 0x57, 0x78, 0x7a, 0x28, 0xc7, 0x2f,
-	0xbc, 0x43, 0xc2, 0x7a, 0x53, 0xf3, 0x35, 0x76, 0x1c, 0x53, 0x68, 0xf5, 0x6b, 0x20, 0x46, 0x53,
-	0xaf, 0xf3, 0xb3, 0x5c, 0xdb, 0x85, 0x7c, 0x83, 0x3a, 0x38, 0xe6, 0x89, 0x15, 0xea, 0x89, 0x2d,
-	0x28, 0x3a, 0xc1, 0x76, 0x41, 0x8c, 0xaf, 0xa5, 0x48, 0xa2, 0x44, 0xa0, 0xda, 0x3b, 0x50, 0x60,
-	0xac, 0x3c, 0xfa, 0x36, 0xc5, 0x3e, 0x83, 0x06, 0x6f, 0xe2, 0x6d, 0x8a, 0xae, 0x28, 0x21, 0xa2,
-	0xd6, 0x06, 0x18, 0x3f, 0x7f, 0x25, 0xde, 0x6e, 0xb8, 0xb4, 0xb7, 0x9b, 0xc9, 0xd7, 0x1f, 0x3e,
-	0xf1, 0xfa, 0x53, 0xfb, 0x01, 0x07, 0xa5, 0xd8, 0xb5, 0xc6, 0xe9, 0x96, 0x0f, 0xf4, 0x25, 0x58,
-	0x75, 0x71, 0x4f, 0x23, 0xcd, 0xba, 0x1a, 0x00, 0xb2, 0x14, 0x70, 0x36, 0x9c, 0xde, 0x67, 0x75,
-	0x46, 0x07, 0x18, 0x73, 0x8e, 0xbf, 0x37, 0x71, 0xd3, 0xef, 0x4d, 0x57, 0x41, 0x34, 0x70, 0x8f,
-	0xfc, 0x03, 0x60, 0x37, 0x54, 0x28, 0x9a, 0x98, 0x78, 0x8d, 0xca, 0x4e, 0xbe, 0x46, 0xfd, 0x84,
-	0x83, 0x62, 0xd3, 0xd6, 0xe5, 0x21, 0xf1, 0xe0, 0xed, 0x89, 0xfe, 0x33, 0x5e, 0x67, 0x43, 0x48,
-	0xac, 0x05, 0xdd, 0x02, 0x56, 0x55, 0xbc, 0xe3, 0x60, 0xcb, 0x54, 0x27, 0x8d, 0x31, 0xe8, 0x06,
-	0x9c, 0x89, 0xbf, 0x72, 0xb2, 0x97, 0x3b, 0x51, 0x59, 0x89, 0x3d, 0x73, 0x7a, 0xb7, 0x7e, 0xc1,
-	0x83, 0x18, 0x35, 0xbb, 0x68, 0x0d, 0x56, 0x9f, 0x48, 0x7b, 0x87, 0xb2, 0xda, 0x7d, 0x7a, 0x20,
-	0xab, 0xed, 0xc3, 0xbd, 0xbd, 0x72, 0x06, 0x5d, 0x04, 0x14, 0x9b, 0xdc, 0xd9, 0xdf, 0xdf, 0x93,
-	0xa5, 0x76, 0x99, 0x4b, 0xcc, 0xef, 0xb6, 0xbb, 0xf2, 0x43, 0x59, 0x29, 0xf3, 0x09, 0x26, 0x7b,
-	0xfb, 0xed, 0x87, 0xe5, 0x2c, 0xba, 0x00, 0xe7, 0x62, 0x93, 0xcd, 0xfd, 0xc3, 0x9d, 0x3d, 0xb9,
-	0x2c, 0x24, 0xa6, 0x3b, 0x5d, 0x65, 0xb7, 0xfd, 0xb0, 0x9c, 0x43, 0xe7, 0xa1, 0x1c, 0xdf, 0xf2,
-	0x69, 0x57, 0xee, 0x94, 0xf3, 0x09, 0xc6, 0x4d, 0xa9, 0x2b, 0x97, 0x0b, 0xa8, 0x0a, 0x17, 0x63,
-	0x93, 0xa4, 0x99, 0x54, 0xf7, 0x77, 0x1e, 0xc9, 0x8d, 0x6e, 0xb9, 0x88, 0x2e, 0xc3, 0x85, 0xe4,
-	0x9a, 0xa4, 0x28, 0xd2, 0xd3, 0xb2, 0x98, 0xe0, 0xd5, 0x95, 0xbf, 0xdd, 0x2d, 0x43, 0x82, 0x57,
-	0xa0, 0x91, 0xda, 0x68, 0x77, 0xcb, 0x25, 0x74, 0x09, 0xd6, 0x12, 0x5a, 0xd1, 0x85, 0x95, 0x5b,
-	0x3f, 0xe7, 0x60, 0x25, 0xee, 0x2e, 0xf4, 0x7f, 0xb0, 0xd1, 0xdc, 0x6f, 0xa8, 0xf2, 0x13, 0xb9,
-	0xdd, 0x0d, 0xd5, 0x6d, 0x1c, 0x3e, 0x96, 0xdb, 0xdd, 0x8e, 0xda, 0x68, 0x49, 0xed, 0x87, 0x72,
-	0xb3, 0x9c, 0x99, 0x8b, 0xfa, 0x50, 0xea, 0x36, 0x5a, 0x72, 0xb3, 0xcc, 0xa1, 0x9b, 0x50, 0x9b,
-	0x89, 0x3a, 0x6c, 0x87, 0x38, 0x1e, 0xdd, 0x80, 0xb7, 0x12, 0xb8, 0x03, 0x45, 0xee, 0xc8, 0xed,
-	0x86, 0x1c, 0x6d, 0x99, 0xdd, 0xb9, 0xfd, 0xeb, 0x57, 0xeb, 0xdc, 0x6f, 0x5f, 0xad, 0x73, 0x7f,
-	0x7a, 0xb5, 0xce, 0xfd, 0xf4, 0xb3, 0xf5, 0x0c, 0x9c, 0x33, 0xf0, 0x30, 0x8c, 0x21, 0xcd, 0x31,
-	0xeb, 0xc3, 0xbb, 0x07, 0xdc, 0x33, 0xa1, 0xfe, 0x60, 0x78, 0xf7, 0x28, 0x4f, 0x4f, 0xc5, 0xaf,
-	0xfe, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x77, 0x83, 0x5a, 0x5a, 0xa2, 0x1f, 0x00, 0x00,
+	// 2243 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x59, 0xcd, 0x8f, 0x1b, 0x49,
+	0x15, 0x77, 0xb7, 0xdb, 0x1f, 0xfd, 0x3c, 0xc9, 0x38, 0x35, 0xf9, 0x70, 0x9c, 0x64, 0x76, 0xe2,
+	0x40, 0x98, 0x4d, 0xc0, 0x93, 0x0c, 0xc9, 0x02, 0x1b, 0x2d, 0xc2, 0x63, 0xf7, 0xc6, 0x93, 0x9d,
+	0x78, 0x46, 0x65, 0x4f, 0x96, 0x44, 0xa0, 0x56, 0x4f, 0x77, 0x25, 0xd3, 0x3b, 0x76, 0xb7, 0xb7,
+	0xbb, 0xed, 0x8d, 0x0f, 0x5c, 0x10, 0x48, 0x1c, 0xe0, 0xc0, 0x8d, 0x3f, 0x00, 0x89, 0xbf, 0x61,
+	0x4f, 0x48, 0x1c, 0x10, 0x37, 0x40, 0x20, 0x71, 0x85, 0xec, 0x01, 0x71, 0x44, 0x48, 0xdc, 0x90,
+	0x50, 0x55, 0x75, 0xb7, 0xdb, 0xed, 0xb6, 0xe3, 0x98, 0x08, 0x65, 0xf7, 0xd6, 0x55, 0xf5, 0x7b,
+	0xaf, 0xde, 0x67, 0xbd, 0xd7, 0x55, 0x70, 0x71, 0x64, 0x3b, 0x27, 0x26, 0xd9, 0x1a, 0xde, 0xde,
+	0x72, 0x88, 0x6b, 0x0f, 0x1c, 0x9d, 0xb8, 0xd5, 0xbe, 0x63, 0x7b, 0x36, 0x92, 0xf9, 0x52, 0x75,
+	0x78, 0xbb, 0xfc, 0xd6, 0x33, 0xdb, 0x7e, 0xd6, 0x25, 0x5b, 0x6c, 0xe1, 0x68, 0xf0, 0x74, 0xcb,
+	0x33, 0x7b, 0xc4, 0xf5, 0xb4, 0x5e, 0x9f, 0x63, 0xcb, 0xeb, 0x71, 0xc0, 0x27, 0x8e, 0xd6, 0xef,
+	0x13, 0xc7, 0xe7, 0x55, 0xf9, 0xb9, 0x08, 0x50, 0x3f, 0xd6, 0xac, 0x67, 0xe4, 0x40, 0xd3, 0x4f,
+	0xd0, 0x55, 0x58, 0x31, 0x6c, 0x7d, 0xd0, 0x23, 0x96, 0xa7, 0x9e, 0x90, 0x51, 0x49, 0xd8, 0x10,
+	0x36, 0x65, 0x5c, 0x08, 0xe6, 0x3e, 0x20, 0x23, 0x74, 0x17, 0x40, 0x3f, 0x26, 0xfa, 0x49, 0xdf,
+	0x36, 0x2d, 0xaf, 0x24, 0x6e, 0x08, 0x9b, 0x85, 0xed, 0x73, 0xd5, 0x50, 0xa4, 0x6a, 0x3d, 0x5c,
+	0xc4, 0x11, 0x20, 0x2a, 0x43, 0xde, 0xb5, 0xb4, 0xbe, 0x7b, 0x6c, 0x7b, 0xa5, 0xf4, 0x86, 0xb0,
+	0xb9, 0x82, 0xc3, 0x31, 0xba, 0x09, 0x39, 0x9d, 0xc9, 0xe0, 0x96, 0xa4, 0x8d, 0xf4, 0x66, 0x61,
+	0xfb, 0xcc, 0x04, 0x3f, 0xba, 0x82, 0x03, 0x04, 0xaa, 0xc1, 0x99, 0x9e, 0x69, 0xa9, 0xee, 0xc8,
+	0xd2, 0x89, 0xa1, 0x7a, 0xa6, 0x7e, 0x42, 0xbc, 0x52, 0x66, 0x4a, 0x8c, 0x8e, 0xd9, 0x23, 0x1d,
+	0xb6, 0x88, 0x57, 0x7b, 0xa6, 0xd5, 0x66, 0x70, 0x3e, 0x81, 0xae, 0x00, 0x98, 0xae, 0xea, 0x90,
+	0x9e, 0x3d, 0x24, 0x46, 0x29, 0xbb, 0x21, 0x6c, 0xe6, 0xb1, 0x6c, 0xba, 0x98, 0x4f, 0x54, 0x7e,
+	0x00, 0x59, 0xbe, 0x29, 0xba, 0x06, 0xa2, 0x69, 0x30, 0x23, 0x14, 0xb6, 0xd7, 0xa6, 0x64, 0xda,
+	0x6d, 0x60, 0xd1, 0x34, 0x50, 0x09, 0x72, 0x3d, 0xe2, 0xba, 0xda, 0x33, 0xc2, 0xac, 0x21, 0xe3,
+	0x60, 0x88, 0xee, 0x00, 0xd8, 0x7d, 0xe2, 0x68, 0x9e, 0x69, 0x5b, 0x6e, 0x29, 0xcd, 0x54, 0x3b,
+	0x1b, 0x61, 0xb3, 0x1f, 0x2c, 0xe2, 0x08, 0xae, 0xf2, 0x63, 0x01, 0xf2, 0xc1, 0x06, 0x54, 0x54,
+	0xbd, 0x6b, 0x52, 0x77, 0xb8, 0xe4, 0x63, 0x26, 0xc9, 0x29, 0x2c, 0xf3, 0x99, 0x36, 0xf9, 0x18,
+	0x5d, 0x05, 0x70, 0x89, 0x33, 0x24, 0x0e, 0x5b, 0xa6, 0xdb, 0xa7, 0x77, 0xc4, 0x5b, 0x02, 0x96,
+	0xf9, 0x2c, 0x85, 0x5c, 0x86, 0x5c, 0x57, 0xeb, 0xf5, 0x6d, 0x87, 0xdb, 0x9d, 0xaf, 0x07, 0x53,
+	0xe8, 0x22, 0xe4, 0x35, 0xdd, 0xb3, 0x1d, 0xd5, 0x34, 0x4a, 0x12, 0x73, 0x4b, 0x8e, 0x8d, 0x77,
+	0x8d, 0xca, 0x67, 0x67, 0x41, 0x0e, 0x25, 0x44, 0x5f, 0x85, 0xb4, 0x4b, 0x3c, 0xdf, 0x16, 0xa5,
+	0x24, 0x25, 0xaa, 0x6d, 0xe2, 0x35, 0x53, 0x98, 0xc2, 0x28, 0x5a, 0x33, 0x0c, 0x3f, 0x3a, 0x92,
+	0xd1, 0x35, 0xc3, 0xa0, 0x68, 0xcd, 0x30, 0xd0, 0x16, 0x48, 0xd4, 0xf2, 0x4c, 0xbe, 0xc2, 0xf6,
+	0xc5, 0x44, 0xf8, 0x43, 0x7b, 0x48, 0x9a, 0x29, 0xcc, 0x80, 0xe8, 0x2e, 0x64, 0xb9, 0xf7, 0x98,
+	0xcc, 0x85, 0xed, 0x4b, 0x89, 0x24, 0xdc, 0x9f, 0xcd, 0x14, 0xf6, 0xc1, 0x74, 0x1f, 0x62, 0x98,
+	0x41, 0xb4, 0x24, 0xef, 0xa3, 0x18, 0x26, 0xd5, 0x82, 0x01, 0xe9, 0x3e, 0x2e, 0xe9, 0x12, 0xdd,
+	0x63, 0x41, 0x32, 0x6b, 0x9f, 0x36, 0x83, 0xd0, 0x7d, 0x38, 0x18, 0x6d, 0x43, 0xc6, 0xf5, 0x46,
+	0x5d, 0x52, 0xca, 0x31, 0xaa, 0x72, 0x32, 0x15, 0x45, 0x34, 0x53, 0x98, 0x43, 0xd1, 0x3d, 0xc8,
+	0x9b, 0x96, 0xee, 0x10, 0xcd, 0x25, 0xa5, 0x3c, 0x23, 0xbb, 0x92, 0x48, 0xb6, 0xeb, 0x83, 0x9a,
+	0x29, 0x1c, 0x12, 0x94, 0x7f, 0x2b, 0x40, 0xba, 0x4d, 0x3c, 0x9a, 0x1b, 0x7d, 0xcd, 0xa1, 0xd1,
+	0x42, 0x17, 0x3c, 0x62, 0xa8, 0x5a, 0xe0, 0xb2, 0x59, 0xb9, 0xc1, 0xf1, 0x75, 0x0e, 0xaf, 0x79,
+	0xa8, 0x08, 0x69, 0x9a, 0xf8, 0x3c, 0x92, 0xe9, 0x27, 0xd5, 0x66, 0xa8, 0x75, 0x07, 0x81, 0x7b,
+	0x2e, 0x47, 0x18, 0x3d, 0x68, 0xef, 0xb7, 0x94, 0x2e, 0xa1, 0x47, 0x43, 0xdb, 0xec, 0xf5, 0xbb,
+	0x04, 0x73, 0x28, 0x7a, 0x07, 0x0a, 0xe4, 0x39, 0xd1, 0x07, 0xbe, 0x08, 0xd2, 0x3c, 0x11, 0x20,
+	0x40, 0xd6, 0xbc, 0xf2, 0xbf, 0x04, 0x48, 0xd7, 0x0c, 0xe3, 0x75, 0x28, 0xf2, 0x1e, 0xac, 0xf6,
+	0x1d, 0x32, 0x8c, 0x32, 0x10, 0xe7, 0x31, 0x38, 0x45, 0xd1, 0x63, 0xf2, 0xff, 0xa7, 0xd6, 0xff,
+	0x16, 0x40, 0xa2, 0xf1, 0xfd, 0x06, 0xa8, 0x7d, 0x07, 0x20, 0x42, 0x99, 0x9e, 0x47, 0x29, 0xeb,
+	0x21, 0xd5, 0xb2, 0x8a, 0x7f, 0x2a, 0x40, 0x96, 0x67, 0xe9, 0xeb, 0x50, 0x7d, 0x52, 0x76, 0x71,
+	0x39, 0xd9, 0xd3, 0x8b, 0xca, 0xfe, 0x1b, 0x09, 0x24, 0x7a, 0x58, 0xbc, 0x0e, 0xc9, 0x6f, 0x80,
+	0xf4, 0xd4, 0xb1, 0x7b, 0xbe, 0xcc, 0xe7, 0xa3, 0x54, 0xe4, 0xb9, 0xd7, 0xb2, 0x0d, 0x72, 0x60,
+	0xbb, 0x98, 0x61, 0xd0, 0x75, 0x10, 0x3d, 0xdb, 0x17, 0x73, 0x16, 0x52, 0xf4, 0x6c, 0x74, 0x0c,
+	0x17, 0xc6, 0xf2, 0xa8, 0x3d, 0xad, 0xaf, 0x1e, 0x8d, 0x54, 0x76, 0xb6, 0xfb, 0x45, 0x76, 0x7b,
+	0xe6, 0xf9, 0x57, 0x0d, 0x25, 0x7b, 0xa8, 0xf5, 0x77, 0x46, 0x35, 0x4a, 0xa4, 0x58, 0x9e, 0x33,
+	0xc2, 0x6b, 0xfa, 0xf4, 0x0a, 0x2d, 0x80, 0xba, 0x6d, 0x79, 0xc4, 0xe2, 0x27, 0xab, 0x8c, 0x83,
+	0x61, 0xdc, 0xb6, 0xd9, 0x05, 0x6d, 0x8b, 0x76, 0x01, 0x34, 0xcf, 0x73, 0xcc, 0xa3, 0x81, 0x47,
+	0xdc, 0x52, 0x8e, 0x89, 0xfb, 0xf6, 0x6c, 0x71, 0x6b, 0x21, 0x96, 0x4b, 0x19, 0x21, 0x2e, 0x7f,
+	0x1f, 0x4a, 0xb3, 0xb4, 0x09, 0xce, 0x3a, 0x61, 0x7c, 0xd6, 0xdd, 0x0c, 0xb2, 0x7e, 0x6e, 0xf4,
+	0x70, 0xcc, 0xbb, 0xe2, 0x37, 0x85, 0xf2, 0x7b, 0xb0, 0x1a, 0xdb, 0x3d, 0x81, 0xeb, 0xd9, 0x28,
+	0x57, 0x39, 0x4a, 0xfe, 0x17, 0x01, 0xb2, 0xbc, 0x7c, 0xbc, 0xa9, 0x61, 0xb4, 0x6c, 0x6a, 0xff,
+	0x4d, 0x84, 0x0c, 0x2b, 0x71, 0x6f, 0xaa, 0x62, 0x0f, 0x26, 0x62, 0x8c, 0xa7, 0xc4, 0x8d, 0xd9,
+	0x95, 0x7a, 0x5e, 0x90, 0xc5, 0x8d, 0x94, 0x59, 0xd4, 0x48, 0xff, 0x63, 0xf4, 0x7c, 0x2a, 0x40,
+	0x3e, 0xe8, 0x07, 0x5e, 0x87, 0x99, 0xb7, 0x27, 0xa3, 0x7f, 0x99, 0x9a, 0xb7, 0xe8, 0xf1, 0xb9,
+	0x93, 0x05, 0xe9, 0xc8, 0x36, 0x46, 0x95, 0x7f, 0x0a, 0x70, 0x66, 0x8a, 0x79, 0xec, 0x28, 0x17,
+	0x16, 0x3c, 0xca, 0x6f, 0x41, 0x9e, 0x75, 0xf0, 0x2f, 0x3d, 0xfe, 0x73, 0x0c, 0xc6, 0x4b, 0x86,
+	0xff, 0x1b, 0xf0, 0xf2, 0x72, 0xe7, 0x03, 0x6b, 0x1e, 0xda, 0x04, 0xc9, 0x1b, 0xf5, 0x79, 0xf3,
+	0x79, 0x7a, 0xa2, 0xa3, 0x7f, 0x44, 0x6d, 0xd2, 0x19, 0xf5, 0x09, 0x66, 0x88, 0xb1, 0xef, 0x32,
+	0xac, 0xb7, 0xe6, 0x83, 0xca, 0x2f, 0x0b, 0x50, 0x88, 0xe8, 0x8c, 0x1a, 0x50, 0xf8, 0xc8, 0xb5,
+	0x2d, 0xd5, 0x3e, 0xfa, 0x88, 0xf6, 0x9a, 0x5c, 0xdd, 0xab, 0xc9, 0xd6, 0x67, 0xdf, 0xfb, 0x0c,
+	0xd8, 0x4c, 0x61, 0xa0, 0x74, 0x7c, 0x84, 0x6a, 0xc0, 0x46, 0xaa, 0xe6, 0x38, 0xda, 0xc8, 0xd7,
+	0x7f, 0x63, 0x0e, 0x93, 0x1a, 0xc5, 0x35, 0x53, 0x58, 0xa6, 0x54, 0x6c, 0x80, 0xbe, 0x03, 0x72,
+	0xdf, 0x31, 0x7b, 0xa6, 0x67, 0x86, 0xdd, 0xf8, 0x2c, 0x0e, 0x07, 0x01, 0x8e, 0x72, 0x08, 0x89,
+	0xd0, 0x6d, 0x90, 0x3c, 0xf2, 0x3c, 0x48, 0x81, 0x4b, 0x33, 0x88, 0x69, 0x2e, 0xd2, 0x26, 0x9b,
+	0x42, 0xd1, 0xbb, 0xb4, 0x7c, 0x0c, 0x2c, 0x8f, 0x38, 0x7e, 0x81, 0x58, 0x9f, 0x41, 0x55, 0xe7,
+	0xa8, 0x66, 0x0a, 0x07, 0x04, 0xe5, 0x3f, 0x0b, 0x00, 0x63, 0x83, 0xa0, 0x4d, 0xc8, 0x58, 0xb6,
+	0x41, 0xdc, 0x92, 0xc0, 0xd2, 0x19, 0x45, 0x18, 0xe1, 0x66, 0x87, 0x66, 0x3f, 0xe6, 0x80, 0x25,
+	0x7b, 0x85, 0x68, 0x80, 0xa5, 0x97, 0x08, 0x30, 0x69, 0xb1, 0x00, 0x2b, 0xff, 0x49, 0x00, 0x39,
+	0x74, 0xd1, 0x5c, 0xad, 0xee, 0xd7, 0x3e, 0x3f, 0x5a, 0xfd, 0x43, 0x00, 0x39, 0x0c, 0x9b, 0x30,
+	0x89, 0x84, 0xc5, 0x93, 0x48, 0x8c, 0x24, 0xd1, 0x92, 0x9d, 0x6a, 0x54, 0x57, 0x69, 0x09, 0x5d,
+	0x33, 0x0b, 0xea, 0xfa, 0x7b, 0x01, 0x24, 0x1a, 0xe5, 0xe8, 0xed, 0x49, 0xe7, 0xad, 0x25, 0x54,
+	0xa4, 0xcf, 0x87, 0xf7, 0xfe, 0x2e, 0x40, 0xce, 0xcf, 0xc0, 0x2f, 0xb6, 0xef, 0xc2, 0xd2, 0xf4,
+	0x10, 0x72, 0xfe, 0xa9, 0x91, 0x50, 0x95, 0x6f, 0x41, 0x8e, 0xf0, 0x73, 0x29, 0xa1, 0x2b, 0x89,
+	0x9c, 0x5a, 0x38, 0x80, 0x55, 0x74, 0xc8, 0xf9, 0xe9, 0x8a, 0xae, 0x83, 0x64, 0xd1, 0x53, 0x92,
+	0x9f, 0xf4, 0x49, 0x09, 0xcd, 0xd6, 0x97, 0xd8, 0xe4, 0x09, 0xac, 0x04, 0x61, 0x45, 0x3b, 0x8b,
+	0xb1, 0xfd, 0x85, 0x48, 0xf3, 0x40, 0xed, 0x32, 0xe8, 0x1b, 0x8b, 0x45, 0x9a, 0x0f, 0xac, 0x79,
+	0x95, 0x3f, 0x8a, 0x90, 0x0f, 0x98, 0xa3, 0x2f, 0x47, 0xae, 0xc6, 0xce, 0x25, 0x04, 0xb5, 0x7f,
+	0x39, 0x96, 0xd8, 0xbc, 0x2c, 0x59, 0x76, 0xef, 0x42, 0xc1, 0xb4, 0x5c, 0x95, 0xfd, 0xde, 0xfa,
+	0xd7, 0x55, 0x33, 0xf7, 0x96, 0x4d, 0xcb, 0x3d, 0x70, 0xc8, 0x70, 0xd7, 0x40, 0xf5, 0x89, 0x46,
+	0x2f, 0xc3, 0xd2, 0xf0, 0x5a, 0x02, 0xd5, 0xdc, 0xdf, 0x88, 0x47, 0x8b, 0x74, 0x6a, 0x5f, 0x9b,
+	0xec, 0x9f, 0x2e, 0x24, 0x6c, 0x42, 0x99, 0x44, 0x5a, 0xb8, 0xca, 0x13, 0x80, 0xb1, 0xd4, 0x4b,
+	0xb6, 0x3d, 0xe7, 0x21, 0x6b, 0x3f, 0x7d, 0xea, 0x12, 0xee, 0xc9, 0x0c, 0xf6, 0x47, 0x95, 0x1e,
+	0x48, 0x87, 0x2e, 0x71, 0xd0, 0xe9, 0xd0, 0x55, 0x32, 0xf3, 0x49, 0x19, 0xf2, 0x03, 0x97, 0x38,
+	0x96, 0xd6, 0x0b, 0xdc, 0x12, 0x8e, 0xd1, 0xb7, 0x12, 0x32, 0xb3, 0x5c, 0xe5, 0x97, 0xc8, 0xd5,
+	0xe0, 0x12, 0x99, 0xc9, 0xc1, 0x6e, 0x99, 0x23, 0x62, 0x54, 0xfe, 0x23, 0x42, 0xee, 0xc0, 0xb1,
+	0x59, 0x21, 0x8e, 0x6f, 0x89, 0x40, 0x8a, 0x6c, 0xc7, 0xbe, 0xd1, 0x15, 0x80, 0xfe, 0xe0, 0xa8,
+	0x6b, 0xea, 0xec, 0xa6, 0x39, 0xcd, 0x56, 0x64, 0x3e, 0xf3, 0x01, 0x19, 0xd1, 0x65, 0x97, 0xe8,
+	0x0e, 0xe1, 0x17, 0xd1, 0x12, 0x5f, 0xe6, 0x33, 0x74, 0x79, 0x13, 0x8a, 0xda, 0xc0, 0x3b, 0x56,
+	0x3f, 0x21, 0x47, 0xc7, 0xb6, 0x7d, 0xa2, 0x0e, 0x9c, 0xae, 0xff, 0xf7, 0x79, 0x9a, 0xce, 0x7f,
+	0xc8, 0xa7, 0x0f, 0x9d, 0x2e, 0xba, 0x05, 0x67, 0x27, 0x90, 0x3d, 0xe2, 0x1d, 0xdb, 0x86, 0x5b,
+	0xca, 0x6e, 0xa4, 0x37, 0x65, 0x8c, 0x22, 0xe8, 0x87, 0x7c, 0x05, 0x7d, 0x1b, 0x2e, 0xf9, 0x97,
+	0xae, 0x06, 0xd1, 0x74, 0xcf, 0x1c, 0x6a, 0x1e, 0x51, 0xbd, 0x63, 0x87, 0xb8, 0xc7, 0x76, 0xd7,
+	0x60, 0xb7, 0x7a, 0x32, 0xbe, 0xc8, 0x21, 0x8d, 0x10, 0xd1, 0x09, 0x00, 0x31, 0x23, 0xe6, 0x5f,
+	0xc1, 0x88, 0x94, 0x34, 0x92, 0x99, 0xf2, 0xcb, 0x49, 0xc7, 0xe9, 0xf9, 0x93, 0x34, 0x9c, 0x3f,
+	0xa4, 0x23, 0xed, 0xa8, 0x4b, 0x7c, 0x47, 0xbc, 0x6f, 0x92, 0xae, 0xe1, 0xa2, 0x5b, 0xbe, 0xf9,
+	0x05, 0xbf, 0xaf, 0x8f, 0xf3, 0x6b, 0x7b, 0x8e, 0x69, 0x3d, 0x63, 0x67, 0xb7, 0xef, 0x9c, 0xf7,
+	0x13, 0xcc, 0x2b, 0x2e, 0x40, 0x1d, 0x37, 0xfe, 0xd3, 0x19, 0xc6, 0xe7, 0x91, 0x75, 0x27, 0x12,
+	0xdb, 0xc9, 0xa2, 0x57, 0x6b, 0x53, 0xee, 0x49, 0x74, 0xd9, 0xf7, 0xe6, 0xbb, 0x4c, 0x5a, 0x40,
+	0xf4, 0xd9, 0x0e, 0x2d, 0x57, 0x01, 0x4d, 0xcb, 0xc1, 0x2f, 0xfe, 0xb9, 0x3a, 0x02, 0x8b, 0xa5,
+	0x60, 0x58, 0xf9, 0xa1, 0x08, 0xab, 0x0d, 0xff, 0xcd, 0xa4, 0x3d, 0xe8, 0xf5, 0x34, 0x67, 0x34,
+	0x95, 0x12, 0xd3, 0x17, 0xad, 0xf1, 0x27, 0x12, 0x39, 0xf2, 0x44, 0x32, 0x19, 0x52, 0xd2, 0xab,
+	0x84, 0xd4, 0x3d, 0x28, 0x68, 0xba, 0x4e, 0x5c, 0x37, 0x5a, 0x05, 0xe7, 0xd1, 0x42, 0x00, 0x9f,
+	0x8a, 0xc7, 0xec, 0xab, 0xc4, 0xe3, 0x4f, 0x05, 0xc8, 0x1f, 0x38, 0xc4, 0x25, 0x96, 0xce, 0xfa,
+	0x00, 0xbd, 0x6b, 0xeb, 0x27, 0xcc, 0x00, 0x19, 0xcc, 0x07, 0xf4, 0x6f, 0x81, 0x3a, 0xbd, 0x24,
+	0xb2, 0x43, 0x39, 0x7a, 0xe1, 0x1d, 0x10, 0x56, 0x1b, 0x9a, 0xa7, 0xf1, 0xe3, 0x98, 0x41, 0xcb,
+	0xdf, 0x00, 0x39, 0x9c, 0x7a, 0x95, 0x9f, 0xe5, 0xca, 0x2e, 0x64, 0xeb, 0xcc, 0xc1, 0x11, 0x4f,
+	0xac, 0x30, 0x4f, 0x6c, 0x41, 0xbe, 0xef, 0x6f, 0xe7, 0xc7, 0xf8, 0x5a, 0x82, 0x24, 0x38, 0x04,
+	0x55, 0xde, 0x81, 0x1c, 0x67, 0xe5, 0xb2, 0xa7, 0x2b, 0xfe, 0xe9, 0x37, 0x78, 0x13, 0x4f, 0x57,
+	0x6c, 0x05, 0x07, 0x88, 0x4a, 0x0b, 0x60, 0xfc, 0x3a, 0x16, 0x7b, 0xbb, 0x11, 0x92, 0xde, 0x6e,
+	0x26, 0x5f, 0x7f, 0xc4, 0xd8, 0xeb, 0x4f, 0xe5, 0x47, 0x02, 0x14, 0x22, 0xd7, 0x1a, 0xaf, 0xb7,
+	0x7c, 0xa0, 0xaf, 0xc0, 0xaa, 0x43, 0xba, 0x1a, 0x6d, 0xd6, 0x55, 0x1f, 0x90, 0x66, 0x80, 0xd3,
+	0xc1, 0xf4, 0x3e, 0xaf, 0x33, 0x3a, 0xc0, 0x98, 0x73, 0xf4, 0xbd, 0x49, 0x98, 0x7e, 0x6f, 0xba,
+	0x0c, 0xb2, 0x41, 0xba, 0xf4, 0x1f, 0x80, 0x38, 0x81, 0x42, 0xe1, 0xc4, 0xc4, 0x6b, 0x54, 0x7a,
+	0xf2, 0x35, 0xea, 0x67, 0x02, 0xe4, 0x1b, 0xb6, 0xae, 0x0c, 0xa9, 0x07, 0x6f, 0x4e, 0xf4, 0x9f,
+	0xd1, 0x3a, 0x1b, 0x40, 0x22, 0x2d, 0xe8, 0x16, 0xf0, 0xaa, 0xe2, 0x1e, 0xfb, 0x5b, 0x26, 0x3a,
+	0x69, 0x8c, 0x41, 0xd7, 0xe0, 0x54, 0xf4, 0x11, 0x94, 0xbf, 0xdc, 0xc9, 0x78, 0x25, 0xf2, 0x0a,
+	0xea, 0xde, 0xf8, 0xb5, 0x08, 0x72, 0xd8, 0xec, 0xa2, 0x35, 0x58, 0x7d, 0x54, 0xdb, 0x3b, 0x54,
+	0xd4, 0xce, 0xe3, 0x03, 0x45, 0x6d, 0x1d, 0xee, 0xed, 0x15, 0x53, 0xe8, 0x3c, 0xa0, 0xc8, 0xe4,
+	0xce, 0xfe, 0xfe, 0x9e, 0x52, 0x6b, 0x15, 0x85, 0xd8, 0xfc, 0x6e, 0xab, 0xa3, 0xdc, 0x57, 0x70,
+	0x51, 0x8c, 0x31, 0xd9, 0xdb, 0x6f, 0xdd, 0x2f, 0xa6, 0xd1, 0x39, 0x38, 0x13, 0x99, 0x6c, 0xec,
+	0x1f, 0xee, 0xec, 0x29, 0x45, 0x29, 0x36, 0xdd, 0xee, 0xe0, 0xdd, 0xd6, 0xfd, 0x62, 0x06, 0x9d,
+	0x85, 0x62, 0x74, 0xcb, 0xc7, 0x1d, 0xa5, 0x5d, 0xcc, 0xc6, 0x18, 0x37, 0x6a, 0x1d, 0xa5, 0x98,
+	0x43, 0x65, 0x38, 0x1f, 0x99, 0xa4, 0xcd, 0xa4, 0xba, 0xbf, 0xf3, 0x40, 0xa9, 0x77, 0x8a, 0x79,
+	0x74, 0x11, 0xce, 0xc5, 0xd7, 0x6a, 0x18, 0xd7, 0x1e, 0x17, 0xe5, 0x18, 0xaf, 0x8e, 0xf2, 0xdd,
+	0x4e, 0x11, 0x62, 0xbc, 0x7c, 0x8d, 0xd4, 0x7a, 0xab, 0x53, 0x2c, 0xa0, 0x0b, 0xb0, 0x16, 0xd3,
+	0x8a, 0x2d, 0xac, 0xdc, 0xf8, 0x95, 0x00, 0x2b, 0x51, 0x77, 0xa1, 0x2f, 0xc1, 0x46, 0x63, 0xbf,
+	0xae, 0x2a, 0x8f, 0x94, 0x56, 0x27, 0x50, 0xb7, 0x7e, 0xf8, 0x50, 0x69, 0x75, 0xda, 0x6a, 0xbd,
+	0x59, 0x6b, 0xdd, 0x57, 0x1a, 0xc5, 0xd4, 0x5c, 0xd4, 0x87, 0xb5, 0x4e, 0xbd, 0xa9, 0x34, 0x8a,
+	0x02, 0xba, 0x0e, 0x95, 0x99, 0xa8, 0xc3, 0x56, 0x80, 0x13, 0xd1, 0x35, 0x78, 0x2b, 0x86, 0x3b,
+	0xc0, 0x4a, 0x5b, 0x69, 0xd5, 0x95, 0x70, 0xcb, 0xf4, 0xce, 0xcd, 0xdf, 0xbd, 0x58, 0x17, 0xfe,
+	0xf0, 0x62, 0x5d, 0xf8, 0xeb, 0x8b, 0x75, 0xe1, 0x17, 0x9f, 0xad, 0xa7, 0xe0, 0x8c, 0x41, 0x86,
+	0x41, 0x0c, 0x69, 0x7d, 0xb3, 0x3a, 0xbc, 0x7d, 0x20, 0x3c, 0x91, 0xaa, 0xf7, 0x86, 0xb7, 0x8f,
+	0xb2, 0xec, 0x54, 0xfc, 0xfa, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x9a, 0xa3, 0x53, 0x9b, 0xc1,
+	0x1f, 0x00, 0x00,
 }
 
 func (m *ChangePack) Marshal() (dAtA []byte, err error) {
@@ -2952,6 +2964,16 @@ func (m *ChangePack) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
+	if m.IsRemoved {
+		i--
+		if m.IsRemoved {
+			dAtA[i] = 1
+		} else {
+			dAtA[i] = 0
+		}
+		i--
+		dAtA[i] = 0x30
+	}
 	if m.MinSyncedTicket != nil {
 		{
 			size, err := m.MinSyncedTicket.MarshalToSizedBuffer(dAtA[:i])
@@ -5552,6 +5574,9 @@ func (m *ChangePack) Size() (n int) {
 		l = m.MinSyncedTicket.Size()
 		n += 1 + l + sovResources(uint64(l))
 	}
+	if m.IsRemoved {
+		n += 2
+	}
 	if m.XXX_unrecognized != nil {
 		n += len(m.XXX_unrecognized)
 	}
@@ -6858,6 +6883,26 @@ func (m *ChangePack) Unmarshal(dAtA []byte) error {
 				return err
 			}
 			iNdEx = postIndex
+		case 6:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field IsRemoved", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowResources
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				v |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.IsRemoved = bool(v != 0)
 		default:
 			iNdEx = preIndex
 			skippy, err := skipResources(dAtA[iNdEx:])
diff --git a/api/yorkie/v1/resources.proto b/api/yorkie/v1/resources.proto
index a4786e2e..d1daf8ac 100644
--- a/api/yorkie/v1/resources.proto
+++ b/api/yorkie/v1/resources.proto
@@ -29,12 +29,15 @@ option java_package = "dev.yorkie.api.v1";
 // Messages for ChangePack             //
 /////////////////////////////////////////
 
+// ChangePack is a message that contains all changes that occurred in a document.
+// It is used to synchronize changes between clients and servers.
 message ChangePack {
   string document_key = 1;
   Checkpoint checkpoint = 2;
   bytes snapshot = 3;
   repeated Change changes = 4;
   TimeTicket min_synced_ticket = 5;
+  bool is_removed = 6;
 }
 
 message Change {
diff --git a/api/yorkie/v1/yorkie.pb.go b/api/yorkie/v1/yorkie.pb.go
index 7feac18f..e1d79082 100644
--- a/api/yorkie/v1/yorkie.pb.go
+++ b/api/yorkie/v1/yorkie.pb.go
@@ -279,7 +279,8 @@ func (m *AttachDocumentRequest) GetChangePack() *ChangePack {
 
 type AttachDocumentResponse struct {
 	ClientId             []byte      `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
-	ChangePack           *ChangePack `protobuf:"bytes,2,opt,name=change_pack,json=changePack,proto3" json:"change_pack,omitempty"`
+	DocumentId           string      `protobuf:"bytes,2,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"`
+	ChangePack           *ChangePack `protobuf:"bytes,3,opt,name=change_pack,json=changePack,proto3" json:"change_pack,omitempty"`
 	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
 	XXX_unrecognized     []byte      `json:"-"`
 	XXX_sizecache        int32       `json:"-"`
@@ -325,6 +326,13 @@ func (m *AttachDocumentResponse) GetClientId() []byte {
 	return nil
 }
 
+func (m *AttachDocumentResponse) GetDocumentId() string {
+	if m != nil {
+		return m.DocumentId
+	}
+	return ""
+}
+
 func (m *AttachDocumentResponse) GetChangePack() *ChangePack {
 	if m != nil {
 		return m.ChangePack
@@ -334,7 +342,8 @@ func (m *AttachDocumentResponse) GetChangePack() *ChangePack {
 
 type DetachDocumentRequest struct {
 	ClientId             []byte      `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
-	ChangePack           *ChangePack `protobuf:"bytes,2,opt,name=change_pack,json=changePack,proto3" json:"change_pack,omitempty"`
+	DocumentId           string      `protobuf:"bytes,2,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"`
+	ChangePack           *ChangePack `protobuf:"bytes,3,opt,name=change_pack,json=changePack,proto3" json:"change_pack,omitempty"`
 	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
 	XXX_unrecognized     []byte      `json:"-"`
 	XXX_sizecache        int32       `json:"-"`
@@ -380,6 +389,13 @@ func (m *DetachDocumentRequest) GetClientId() []byte {
 	return nil
 }
 
+func (m *DetachDocumentRequest) GetDocumentId() string {
+	if m != nil {
+		return m.DocumentId
+	}
+	return ""
+}
+
 func (m *DetachDocumentRequest) GetChangePack() *ChangePack {
 	if m != nil {
 		return m.ChangePack
@@ -632,26 +648,27 @@ func (m *WatchDocumentsResponse_Initialization) GetPeersMapByDoc() map[string]*C
 	return nil
 }
 
-type PushPullRequest struct {
+type RemoveDocumentRequest struct {
 	ClientId             []byte      `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
-	ChangePack           *ChangePack `protobuf:"bytes,2,opt,name=change_pack,json=changePack,proto3" json:"change_pack,omitempty"`
+	DocumentId           string      `protobuf:"bytes,2,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"`
+	ChangePack           *ChangePack `protobuf:"bytes,3,opt,name=change_pack,json=changePack,proto3" json:"change_pack,omitempty"`
 	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
 	XXX_unrecognized     []byte      `json:"-"`
 	XXX_sizecache        int32       `json:"-"`
 }
 
-func (m *PushPullRequest) Reset()         { *m = PushPullRequest{} }
-func (m *PushPullRequest) String() string { return proto.CompactTextString(m) }
-func (*PushPullRequest) ProtoMessage()    {}
-func (*PushPullRequest) Descriptor() ([]byte, []int) {
+func (m *RemoveDocumentRequest) Reset()         { *m = RemoveDocumentRequest{} }
+func (m *RemoveDocumentRequest) String() string { return proto.CompactTextString(m) }
+func (*RemoveDocumentRequest) ProtoMessage()    {}
+func (*RemoveDocumentRequest) Descriptor() ([]byte, []int) {
 	return fileDescriptor_40070c858814ab24, []int{10}
 }
-func (m *PushPullRequest) XXX_Unmarshal(b []byte) error {
+func (m *RemoveDocumentRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
 }
-func (m *PushPullRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+func (m *RemoveDocumentRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	if deterministic {
-		return xxx_messageInfo_PushPullRequest.Marshal(b, m, deterministic)
+		return xxx_messageInfo_RemoveDocumentRequest.Marshal(b, m, deterministic)
 	} else {
 		b = b[:cap(b)]
 		n, err := m.MarshalToSizedBuffer(b)
@@ -661,52 +678,177 @@ func (m *PushPullRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, err
 		return b[:n], nil
 	}
 }
-func (m *PushPullRequest) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_PushPullRequest.Merge(m, src)
+func (m *RemoveDocumentRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_RemoveDocumentRequest.Merge(m, src)
 }
-func (m *PushPullRequest) XXX_Size() int {
+func (m *RemoveDocumentRequest) XXX_Size() int {
 	return m.Size()
 }
-func (m *PushPullRequest) XXX_DiscardUnknown() {
-	xxx_messageInfo_PushPullRequest.DiscardUnknown(m)
+func (m *RemoveDocumentRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_RemoveDocumentRequest.DiscardUnknown(m)
 }
 
-var xxx_messageInfo_PushPullRequest proto.InternalMessageInfo
+var xxx_messageInfo_RemoveDocumentRequest proto.InternalMessageInfo
 
-func (m *PushPullRequest) GetClientId() []byte {
+func (m *RemoveDocumentRequest) GetClientId() []byte {
 	if m != nil {
 		return m.ClientId
 	}
 	return nil
 }
 
-func (m *PushPullRequest) GetChangePack() *ChangePack {
+func (m *RemoveDocumentRequest) GetDocumentId() string {
+	if m != nil {
+		return m.DocumentId
+	}
+	return ""
+}
+
+func (m *RemoveDocumentRequest) GetChangePack() *ChangePack {
 	if m != nil {
 		return m.ChangePack
 	}
 	return nil
 }
 
-type PushPullResponse struct {
-	ClientId             []byte      `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
+type RemoveDocumentResponse struct {
+	ClientKey            string      `protobuf:"bytes,1,opt,name=client_key,json=clientKey,proto3" json:"client_key,omitempty"`
 	ChangePack           *ChangePack `protobuf:"bytes,2,opt,name=change_pack,json=changePack,proto3" json:"change_pack,omitempty"`
 	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
 	XXX_unrecognized     []byte      `json:"-"`
 	XXX_sizecache        int32       `json:"-"`
 }
 
-func (m *PushPullResponse) Reset()         { *m = PushPullResponse{} }
-func (m *PushPullResponse) String() string { return proto.CompactTextString(m) }
-func (*PushPullResponse) ProtoMessage()    {}
-func (*PushPullResponse) Descriptor() ([]byte, []int) {
+func (m *RemoveDocumentResponse) Reset()         { *m = RemoveDocumentResponse{} }
+func (m *RemoveDocumentResponse) String() string { return proto.CompactTextString(m) }
+func (*RemoveDocumentResponse) ProtoMessage()    {}
+func (*RemoveDocumentResponse) Descriptor() ([]byte, []int) {
 	return fileDescriptor_40070c858814ab24, []int{11}
 }
-func (m *PushPullResponse) XXX_Unmarshal(b []byte) error {
+func (m *RemoveDocumentResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *RemoveDocumentResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_RemoveDocumentResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *RemoveDocumentResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_RemoveDocumentResponse.Merge(m, src)
+}
+func (m *RemoveDocumentResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *RemoveDocumentResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_RemoveDocumentResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_RemoveDocumentResponse proto.InternalMessageInfo
+
+func (m *RemoveDocumentResponse) GetClientKey() string {
+	if m != nil {
+		return m.ClientKey
+	}
+	return ""
+}
+
+func (m *RemoveDocumentResponse) GetChangePack() *ChangePack {
+	if m != nil {
+		return m.ChangePack
+	}
+	return nil
+}
+
+type PushPullChangesRequest struct {
+	ClientId             []byte      `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
+	DocumentId           string      `protobuf:"bytes,2,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"`
+	ChangePack           *ChangePack `protobuf:"bytes,3,opt,name=change_pack,json=changePack,proto3" json:"change_pack,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
+	XXX_unrecognized     []byte      `json:"-"`
+	XXX_sizecache        int32       `json:"-"`
+}
+
+func (m *PushPullChangesRequest) Reset()         { *m = PushPullChangesRequest{} }
+func (m *PushPullChangesRequest) String() string { return proto.CompactTextString(m) }
+func (*PushPullChangesRequest) ProtoMessage()    {}
+func (*PushPullChangesRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_40070c858814ab24, []int{12}
+}
+func (m *PushPullChangesRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *PushPullChangesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_PushPullChangesRequest.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *PushPullChangesRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_PushPullChangesRequest.Merge(m, src)
+}
+func (m *PushPullChangesRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *PushPullChangesRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_PushPullChangesRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PushPullChangesRequest proto.InternalMessageInfo
+
+func (m *PushPullChangesRequest) GetClientId() []byte {
+	if m != nil {
+		return m.ClientId
+	}
+	return nil
+}
+
+func (m *PushPullChangesRequest) GetDocumentId() string {
+	if m != nil {
+		return m.DocumentId
+	}
+	return ""
+}
+
+func (m *PushPullChangesRequest) GetChangePack() *ChangePack {
+	if m != nil {
+		return m.ChangePack
+	}
+	return nil
+}
+
+type PushPullChangesResponse struct {
+	ClientId             []byte      `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
+	ChangePack           *ChangePack `protobuf:"bytes,2,opt,name=change_pack,json=changePack,proto3" json:"change_pack,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
+	XXX_unrecognized     []byte      `json:"-"`
+	XXX_sizecache        int32       `json:"-"`
+}
+
+func (m *PushPullChangesResponse) Reset()         { *m = PushPullChangesResponse{} }
+func (m *PushPullChangesResponse) String() string { return proto.CompactTextString(m) }
+func (*PushPullChangesResponse) ProtoMessage()    {}
+func (*PushPullChangesResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_40070c858814ab24, []int{13}
+}
+func (m *PushPullChangesResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
 }
-func (m *PushPullResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+func (m *PushPullChangesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	if deterministic {
-		return xxx_messageInfo_PushPullResponse.Marshal(b, m, deterministic)
+		return xxx_messageInfo_PushPullChangesResponse.Marshal(b, m, deterministic)
 	} else {
 		b = b[:cap(b)]
 		n, err := m.MarshalToSizedBuffer(b)
@@ -716,26 +858,26 @@ func (m *PushPullResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, er
 		return b[:n], nil
 	}
 }
-func (m *PushPullResponse) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_PushPullResponse.Merge(m, src)
+func (m *PushPullChangesResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_PushPullChangesResponse.Merge(m, src)
 }
-func (m *PushPullResponse) XXX_Size() int {
+func (m *PushPullChangesResponse) XXX_Size() int {
 	return m.Size()
 }
-func (m *PushPullResponse) XXX_DiscardUnknown() {
-	xxx_messageInfo_PushPullResponse.DiscardUnknown(m)
+func (m *PushPullChangesResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_PushPullChangesResponse.DiscardUnknown(m)
 }
 
-var xxx_messageInfo_PushPullResponse proto.InternalMessageInfo
+var xxx_messageInfo_PushPullChangesResponse proto.InternalMessageInfo
 
-func (m *PushPullResponse) GetClientId() []byte {
+func (m *PushPullChangesResponse) GetClientId() []byte {
 	if m != nil {
 		return m.ClientId
 	}
 	return nil
 }
 
-func (m *PushPullResponse) GetChangePack() *ChangePack {
+func (m *PushPullChangesResponse) GetChangePack() *ChangePack {
 	if m != nil {
 		return m.ChangePack
 	}
@@ -754,7 +896,7 @@ func (m *UpdatePresenceRequest) Reset()         { *m = UpdatePresenceRequest{} }
 func (m *UpdatePresenceRequest) String() string { return proto.CompactTextString(m) }
 func (*UpdatePresenceRequest) ProtoMessage()    {}
 func (*UpdatePresenceRequest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_40070c858814ab24, []int{12}
+	return fileDescriptor_40070c858814ab24, []int{14}
 }
 func (m *UpdatePresenceRequest) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -807,7 +949,7 @@ func (m *UpdatePresenceResponse) Reset()         { *m = UpdatePresenceResponse{}
 func (m *UpdatePresenceResponse) String() string { return proto.CompactTextString(m) }
 func (*UpdatePresenceResponse) ProtoMessage()    {}
 func (*UpdatePresenceResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_40070c858814ab24, []int{13}
+	return fileDescriptor_40070c858814ab24, []int{15}
 }
 func (m *UpdatePresenceResponse) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -849,8 +991,10 @@ func init() {
 	proto.RegisterType((*WatchDocumentsResponse)(nil), "yorkie.v1.WatchDocumentsResponse")
 	proto.RegisterType((*WatchDocumentsResponse_Initialization)(nil), "yorkie.v1.WatchDocumentsResponse.Initialization")
 	proto.RegisterMapType((map[string]*Clients)(nil), "yorkie.v1.WatchDocumentsResponse.Initialization.PeersMapByDocEntry")
-	proto.RegisterType((*PushPullRequest)(nil), "yorkie.v1.PushPullRequest")
-	proto.RegisterType((*PushPullResponse)(nil), "yorkie.v1.PushPullResponse")
+	proto.RegisterType((*RemoveDocumentRequest)(nil), "yorkie.v1.RemoveDocumentRequest")
+	proto.RegisterType((*RemoveDocumentResponse)(nil), "yorkie.v1.RemoveDocumentResponse")
+	proto.RegisterType((*PushPullChangesRequest)(nil), "yorkie.v1.PushPullChangesRequest")
+	proto.RegisterType((*PushPullChangesResponse)(nil), "yorkie.v1.PushPullChangesResponse")
 	proto.RegisterType((*UpdatePresenceRequest)(nil), "yorkie.v1.UpdatePresenceRequest")
 	proto.RegisterType((*UpdatePresenceResponse)(nil), "yorkie.v1.UpdatePresenceResponse")
 }
@@ -858,50 +1002,53 @@ func init() {
 func init() { proto.RegisterFile("yorkie/v1/yorkie.proto", fileDescriptor_40070c858814ab24) }
 
 var fileDescriptor_40070c858814ab24 = []byte{
-	// 681 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0xc1, 0x4e, 0xdb, 0x4c,
-	0x10, 0x8e, 0x09, 0x20, 0x32, 0x81, 0xfc, 0x61, 0x7f, 0x25, 0x4d, 0x8d, 0x1a, 0xa5, 0xe6, 0x92,
-	0x0a, 0xc9, 0x90, 0x54, 0xa2, 0x55, 0x7b, 0x02, 0x82, 0x04, 0x42, 0x95, 0x22, 0x97, 0x0a, 0x81,
-	0x54, 0x45, 0xcb, 0x7a, 0x1a, 0xac, 0x18, 0xdb, 0xb5, 0x1d, 0x4b, 0xee, 0xa9, 0x8f, 0xd1, 0x47,
-	0xea, 0xb1, 0x87, 0x3e, 0x40, 0x45, 0x5f, 0xa1, 0x0f, 0x50, 0xc5, 0xeb, 0x10, 0xaf, 0x59, 0x51,
-	0x8a, 0x44, 0x6f, 0xeb, 0xdd, 0xf9, 0xbe, 0x6f, 0x66, 0x3c, 0xfb, 0xd9, 0x50, 0x8f, 0x5d, 0x7f,
-	0x64, 0xe1, 0x66, 0xd4, 0xd9, 0xe4, 0x2b, 0xdd, 0xf3, 0xdd, 0xd0, 0x25, 0xa5, 0xf4, 0x29, 0xea,
-	0xa8, 0x8f, 0x67, 0x21, 0x3e, 0x06, 0xee, 0xd8, 0x67, 0x18, 0xf0, 0x28, 0x6d, 0x1b, 0x6a, 0x3b,
-	0x2c, 0xb4, 0x22, 0x1a, 0xe2, 0x9e, 0x6d, 0xa1, 0x13, 0x1a, 0xf8, 0x71, 0x8c, 0x41, 0x48, 0x9e,
-	0x00, 0xb0, 0x64, 0x63, 0x30, 0xc2, 0xb8, 0xa1, 0xb4, 0x94, 0x76, 0xc9, 0x28, 0xf1, 0x9d, 0x23,
-	0x8c, 0xb5, 0x63, 0xa8, 0xe7, 0x71, 0x81, 0xe7, 0x3a, 0x01, 0xfe, 0x01, 0x48, 0xd6, 0x20, 0x7d,
-	0x18, 0x58, 0x66, 0x63, 0xae, 0xa5, 0xb4, 0x97, 0x8d, 0x25, 0xbe, 0x71, 0x68, 0x6a, 0xdb, 0xf0,
-	0xa8, 0x87, 0x54, 0x9a, 0x8f, 0x80, 0x53, 0x72, 0xb8, 0x17, 0xd0, 0xb8, 0x89, 0x4b, 0xf3, 0xb9,
-	0x15, 0x68, 0x43, 0x6d, 0x27, 0x0c, 0x29, 0xbb, 0xe8, 0xb9, 0x6c, 0x7c, 0x79, 0x47, 0x39, 0xb2,
-	0x0d, 0x65, 0x76, 0x41, 0x9d, 0x21, 0x0e, 0x3c, 0xca, 0x46, 0x49, 0x15, 0xe5, 0x6e, 0x4d, 0xbf,
-	0x6e, 0xb8, 0xbe, 0x97, 0x9c, 0xf6, 0x29, 0x1b, 0x19, 0xc0, 0xae, 0xd7, 0xda, 0x25, 0xd4, 0xf3,
-	0x6a, 0x77, 0x48, 0xf2, 0xde, 0x72, 0x36, 0xd4, 0x7a, 0xf8, 0xcf, 0x8a, 0x73, 0xa1, 0x9e, 0x57,
-	0xbb, 0xdb, 0x44, 0xdc, 0x57, 0x70, 0x08, 0xb5, 0x13, 0x1a, 0xce, 0xf4, 0x82, 0x69, 0x79, 0xcf,
-	0x60, 0x91, 0xb3, 0x27, 0x5a, 0xe5, 0xee, 0x6a, 0x96, 0x8b, 0x0f, 0x47, 0x1a, 0x40, 0xd6, 0x61,
-	0xc5, 0x4c, 0xe1, 0x93, 0xe4, 0x82, 0xc6, 0x5c, 0xab, 0xd8, 0x2e, 0x19, 0xcb, 0xd3, 0xcd, 0x23,
-	0x8c, 0x03, 0xed, 0x73, 0x11, 0xea, 0x79, 0xa5, 0xb4, 0xb4, 0x33, 0xa8, 0x58, 0x8e, 0x15, 0x5a,
-	0xd4, 0xb6, 0x3e, 0xd1, 0xd0, 0x72, 0x9d, 0x54, 0x72, 0x2b, 0x23, 0x29, 0x87, 0xea, 0x87, 0x02,
-	0xee, 0xa0, 0x60, 0xe4, 0x98, 0xc8, 0x06, 0x2c, 0x60, 0x34, 0xa9, 0x82, 0x77, 0xe4, 0xff, 0x0c,
-	0x65, 0xcf, 0x65, 0xfb, 0x93, 0xa3, 0x83, 0x82, 0xc1, 0x63, 0xd4, 0xef, 0x0a, 0x54, 0x44, 0x46,
-	0x62, 0x43, 0xd5, 0x43, 0xf4, 0x83, 0xc1, 0x25, 0xf5, 0x06, 0xe7, 0xf1, 0xc0, 0x74, 0x59, 0x43,
-	0x69, 0x15, 0xdb, 0xe5, 0xee, 0xde, 0xdf, 0x66, 0xa7, 0xf7, 0x27, 0x44, 0x6f, 0xa8, 0xb7, 0x1b,
-	0x4f, 0xa4, 0x9d, 0xd0, 0x8f, 0x8d, 0x15, 0x2f, 0xbb, 0xa7, 0x1e, 0x03, 0xb9, 0x19, 0x44, 0xaa,
-	0x50, 0x9c, 0xbd, 0xf3, 0xc9, 0x92, 0xb4, 0x61, 0x21, 0xa2, 0xf6, 0x18, 0xd3, 0xaa, 0xc8, 0x8d,
-	0x77, 0x13, 0x18, 0x3c, 0xe0, 0xd5, 0xdc, 0x4b, 0x65, 0x77, 0x11, 0xe6, 0xcf, 0x5d, 0x33, 0xd6,
-	0x3e, 0xc0, 0x7f, 0xfd, 0x71, 0x70, 0xd1, 0x1f, 0xdb, 0xf6, 0x83, 0x0e, 0xf1, 0x10, 0xaa, 0x33,
-	0x9d, 0x87, 0xbc, 0x9b, 0x43, 0xa8, 0xbd, 0xf3, 0x4c, 0x1a, 0x62, 0xdf, 0xc7, 0x00, 0x1d, 0x86,
-	0x0f, 0x35, 0xbc, 0x0d, 0xa8, 0xe7, 0x85, 0x78, 0x5d, 0xdd, 0x5f, 0xf3, 0xb0, 0x72, 0x9a, 0x70,
-	0xbf, 0x45, 0x3f, 0xb2, 0x18, 0x92, 0x13, 0xa8, 0x88, 0xa6, 0x4e, 0x5a, 0x19, 0x75, 0xe9, 0x77,
-	0x42, 0x7d, 0x7a, 0x4b, 0x04, 0x17, 0xd2, 0x0a, 0xe4, 0x3d, 0x54, 0xf3, 0xfe, 0x4c, 0xb4, 0xec,
-	0x3c, 0xcb, 0x4d, 0x5f, 0x5d, 0xbf, 0x35, 0xe6, 0x9a, 0xfe, 0x04, 0x2a, 0x62, 0x8d, 0x42, 0xde,
-	0xd2, 0x3e, 0x0b, 0x79, 0xcb, 0x1b, 0xc4, 0x89, 0x45, 0xc3, 0x16, 0x1b, 0x22, 0xfb, 0x72, 0x88,
-	0x0d, 0x91, 0xba, 0x3d, 0x27, 0x16, 0xcd, 0x52, 0x20, 0x96, 0xba, 0xb6, 0x40, 0x2c, 0x77, 0x5a,
-	0xad, 0x40, 0x4e, 0xa1, 0x22, 0xde, 0x68, 0x81, 0x58, 0xea, 0x97, 0x02, 0xb1, 0xdc, 0x0e, 0xb4,
-	0xc2, 0x96, 0x42, 0xf6, 0x61, 0x69, 0x7a, 0x37, 0x88, 0x9a, 0x81, 0xe4, 0x2e, 0xa6, 0xba, 0x26,
-	0x3d, 0x9b, 0x12, 0xed, 0x6e, 0x7c, 0xbd, 0x6a, 0x2a, 0xdf, 0xae, 0x9a, 0xca, 0x8f, 0xab, 0xa6,
-	0xf2, 0xe5, 0x67, 0xb3, 0x00, 0xab, 0x26, 0x46, 0x53, 0x0c, 0xf5, 0x2c, 0x3d, 0xea, 0xf4, 0x95,
-	0xb3, 0x79, 0xfd, 0x75, 0xd4, 0x39, 0x5f, 0x4c, 0xfe, 0x52, 0x9e, 0xff, 0x0e, 0x00, 0x00, 0xff,
-	0xff, 0xe9, 0xa4, 0xcb, 0xd5, 0xe5, 0x08, 0x00, 0x00,
+	// 732 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x56, 0xd1, 0x6e, 0xd3, 0x4a,
+	0x10, 0xcd, 0x36, 0x6d, 0x75, 0x33, 0x69, 0x73, 0xdb, 0xbd, 0x4a, 0x9a, 0x1b, 0x44, 0x08, 0xee,
+	0x4b, 0x50, 0x25, 0xb7, 0x09, 0x52, 0x41, 0xf0, 0xd4, 0x36, 0x48, 0xad, 0x2a, 0xa4, 0xc8, 0x14,
+	0x55, 0xad, 0x84, 0xa2, 0xad, 0x3d, 0x6a, 0xad, 0xb8, 0xb6, 0xb1, 0x1d, 0x4b, 0xe6, 0x89, 0x1f,
+	0x80, 0x67, 0x3e, 0x89, 0x47, 0x1e, 0xf8, 0x00, 0x14, 0x7e, 0x04, 0xc5, 0xeb, 0xa4, 0x5e, 0x67,
+	0x15, 0x52, 0x50, 0x05, 0x6f, 0xce, 0x78, 0xe6, 0x9c, 0x33, 0xe3, 0xdd, 0x39, 0x81, 0x4a, 0xe4,
+	0x78, 0x7d, 0x13, 0xb7, 0xc3, 0xd6, 0x36, 0x7f, 0x52, 0x5d, 0xcf, 0x09, 0x1c, 0x5a, 0x48, 0x7e,
+	0x85, 0xad, 0xda, 0xff, 0x37, 0x29, 0x1e, 0xfa, 0xce, 0xc0, 0xd3, 0xd1, 0xe7, 0x59, 0xca, 0x2e,
+	0x94, 0xf7, 0xf4, 0xc0, 0x0c, 0x59, 0x80, 0x07, 0x96, 0x89, 0x76, 0xa0, 0xe1, 0xdb, 0x01, 0xfa,
+	0x01, 0xbd, 0x0f, 0xa0, 0xc7, 0x81, 0x5e, 0x1f, 0xa3, 0x2a, 0x69, 0x90, 0x66, 0x41, 0x2b, 0xf0,
+	0xc8, 0x31, 0x46, 0xca, 0x09, 0x54, 0xb2, 0x75, 0xbe, 0xeb, 0xd8, 0x3e, 0xfe, 0xa4, 0x90, 0xde,
+	0x83, 0xe4, 0x47, 0xcf, 0x34, 0xaa, 0x0b, 0x0d, 0xd2, 0x5c, 0xd1, 0xfe, 0xe1, 0x81, 0x23, 0x43,
+	0xd9, 0x85, 0x8d, 0x0e, 0x32, 0xa9, 0x1e, 0xa1, 0x8e, 0x64, 0xea, 0x9e, 0x40, 0x75, 0xba, 0x2e,
+	0xd1, 0x33, 0xb3, 0xd0, 0x82, 0xf2, 0x5e, 0x10, 0x30, 0xfd, 0xaa, 0xe3, 0xe8, 0x83, 0xeb, 0x39,
+	0xe9, 0xe8, 0x2e, 0x14, 0xf5, 0x2b, 0x66, 0x5f, 0x62, 0xcf, 0x65, 0x7a, 0x3f, 0xee, 0xa2, 0xd8,
+	0x2e, 0xab, 0x93, 0x81, 0xab, 0x07, 0xf1, 0xdb, 0x2e, 0xd3, 0xfb, 0x1a, 0xe8, 0x93, 0x67, 0xe5,
+	0x23, 0x81, 0x4a, 0x96, 0x6e, 0x0e, 0x95, 0xf4, 0x01, 0x14, 0x8d, 0xa4, 0x60, 0x3c, 0xb5, 0x82,
+	0x06, 0xe3, 0xd0, 0xb4, 0xa0, 0xfc, 0xbc, 0x82, 0x3e, 0x10, 0x28, 0x77, 0xf0, 0xd6, 0xfd, 0xdf,
+	0x99, 0x1e, 0x07, 0x2a, 0x59, 0x39, 0xf3, 0x9d, 0xaa, 0x5f, 0xfd, 0x22, 0x97, 0x50, 0x3e, 0x65,
+	0xc1, 0x0d, 0x9f, 0x3f, 0xee, 0xff, 0x11, 0x2c, 0x73, 0xf4, 0x98, 0xab, 0xd8, 0x5e, 0x4f, 0x63,
+	0xf1, 0x03, 0x96, 0x24, 0xd0, 0x4d, 0x58, 0x9d, 0x4c, 0xa3, 0x8f, 0x91, 0x5f, 0x5d, 0x68, 0xe4,
+	0x9b, 0x05, 0x6d, 0x65, 0x1c, 0x3c, 0xc6, 0xc8, 0x57, 0xde, 0xe7, 0xa1, 0x92, 0x65, 0x4a, 0x5a,
+	0x3b, 0x87, 0x92, 0x69, 0x9b, 0x81, 0xc9, 0x2c, 0xf3, 0x1d, 0x0b, 0x4c, 0xc7, 0x4e, 0x28, 0x77,
+	0x52, 0x94, 0xf2, 0x52, 0xf5, 0x48, 0xa8, 0x3b, 0xcc, 0x69, 0x19, 0x24, 0xba, 0x05, 0x4b, 0x18,
+	0x8e, 0xba, 0xe0, 0x13, 0xf9, 0x2f, 0x05, 0xd9, 0x71, 0xf4, 0x17, 0xa3, 0x57, 0x87, 0x39, 0x8d,
+	0xe7, 0xd4, 0xbe, 0x12, 0x28, 0x89, 0x88, 0xd4, 0x82, 0x35, 0x17, 0xd1, 0xf3, 0x7b, 0xd7, 0xcc,
+	0xed, 0x5d, 0x44, 0x3d, 0xc3, 0xd1, 0xab, 0xa4, 0x91, 0x6f, 0x16, 0xdb, 0x07, 0xb7, 0x55, 0xa7,
+	0x76, 0x47, 0x40, 0x2f, 0x99, 0xbb, 0x1f, 0x8d, 0xa8, 0xed, 0xc0, 0x8b, 0xb4, 0x55, 0x37, 0x1d,
+	0xab, 0x9d, 0x00, 0x9d, 0x4e, 0xa2, 0x6b, 0x90, 0xbf, 0xf9, 0xe6, 0xa3, 0x47, 0xda, 0x84, 0xa5,
+	0x90, 0x59, 0x03, 0x4c, 0xba, 0xa2, 0x53, 0xdf, 0xc6, 0xd7, 0x78, 0xc2, 0xb3, 0x85, 0xa7, 0x64,
+	0x7f, 0x19, 0x16, 0x2f, 0x1c, 0x23, 0x8a, 0x0f, 0xbb, 0x86, 0xd7, 0x4e, 0x88, 0x7f, 0xcd, 0x61,
+	0xcf, 0xca, 0xb9, 0xdb, 0xc3, 0x3e, 0x5a, 0x3f, 0xdd, 0x81, 0x7f, 0xd5, 0x1d, 0x58, 0x16, 0x4f,
+	0xf1, 0xff, 0xec, 0x04, 0x6c, 0xd8, 0x98, 0xd2, 0x33, 0xcf, 0x3e, 0xfc, 0x8d, 0xdb, 0xfe, 0xda,
+	0x35, 0x58, 0x80, 0x5d, 0x0f, 0x7d, 0xb4, 0x75, 0xbc, 0xab, 0xdb, 0x5e, 0x85, 0x4a, 0x96, 0x88,
+	0xf7, 0xd5, 0x1e, 0x2e, 0xc1, 0xea, 0x59, 0x8c, 0xfd, 0x0a, 0xbd, 0xd0, 0xd4, 0x91, 0x9e, 0x42,
+	0x49, 0x74, 0x52, 0xda, 0x48, 0xb1, 0x4b, 0xcd, 0xb9, 0xf6, 0x70, 0x46, 0x06, 0x27, 0x52, 0x72,
+	0xf4, 0x0d, 0xac, 0x65, 0x4d, 0x91, 0x2a, 0xe9, 0x05, 0x20, 0x77, 0xda, 0xda, 0xe6, 0xcc, 0x9c,
+	0x09, 0xfc, 0x29, 0x94, 0xc4, 0x1e, 0x05, 0xdd, 0xd2, 0x39, 0x0b, 0xba, 0xe5, 0x03, 0xe2, 0xc0,
+	0xa2, 0x49, 0x8a, 0x03, 0x91, 0xd9, 0xb5, 0x38, 0x10, 0xa9, 0xc3, 0x72, 0x60, 0xd1, 0x5d, 0x04,
+	0x60, 0xa9, 0x0f, 0x0a, 0xc0, 0x72, 0x6b, 0xe2, 0xc0, 0xe2, 0x4d, 0x16, 0x80, 0xa5, 0x3b, 0x47,
+	0x00, 0x96, 0xaf, 0x01, 0x25, 0x47, 0xcf, 0xe1, 0xdf, 0xcc, 0x05, 0xa1, 0xe9, 0x3a, 0xf9, 0x65,
+	0xae, 0x29, 0xb3, 0x52, 0x26, 0xd8, 0x67, 0x50, 0x12, 0xf7, 0xb6, 0x20, 0x5a, 0xea, 0x8a, 0x82,
+	0x68, 0xf9, 0xd2, 0x57, 0x72, 0x3b, 0x64, 0x7f, 0xeb, 0xf3, 0xb0, 0x4e, 0xbe, 0x0c, 0xeb, 0xe4,
+	0xdb, 0xb0, 0x4e, 0x3e, 0x7d, 0xaf, 0xe7, 0x60, 0xdd, 0xc0, 0x70, 0x5c, 0xcb, 0x5c, 0x53, 0x0d,
+	0x5b, 0x5d, 0x72, 0xbe, 0xa8, 0x3e, 0x0f, 0x5b, 0x17, 0xcb, 0xf1, 0x1f, 0xd1, 0xc7, 0x3f, 0x02,
+	0x00, 0x00, 0xff, 0xff, 0xa0, 0xa4, 0x94, 0x72, 0xc8, 0x0a, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -921,8 +1068,9 @@ type YorkieServiceClient interface {
 	UpdatePresence(ctx context.Context, in *UpdatePresenceRequest, opts ...grpc.CallOption) (*UpdatePresenceResponse, error)
 	AttachDocument(ctx context.Context, in *AttachDocumentRequest, opts ...grpc.CallOption) (*AttachDocumentResponse, error)
 	DetachDocument(ctx context.Context, in *DetachDocumentRequest, opts ...grpc.CallOption) (*DetachDocumentResponse, error)
+	RemoveDocument(ctx context.Context, in *RemoveDocumentRequest, opts ...grpc.CallOption) (*RemoveDocumentResponse, error)
+	PushPullChanges(ctx context.Context, in *PushPullChangesRequest, opts ...grpc.CallOption) (*PushPullChangesResponse, error)
 	WatchDocuments(ctx context.Context, in *WatchDocumentsRequest, opts ...grpc.CallOption) (YorkieService_WatchDocumentsClient, error)
-	PushPull(ctx context.Context, in *PushPullRequest, opts ...grpc.CallOption) (*PushPullResponse, error)
 }
 
 type yorkieServiceClient struct {
@@ -978,6 +1126,24 @@ func (c *yorkieServiceClient) DetachDocument(ctx context.Context, in *DetachDocu
 	return out, nil
 }
 
+func (c *yorkieServiceClient) RemoveDocument(ctx context.Context, in *RemoveDocumentRequest, opts ...grpc.CallOption) (*RemoveDocumentResponse, error) {
+	out := new(RemoveDocumentResponse)
+	err := c.cc.Invoke(ctx, "/yorkie.v1.YorkieService/RemoveDocument", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *yorkieServiceClient) PushPullChanges(ctx context.Context, in *PushPullChangesRequest, opts ...grpc.CallOption) (*PushPullChangesResponse, error) {
+	out := new(PushPullChangesResponse)
+	err := c.cc.Invoke(ctx, "/yorkie.v1.YorkieService/PushPullChanges", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 func (c *yorkieServiceClient) WatchDocuments(ctx context.Context, in *WatchDocumentsRequest, opts ...grpc.CallOption) (YorkieService_WatchDocumentsClient, error) {
 	stream, err := c.cc.NewStream(ctx, &_YorkieService_serviceDesc.Streams[0], "/yorkie.v1.YorkieService/WatchDocuments", opts...)
 	if err != nil {
@@ -1010,15 +1176,6 @@ func (x *yorkieServiceWatchDocumentsClient) Recv() (*WatchDocumentsResponse, err
 	return m, nil
 }
 
-func (c *yorkieServiceClient) PushPull(ctx context.Context, in *PushPullRequest, opts ...grpc.CallOption) (*PushPullResponse, error) {
-	out := new(PushPullResponse)
-	err := c.cc.Invoke(ctx, "/yorkie.v1.YorkieService/PushPull", in, out, opts...)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
 // YorkieServiceServer is the server API for YorkieService service.
 type YorkieServiceServer interface {
 	ActivateClient(context.Context, *ActivateClientRequest) (*ActivateClientResponse, error)
@@ -1026,8 +1183,9 @@ type YorkieServiceServer interface {
 	UpdatePresence(context.Context, *UpdatePresenceRequest) (*UpdatePresenceResponse, error)
 	AttachDocument(context.Context, *AttachDocumentRequest) (*AttachDocumentResponse, error)
 	DetachDocument(context.Context, *DetachDocumentRequest) (*DetachDocumentResponse, error)
+	RemoveDocument(context.Context, *RemoveDocumentRequest) (*RemoveDocumentResponse, error)
+	PushPullChanges(context.Context, *PushPullChangesRequest) (*PushPullChangesResponse, error)
 	WatchDocuments(*WatchDocumentsRequest, YorkieService_WatchDocumentsServer) error
-	PushPull(context.Context, *PushPullRequest) (*PushPullResponse, error)
 }
 
 // UnimplementedYorkieServiceServer can be embedded to have forward compatible implementations.
@@ -1049,12 +1207,15 @@ func (*UnimplementedYorkieServiceServer) AttachDocument(ctx context.Context, req
 func (*UnimplementedYorkieServiceServer) DetachDocument(ctx context.Context, req *DetachDocumentRequest) (*DetachDocumentResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method DetachDocument not implemented")
 }
+func (*UnimplementedYorkieServiceServer) RemoveDocument(ctx context.Context, req *RemoveDocumentRequest) (*RemoveDocumentResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method RemoveDocument not implemented")
+}
+func (*UnimplementedYorkieServiceServer) PushPullChanges(ctx context.Context, req *PushPullChangesRequest) (*PushPullChangesResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method PushPullChanges not implemented")
+}
 func (*UnimplementedYorkieServiceServer) WatchDocuments(req *WatchDocumentsRequest, srv YorkieService_WatchDocumentsServer) error {
 	return status.Errorf(codes.Unimplemented, "method WatchDocuments not implemented")
 }
-func (*UnimplementedYorkieServiceServer) PushPull(ctx context.Context, req *PushPullRequest) (*PushPullResponse, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method PushPull not implemented")
-}
 
 func RegisterYorkieServiceServer(s *grpc.Server, srv YorkieServiceServer) {
 	s.RegisterService(&_YorkieService_serviceDesc, srv)
@@ -1150,6 +1311,42 @@ func _YorkieService_DetachDocument_Handler(srv interface{}, ctx context.Context,
 	return interceptor(ctx, in, info, handler)
 }
 
+func _YorkieService_RemoveDocument_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(RemoveDocumentRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(YorkieServiceServer).RemoveDocument(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/yorkie.v1.YorkieService/RemoveDocument",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(YorkieServiceServer).RemoveDocument(ctx, req.(*RemoveDocumentRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _YorkieService_PushPullChanges_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(PushPullChangesRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(YorkieServiceServer).PushPullChanges(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/yorkie.v1.YorkieService/PushPullChanges",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(YorkieServiceServer).PushPullChanges(ctx, req.(*PushPullChangesRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 func _YorkieService_WatchDocuments_Handler(srv interface{}, stream grpc.ServerStream) error {
 	m := new(WatchDocumentsRequest)
 	if err := stream.RecvMsg(m); err != nil {
@@ -1171,24 +1368,6 @@ func (x *yorkieServiceWatchDocumentsServer) Send(m *WatchDocumentsResponse) erro
 	return x.ServerStream.SendMsg(m)
 }
 
-func _YorkieService_PushPull_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(PushPullRequest)
-	if err := dec(in); err != nil {
-		return nil, err
-	}
-	if interceptor == nil {
-		return srv.(YorkieServiceServer).PushPull(ctx, in)
-	}
-	info := &grpc.UnaryServerInfo{
-		Server:     srv,
-		FullMethod: "/yorkie.v1.YorkieService/PushPull",
-	}
-	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(YorkieServiceServer).PushPull(ctx, req.(*PushPullRequest))
-	}
-	return interceptor(ctx, in, info, handler)
-}
-
 var _YorkieService_serviceDesc = grpc.ServiceDesc{
 	ServiceName: "yorkie.v1.YorkieService",
 	HandlerType: (*YorkieServiceServer)(nil),
@@ -1214,8 +1393,12 @@ var _YorkieService_serviceDesc = grpc.ServiceDesc{
 			Handler:    _YorkieService_DetachDocument_Handler,
 		},
 		{
-			MethodName: "PushPull",
-			Handler:    _YorkieService_PushPull_Handler,
+			MethodName: "RemoveDocument",
+			Handler:    _YorkieService_RemoveDocument_Handler,
+		},
+		{
+			MethodName: "PushPullChanges",
+			Handler:    _YorkieService_PushPullChanges_Handler,
 		},
 	},
 	Streams: []grpc.StreamDesc{
@@ -1451,6 +1634,13 @@ func (m *AttachDocumentResponse) MarshalToSizedBuffer(dAtA []byte) (int, error)
 			i = encodeVarintYorkie(dAtA, i, uint64(size))
 		}
 		i--
+		dAtA[i] = 0x1a
+	}
+	if len(m.DocumentId) > 0 {
+		i -= len(m.DocumentId)
+		copy(dAtA[i:], m.DocumentId)
+		i = encodeVarintYorkie(dAtA, i, uint64(len(m.DocumentId)))
+		i--
 		dAtA[i] = 0x12
 	}
 	if len(m.ClientId) > 0 {
@@ -1497,6 +1687,13 @@ func (m *DetachDocumentRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 			i = encodeVarintYorkie(dAtA, i, uint64(size))
 		}
 		i--
+		dAtA[i] = 0x1a
+	}
+	if len(m.DocumentId) > 0 {
+		i -= len(m.DocumentId)
+		copy(dAtA[i:], m.DocumentId)
+		i = encodeVarintYorkie(dAtA, i, uint64(len(m.DocumentId)))
+		i--
 		dAtA[i] = 0x12
 	}
 	if len(m.ClientId) > 0 {
@@ -1734,7 +1931,7 @@ func (m *WatchDocumentsResponse_Initialization) MarshalToSizedBuffer(dAtA []byte
 	return len(dAtA) - i, nil
 }
 
-func (m *PushPullRequest) Marshal() (dAtA []byte, err error) {
+func (m *RemoveDocumentRequest) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
 	n, err := m.MarshalToSizedBuffer(dAtA[:size])
@@ -1744,12 +1941,12 @@ func (m *PushPullRequest) Marshal() (dAtA []byte, err error) {
 	return dAtA[:n], nil
 }
 
-func (m *PushPullRequest) MarshalTo(dAtA []byte) (int, error) {
+func (m *RemoveDocumentRequest) MarshalTo(dAtA []byte) (int, error) {
 	size := m.Size()
 	return m.MarshalToSizedBuffer(dAtA[:size])
 }
 
-func (m *PushPullRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+func (m *RemoveDocumentRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	i := len(dAtA)
 	_ = i
 	var l int
@@ -1768,6 +1965,13 @@ func (m *PushPullRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 			i = encodeVarintYorkie(dAtA, i, uint64(size))
 		}
 		i--
+		dAtA[i] = 0x1a
+	}
+	if len(m.DocumentId) > 0 {
+		i -= len(m.DocumentId)
+		copy(dAtA[i:], m.DocumentId)
+		i = encodeVarintYorkie(dAtA, i, uint64(len(m.DocumentId)))
+		i--
 		dAtA[i] = 0x12
 	}
 	if len(m.ClientId) > 0 {
@@ -1780,7 +1984,7 @@ func (m *PushPullRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	return len(dAtA) - i, nil
 }
 
-func (m *PushPullResponse) Marshal() (dAtA []byte, err error) {
+func (m *RemoveDocumentResponse) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
 	n, err := m.MarshalToSizedBuffer(dAtA[:size])
@@ -1790,12 +1994,12 @@ func (m *PushPullResponse) Marshal() (dAtA []byte, err error) {
 	return dAtA[:n], nil
 }
 
-func (m *PushPullResponse) MarshalTo(dAtA []byte) (int, error) {
+func (m *RemoveDocumentResponse) MarshalTo(dAtA []byte) (int, error) {
 	size := m.Size()
 	return m.MarshalToSizedBuffer(dAtA[:size])
 }
 
-func (m *PushPullResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+func (m *RemoveDocumentResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	i := len(dAtA)
 	_ = i
 	var l int
@@ -1816,17 +2020,17 @@ func (m *PushPullResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i--
 		dAtA[i] = 0x12
 	}
-	if len(m.ClientId) > 0 {
-		i -= len(m.ClientId)
-		copy(dAtA[i:], m.ClientId)
-		i = encodeVarintYorkie(dAtA, i, uint64(len(m.ClientId)))
+	if len(m.ClientKey) > 0 {
+		i -= len(m.ClientKey)
+		copy(dAtA[i:], m.ClientKey)
+		i = encodeVarintYorkie(dAtA, i, uint64(len(m.ClientKey)))
 		i--
 		dAtA[i] = 0xa
 	}
 	return len(dAtA) - i, nil
 }
 
-func (m *UpdatePresenceRequest) Marshal() (dAtA []byte, err error) {
+func (m *PushPullChangesRequest) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
 	n, err := m.MarshalToSizedBuffer(dAtA[:size])
@@ -1836,12 +2040,12 @@ func (m *UpdatePresenceRequest) Marshal() (dAtA []byte, err error) {
 	return dAtA[:n], nil
 }
 
-func (m *UpdatePresenceRequest) MarshalTo(dAtA []byte) (int, error) {
+func (m *PushPullChangesRequest) MarshalTo(dAtA []byte) (int, error) {
 	size := m.Size()
 	return m.MarshalToSizedBuffer(dAtA[:size])
 }
 
-func (m *UpdatePresenceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+func (m *PushPullChangesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	i := len(dAtA)
 	_ = i
 	var l int
@@ -1850,18 +2054,9 @@ func (m *UpdatePresenceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
-	if len(m.DocumentKeys) > 0 {
-		for iNdEx := len(m.DocumentKeys) - 1; iNdEx >= 0; iNdEx-- {
-			i -= len(m.DocumentKeys[iNdEx])
-			copy(dAtA[i:], m.DocumentKeys[iNdEx])
-			i = encodeVarintYorkie(dAtA, i, uint64(len(m.DocumentKeys[iNdEx])))
-			i--
-			dAtA[i] = 0x12
-		}
-	}
-	if m.Client != nil {
+	if m.ChangePack != nil {
 		{
-			size, err := m.Client.MarshalToSizedBuffer(dAtA[:i])
+			size, err := m.ChangePack.MarshalToSizedBuffer(dAtA[:i])
 			if err != nil {
 				return 0, err
 			}
@@ -1869,12 +2064,26 @@ func (m *UpdatePresenceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 			i = encodeVarintYorkie(dAtA, i, uint64(size))
 		}
 		i--
+		dAtA[i] = 0x1a
+	}
+	if len(m.DocumentId) > 0 {
+		i -= len(m.DocumentId)
+		copy(dAtA[i:], m.DocumentId)
+		i = encodeVarintYorkie(dAtA, i, uint64(len(m.DocumentId)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.ClientId) > 0 {
+		i -= len(m.ClientId)
+		copy(dAtA[i:], m.ClientId)
+		i = encodeVarintYorkie(dAtA, i, uint64(len(m.ClientId)))
+		i--
 		dAtA[i] = 0xa
 	}
 	return len(dAtA) - i, nil
 }
 
-func (m *UpdatePresenceResponse) Marshal() (dAtA []byte, err error) {
+func (m *PushPullChangesResponse) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
 	n, err := m.MarshalToSizedBuffer(dAtA[:size])
@@ -1884,12 +2093,12 @@ func (m *UpdatePresenceResponse) Marshal() (dAtA []byte, err error) {
 	return dAtA[:n], nil
 }
 
-func (m *UpdatePresenceResponse) MarshalTo(dAtA []byte) (int, error) {
+func (m *PushPullChangesResponse) MarshalTo(dAtA []byte) (int, error) {
 	size := m.Size()
 	return m.MarshalToSizedBuffer(dAtA[:size])
 }
 
-func (m *UpdatePresenceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+func (m *PushPullChangesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	i := len(dAtA)
 	_ = i
 	var l int
@@ -1898,24 +2107,118 @@ func (m *UpdatePresenceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error)
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
+	if m.ChangePack != nil {
+		{
+			size, err := m.ChangePack.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintYorkie(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.ClientId) > 0 {
+		i -= len(m.ClientId)
+		copy(dAtA[i:], m.ClientId)
+		i = encodeVarintYorkie(dAtA, i, uint64(len(m.ClientId)))
+		i--
+		dAtA[i] = 0xa
+	}
 	return len(dAtA) - i, nil
 }
 
-func encodeVarintYorkie(dAtA []byte, offset int, v uint64) int {
-	offset -= sovYorkie(v)
-	base := offset
-	for v >= 1<<7 {
-		dAtA[offset] = uint8(v&0x7f | 0x80)
-		v >>= 7
-		offset++
+func (m *UpdatePresenceRequest) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
 	}
-	dAtA[offset] = uint8(v)
-	return base
+	return dAtA[:n], nil
 }
-func (m *ActivateClientRequest) Size() (n int) {
-	if m == nil {
-		return 0
-	}
+
+func (m *UpdatePresenceRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *UpdatePresenceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.XXX_unrecognized != nil {
+		i -= len(m.XXX_unrecognized)
+		copy(dAtA[i:], m.XXX_unrecognized)
+	}
+	if len(m.DocumentKeys) > 0 {
+		for iNdEx := len(m.DocumentKeys) - 1; iNdEx >= 0; iNdEx-- {
+			i -= len(m.DocumentKeys[iNdEx])
+			copy(dAtA[i:], m.DocumentKeys[iNdEx])
+			i = encodeVarintYorkie(dAtA, i, uint64(len(m.DocumentKeys[iNdEx])))
+			i--
+			dAtA[i] = 0x12
+		}
+	}
+	if m.Client != nil {
+		{
+			size, err := m.Client.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintYorkie(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *UpdatePresenceResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *UpdatePresenceResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *UpdatePresenceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.XXX_unrecognized != nil {
+		i -= len(m.XXX_unrecognized)
+		copy(dAtA[i:], m.XXX_unrecognized)
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintYorkie(dAtA []byte, offset int, v uint64) int {
+	offset -= sovYorkie(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *ActivateClientRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
 	var l int
 	_ = l
 	l = len(m.ClientKey)
@@ -2010,6 +2313,10 @@ func (m *AttachDocumentResponse) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovYorkie(uint64(l))
 	}
+	l = len(m.DocumentId)
+	if l > 0 {
+		n += 1 + l + sovYorkie(uint64(l))
+	}
 	if m.ChangePack != nil {
 		l = m.ChangePack.Size()
 		n += 1 + l + sovYorkie(uint64(l))
@@ -2030,6 +2337,10 @@ func (m *DetachDocumentRequest) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovYorkie(uint64(l))
 	}
+	l = len(m.DocumentId)
+	if l > 0 {
+		n += 1 + l + sovYorkie(uint64(l))
+	}
 	if m.ChangePack != nil {
 		l = m.ChangePack.Size()
 		n += 1 + l + sovYorkie(uint64(l))
@@ -2146,7 +2457,51 @@ func (m *WatchDocumentsResponse_Initialization) Size() (n int) {
 	return n
 }
 
-func (m *PushPullRequest) Size() (n int) {
+func (m *RemoveDocumentRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.ClientId)
+	if l > 0 {
+		n += 1 + l + sovYorkie(uint64(l))
+	}
+	l = len(m.DocumentId)
+	if l > 0 {
+		n += 1 + l + sovYorkie(uint64(l))
+	}
+	if m.ChangePack != nil {
+		l = m.ChangePack.Size()
+		n += 1 + l + sovYorkie(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func (m *RemoveDocumentResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.ClientKey)
+	if l > 0 {
+		n += 1 + l + sovYorkie(uint64(l))
+	}
+	if m.ChangePack != nil {
+		l = m.ChangePack.Size()
+		n += 1 + l + sovYorkie(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func (m *PushPullChangesRequest) Size() (n int) {
 	if m == nil {
 		return 0
 	}
@@ -2156,6 +2511,10 @@ func (m *PushPullRequest) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovYorkie(uint64(l))
 	}
+	l = len(m.DocumentId)
+	if l > 0 {
+		n += 1 + l + sovYorkie(uint64(l))
+	}
 	if m.ChangePack != nil {
 		l = m.ChangePack.Size()
 		n += 1 + l + sovYorkie(uint64(l))
@@ -2166,7 +2525,7 @@ func (m *PushPullRequest) Size() (n int) {
 	return n
 }
 
-func (m *PushPullResponse) Size() (n int) {
+func (m *PushPullChangesResponse) Size() (n int) {
 	if m == nil {
 		return 0
 	}
@@ -2781,6 +3140,38 @@ func (m *AttachDocumentResponse) Unmarshal(dAtA []byte) error {
 			}
 			iNdEx = postIndex
 		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DocumentId", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowYorkie
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.DocumentId = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ChangePack", wireType)
 			}
@@ -2902,6 +3293,38 @@ func (m *DetachDocumentRequest) Unmarshal(dAtA []byte) error {
 			}
 			iNdEx = postIndex
 		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DocumentId", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowYorkie
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.DocumentId = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ChangePack", wireType)
 			}
@@ -3498,7 +3921,279 @@ func (m *WatchDocumentsResponse_Initialization) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
-func (m *PushPullRequest) Unmarshal(dAtA []byte) error {
+func (m *RemoveDocumentRequest) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowYorkie
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: RemoveDocumentRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: RemoveDocumentRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ClientId", wireType)
+			}
+			var byteLen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowYorkie
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				byteLen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.ClientId = append(m.ClientId[:0], dAtA[iNdEx:postIndex]...)
+			if m.ClientId == nil {
+				m.ClientId = []byte{}
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DocumentId", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowYorkie
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.DocumentId = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ChangePack", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowYorkie
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.ChangePack == nil {
+				m.ChangePack = &ChangePack{}
+			}
+			if err := m.ChangePack.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipYorkie(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *RemoveDocumentResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowYorkie
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: RemoveDocumentResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: RemoveDocumentResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ClientKey", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowYorkie
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.ClientKey = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ChangePack", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowYorkie
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.ChangePack == nil {
+				m.ChangePack = &ChangePack{}
+			}
+			if err := m.ChangePack.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipYorkie(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *PushPullChangesRequest) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
 	for iNdEx < l {
@@ -3521,10 +4216,10 @@ func (m *PushPullRequest) Unmarshal(dAtA []byte) error {
 		fieldNum := int32(wire >> 3)
 		wireType := int(wire & 0x7)
 		if wireType == 4 {
-			return fmt.Errorf("proto: PushPullRequest: wiretype end group for non-group")
+			return fmt.Errorf("proto: PushPullChangesRequest: wiretype end group for non-group")
 		}
 		if fieldNum <= 0 {
-			return fmt.Errorf("proto: PushPullRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+			return fmt.Errorf("proto: PushPullChangesRequest: illegal tag %d (wire type %d)", fieldNum, wire)
 		}
 		switch fieldNum {
 		case 1:
@@ -3562,6 +4257,38 @@ func (m *PushPullRequest) Unmarshal(dAtA []byte) error {
 			}
 			iNdEx = postIndex
 		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DocumentId", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowYorkie
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthYorkie
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.DocumentId = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field ChangePack", wireType)
 			}
@@ -3619,7 +4346,7 @@ func (m *PushPullRequest) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
-func (m *PushPullResponse) Unmarshal(dAtA []byte) error {
+func (m *PushPullChangesResponse) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
 	for iNdEx < l {
@@ -3642,10 +4369,10 @@ func (m *PushPullResponse) Unmarshal(dAtA []byte) error {
 		fieldNum := int32(wire >> 3)
 		wireType := int(wire & 0x7)
 		if wireType == 4 {
-			return fmt.Errorf("proto: PushPullResponse: wiretype end group for non-group")
+			return fmt.Errorf("proto: PushPullChangesResponse: wiretype end group for non-group")
 		}
 		if fieldNum <= 0 {
-			return fmt.Errorf("proto: PushPullResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+			return fmt.Errorf("proto: PushPullChangesResponse: illegal tag %d (wire type %d)", fieldNum, wire)
 		}
 		switch fieldNum {
 		case 1:
diff --git a/api/yorkie/v1/yorkie.proto b/api/yorkie/v1/yorkie.proto
index bb283753..5434c251 100644
--- a/api/yorkie/v1/yorkie.proto
+++ b/api/yorkie/v1/yorkie.proto
@@ -32,8 +32,10 @@ service YorkieService {
 
   rpc AttachDocument (AttachDocumentRequest) returns (AttachDocumentResponse) {}
   rpc DetachDocument (DetachDocumentRequest) returns (DetachDocumentResponse) {}
+  rpc RemoveDocument (RemoveDocumentRequest) returns (RemoveDocumentResponse) {}
+  rpc PushPullChanges (PushPullChangesRequest) returns (PushPullChangesResponse) {}
+
   rpc WatchDocuments (WatchDocumentsRequest) returns (stream WatchDocumentsResponse) {}
-  rpc PushPull (PushPullRequest) returns (PushPullResponse) {}
 }
 
 message ActivateClientRequest {
@@ -60,12 +62,14 @@ message AttachDocumentRequest {
 
 message AttachDocumentResponse {
   bytes client_id = 1;
-  ChangePack change_pack = 2;
+  string document_id = 2;
+  ChangePack change_pack = 3;
 }
 
 message DetachDocumentRequest {
   bytes client_id = 1;
-  ChangePack change_pack = 2;
+  string document_id = 2;
+  ChangePack change_pack = 3;
 }
 
 message DetachDocumentResponse {
@@ -89,12 +93,24 @@ message WatchDocumentsResponse {
   }
 }
 
-message PushPullRequest {
+message RemoveDocumentRequest {
   bytes client_id = 1;
+  string document_id = 2;
+  ChangePack change_pack = 3;
+}
+
+message RemoveDocumentResponse {
+  string client_key = 1;
   ChangePack change_pack = 2;
 }
 
-message PushPullResponse {
+message PushPullChangesRequest {
+  bytes client_id = 1;
+  string document_id = 2;
+  ChangePack change_pack = 3;
+}
+
+message PushPullChangesResponse {
   bytes client_id = 1;
   ChangePack change_pack = 2;
 }
diff --git a/client/client.go b/client/client.go
index d14f2edf..0f23c6e3 100644
--- a/client/client.go
+++ b/client/client.go
@@ -53,6 +53,10 @@ var (
 	// this client.
 	ErrDocumentNotAttached = errors.New("document is not attached")
 
+	// ErrDocumentNotDetached occurs when the given document is not detached from
+	// this client.
+	ErrDocumentNotDetached = errors.New("document is not detached")
+
 	// ErrUnsupportedWatchResponseType occurs when the given WatchResponseType
 	// is not supported.
 	ErrUnsupportedWatchResponseType = errors.New("unsupported watch response type")
@@ -61,6 +65,7 @@ var (
 // Attachment represents the document attached and peers.
 type Attachment struct {
 	doc   *document.Document
+	docID types.ID
 	peers map[string]types.PresenceInfo
 }
 
@@ -245,6 +250,10 @@ func (c *Client) Attach(ctx context.Context, doc *document.Document) error {
 		return ErrClientNotActivated
 	}
 
+	if doc.Status() != document.StatusDetached {
+		return ErrDocumentNotDetached
+	}
+
 	doc.SetActor(c.id)
 
 	pbChangePack, err := converter.ToChangePack(doc.CreateChangePack())
@@ -268,7 +277,6 @@ func (c *Client) Attach(ctx context.Context, doc *document.Document) error {
 	if err := doc.ApplyChangePack(pack); err != nil {
 		return err
 	}
-
 	if c.logger.Core().Enabled(zap.DebugLevel) {
 		c.logger.Debug(fmt.Sprintf(
 			"after apply %d changes: %s",
@@ -277,9 +285,14 @@ func (c *Client) Attach(ctx context.Context, doc *document.Document) error {
 		))
 	}
 
-	doc.SetStatus(document.Attached)
+	if doc.Status() == document.StatusRemoved {
+		return nil
+	}
+
+	doc.SetStatus(document.StatusAttached)
 	c.attachments[doc.Key().String()] = &Attachment{
 		doc:   doc,
+		docID: types.ID(res.DocumentId),
 		peers: make(map[string]types.PresenceInfo),
 	}
 
@@ -297,7 +310,8 @@ func (c *Client) Detach(ctx context.Context, doc *document.Document) error {
 		return ErrClientNotActivated
 	}
 
-	if _, ok := c.attachments[doc.Key().String()]; !ok {
+	attachment, ok := c.attachments[doc.Key().String()]
+	if !ok {
 		return ErrDocumentNotAttached
 	}
 
@@ -308,6 +322,7 @@ func (c *Client) Detach(ctx context.Context, doc *document.Document) error {
 
 	res, err := c.client.DetachDocument(ctx, &api.DetachDocumentRequest{
 		ClientId:   c.id.Bytes(),
+		DocumentId: attachment.docID.String(),
 		ChangePack: pbChangePack,
 	})
 	if err != nil {
@@ -322,8 +337,9 @@ func (c *Client) Detach(ctx context.Context, doc *document.Document) error {
 	if err := doc.ApplyChangePack(pack); err != nil {
 		return err
 	}
-
-	doc.SetStatus(document.Detached)
+	if doc.Status() != document.StatusRemoved {
+		doc.SetStatus(document.StatusDetached)
+	}
 	delete(c.attachments, doc.Key().String())
 
 	return nil
@@ -340,7 +356,7 @@ func (c *Client) Sync(ctx context.Context, keys ...key.Key) error {
 	}
 
 	for _, k := range keys {
-		if err := c.sync(ctx, k); err != nil {
+		if err := c.pushPull(ctx, k); err != nil {
 			return err
 		}
 	}
@@ -531,7 +547,7 @@ func (c *Client) IsActive() bool {
 	return c.status == activated
 }
 
-func (c *Client) sync(ctx context.Context, key key.Key) error {
+func (c *Client) pushPull(ctx context.Context, key key.Key) error {
 	if c.status != activated {
 		return ErrClientNotActivated
 	}
@@ -546,12 +562,12 @@ func (c *Client) sync(ctx context.Context, key key.Key) error {
 		return err
 	}
 
-	res, err := c.client.PushPull(ctx, &api.PushPullRequest{
+	res, err := c.client.PushPullChanges(ctx, &api.PushPullChangesRequest{
 		ClientId:   c.id.Bytes(),
+		DocumentId: attachment.docID.String(),
 		ChangePack: pbChangePack,
 	})
 	if err != nil {
-		c.logger.Error("failed to sync", zap.Error(err))
 		return err
 	}
 
@@ -561,9 +577,52 @@ func (c *Client) sync(ctx context.Context, key key.Key) error {
 	}
 
 	if err := attachment.doc.ApplyChangePack(pack); err != nil {
-		c.logger.Error("failed to apply change pack", zap.Error(err))
 		return err
 	}
+	if attachment.doc.Status() == document.StatusRemoved {
+		delete(c.attachments, attachment.doc.Key().String())
+	}
+
+	return nil
+}
+
+// Remove removes the given document.
+func (c *Client) Remove(ctx context.Context, doc *document.Document) error {
+	if c.status != activated {
+		return ErrClientNotActivated
+	}
+
+	attachment, ok := c.attachments[doc.Key().String()]
+	if !ok {
+		return ErrDocumentNotAttached
+	}
+
+	pbChangePack, err := converter.ToChangePack(doc.CreateChangePack())
+	if err != nil {
+		return err
+	}
+	pbChangePack.IsRemoved = true
+
+	res, err := c.client.RemoveDocument(ctx, &api.RemoveDocumentRequest{
+		ClientId:   c.id.Bytes(),
+		DocumentId: attachment.docID.String(),
+		ChangePack: pbChangePack,
+	})
+	if err != nil {
+		return err
+	}
+
+	pack, err := converter.FromChangePack(res.ChangePack)
+	if err != nil {
+		return err
+	}
+
+	if err := doc.ApplyChangePack(pack); err != nil {
+		return err
+	}
+	if doc.Status() == document.StatusRemoved {
+		delete(c.attachments, doc.Key().String())
+	}
 
 	return nil
 }
diff --git a/pkg/document/change/pack.go b/pkg/document/change/pack.go
index 27a81649..55f6409a 100644
--- a/pkg/document/change/pack.go
+++ b/pkg/document/change/pack.go
@@ -38,6 +38,9 @@ type Pack struct {
 	// MinSyncedTicket is the minimum logical time taken by clients who attach the document.
 	// It used to collect garbage on the replica on the client.
 	MinSyncedTicket *time.Ticket
+
+	// IsRemoved is a flag that indicates whether the document is removed.
+	IsRemoved bool
 }
 
 // NewPack creates a new instance of Pack.
diff --git a/pkg/document/document.go b/pkg/document/document.go
index 4345bc5a..cc1bcbaf 100644
--- a/pkg/document/document.go
+++ b/pkg/document/document.go
@@ -55,6 +55,10 @@ func (d *Document) Update(
 	updater func(root *json.Object) error,
 	msgAndArgs ...interface{},
 ) error {
+	if d.doc.status == StatusRemoved {
+		return ErrDocumentRemoved
+	}
+
 	d.ensureClone()
 
 	ctx := change.NewContext(
@@ -119,6 +123,11 @@ func (d *Document) ApplyChangePack(pack *change.Pack) error {
 	// 04. Do Garbage collection.
 	d.GarbageCollect(pack.MinSyncedTicket)
 
+	// 05. Update the status.
+	if pack.IsRemoved {
+		d.SetStatus(StatusRemoved)
+	}
+
 	return nil
 }
 
@@ -164,10 +173,15 @@ func (d *Document) ActorID() *time.ActorID {
 }
 
 // SetStatus updates the status of this document.
-func (d *Document) SetStatus(status statusType) {
+func (d *Document) SetStatus(status StatusType) {
 	d.doc.SetStatus(status)
 }
 
+// Status returns the status of this document.
+func (d *Document) Status() StatusType {
+	return d.doc.status
+}
+
 // IsAttached returns the whether this document is attached or not.
 func (d *Document) IsAttached() bool {
 	return d.doc.IsAttached()
diff --git a/pkg/document/internal_document.go b/pkg/document/internal_document.go
index 5030b02b..1a0b7ca8 100644
--- a/pkg/document/internal_document.go
+++ b/pkg/document/internal_document.go
@@ -17,6 +17,8 @@
 package document
 
 import (
+	"errors"
+
 	"github.com/yorkie-team/yorkie/api/converter"
 	"github.com/yorkie-team/yorkie/pkg/document/change"
 	"github.com/yorkie-team/yorkie/pkg/document/crdt"
@@ -24,22 +26,32 @@ import (
 	"github.com/yorkie-team/yorkie/pkg/document/time"
 )
 
-type statusType int
+// StatusType represents the status of the document.
+type StatusType int
 
 const (
-	// Detached means that the document is not attached to the client.
+	// StatusDetached means that the document is not attached to the client.
 	// The actor of the ticket is created without being assigned.
-	Detached statusType = iota
+	StatusDetached StatusType = iota
 
-	// Attached means that this document is attached to the client.
+	// StatusAttached means that this document is attached to the client.
 	// The actor of the ticket is created with being assigned by the client.
-	Attached
+	StatusAttached
+
+	// StatusRemoved means that this document is removed. If the document is removed,
+	// it cannot be edited.
+	StatusRemoved
+)
+
+var (
+	// ErrDocumentRemoved occurs when the document is removed.
+	ErrDocumentRemoved = errors.New("document is removed")
 )
 
 // InternalDocument represents a document in MongoDB and contains logical clocks.
 type InternalDocument struct {
 	key          key.Key
-	status       statusType
+	status       StatusType
 	root         *crdt.Root
 	checkpoint   change.Checkpoint
 	changeID     change.ID
@@ -52,7 +64,7 @@ func NewInternalDocument(k key.Key) *InternalDocument {
 
 	return &InternalDocument{
 		key:        k,
-		status:     Detached,
+		status:     StatusDetached,
 		root:       crdt.NewRoot(root),
 		checkpoint: change.InitialCheckpoint,
 		changeID:   change.InitialID,
@@ -73,7 +85,7 @@ func NewInternalDocumentFromSnapshot(
 
 	return &InternalDocument{
 		key:        k,
-		status:     Detached,
+		status:     StatusDetached,
 		root:       crdt.NewRoot(obj),
 		checkpoint: change.InitialCheckpoint.NextServerSeq(serverSeq),
 		changeID:   change.InitialID.SyncLamport(lamport),
@@ -170,13 +182,13 @@ func (d *InternalDocument) ActorID() *time.ActorID {
 }
 
 // SetStatus sets the status of this document.
-func (d *InternalDocument) SetStatus(status statusType) {
+func (d *InternalDocument) SetStatus(status StatusType) {
 	d.status = status
 }
 
 // IsAttached returns the whether this document is attached or not.
 func (d *InternalDocument) IsAttached() bool {
-	return d.status == Attached
+	return d.status == StatusAttached
 }
 
 // Root returns the root of this document.
diff --git a/server/backend/database/client_info.go b/server/backend/database/client_info.go
index eaa427b1..a33b9dac 100644
--- a/server/backend/database/client_info.go
+++ b/server/backend/database/client_info.go
@@ -39,9 +39,11 @@ const (
 	ClientActivated   = "activated"
 )
 
+// Below are statuses of the document.
 const (
-	documentAttached = "attached"
-	documentDetached = "detached"
+	DocumentAttached = "attached"
+	DocumentDetached = "detached"
+	DocumentRemoved  = "removed"
 )
 
 // ClientDocInfo is a structure representing information of the document
@@ -79,7 +81,7 @@ type ClientInfo struct {
 }
 
 // CheckIfInProject checks if the client is in the project.
-func (i ClientInfo) CheckIfInProject(projectID types.ID) error {
+func (i *ClientInfo) CheckIfInProject(projectID types.ID) error {
 	if i.ProjectID != projectID {
 		return fmt.Errorf(
 			"check client(%s,%s) in project(%s): %w",
@@ -108,12 +110,12 @@ func (i *ClientInfo) AttachDocument(docID types.ID) error {
 		i.Documents = make(map[types.ID]*ClientDocInfo)
 	}
 
-	if i.hasDocument(docID) && i.Documents[docID].Status == documentAttached {
+	if i.hasDocument(docID) && i.Documents[docID].Status == DocumentAttached {
 		return fmt.Errorf("client(%s) attaches document(%s): %w", i.ID.String(), docID.String(), ErrDocumentAlreadyAttached)
 	}
 
 	i.Documents[docID] = &ClientDocInfo{
-		Status:    documentAttached,
+		Status:    DocumentAttached,
 		ServerSeq: 0,
 		ClientSeq: 0,
 	}
@@ -128,7 +130,21 @@ func (i *ClientInfo) DetachDocument(docID types.ID) error {
 		return err
 	}
 
-	i.Documents[docID].Status = documentDetached
+	i.Documents[docID].Status = DocumentDetached
+	i.Documents[docID].ClientSeq = 0
+	i.Documents[docID].ServerSeq = 0
+	i.UpdatedAt = time.Now()
+
+	return nil
+}
+
+// RemoveDocument removes the given document from this client.
+func (i *ClientInfo) RemoveDocument(docID types.ID) error {
+	if err := i.EnsureDocumentAttached(docID); err != nil {
+		return err
+	}
+
+	i.Documents[docID].Status = DocumentRemoved
 	i.Documents[docID].ClientSeq = 0
 	i.Documents[docID].ServerSeq = 0
 	i.UpdatedAt = time.Now()
@@ -142,7 +158,7 @@ func (i *ClientInfo) IsAttached(docID types.ID) (bool, error) {
 		return false, fmt.Errorf("check document(%s) is attached: %w", docID.String(), ErrDocumentNeverAttached)
 	}
 
-	return i.Documents[docID].Status == documentAttached, nil
+	return i.Documents[docID].Status == DocumentAttached, nil
 }
 
 // Checkpoint returns the checkpoint of the given document.
@@ -181,7 +197,7 @@ func (i *ClientInfo) EnsureDocumentAttached(docID types.ID) error {
 		)
 	}
 
-	if !i.hasDocument(docID) || i.Documents[docID].Status == documentDetached {
+	if !i.hasDocument(docID) || i.Documents[docID].Status != DocumentAttached {
 		return fmt.Errorf("ensure attached document(%s) in client(%s): %w",
 			docID.String(),
 			i.ID.String(),
diff --git a/server/backend/database/database.go b/server/backend/database/database.go
index a7496ef9..6c5e6314 100644
--- a/server/backend/database/database.go
+++ b/server/backend/database/database.go
@@ -158,6 +158,7 @@ type Database interface {
 	// FindDocInfoByID finds the document of the given ID.
 	FindDocInfoByID(
 		ctx context.Context,
+		projectID types.ID,
 		id types.ID,
 	) (*DocInfo, error)
 
@@ -168,6 +169,7 @@ type Database interface {
 		docInfo *DocInfo,
 		initialServerSeq int64,
 		changes []*change.Change,
+		isRemoved bool,
 	) error
 
 	// PurgeStaleChanges delete changes before the smallest in `syncedseqs` to
diff --git a/server/backend/database/doc_info.go b/server/backend/database/doc_info.go
index 206d9346..84745845 100644
--- a/server/backend/database/doc_info.go
+++ b/server/backend/database/doc_info.go
@@ -48,6 +48,9 @@ type DocInfo struct {
 
 	// UpdatedAt is the time when the document is updated.
 	UpdatedAt time.Time `bson:"updated_at"`
+
+	// RemovedAt is the time when the document is removed.
+	RemovedAt time.Time `bson:"removed_at"`
 }
 
 // IncreaseServerSeq increases server sequence of the document.
@@ -56,6 +59,11 @@ func (info *DocInfo) IncreaseServerSeq() int64 {
 	return info.ServerSeq
 }
 
+// IsRemoved returns true if the document is removed
+func (info *DocInfo) IsRemoved() bool {
+	return !info.RemovedAt.IsZero()
+}
+
 // DeepCopy creates a deep copy of this DocInfo.
 func (info *DocInfo) DeepCopy() *DocInfo {
 	if info == nil {
@@ -71,5 +79,6 @@ func (info *DocInfo) DeepCopy() *DocInfo {
 		CreatedAt:  info.CreatedAt,
 		AccessedAt: info.AccessedAt,
 		UpdatedAt:  info.UpdatedAt,
+		RemovedAt:  info.RemovedAt,
 	}
 }
diff --git a/server/backend/database/memory/database.go b/server/backend/database/memory/database.go
index f5b26e0d..07ded27f 100644
--- a/server/backend/database/memory/database.go
+++ b/server/backend/database/memory/database.go
@@ -631,12 +631,18 @@ func (d *DB) FindDocInfoByKeyAndOwner(
 	txn := d.db.Txn(true)
 	defer txn.Abort()
 
-	raw, err := txn.First(tblDocuments, "project_id_key", projectID.String(), key.String())
+	raw, err := txn.First(tblDocuments, "project_id_key_removed_at", projectID.String(), key.String(), gotime.Time{})
 	if err != nil {
 		return nil, fmt.Errorf("find document by key: %w", err)
 	}
 	if !createDocIfNotExist && raw == nil {
-		return nil, fmt.Errorf("%s: %w", key, database.ErrDocumentNotFound)
+		raw, err = txn.First(tblDocuments, "project_id_key", projectID.String(), key.String())
+		if err != nil {
+			return nil, fmt.Errorf("find document by key: %w", err)
+		}
+		if raw == nil {
+			return nil, fmt.Errorf("%s: %w", key, database.ErrDocumentNotFound)
+		}
 	}
 
 	now := gotime.Now()
@@ -671,7 +677,7 @@ func (d *DB) FindDocInfoByKey(
 	txn := d.db.Txn(false)
 	defer txn.Abort()
 
-	raw, err := txn.First(tblDocuments, "project_id_key", projectID.String(), key.String())
+	raw, err := txn.First(tblDocuments, "project_id_key_removed_at", projectID.String(), key.String(), gotime.Time{})
 	if err != nil {
 		return nil, fmt.Errorf("find document by key: %w", err)
 	}
@@ -685,6 +691,7 @@ func (d *DB) FindDocInfoByKey(
 // FindDocInfoByID finds a docInfo of the given ID.
 func (d *DB) FindDocInfoByID(
 	ctx context.Context,
+	projectID types.ID,
 	id types.ID,
 ) (*database.DocInfo, error) {
 	txn := d.db.Txn(true)
@@ -696,20 +703,26 @@ func (d *DB) FindDocInfoByID(
 	}
 
 	if raw == nil {
-		return nil, fmt.Errorf("%s: %w", id, database.ErrDocumentNotFound)
+		return nil, fmt.Errorf("finding doc info by ID(%s): %w", id, database.ErrDocumentNotFound)
 	}
 
 	docInfo := raw.(*database.DocInfo)
+	if docInfo.ProjectID != projectID {
+		return nil, fmt.Errorf("finding doc info by ID(%s): %w", id, database.ErrDocumentNotFound)
+	}
+
 	return docInfo.DeepCopy(), nil
 }
 
-// CreateChangeInfos stores the given changes and doc info.
+// CreateChangeInfos stores the given changes and doc info. If the
+// removeDoc condition is true, mark IsRemoved to true in doc info.
 func (d *DB) CreateChangeInfos(
 	ctx context.Context,
 	projectID types.ID,
 	docInfo *database.DocInfo,
 	initialServerSeq int64,
 	changes []*change.Change,
+	isRemoved bool,
 ) error {
 	txn := d.db.Txn(true)
 	defer txn.Abort()
@@ -734,7 +747,13 @@ func (d *DB) CreateChangeInfos(
 		}
 	}
 
-	raw, err := txn.First(tblDocuments, "project_id_key", projectID.String(), docInfo.Key.String())
+	raw, err := txn.First(
+		tblDocuments,
+		"project_id_key_removed_at",
+		projectID.String(),
+		docInfo.Key.String(),
+		gotime.Time{},
+	)
 	if err != nil {
 		return fmt.Errorf("find document by key: %w", err)
 	}
@@ -746,13 +765,21 @@ func (d *DB) CreateChangeInfos(
 		return fmt.Errorf("%s: %w", docInfo.ID, database.ErrConflictOnUpdate)
 	}
 
+	now := gotime.Now()
 	loadedDocInfo.ServerSeq = docInfo.ServerSeq
-	loadedDocInfo.UpdatedAt = gotime.Now()
+	loadedDocInfo.UpdatedAt = now
+	if isRemoved {
+		loadedDocInfo.RemovedAt = now
+	}
 	if err := txn.Insert(tblDocuments, loadedDocInfo); err != nil {
 		return fmt.Errorf("update document: %w", err)
 	}
-
 	txn.Commit()
+
+	if isRemoved {
+		docInfo.RemovedAt = now
+	}
+
 	return nil
 }
 
diff --git a/server/backend/database/memory/indexes.go b/server/backend/database/memory/indexes.go
index 497dcb94..6d079af5 100644
--- a/server/backend/database/memory/indexes.go
+++ b/server/backend/database/memory/indexes.go
@@ -124,12 +124,21 @@ var schema = &memdb.DBSchema{
 					},
 				},
 				"project_id_key": {
-					Name:   "project_id_key",
-					Unique: true,
+					Name: "project_id_key",
+					Indexer: &memdb.CompoundIndex{
+						Indexes: []memdb.Indexer{
+							&memdb.StringFieldIndex{Field: "ProjectID"},
+							&memdb.StringFieldIndex{Field: "Key"},
+						},
+					},
+				},
+				"project_id_key_removed_at": {
+					Name: "project_id_key_removed_at",
 					Indexer: &memdb.CompoundIndex{
 						Indexes: []memdb.Indexer{
 							&memdb.StringFieldIndex{Field: "ProjectID"},
 							&memdb.StringFieldIndex{Field: "Key"},
+							&memdb.TimeFieldIndex{Field: "RemovedAt"},
 						},
 					},
 				},
diff --git a/server/backend/database/mongo/client.go b/server/backend/database/mongo/client.go
index 8873858a..c5827551 100644
--- a/server/backend/database/mongo/client.go
+++ b/server/backend/database/mongo/client.go
@@ -694,11 +694,15 @@ func (c *Client) FindDocInfoByKeyAndOwner(
 		return nil, err
 	}
 
-	now := gotime.Now()
-	res, err := c.collection(colDocuments).UpdateOne(ctx, bson.M{
+	filter := bson.M{
 		"project_id": encodedProjectID,
 		"key":        docKey,
-	}, bson.M{
+		"removed_at": bson.M{
+			"$exists": false,
+		},
+	}
+	now := gotime.Now()
+	res, err := c.collection(colDocuments).UpdateOne(ctx, filter, bson.M{
 		"$set": bson.M{
 			"accessed_at": now,
 		},
@@ -720,10 +724,7 @@ func (c *Client) FindDocInfoByKeyAndOwner(
 			},
 		})
 	} else {
-		result = c.collection(colDocuments).FindOne(ctx, bson.M{
-			"project_id": encodedProjectID,
-			"key":        docKey,
-		})
+		result = c.collection(colDocuments).FindOne(ctx, filter)
 		if result.Err() == mongo.ErrNoDocuments {
 			return nil, fmt.Errorf("%s %s: %w", projectID, docKey, database.ErrDocumentNotFound)
 		}
@@ -755,6 +756,9 @@ func (c *Client) FindDocInfoByKey(
 	result := c.collection(colDocuments).FindOne(ctx, bson.M{
 		"project_id": encodedProjectID,
 		"key":        docKey,
+		"removed_at": bson.M{
+			"$exists": false,
+		},
 	})
 	if result.Err() == mongo.ErrNoDocuments {
 		return nil, fmt.Errorf("%s %s: %w", projectID, docKey, database.ErrDocumentNotFound)
@@ -773,14 +777,24 @@ func (c *Client) FindDocInfoByKey(
 }
 
 // FindDocInfoByID finds a docInfo of the given ID.
-func (c *Client) FindDocInfoByID(ctx context.Context, id types.ID) (*database.DocInfo, error) {
+func (c *Client) FindDocInfoByID(
+	ctx context.Context,
+	projectID types.ID,
+	id types.ID,
+) (*database.DocInfo, error) {
+	encodedProjectID, err := encodeID(projectID)
+	if err != nil {
+		return nil, err
+	}
+
 	encodedDocID, err := encodeID(id)
 	if err != nil {
 		return nil, err
 	}
 
 	result := c.collection(colDocuments).FindOne(ctx, bson.M{
-		"_id": encodedDocID,
+		"_id":        encodedDocID,
+		"project_id": encodedProjectID,
 	})
 	if result.Err() == mongo.ErrNoDocuments {
 		logging.From(ctx).Error(result.Err())
@@ -806,6 +820,7 @@ func (c *Client) CreateChangeInfos(
 	docInfo *database.DocInfo,
 	initialServerSeq int64,
 	changes []*change.Change,
+	isRemoved bool,
 ) error {
 	encodedDocID, err := encodeID(docInfo.ID)
 	if err != nil {
@@ -833,23 +848,31 @@ func (c *Client) CreateChangeInfos(
 
 	// TODO(hackerwins): We need to handle the updates for the two collections
 	// below atomically.
-	if _, err = c.collection(colChanges).BulkWrite(
-		ctx,
-		models,
-		options.BulkWrite().SetOrdered(true),
-	); err != nil {
-		logging.From(ctx).Error(err)
-		return fmt.Errorf("bulk write changes: %w", err)
+	if len(changes) > 0 {
+		if _, err = c.collection(colChanges).BulkWrite(
+			ctx,
+			models,
+			options.BulkWrite().SetOrdered(true),
+		); err != nil {
+			logging.From(ctx).Error(err)
+			return fmt.Errorf("bulk write changes: %w", err)
+		}
+	}
+
+	now := gotime.Now()
+	updateFields := bson.M{
+		"server_seq": docInfo.ServerSeq,
+		"updated_at": now,
+	}
+	if isRemoved {
+		updateFields["removed_at"] = now
 	}
 
 	res, err := c.collection(colDocuments).UpdateOne(ctx, bson.M{
 		"_id":        encodedDocID,
 		"server_seq": initialServerSeq,
 	}, bson.M{
-		"$set": bson.M{
-			"server_seq": docInfo.ServerSeq,
-			"updated_at": gotime.Now(),
-		},
+		"$set": updateFields,
 	})
 	if err != nil {
 		logging.From(ctx).Error(err)
@@ -858,6 +881,9 @@ func (c *Client) CreateChangeInfos(
 	if res.MatchedCount == 0 {
 		return fmt.Errorf("%s: %w", docInfo.ID, database.ErrConflictOnUpdate)
 	}
+	if isRemoved {
+		docInfo.RemovedAt = now
+	}
 
 	return nil
 }
diff --git a/server/backend/database/mongo/indexes.go b/server/backend/database/mongo/indexes.go
index 1734d4da..cd0cd0e2 100644
--- a/server/backend/database/mongo/indexes.go
+++ b/server/backend/database/mongo/indexes.go
@@ -87,7 +87,11 @@ var collectionInfos = []collectionInfo{
 				{Key: "project_id", Value: bsonx.Int32(1)},
 				{Key: "key", Value: bsonx.Int32(1)},
 			},
-			Options: options.Index().SetUnique(true),
+			Options: options.Index().SetPartialFilterExpression(
+				bsonx.Doc{
+					{Key: "removed_at", Value: bsonx.Null()},
+				},
+			).SetUnique(true),
 		}},
 	}, {
 		name: colChanges,
diff --git a/server/documents/documents.go b/server/documents/documents.go
index 4d407f32..7c09cff7 100644
--- a/server/documents/documents.go
+++ b/server/documents/documents.go
@@ -182,7 +182,18 @@ func FindDocInfoByKey(
 	)
 }
 
-// FindDocInfoByKeyAndOwner returns a document for the given document key and owner.
+// FindDocInfo returns a document for the given document ID.
+func FindDocInfo(
+	ctx context.Context,
+	be *backend.Backend,
+	project *types.Project,
+	docID types.ID,
+) (*database.DocInfo, error) {
+	return be.DB.FindDocInfoByID(ctx, project.ID, docID)
+}
+
+// FindDocInfoByKeyAndOwner returns a document for the given document key. If
+// createDocIfNotExist is true, it creates a new document if it does not exist.
 func FindDocInfoByKeyAndOwner(
 	ctx context.Context,
 	be *backend.Backend,
diff --git a/server/packs/packs.go b/server/packs/packs.go
index 0f0e4a0c..4119f16b 100644
--- a/server/packs/packs.go
+++ b/server/packs/packs.go
@@ -47,6 +47,9 @@ func SnapshotKey(projectID types.ID, docKey key.Key) sync.Key {
 
 // PushPull stores the given changes and returns accumulated changes of the
 // given document.
+//
+// CAUTION(hackerwins, krapie): docInfo's state is constantly mutating as they are
+// constantly used as parameters in subsequent subroutines.
 func PushPull(
 	ctx context.Context,
 	be *backend.Backend,
@@ -83,8 +86,15 @@ func PushPull(
 	}
 
 	// 03. store pushed changes, docInfo and checkpoint of the client to DB.
-	if len(pushedChanges) > 0 {
-		if err := be.DB.CreateChangeInfos(ctx, project.ID, docInfo, initialServerSeq, pushedChanges); err != nil {
+	if len(pushedChanges) > 0 || reqPack.IsRemoved {
+		if err := be.DB.CreateChangeInfos(
+			ctx,
+			project.ID,
+			docInfo,
+			initialServerSeq,
+			pushedChanges,
+			reqPack.IsRemoved,
+		); err != nil {
 			return nil, err
 		}
 	}
@@ -106,9 +116,10 @@ func PushPull(
 		return nil, err
 	}
 	respPack.MinSyncedTicket = minSyncedTicket
+	respPack.ApplyDocInfo(docInfo)
 
 	// 05. publish document change event then store snapshot asynchronously.
-	if reqPack.HasChanges() {
+	if len(pushedChanges) > 0 || reqPack.IsRemoved {
 		be.Background.AttachGoroutine(func(ctx context.Context) {
 			publisherID, err := clientInfo.ID.ToActorID()
 			if err != nil {
diff --git a/server/packs/pushpull.go b/server/packs/pushpull.go
index 9d98ecb5..09a5cc9e 100644
--- a/server/packs/pushpull.go
+++ b/server/packs/pushpull.go
@@ -110,7 +110,8 @@ func pullPack(
 		if err != nil {
 			return nil, err
 		}
-		return NewServerPack(docInfo.Key, cpAfterPull, pulledChanges, nil), err
+
+		return NewServerPack(docInfo.Key, cpAfterPull, pulledChanges, nil), nil
 	}
 
 	return pullSnapshot(ctx, be, clientInfo, docInfo, reqPack, cpAfterPush, initialServerSeq)
diff --git a/server/packs/serverpacks.go b/server/packs/serverpacks.go
index a89ae05b..cf51db0e 100644
--- a/server/packs/serverpacks.go
+++ b/server/packs/serverpacks.go
@@ -43,6 +43,9 @@ type ServerPack struct {
 	// MinSyncedTicket is the minimum logical time taken by clients who attach the document.
 	// It used to collect garbage on the replica on the client.
 	MinSyncedTicket *time.Ticket
+
+	// IsRemoved is a flag that indicates whether the document is removed.
+	IsRemoved bool
 }
 
 // NewServerPack creates a new instance of ServerPack.
@@ -111,5 +114,13 @@ func (p *ServerPack) ToPBChangePack() (*api.ChangePack, error) {
 		Changes:         pbChanges,
 		Snapshot:        p.Snapshot,
 		MinSyncedTicket: converter.ToTimeTicket(p.MinSyncedTicket),
+		IsRemoved:       p.IsRemoved,
 	}, nil
 }
+
+// ApplyDocInfo applies the given DocInfo to the ServerPack.
+func (p *ServerPack) ApplyDocInfo(info *database.DocInfo) {
+	if info.IsRemoved() {
+		p.IsRemoved = true
+	}
+}
diff --git a/server/rpc/yorkie_server.go b/server/rpc/yorkie_server.go
index 52e279b6..6dd271bd 100644
--- a/server/rpc/yorkie_server.go
+++ b/server/rpc/yorkie_server.go
@@ -176,6 +176,7 @@ func (s *yorkieServer) AttachDocument(
 		pack.DocumentKey,
 		true,
 	)
+
 	if err != nil {
 		return nil, err
 	}
@@ -196,6 +197,7 @@ func (s *yorkieServer) AttachDocument(
 
 	return &api.AttachDocumentResponse{
 		ChangePack: pbChangePack,
+		DocumentId: docInfo.ID.String(),
 	}, nil
 }
 
@@ -213,6 +215,10 @@ func (s *yorkieServer) DetachDocument(
 	if err != nil {
 		return nil, err
 	}
+	docID := types.ID(req.DocumentId)
+	if err := docID.Validate(); err != nil {
+		return nil, err
+	}
 
 	if err := auth.VerifyAccess(ctx, s.backend, &types.AccessInfo{
 		Method:     types.DetachDocument,
@@ -249,13 +255,11 @@ func (s *yorkieServer) DetachDocument(
 	if err != nil {
 		return nil, err
 	}
-	docInfo, err := documents.FindDocInfoByKeyAndOwner(
+	docInfo, err := documents.FindDocInfo(
 		ctx,
 		s.backend,
 		projects.From(ctx),
-		clientInfo,
-		pack.DocumentKey,
-		false,
+		docID,
 	)
 	if err != nil {
 		return nil, err
@@ -280,12 +284,12 @@ func (s *yorkieServer) DetachDocument(
 	}, nil
 }
 
-// PushPull stores the changes sent by the client and delivers the changes
+// PushPullChanges stores the changes sent by the client and delivers the changes
 // accumulated in the server to the client.
-func (s *yorkieServer) PushPull(
+func (s *yorkieServer) PushPullChanges(
 	ctx context.Context,
-	req *api.PushPullRequest,
-) (*api.PushPullResponse, error) {
+	req *api.PushPullChangesRequest,
+) (*api.PushPullChangesResponse, error) {
 	actorID, err := time.ActorIDFromBytes(req.ClientId)
 	if err != nil {
 		return nil, err
@@ -295,6 +299,10 @@ func (s *yorkieServer) PushPull(
 	if err != nil {
 		return nil, err
 	}
+	docID := types.ID(req.DocumentId)
+	if err := docID.Validate(); err != nil {
+		return nil, err
+	}
 
 	if err := auth.VerifyAccess(ctx, s.backend, &types.AccessInfo{
 		Method:     types.PushPull,
@@ -331,13 +339,11 @@ func (s *yorkieServer) PushPull(
 	if err != nil {
 		return nil, err
 	}
-	docInfo, err := documents.FindDocInfoByKeyAndOwner(
+	docInfo, err := documents.FindDocInfo(
 		ctx,
 		s.backend,
 		projects.From(ctx),
-		clientInfo,
-		pack.DocumentKey,
-		false,
+		docID,
 	)
 	if err != nil {
 		return nil, err
@@ -357,7 +363,7 @@ func (s *yorkieServer) PushPull(
 		return nil, err
 	}
 
-	return &api.PushPullResponse{
+	return &api.PushPullChangesResponse{
 		ChangePack: pbChangePack,
 	}, nil
 }
@@ -452,6 +458,89 @@ func (s *yorkieServer) WatchDocuments(
 	}
 }
 
+// RemoveDocument removes the given document.
+func (s *yorkieServer) RemoveDocument(
+	ctx context.Context,
+	req *api.RemoveDocumentRequest,
+) (*api.RemoveDocumentResponse, error) {
+	actorID, err := time.ActorIDFromBytes(req.ClientId)
+	if err != nil {
+		return nil, err
+	}
+
+	pack, err := converter.FromChangePack(req.ChangePack)
+	if err != nil {
+		return nil, err
+	}
+	docID := types.ID(req.DocumentId)
+	if err := docID.Validate(); err != nil {
+		return nil, err
+	}
+
+	if err := auth.VerifyAccess(ctx, s.backend, &types.AccessInfo{
+		Method:     types.RemoveDocument,
+		Attributes: auth.AccessAttributes(pack),
+	}); err != nil {
+		return nil, err
+	}
+
+	if pack.HasChanges() {
+		locker, err := s.backend.Coordinator.NewLocker(
+			ctx,
+			packs.PushPullKey(projects.From(ctx).ID, pack.DocumentKey),
+		)
+		if err != nil {
+			return nil, err
+		}
+
+		if err := locker.Lock(ctx); err != nil {
+			return nil, err
+		}
+		defer func() {
+			if err := locker.Unlock(ctx); err != nil {
+				logging.DefaultLogger().Error(err)
+			}
+		}()
+	}
+
+	clientInfo, err := clients.FindClientInfo(
+		ctx,
+		s.backend.DB,
+		projects.From(ctx),
+		actorID,
+	)
+	if err != nil {
+		return nil, err
+	}
+	docInfo, err := documents.FindDocInfo(
+		ctx,
+		s.backend,
+		projects.From(ctx),
+		docID,
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	if err := clientInfo.RemoveDocument(docInfo.ID); err != nil {
+		return nil, err
+	}
+
+	pulled, err := packs.PushPull(ctx, s.backend, projects.From(ctx), clientInfo, docInfo, pack)
+	if err != nil {
+		return nil, err
+	}
+
+	pbChangePack, err := pulled.ToPBChangePack()
+	if err != nil {
+		return nil, err
+	}
+
+	return &api.RemoveDocumentResponse{
+		ChangePack: pbChangePack,
+	}, nil
+}
+
 // UpdatePresence updates the presence of the given client.
 func (s *yorkieServer) UpdatePresence(
 	ctx context.Context,


Test patch:
diff --git a/pkg/document/document_test.go b/pkg/document/document_test.go
index a832131d..b4a221a3 100644
--- a/pkg/document/document_test.go
+++ b/pkg/document/document_test.go
@@ -45,7 +45,7 @@ func TestDocument(t *testing.T) {
 	t.Run("status test", func(t *testing.T) {
 		doc := document.New("d1")
 		assert.False(t, doc.IsAttached())
-		doc.SetStatus(document.Attached)
+		doc.SetStatus(document.StatusAttached)
 		assert.True(t, doc.IsAttached())
 	})
 
diff --git a/server/backend/database/memory/database_test.go b/server/backend/database/memory/database_test.go
index 970976ab..c3340930 100644
--- a/server/backend/database/memory/database_test.go
+++ b/server/backend/database/memory/database_test.go
@@ -178,6 +178,53 @@ func TestDB(t *testing.T) {
 		assert.Equal(t, 15, res.TotalCount)
 	})
 
+	t.Run("set RemovedAt in docInfo test", func(t *testing.T) {
+		docKey := key.Key(fmt.Sprintf("tests$%s", t.Name()))
+
+		clientInfo, _ := db.ActivateClient(ctx, projectID, t.Name())
+		docInfo, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo.ID, docKey, true)
+		assert.NoError(t, clientInfo.AttachDocument(docInfo.ID))
+		assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo))
+
+		doc := document.New(key.Key(t.Name()))
+		pack := doc.CreateChangePack()
+
+		// Set RemovedAt in docInfo and store changes
+		err = db.CreateChangeInfos(ctx, projectID, docInfo, 0, pack.Changes, true)
+		assert.NoError(t, err)
+
+		// Check whether RemovedAt is set in docInfo
+		docInfo, err = db.FindDocInfoByID(ctx, projectID, docInfo.ID)
+		assert.NoError(t, err)
+		assert.Equal(t, false, docInfo.RemovedAt.IsZero())
+	})
+
+	t.Run("reuse same key to create docInfo test ", func(t *testing.T) {
+		docKey := key.Key(fmt.Sprintf("tests$%s", t.Name()))
+
+		clientInfo1, _ := db.ActivateClient(ctx, projectID, t.Name())
+		docInfo1, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo1.ID, docKey, true)
+		assert.NoError(t, clientInfo1.AttachDocument(docInfo1.ID))
+		assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo1, docInfo1))
+
+		doc := document.New(key.Key(t.Name()))
+		pack := doc.CreateChangePack()
+
+		// Set RemovedAt in docInfo and store changes
+		err = db.CreateChangeInfos(ctx, projectID, docInfo1, 0, pack.Changes, true)
+		assert.NoError(t, err)
+
+		// Use same key to create docInfo
+		clientInfo2, _ := db.ActivateClient(ctx, projectID, t.Name())
+		docInfo2, _ := db.FindDocInfoByKeyAndOwner(ctx, projectID, clientInfo2.ID, docKey, true)
+		assert.NoError(t, clientInfo2.AttachDocument(docInfo2.ID))
+		assert.NoError(t, db.UpdateClientInfoAfterPushPull(ctx, clientInfo2, docInfo2))
+
+		// Check they have same key but different id
+		assert.Equal(t, docInfo1.Key, docInfo2.Key)
+		assert.NotEqual(t, docInfo1.ID, docInfo2.ID)
+	})
+
 	t.Run("update clientInfo after PushPull test", func(t *testing.T) {
 		clientInfo, err := db.ActivateClient(ctx, projectID, t.Name())
 		assert.NoError(t, err)
@@ -220,7 +267,7 @@ func TestDB(t *testing.T) {
 		}
 
 		// Store changes
-		err = db.CreateChangeInfos(ctx, projectID, docInfo, 0, pack.Changes)
+		err = db.CreateChangeInfos(ctx, projectID, docInfo, 0, pack.Changes, false)
 		assert.NoError(t, err)
 
 		// Find changes
@@ -340,7 +387,7 @@ func TestDB(t *testing.T) {
 		localDB, err := memory.New()
 		assert.NoError(t, err)
 
-		_, err = localDB.FindDocInfoByID(context.Background(), notExistsID)
+		_, err = localDB.FindDocInfoByID(context.Background(), projectID, notExistsID)
 		assert.ErrorIs(t, err, database.ErrDocumentNotFound)
 	})
 
diff --git a/server/backend/database/mongo/client_test.go b/server/backend/database/mongo/client_test.go
index 9c51b721..14a9c5e0 100644
--- a/server/backend/database/mongo/client_test.go
+++ b/server/backend/database/mongo/client_test.go
@@ -18,12 +18,15 @@ package mongo_test
 
 import (
 	"context"
+	"fmt"
 	"strconv"
 	"testing"
+	"time"
 
 	"github.com/stretchr/testify/assert"
 
 	"github.com/yorkie-team/yorkie/api/types"
+	"github.com/yorkie-team/yorkie/pkg/document"
 	"github.com/yorkie-team/yorkie/pkg/document/key"
 	"github.com/yorkie-team/yorkie/server/backend/database"
 	"github.com/yorkie-team/yorkie/server/backend/database/mongo"
@@ -31,6 +34,7 @@ import (
 )
 
 const (
+	dummyProjectID            = types.ID("000000000000000000000000")
 	dummyOwnerID              = types.ID("000000000000000000000000")
 	otherOwnerID              = types.ID("000000000000000000000001")
 	dummyClientID             = types.ID("000000000000000000000000")
@@ -135,6 +139,61 @@ func TestClient(t *testing.T) {
 		assert.Equal(t, info1.ID, info2.ID)
 	})
 
+	t.Run("set removed_at in docInfo test", func(t *testing.T) {
+		ctx := context.Background()
+		docKey := key.Key(fmt.Sprintf("tests$%s", t.Name()))
+
+		clientInfo, _ := cli.ActivateClient(ctx, dummyProjectID, t.Name())
+		docInfo, _ := cli.FindDocInfoByKeyAndOwner(ctx, dummyProjectID, clientInfo.ID, docKey, true)
+		assert.NoError(t, clientInfo.AttachDocument(docInfo.ID))
+		assert.NoError(t, cli.UpdateClientInfoAfterPushPull(ctx, clientInfo, docInfo))
+
+		doc := document.New(key.Key(t.Name()))
+		pack := doc.CreateChangePack()
+
+		// Set removed_at in docInfo and store changes
+		assert.NoError(t, clientInfo.RemoveDocument(docInfo.ID))
+		err := cli.CreateChangeInfos(ctx, dummyProjectID, docInfo, 0, pack.Changes, true)
+		assert.NoError(t, err)
+
+		// Check whether removed_at is set in docInfo
+		docInfo, err = cli.FindDocInfoByID(ctx, dummyProjectID, docInfo.ID)
+		assert.NoError(t, err)
+		assert.NotEqual(t, time.Time{}, docInfo.RemovedAt)
+
+		// Check whether DocumentRemoved status is set in clientInfo
+		clientInfo, err = cli.FindClientInfoByID(ctx, dummyProjectID, clientInfo.ID)
+		assert.NoError(t, err)
+		assert.NotEqual(t, database.DocumentRemoved, clientInfo.Documents[docInfo.ID].Status)
+	})
+
+	t.Run("reuse same key to create docInfo test ", func(t *testing.T) {
+		ctx := context.Background()
+		docKey := key.Key(fmt.Sprintf("tests$%s", t.Name()))
+
+		clientInfo1, _ := cli.ActivateClient(ctx, dummyProjectID, t.Name())
+		docInfo1, _ := cli.FindDocInfoByKeyAndOwner(ctx, dummyProjectID, clientInfo1.ID, docKey, true)
+		assert.NoError(t, clientInfo1.AttachDocument(docInfo1.ID))
+		assert.NoError(t, cli.UpdateClientInfoAfterPushPull(ctx, clientInfo1, docInfo1))
+
+		doc := document.New(key.Key(t.Name()))
+		pack := doc.CreateChangePack()
+
+		// Set removed_at in docInfo and store changes
+		err := cli.CreateChangeInfos(ctx, dummyProjectID, docInfo1, 0, pack.Changes, true)
+		assert.NoError(t, err)
+
+		// Use same key to create docInfo
+		clientInfo2, _ := cli.ActivateClient(ctx, dummyProjectID, t.Name())
+		docInfo2, _ := cli.FindDocInfoByKeyAndOwner(ctx, dummyProjectID, clientInfo2.ID, docKey, true)
+		assert.NoError(t, clientInfo2.AttachDocument(docInfo2.ID))
+		assert.NoError(t, cli.UpdateClientInfoAfterPushPull(ctx, clientInfo2, docInfo2))
+
+		// Check they have same key but different id
+		assert.Equal(t, docInfo1.Key, docInfo2.Key)
+		assert.NotEqual(t, docInfo1.ID, docInfo2.ID)
+	})
+
 	t.Run("FindDocInfosByPaging test", func(t *testing.T) {
 		const testDocCnt = 25
 
diff --git a/server/rpc/server_test.go b/server/rpc/server_test.go
index 61c674f4..82834175 100644
--- a/server/rpc/server_test.go
+++ b/server/rpc/server_test.go
@@ -175,7 +175,7 @@ func TestRPCServerBackend(t *testing.T) {
 			Checkpoint:  &api.Checkpoint{ServerSeq: 0, ClientSeq: 0},
 		}
 
-		_, err = testClient.AttachDocument(
+		resPack, err := testClient.AttachDocument(
 			context.Background(),
 			&api.AttachDocumentRequest{
 				ClientId:   activateResp.ClientId,
@@ -228,6 +228,7 @@ func TestRPCServerBackend(t *testing.T) {
 			context.Background(),
 			&api.DetachDocumentRequest{
 				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
 				ChangePack: packWithNoChanges,
 			},
 		)
@@ -238,6 +239,7 @@ func TestRPCServerBackend(t *testing.T) {
 			context.Background(),
 			&api.DetachDocumentRequest{
 				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
 				ChangePack: packWithNoChanges,
 			},
 		)
@@ -256,10 +258,10 @@ func TestRPCServerBackend(t *testing.T) {
 		_, err = testClient.DetachDocument(
 			context.Background(),
 			&api.DetachDocumentRequest{
-				ClientId: activateResp.ClientId,
+				ClientId:   activateResp.ClientId,
+				DocumentId: "000000000000000000000000",
 				ChangePack: &api.ChangePack{
-					DocumentKey: "invalid",
-					Checkpoint:  &api.Checkpoint{ServerSeq: 0, ClientSeq: 0},
+					Checkpoint: &api.Checkpoint{ServerSeq: 0, ClientSeq: 0},
 				},
 			},
 		)
@@ -282,6 +284,76 @@ func TestRPCServerBackend(t *testing.T) {
 		assert.Equal(t, codes.FailedPrecondition, status.Convert(err).Code())
 	})
 
+	t.Run("attach/detach on removed document test", func(t *testing.T) {
+		activateResp, err := testClient.ActivateClient(
+			context.Background(),
+			&api.ActivateClientRequest{ClientKey: t.Name()},
+		)
+		assert.NoError(t, err)
+
+		packWithNoChanges := &api.ChangePack{
+			DocumentKey: helper.TestDocKey(t),
+			Checkpoint:  &api.Checkpoint{ServerSeq: 0, ClientSeq: 0},
+		}
+
+		packWithRemoveRequest := &api.ChangePack{
+			DocumentKey: helper.TestDocKey(t),
+			Checkpoint:  &api.Checkpoint{ServerSeq: 0, ClientSeq: 0},
+			IsRemoved:   true,
+		}
+
+		resPack, err := testClient.AttachDocument(
+			context.Background(),
+			&api.AttachDocumentRequest{
+				ClientId:   activateResp.ClientId,
+				ChangePack: packWithNoChanges,
+			},
+		)
+		assert.NoError(t, err)
+
+		_, err = testClient.RemoveDocument(
+			context.Background(),
+			&api.RemoveDocumentRequest{
+				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
+				ChangePack: packWithRemoveRequest,
+			},
+		)
+		assert.NoError(t, err)
+
+		// try to detach document with same ID as removed document
+		// FailedPrecondition because document is not attached.
+		_, err = testClient.DetachDocument(
+			context.Background(),
+			&api.DetachDocumentRequest{
+				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
+				ChangePack: packWithNoChanges,
+			},
+		)
+		assert.Equal(t, codes.FailedPrecondition, status.Convert(err).Code())
+
+		// try to create new document with same key as removed document
+		resPack, err = testClient.AttachDocument(
+			context.Background(),
+			&api.AttachDocumentRequest{
+				ClientId:   activateResp.ClientId,
+				ChangePack: packWithNoChanges,
+			},
+		)
+		assert.NoError(t, err)
+
+		_, err = testClient.RemoveDocument(
+			context.Background(),
+			&api.RemoveDocumentRequest{
+				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
+				ChangePack: packWithRemoveRequest,
+			},
+		)
+		assert.NoError(t, err)
+	})
+
 	t.Run("push/pull changes test", func(t *testing.T) {
 		packWithNoChanges := &api.ChangePack{
 			DocumentKey: helper.TestDocKey(t),
@@ -294,7 +366,7 @@ func TestRPCServerBackend(t *testing.T) {
 		)
 		assert.NoError(t, err)
 
-		_, err = testClient.AttachDocument(
+		resPack, err := testClient.AttachDocument(
 			context.Background(),
 			&api.AttachDocumentRequest{
 				ClientId: activateResp.ClientId,
@@ -313,10 +385,11 @@ func TestRPCServerBackend(t *testing.T) {
 		)
 		assert.NoError(t, err)
 
-		_, err = testClient.PushPull(
+		_, err = testClient.PushPullChanges(
 			context.Background(),
-			&api.PushPullRequest{
-				ClientId: activateResp.ClientId,
+			&api.PushPullChangesRequest{
+				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
 				ChangePack: &api.ChangePack{
 					DocumentKey: helper.TestDocKey(t),
 					Checkpoint:  &api.Checkpoint{ServerSeq: 0, ClientSeq: 2},
@@ -335,7 +408,8 @@ func TestRPCServerBackend(t *testing.T) {
 		_, err = testClient.DetachDocument(
 			context.Background(),
 			&api.DetachDocumentRequest{
-				ClientId: activateResp.ClientId,
+				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
 				ChangePack: &api.ChangePack{
 					DocumentKey: helper.TestDocKey(t),
 					Checkpoint:  &api.Checkpoint{ServerSeq: 0, ClientSeq: 3},
@@ -352,20 +426,22 @@ func TestRPCServerBackend(t *testing.T) {
 		assert.NoError(t, err)
 
 		// try to push/pull with detached document
-		_, err = testClient.PushPull(
+		_, err = testClient.PushPullChanges(
 			context.Background(),
-			&api.PushPullRequest{
+			&api.PushPullChangesRequest{
 				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
 				ChangePack: packWithNoChanges,
 			},
 		)
 		assert.Equal(t, codes.FailedPrecondition, status.Convert(err).Code())
 
 		// try to push/pull with invalid pack
-		_, err = testClient.PushPull(
+		_, err = testClient.PushPullChanges(
 			context.Background(),
-			&api.PushPullRequest{
+			&api.PushPullChangesRequest{
 				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
 				ChangePack: invalidChangePack,
 			},
 		)
@@ -378,13 +454,178 @@ func TestRPCServerBackend(t *testing.T) {
 		assert.NoError(t, err)
 
 		// try to push/pull with deactivated client
-		_, err = testClient.PushPull(
+		_, err = testClient.PushPullChanges(
+			context.Background(),
+			&api.PushPullChangesRequest{
+				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
+				ChangePack: packWithNoChanges,
+			},
+		)
+		assert.Equal(t, codes.FailedPrecondition, status.Convert(err).Code())
+	})
+
+	t.Run("push/pull on removed document test", func(t *testing.T) {
+		packWithNoChanges := &api.ChangePack{
+			DocumentKey: helper.TestDocKey(t),
+			Checkpoint:  &api.Checkpoint{ServerSeq: 0, ClientSeq: 0},
+		}
+
+		packWithRemoveRequest := &api.ChangePack{
+			DocumentKey: helper.TestDocKey(t),
+			Checkpoint:  &api.Checkpoint{ServerSeq: 0, ClientSeq: 0},
+			IsRemoved:   true,
+		}
+
+		activateResp, err := testClient.ActivateClient(
 			context.Background(),
-			&api.PushPullRequest{
+			&api.ActivateClientRequest{ClientKey: helper.TestDocKey(t)},
+		)
+		assert.NoError(t, err)
+
+		resPack, err := testClient.AttachDocument(
+			context.Background(),
+			&api.AttachDocumentRequest{
 				ClientId:   activateResp.ClientId,
 				ChangePack: packWithNoChanges,
 			},
 		)
+		assert.NoError(t, err)
+
+		_, err = testClient.RemoveDocument(
+			context.Background(),
+			&api.RemoveDocumentRequest{
+				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
+				ChangePack: packWithRemoveRequest,
+			},
+		)
+		assert.NoError(t, err)
+
+		// try to push/pull on removed document
+		_, err = testClient.PushPullChanges(
+			context.Background(),
+			&api.PushPullChangesRequest{
+				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
+				ChangePack: packWithNoChanges,
+			},
+		)
+		assert.Equal(t, codes.FailedPrecondition, status.Convert(err).Code())
+	})
+
+	t.Run("remove document test", func(t *testing.T) {
+		activateResp, err := testClient.ActivateClient(
+			context.Background(),
+			&api.ActivateClientRequest{ClientKey: t.Name()},
+		)
+		assert.NoError(t, err)
+
+		packWithNoChanges := &api.ChangePack{
+			DocumentKey: helper.TestDocKey(t),
+			Checkpoint:  &api.Checkpoint{ServerSeq: 0, ClientSeq: 0},
+		}
+
+		packWithRemoveRequest := &api.ChangePack{
+			DocumentKey: helper.TestDocKey(t),
+			Checkpoint:  &api.Checkpoint{ServerSeq: 0, ClientSeq: 0},
+			IsRemoved:   true,
+		}
+
+		resPack, err := testClient.AttachDocument(
+			context.Background(),
+			&api.AttachDocumentRequest{
+				ClientId:   activateResp.ClientId,
+				ChangePack: packWithNoChanges,
+			},
+		)
+		assert.NoError(t, err)
+
+		_, err = testClient.RemoveDocument(
+			context.Background(),
+			&api.RemoveDocumentRequest{
+				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
+				ChangePack: packWithRemoveRequest,
+			},
+		)
+		assert.NoError(t, err)
+
+		// try to remove removed document
+		_, err = testClient.RemoveDocument(
+			context.Background(),
+			&api.RemoveDocumentRequest{
+				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
+				ChangePack: packWithRemoveRequest,
+			},
+		)
+		assert.Equal(t, codes.FailedPrecondition, status.Convert(err).Code())
+	})
+
+	t.Run("remove document with invalid client state test", func(t *testing.T) {
+		activateResp, err := testClient.ActivateClient(
+			context.Background(),
+			&api.ActivateClientRequest{ClientKey: t.Name()},
+		)
+		assert.NoError(t, err)
+
+		packWithNoChanges := &api.ChangePack{
+			DocumentKey: helper.TestDocKey(t),
+			Checkpoint:  &api.Checkpoint{ServerSeq: 0, ClientSeq: 0},
+		}
+
+		packWithRemoveRequest := &api.ChangePack{
+			DocumentKey: helper.TestDocKey(t),
+			Checkpoint:  &api.Checkpoint{ServerSeq: 0, ClientSeq: 0},
+			IsRemoved:   true,
+		}
+
+		resPack, err := testClient.AttachDocument(
+			context.Background(),
+			&api.AttachDocumentRequest{
+				ClientId:   activateResp.ClientId,
+				ChangePack: packWithNoChanges,
+			},
+		)
+		assert.NoError(t, err)
+
+		_, err = testClient.DetachDocument(
+			context.Background(),
+			&api.DetachDocumentRequest{
+				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
+				ChangePack: packWithNoChanges,
+			},
+		)
+		assert.NoError(t, err)
+
+		// try to remove detached document
+		_, err = testClient.RemoveDocument(
+			context.Background(),
+			&api.RemoveDocumentRequest{
+				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
+				ChangePack: packWithRemoveRequest,
+			},
+		)
+		assert.Equal(t, codes.FailedPrecondition, status.Convert(err).Code())
+
+		_, err = testClient.DeactivateClient(
+			context.Background(),
+			&api.DeactivateClientRequest{ClientId: activateResp.ClientId},
+		)
+		assert.NoError(t, err)
+
+		// try to remove document with a deactivated client
+		_, err = testClient.RemoveDocument(
+			context.Background(),
+			&api.RemoveDocumentRequest{
+				ClientId:   activateResp.ClientId,
+				DocumentId: resPack.DocumentId,
+				ChangePack: packWithRemoveRequest,
+			},
+		)
 		assert.Equal(t, codes.FailedPrecondition, status.Convert(err).Code())
 	})
 }
diff --git a/test/bench/document_bench_test.go b/test/bench/document_bench_test.go
index 20dae0af..d71f2889 100644
--- a/test/bench/document_bench_test.go
+++ b/test/bench/document_bench_test.go
@@ -46,7 +46,7 @@ func BenchmarkDocument(b *testing.B) {
 		for i := 0; i < b.N; i++ {
 			doc := document.New("d1")
 			assert.False(b, doc.IsAttached())
-			doc.SetStatus(document.Attached)
+			doc.SetStatus(document.StatusAttached)
 			assert.True(b, doc.IsAttached())
 		}
 	})
diff --git a/test/integration/document_test.go b/test/integration/document_test.go
index 518f5085..3188e60f 100644
--- a/test/integration/document_test.go
+++ b/test/integration/document_test.go
@@ -196,4 +196,167 @@ func TestDocument(t *testing.T) {
 		syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}})
 		assert.NotNil(t, prevArray.RemovedAt())
 	})
+
+	t.Run("single-client document deletion test", func(t *testing.T) {
+		ctx := context.Background()
+
+		cli, err := client.Dial(defaultServer.RPCAddr())
+		assert.NoError(t, err)
+		defer func() {
+			assert.NoError(t, cli.Close())
+		}()
+		d1 := document.New(key.Key(helper.TestDocKey(t)))
+
+		// 01. client is not activated.
+		assert.ErrorIs(t, cli.Remove(ctx, d1), client.ErrClientNotActivated)
+
+		// 02. document is not attached.
+		assert.NoError(t, cli.Activate(ctx))
+		assert.ErrorIs(t, cli.Remove(ctx, d1), client.ErrDocumentNotAttached)
+
+		// 03. document is attached.
+		assert.NoError(t, cli.Attach(ctx, d1))
+		assert.NoError(t, cli.Remove(ctx, d1))
+		assert.Equal(t, document.StatusRemoved, d1.Status())
+
+		// 04. try to update a removed document.
+		assert.ErrorIs(t, d1.Update(func(root *json.Object) error {
+			root.SetString("k1", "v1")
+			return nil
+		}), document.ErrDocumentRemoved)
+
+		// 05. try to attach a removed document.
+		assert.ErrorIs(t, cli.Attach(ctx, d1), client.ErrDocumentNotDetached)
+	})
+
+	t.Run("removed document creation test", func(t *testing.T) {
+		ctx := context.Background()
+
+		// 01. cli1 creates d1 and removes it.
+		d1 := document.New(key.Key(helper.TestDocKey(t)))
+		err := d1.Update(func(root *json.Object) error {
+			root.SetString("k1", "v1")
+			return nil
+		})
+		assert.NoError(t, err)
+		assert.NoError(t, c1.Attach(ctx, d1))
+		assert.NoError(t, c1.Remove(ctx, d1))
+
+		// 02. cli2 creates d2 with the same key.
+		d2 := document.New(key.Key(helper.TestDocKey(t)))
+		assert.NoError(t, c2.Attach(ctx, d2))
+
+		// 03. cli1 creates d3 with the same key.
+		d3 := document.New(key.Key(helper.TestDocKey(t)))
+		assert.NoError(t, c1.Attach(ctx, d3))
+		syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d3}, {c2, d2}})
+	})
+
+	t.Run("removed document pushpull test", func(t *testing.T) {
+		ctx := context.Background()
+
+		// 01. cli1 creates d1 and cli2 syncs.
+		d1 := document.New(key.Key(helper.TestDocKey(t)))
+		err := d1.Update(func(root *json.Object) error {
+			root.SetString("k1", "v1")
+			return nil
+		})
+		assert.NoError(t, err)
+		assert.NoError(t, c1.Attach(ctx, d1))
+
+		d2 := document.New(key.Key(helper.TestDocKey(t)))
+		assert.NoError(t, c2.Attach(ctx, d2))
+		syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}})
+
+		// 02. cli1 updates d1 and removes it.
+		err = d1.Update(func(root *json.Object) error {
+			root.SetString("k1", "v2")
+			return nil
+		})
+		assert.NoError(t, err)
+		assert.NoError(t, c1.Remove(ctx, d1))
+
+		// 03. cli2 syncs and checks that d2 is removed.
+		assert.NoError(t, c2.Sync(ctx))
+		assert.Equal(t, d1.Status(), document.StatusRemoved)
+		assert.Equal(t, d2.Status(), document.StatusRemoved)
+		assert.Equal(t, d1.Marshal(), d2.Marshal())
+	})
+
+	t.Run("removed document detachment test", func(t *testing.T) {
+		ctx := context.Background()
+
+		// 01. cli1 creates d1 and cli2 syncs.
+		d1 := document.New(key.Key(helper.TestDocKey(t)))
+		err := d1.Update(func(root *json.Object) error {
+			root.SetString("k1", "v1")
+			return nil
+		})
+		assert.NoError(t, err)
+		assert.NoError(t, c1.Attach(ctx, d1))
+		d2 := document.New(key.Key(helper.TestDocKey(t)))
+		assert.NoError(t, c2.Attach(ctx, d2))
+		syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}})
+
+		// 02. cli1 removes d1 and cli2 detaches d2.
+		assert.NoError(t, c1.Remove(ctx, d1))
+		assert.NoError(t, c2.Detach(ctx, d2))
+		assert.Equal(t, d1.Status(), document.StatusRemoved)
+		assert.Equal(t, d2.Status(), document.StatusRemoved)
+	})
+
+	t.Run("removed document removal test", func(t *testing.T) {
+		ctx := context.Background()
+
+		// 01. cli1 creates d1 and cli2 syncs.
+		d1 := document.New(key.Key(helper.TestDocKey(t)))
+		err := d1.Update(func(root *json.Object) error {
+			root.SetString("k1", "v1")
+			return nil
+		})
+		assert.NoError(t, err)
+		assert.NoError(t, c1.Attach(ctx, d1))
+		d2 := document.New(key.Key(helper.TestDocKey(t)))
+		assert.NoError(t, c2.Attach(ctx, d2))
+		syncClientsThenAssertEqual(t, []clientAndDocPair{{c1, d1}, {c2, d2}})
+
+		// 02. cli1 removes d1 and cli2 removes d2.
+		assert.NoError(t, c1.Remove(ctx, d1))
+		assert.NoError(t, c2.Remove(ctx, d2))
+		assert.Equal(t, d1.Status(), document.StatusRemoved)
+		assert.Equal(t, d2.Status(), document.StatusRemoved)
+	})
+
+	// State transition of document
+	// ┌──────────┐ Attach ┌──────────┐ Remove ┌─────────┐
+	// │ Detached ├───────►│ Attached ├───────►│ Removed │
+	// └──────────┘        └─┬─┬──────┘        └─────────┘
+	//           ▲           │ │     ▲
+	//           └───────────┘ └─────┘
+	//              Detach     PushPull
+	t.Run("document state transition test", func(t *testing.T) {
+		ctx := context.Background()
+		cli, err := client.Dial(defaultServer.RPCAddr())
+		assert.NoError(t, err)
+		assert.NoError(t, cli.Activate(ctx))
+		defer func() {
+			assert.NoError(t, cli.Close())
+		}()
+
+		// 01. abnormal behavior on detached state
+		d1 := document.New(key.Key(helper.TestDocKey(t)))
+		assert.ErrorIs(t, cli.Detach(ctx, d1), client.ErrDocumentNotAttached)
+		assert.ErrorIs(t, cli.Sync(ctx, d1.Key()), client.ErrDocumentNotAttached)
+		assert.ErrorIs(t, cli.Remove(ctx, d1), client.ErrDocumentNotAttached)
+
+		// 02. abnormal behavior on attached state
+		assert.NoError(t, cli.Attach(ctx, d1))
+		assert.ErrorIs(t, cli.Attach(ctx, d1), client.ErrDocumentNotDetached)
+
+		// 03. abnormal behavior on removed state
+		assert.NoError(t, cli.Remove(ctx, d1))
+		assert.ErrorIs(t, cli.Remove(ctx, d1), client.ErrDocumentNotAttached)
+		assert.ErrorIs(t, cli.Sync(ctx, d1.Key()), client.ErrDocumentNotAttached)
+		assert.ErrorIs(t, cli.Detach(ctx, d1), client.ErrDocumentNotAttached)
+	})
 }