Repository: yorkie-team/yorkie
Pull request #596 (2023-08-02 06:16:26)

Remove panic from crdt.RGATreeList
**What this PR does / why we need it**:
This removes panic in `crdt/rga_tree_list` that should be avoided in production.

**Which issue(s) this PR fixes**:
Related to #497 

**Special notes for your reviewer**:
I guess it would be good to add some test cases for errors.
Would you mind giving me some comments about this?
I'm just planning to add tests for a basic success and the errors included in this pr.

**Does this PR introduce a user-facing change?**:
```release-note

```

**Additional documentation**
```docs

```

**Checklist**:
- [x] Added relevant tests or not required
- [x] Didn't break anything


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/yorkie-team/yorkie/pull/596?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) Report
> Merging [#596](https://app.codecov.io/gh/yorkie-team/yorkie/pull/596?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) (78825d3) into [main](https://app.codecov.io/gh/yorkie-team/yorkie/commit/1b557be51471337d3c6949d5f9ed5634b3a677e2?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) (1b557be) will **decrease** coverage by `0.12%`.
> Report is 2 commits behind head on main.
> The diff coverage is `29.41%`.

```diff
@@            Coverage Diff             @@
##             main     #596      +/-   ##
==========================================
- Coverage   51.11%   50.99%   -0.12%     
==========================================
  Files          66       66              
  Lines        7008     6979      -29     
==========================================
- Hits         3582     3559      -23     
+ Misses       2952     2947       -5     
+ Partials      474      473       -1     
```


| [Files Changed](https://app.codecov.io/gh/yorkie-team/yorkie/pull/596?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\_bytes.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/596?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-YXBpL2NvbnZlcnRlci9mcm9tX2J5dGVzLmdv) | `52.42% <0.00%> (-0.75%)` | :arrow_down: |
| [pkg/document/crdt/element\_rht.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/596?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvZWxlbWVudF9yaHQuZ28=) | `62.85% <0.00%> (ø)` | |
| [pkg/document/crdt/object.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/596?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvb2JqZWN0Lmdv) | `21.62% <ø> (ø)` | |
| [pkg/document/crdt/array.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/596?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvYXJyYXkuZ28=) | `30.35% <33.33%> (-1.72%)` | :arrow_down: |
| [pkg/document/crdt/rga\_tree\_list.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/596?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvcmdhX3RyZWVfbGlzdC5nbw==) | `61.06% <35.00%> (-1.63%)` | :arrow_down: |

... and [4 files with indirect coverage changes](https://app.codecov.io/gh/yorkie-team/yorkie/pull/596/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team)



Patch:
diff --git a/api/converter/from_bytes.go b/api/converter/from_bytes.go
index 6f0b6e8f..04662ade 100644
--- a/api/converter/from_bytes.go
+++ b/api/converter/from_bytes.go
@@ -147,7 +147,9 @@ func fromJSONArray(pbArr *api.JSONElement_JSONArray) (*crdt.Array, error) {
 		if err != nil {
 			return nil, err
 		}
-		elements.Add(elem)
+		if err = elements.Add(elem); err != nil {
+			return nil, err
+		}
 	}
 
 	createdAt, err := fromTimeTicket(pbArr.CreatedAt)
diff --git a/api/converter/from_pb.go b/api/converter/from_pb.go
index ac4d6eff..f63585a2 100644
--- a/api/converter/from_pb.go
+++ b/api/converter/from_pb.go
@@ -29,7 +29,6 @@ import (
 	"github.com/yorkie-team/yorkie/pkg/document/key"
 	"github.com/yorkie-team/yorkie/pkg/document/operations"
 	"github.com/yorkie-team/yorkie/pkg/document/time"
-	"github.com/yorkie-team/yorkie/server/backend/sync"
 )
 
 // FromUser converts the given Protobuf formats to model format.
@@ -192,16 +191,6 @@ func fromChangeID(id *api.ChangeID) (change.ID, error) {
 	), nil
 }
 
-// FromDocumentKey converts the given Protobuf formats to model format.
-func FromDocumentKey(pbKey string) (key.Key, error) {
-	k := key.Key(pbKey)
-	if err := k.Validate(); err != nil {
-		return "", err
-	}
-
-	return k, nil
-}
-
 // FromDocumentID converts the given Protobuf formats to model format.
 func FromDocumentID(pbID string) (types.ID, error) {
 	id := types.ID(pbID)
@@ -225,24 +214,6 @@ func FromEventType(pbDocEventType api.DocEventType) (types.DocEventType, error)
 	return "", fmt.Errorf("%v: %w", pbDocEventType, ErrUnsupportedEventType)
 }
 
-// FromDocEvent converts the given Protobuf formats to model format.
-func FromDocEvent(docEvent *api.DocEvent) (*sync.DocEvent, error) {
-	client, err := time.ActorIDFromBytes(docEvent.Publisher)
-	if err != nil {
-		return nil, err
-	}
-
-	eventType, err := FromEventType(docEvent.Type)
-	if err != nil {
-		return nil, err
-	}
-
-	return &sync.DocEvent{
-		Type:      eventType,
-		Publisher: client,
-	}, nil
-}
-
 // FromOperations converts the given Protobuf formats to model format.
 func FromOperations(pbOps []*api.Operation) ([]operations.Operation, error) {
 	var ops []operations.Operation
diff --git a/api/converter/to_bytes.go b/api/converter/to_bytes.go
index 29fbd54b..7313e9f5 100644
--- a/api/converter/to_bytes.go
+++ b/api/converter/to_bytes.go
@@ -29,7 +29,7 @@ import (
 )
 
 // SnapshotToBytes converts the given document to byte array.
-func SnapshotToBytes(obj *crdt.Object, presences *innerpresence.Map) ([]byte, error) {
+func SnapshotToBytes(obj *crdt.Object, presences map[string]innerpresence.Presence) ([]byte, error) {
 	pbElem, err := toJSONElement(obj)
 	if err != nil {
 		return nil, err
diff --git a/api/converter/to_pb.go b/api/converter/to_pb.go
index 48362c6a..49d736ff 100644
--- a/api/converter/to_pb.go
+++ b/api/converter/to_pb.go
@@ -29,7 +29,6 @@ import (
 	"github.com/yorkie-team/yorkie/pkg/document/innerpresence"
 	"github.com/yorkie-team/yorkie/pkg/document/operations"
 	"github.com/yorkie-team/yorkie/pkg/document/time"
-	"github.com/yorkie-team/yorkie/server/backend/sync"
 )
 
 // ToUser converts the given model format to Protobuf format.
@@ -123,12 +122,11 @@ func ToDocumentSummary(summary *types.DocumentSummary) (*api.DocumentSummary, er
 }
 
 // ToPresences converts the given model to Protobuf format.
-func ToPresences(presences *innerpresence.Map) map[string]*api.Presence {
+func ToPresences(presences map[string]innerpresence.Presence) map[string]*api.Presence {
 	pbPresences := make(map[string]*api.Presence)
-	presences.Range(func(k string, v innerpresence.Presence) bool {
+	for k, v := range presences {
 		pbPresences[k] = ToPresence(v)
-		return true
-	})
+	}
 	return pbPresences
 }
 
@@ -214,19 +212,6 @@ func ToDocEventType(eventType types.DocEventType) (api.DocEventType, error) {
 	}
 }
 
-// ToDocEvent converts the given model to Protobuf format.
-func ToDocEvent(docEvent sync.DocEvent) (*api.DocEvent, error) {
-	eventType, err := ToDocEventType(docEvent.Type)
-	if err != nil {
-		return nil, err
-	}
-
-	return &api.DocEvent{
-		Type:      eventType,
-		Publisher: docEvent.Publisher.Bytes(),
-	}, nil
-}
-
 // ToOperations converts the given model format to Protobuf format.
 func ToOperations(ops []operations.Operation) ([]*api.Operation, error) {
 	var pbOperations []*api.Operation
diff --git a/api/yorkie/v1/resources.proto b/api/yorkie/v1/resources.proto
index 46d879aa..7f6b3ca1 100644
--- a/api/yorkie/v1/resources.proto
+++ b/api/yorkie/v1/resources.proto
@@ -95,6 +95,10 @@ message Operation {
     TimeTicket executed_at = 6;
     map<string, string> attributes = 7;
   }
+  // NOTE(hackerwins): Select Operation is not used in the current version.
+  // In the previous version, it was used to represent selection of Text.
+  // However, it has been replaced by Presence now. It is retained for backward
+  // compatibility purposes.
   message Select {
     TimeTicket parent_created_at = 1;
     TextNodePos from = 2;
diff --git a/client/client.go b/client/client.go
index 65543396..76cc16f9 100644
--- a/client/client.go
+++ b/client/client.go
@@ -432,7 +432,7 @@ func (c *Client) Watch(
 				clientIDs = append(clientIDs, id.String())
 			}
 
-			doc.SetOnlineClientSet(clientIDs...)
+			doc.SetOnlineClients(clientIDs...)
 			return nil, nil
 		case *api.WatchDocumentResponse_Event:
 			eventType, err := converter.FromEventType(resp.Event.Type)
@@ -450,19 +450,22 @@ func (c *Client) Watch(
 				return &WatchResponse{Type: DocumentChanged}, nil
 			case types.DocumentsWatchedEvent:
 				doc.AddOnlineClient(cli.String())
-				if doc.OnlinePresence(cli.String()) == nil {
+				if doc.Presence(cli.String()) == nil {
 					return nil, nil
 				}
 
 				return &WatchResponse{
 					Type: DocumentWatched,
 					Presences: map[string]innerpresence.Presence{
-						cli.String(): doc.OnlinePresence(cli.String()),
+						cli.String(): doc.Presence(cli.String()),
 					},
 				}, nil
 			case types.DocumentsUnwatchedEvent:
-				p := doc.OnlinePresence(cli.String())
+				p := doc.Presence(cli.String())
 				doc.RemoveOnlineClient(cli.String())
+				if p == nil {
+					return nil, nil
+				}
 
 				return &WatchResponse{
 					Type: DocumentUnwatched,
@@ -521,6 +524,8 @@ func (c *Client) Watch(
 				t := PresenceChanged
 				if e.Type == document.WatchedEvent {
 					t = DocumentWatched
+				} else if e.Type == document.UnwatchedEvent {
+					t = DocumentUnwatched
 				}
 				rch <- WatchResponse{Type: t, Presences: e.Presences}
 			case <-ctx.Done():
diff --git a/cmd/yorkie/server.go b/cmd/yorkie/server.go
index 58318869..3fdb18a9 100644
--- a/cmd/yorkie/server.go
+++ b/cmd/yorkie/server.go
@@ -51,12 +51,6 @@ var (
 	authWebhookCacheAuthTTL    time.Duration
 	authWebhookCacheUnauthTTL  time.Duration
 
-	etcdEndpoints     []string
-	etcdDialTimeout   time.Duration
-	etcdUsername      string
-	etcdPassword      string
-	etcdLockLeaseTime time.Duration
-
 	conf = server.NewConfig()
 )
 
diff --git a/pkg/document/crdt/array.go b/pkg/document/crdt/array.go
index 9faf4441..458c4202 100644
--- a/pkg/document/crdt/array.go
+++ b/pkg/document/crdt/array.go
@@ -43,9 +43,8 @@ func (a *Array) Purge(elem Element) error {
 }
 
 // Add adds the given element at the last.
-func (a *Array) Add(elem Element) *Array {
-	a.elements.Add(elem)
-	return a
+func (a *Array) Add(elem Element) error {
+	return a.elements.Add(elem)
 }
 
 // Get returns the element of the given index.
@@ -59,7 +58,7 @@ func (a *Array) Get(idx int) (Element, error) {
 
 // FindPrevCreatedAt returns the creation time of the previous element of the
 // given element.
-func (a *Array) FindPrevCreatedAt(createdAt *time.Ticket) *time.Ticket {
+func (a *Array) FindPrevCreatedAt(createdAt *time.Ticket) (*time.Ticket, error) {
 	return a.elements.FindPrevCreatedAt(createdAt)
 }
 
@@ -74,8 +73,8 @@ func (a *Array) Delete(idx int, deletedAt *time.Ticket) (Element, error) {
 
 // MoveAfter moves the given `createdAt` element after the `prevCreatedAt`
 // element.
-func (a *Array) MoveAfter(prevCreatedAt, createdAt, executedAt *time.Ticket) {
-	a.elements.MoveAfter(prevCreatedAt, createdAt, executedAt)
+func (a *Array) MoveAfter(prevCreatedAt, createdAt, executedAt *time.Ticket) error {
+	return a.elements.MoveAfter(prevCreatedAt, createdAt, executedAt)
 }
 
 // Elements returns an array of elements contained in this RGATreeList.
@@ -111,7 +110,9 @@ func (a *Array) DeepCopy() (Element, error) {
 		if err != nil {
 			return nil, err
 		}
-		elements.Add(copiedNode)
+		if err = elements.Add(copiedNode); err != nil {
+			return nil, err
+		}
 	}
 
 	array := NewArray(elements, a.createdAt)
@@ -160,13 +161,17 @@ func (a *Array) LastCreatedAt() *time.Ticket {
 }
 
 // InsertAfter inserts the given element after the given previous element.
-func (a *Array) InsertAfter(prevCreatedAt *time.Ticket, element Element) {
-	a.elements.InsertAfter(prevCreatedAt, element)
+func (a *Array) InsertAfter(prevCreatedAt *time.Ticket, element Element) error {
+	return a.elements.InsertAfter(prevCreatedAt, element)
 }
 
 // DeleteByCreatedAt deletes the given element.
-func (a *Array) DeleteByCreatedAt(createdAt *time.Ticket, deletedAt *time.Ticket) Element {
-	return a.elements.DeleteByCreatedAt(createdAt, deletedAt).elem
+func (a *Array) DeleteByCreatedAt(createdAt *time.Ticket, deletedAt *time.Ticket) (Element, error) {
+	node, err := a.elements.DeleteByCreatedAt(createdAt, deletedAt)
+	if err != nil {
+		return nil, err
+	}
+	return node.elem, nil
 }
 
 // Len returns length of this Array.
diff --git a/pkg/document/crdt/element.go b/pkg/document/crdt/element.go
index 2702d2de..5330ced4 100644
--- a/pkg/document/crdt/element.go
+++ b/pkg/document/crdt/element.go
@@ -36,7 +36,7 @@ type Container interface {
 	Descendants(callback func(elem Element, parent Container) bool)
 
 	// DeleteByCreatedAt removes the given element from this container.
-	DeleteByCreatedAt(createdAt *time.Ticket, deletedAt *time.Ticket) Element
+	DeleteByCreatedAt(createdAt *time.Ticket, deletedAt *time.Ticket) (Element, error)
 }
 
 // GCElement represents Element which has GC.
diff --git a/pkg/document/crdt/element_rht.go b/pkg/document/crdt/element_rht.go
index 5d90699e..8186bc3f 100644
--- a/pkg/document/crdt/element_rht.go
+++ b/pkg/document/crdt/element_rht.go
@@ -129,17 +129,17 @@ func (rht *ElementRHT) Delete(k string, deletedAt *time.Ticket) Element {
 }
 
 // DeleteByCreatedAt deletes the Element of the given creation time.
-func (rht *ElementRHT) DeleteByCreatedAt(createdAt *time.Ticket, deletedAt *time.Ticket) Element {
+func (rht *ElementRHT) DeleteByCreatedAt(createdAt *time.Ticket, deletedAt *time.Ticket) (Element, error) {
 	node, ok := rht.nodeMapByCreatedAt[createdAt.Key()]
 	if !ok {
-		return nil
+		return nil, fmt.Errorf("DeleteByCreatedAt %s: %w", createdAt.Key(), ErrChildNotFound)
 	}
 
 	if !node.Remove(deletedAt) {
-		return nil
+		return nil, nil
 	}
 
-	return node.elem
+	return node.elem, nil
 }
 
 // Elements returns a map of elements because the map easy to use for loop.
diff --git a/pkg/document/crdt/object.go b/pkg/document/crdt/object.go
index c27d43d3..abcba45e 100644
--- a/pkg/document/crdt/object.go
+++ b/pkg/document/crdt/object.go
@@ -63,7 +63,7 @@ func (o *Object) Has(k string) bool {
 }
 
 // DeleteByCreatedAt deletes the element of the given creation time.
-func (o *Object) DeleteByCreatedAt(createdAt *time.Ticket, deletedAt *time.Ticket) Element {
+func (o *Object) DeleteByCreatedAt(createdAt *time.Ticket, deletedAt *time.Ticket) (Element, error) {
 	return o.memberNodes.DeleteByCreatedAt(createdAt, deletedAt)
 }
 
diff --git a/pkg/document/crdt/primitive.go b/pkg/document/crdt/primitive.go
index 0308db5c..c109007a 100644
--- a/pkg/document/crdt/primitive.go
+++ b/pkg/document/crdt/primitive.go
@@ -52,7 +52,7 @@ func ValueFromBytes(valueType ValueType, value []byte) interface{} {
 		return false
 	case Integer:
 		val := int32(binary.LittleEndian.Uint32(value))
-		return int32(val)
+		return val
 	case Long:
 		return int64(binary.LittleEndian.Uint64(value))
 	case Double:
diff --git a/pkg/document/crdt/rga_tree_list.go b/pkg/document/crdt/rga_tree_list.go
index 0273b4c6..26d13285 100644
--- a/pkg/document/crdt/rga_tree_list.go
+++ b/pkg/document/crdt/rga_tree_list.go
@@ -149,8 +149,8 @@ func (a *RGATreeList) Marshal() string {
 }
 
 // Add adds the given element at the last.
-func (a *RGATreeList) Add(elem Element) {
-	a.insertAfter(a.last.CreatedAt(), elem, elem.CreatedAt())
+func (a *RGATreeList) Add(elem Element) error {
+	return a.insertAfter(a.last.CreatedAt(), elem, elem.CreatedAt())
 }
 
 // Nodes returns an array of elements contained in this RGATreeList.
@@ -175,8 +175,8 @@ func (a *RGATreeList) LastCreatedAt() *time.Ticket {
 }
 
 // InsertAfter inserts the given element after the given previous element.
-func (a *RGATreeList) InsertAfter(prevCreatedAt *time.Ticket, elem Element) {
-	a.insertAfter(prevCreatedAt, elem, elem.CreatedAt())
+func (a *RGATreeList) InsertAfter(prevCreatedAt *time.Ticket, elem Element) error {
+	return a.insertAfter(prevCreatedAt, elem, elem.CreatedAt())
 }
 
 // Get returns the element of the given index.
@@ -207,17 +207,17 @@ func (a *RGATreeList) Get(idx int) (*RGATreeListNode, error) {
 }
 
 // DeleteByCreatedAt deletes the given element.
-func (a *RGATreeList) DeleteByCreatedAt(createdAt *time.Ticket, deletedAt *time.Ticket) *RGATreeListNode {
+func (a *RGATreeList) DeleteByCreatedAt(createdAt *time.Ticket, deletedAt *time.Ticket) (*RGATreeListNode, error) {
 	node, ok := a.nodeMapByCreatedAt[createdAt.Key()]
 	if !ok {
-		panic("fail to find the given createdAt: " + createdAt.Key())
+		return nil, fmt.Errorf("DeleteByCreatedAt %s: %w", createdAt.Key(), ErrChildNotFound)
 	}
 
 	alreadyRemoved := node.isRemoved()
 	if node.elem.Remove(deletedAt) && !alreadyRemoved {
 		a.nodeMapByIndex.Splay(node.indexNode)
 	}
-	return node
+	return node, nil
 }
 
 // Len returns length of this RGATreeList.
@@ -237,35 +237,38 @@ func (a *RGATreeList) Delete(idx int, deletedAt *time.Ticket) (*RGATreeListNode,
 	if err != nil {
 		return nil, err
 	}
-	return a.DeleteByCreatedAt(target.CreatedAt(), deletedAt), nil
+	return a.DeleteByCreatedAt(target.CreatedAt(), deletedAt)
 }
 
 // MoveAfter moves the given `createdAt` element after the `prevCreatedAt`
 // element.
-func (a *RGATreeList) MoveAfter(prevCreatedAt, createdAt, executedAt *time.Ticket) {
+func (a *RGATreeList) MoveAfter(prevCreatedAt, createdAt, executedAt *time.Ticket) error {
 	prevNode, ok := a.nodeMapByCreatedAt[prevCreatedAt.Key()]
 	if !ok {
-		panic("fail to find the given prevCreatedAt: " + prevCreatedAt.Key())
+		return fmt.Errorf("MoveAfter %s: %w", prevCreatedAt.Key(), ErrChildNotFound)
 	}
 
 	node, ok := a.nodeMapByCreatedAt[createdAt.Key()]
 	if !ok {
-		panic("fail to find the given createdAt: " + createdAt.Key())
+		return fmt.Errorf("MoveAfter %s: %w", createdAt.Key(), ErrChildNotFound)
 	}
 
 	if node.elem.MovedAt() == nil || executedAt.After(node.elem.MovedAt()) {
 		a.release(node)
-		a.insertAfter(prevNode.CreatedAt(), node.elem, executedAt)
+		if err := a.insertAfter(prevNode.CreatedAt(), node.elem, executedAt); err != nil {
+			return err
+		}
 		node.elem.SetMovedAt(executedAt)
 	}
+	return nil
 }
 
 // FindPrevCreatedAt returns the creation time of the previous element of the
 // given element.
-func (a *RGATreeList) FindPrevCreatedAt(createdAt *time.Ticket) *time.Ticket {
+func (a *RGATreeList) FindPrevCreatedAt(createdAt *time.Ticket) (*time.Ticket, error) {
 	node, ok := a.nodeMapByCreatedAt[createdAt.Key()]
 	if !ok {
-		panic("fail to find the given prevCreatedAt: " + createdAt.Key())
+		return nil, fmt.Errorf("FindPrevCreatedAt %s: %w", createdAt.Key(), ErrChildNotFound)
 	}
 
 	for {
@@ -275,7 +278,7 @@ func (a *RGATreeList) FindPrevCreatedAt(createdAt *time.Ticket) *time.Ticket {
 		}
 	}
 
-	return node.CreatedAt()
+	return node.CreatedAt(), nil
 }
 
 // purge physically purge child element.
@@ -293,17 +296,17 @@ func (a *RGATreeList) purge(elem Element) error {
 func (a *RGATreeList) findNextBeforeExecutedAt(
 	createdAt *time.Ticket,
 	executedAt *time.Ticket,
-) *RGATreeListNode {
+) (*RGATreeListNode, error) {
 	node, ok := a.nodeMapByCreatedAt[createdAt.Key()]
 	if !ok {
-		panic("fail to find the given createdAt: " + createdAt.Key())
+		return nil, fmt.Errorf("findNextBeforeExecutedAt %s: %w", createdAt.Key(), ErrChildNotFound)
 	}
 
 	for node.next != nil && node.next.PositionedAt().After(executedAt) {
 		node = node.next
 	}
 
-	return node
+	return node, nil
 }
 
 func (a *RGATreeList) release(node *RGATreeListNode) {
@@ -325,8 +328,12 @@ func (a *RGATreeList) insertAfter(
 	prevCreatedAt *time.Ticket,
 	value Element,
 	executedAt *time.Ticket,
-) {
-	prevNode := a.findNextBeforeExecutedAt(prevCreatedAt, executedAt)
+) error {
+	prevNode, err := a.findNextBeforeExecutedAt(prevCreatedAt, executedAt)
+	if err != nil {
+		return err
+	}
+
 	newNode := newRGATreeListNodeAfter(prevNode, value)
 	if prevNode == a.last {
 		a.last = newNode
@@ -334,4 +341,5 @@ func (a *RGATreeList) insertAfter(
 
 	a.nodeMapByIndex.InsertAfter(prevNode.indexNode, newNode.indexNode)
 	a.nodeMapByCreatedAt[value.CreatedAt().Key()] = newNode
+	return nil
 }
diff --git a/pkg/document/crdt/tree.go b/pkg/document/crdt/tree.go
index 0e089467..efd14500 100644
--- a/pkg/document/crdt/tree.go
+++ b/pkg/document/crdt/tree.go
@@ -117,7 +117,7 @@ func NewTreeNode(pos *TreePos, nodeType string, attributes *RHT, value ...string
 
 // Type returns the type of the Node.
 func (n *TreeNode) Type() string {
-	return string(n.IndexTreeNode.Type)
+	return n.IndexTreeNode.Type
 }
 
 // Len returns the length of the Node.
diff --git a/pkg/document/document.go b/pkg/document/document.go
index 985dd2f6..d9ca6b2d 100644
--- a/pkg/document/document.go
+++ b/pkg/document/document.go
@@ -43,6 +43,10 @@ const (
 	// enabling real-time synchronization.
 	WatchedEvent DocEventType = "watched"
 
+	// UnwatchedEvent means that the client has disconnected from the server,
+	// disabling real-time synchronization.
+	UnwatchedEvent DocEventType = "unwatched"
+
 	// PresenceChangedEvent means that the presences of the clients who are editing
 	// the document have changed.
 	PresenceChangedEvent DocEventType = "presence-changed"
@@ -282,48 +286,50 @@ func (d *Document) ensureClone() error {
 	return nil
 }
 
-// Presences returns the presence map of this document.
-func (d *Document) Presences() map[string]innerpresence.Presence {
-	// TODO(hackerwins): We need to use client key instead of actor ID for exposing presence.
-	presences := make(map[string]innerpresence.Presence)
-	d.doc.presences.Range(func(key string, value innerpresence.Presence) bool {
-		presences[key] = value
-		return true
-	})
-	return presences
+// MyPresence returns the presence of the actor.
+func (d *Document) MyPresence() innerpresence.Presence {
+	return d.doc.MyPresence()
 }
 
 // Presence returns the presence of the given client.
+// If the client is not online, it returns nil.
 func (d *Document) Presence(clientID string) innerpresence.Presence {
 	return d.doc.Presence(clientID)
 }
 
-// MyPresence returns the presence of the actor.
-func (d *Document) MyPresence() innerpresence.Presence {
-	return d.doc.MyPresence()
+// PresenceForTest returns the presence of the given client
+// regardless of whether the client is online or not.
+func (d *Document) PresenceForTest(clientID string) innerpresence.Presence {
+	return d.doc.PresenceForTest(clientID)
 }
 
-// SetOnlineClientSet sets the online client set.
-func (d *Document) SetOnlineClientSet(clientIDs ...string) {
-	d.doc.SetOnlineClientSet(clientIDs...)
+// Presences returns the presence map of online clients.
+func (d *Document) Presences() map[string]innerpresence.Presence {
+	// TODO(hackerwins): We need to use client key instead of actor ID for exposing presence.
+	return d.doc.Presences()
+}
+
+// AllPresences returns the presence map of all clients
+// regardless of whether the client is online or not.
+func (d *Document) AllPresences() map[string]innerpresence.Presence {
+	return d.doc.AllPresences()
 }
 
-// AddOnlineClient adds the given client to the online client set.
+// SetOnlineClients sets the online clients.
+func (d *Document) SetOnlineClients(clientIDs ...string) {
+	d.doc.SetOnlineClients(clientIDs...)
+}
+
+// AddOnlineClient adds the given client to the online clients.
 func (d *Document) AddOnlineClient(clientID string) {
 	d.doc.AddOnlineClient(clientID)
 }
 
-// RemoveOnlineClient removes the given client from the online client set.
+// RemoveOnlineClient removes the given client from the online clients.
 func (d *Document) RemoveOnlineClient(clientID string) {
 	d.doc.RemoveOnlineClient(clientID)
 }
 
-// OnlinePresence returns the presence of the given client. If the client is not
-// online, it returns nil.
-func (d *Document) OnlinePresence(clientID string) innerpresence.Presence {
-	return d.doc.OnlinePresence(clientID)
-}
-
 // Events returns the events of this document.
 func (d *Document) Events() <-chan DocEvent {
 	return d.events
diff --git a/pkg/document/innerpresence/presence.go b/pkg/document/innerpresence/presence.go
index 7376d0f0..fb1168a0 100644
--- a/pkg/document/innerpresence/presence.go
+++ b/pkg/document/innerpresence/presence.go
@@ -50,13 +50,13 @@ func (m *Map) Range(f func(clientID string, presence Presence) bool) {
 }
 
 // Load returns the presence for the given clientID.
-func (m *Map) Load(clientID string) (Presence, bool) {
+func (m *Map) Load(clientID string) Presence {
 	presence, ok := m.presences.Load(clientID)
 	if !ok {
-		return nil, false
+		return nil
 	}
 
-	return presence.(Presence), true
+	return presence.(Presence)
 }
 
 // LoadOrStore returns the existing presence if exists.
@@ -140,6 +140,9 @@ func (p Presence) Clear() {
 
 // DeepCopy copies itself deeply.
 func (p Presence) DeepCopy() Presence {
+	if p == nil {
+		return nil
+	}
 	clone := make(map[string]string)
 	for k, v := range p {
 		clone[k] = v
diff --git a/pkg/document/internal_document.go b/pkg/document/internal_document.go
index e4ce0d8f..f71cbd43 100644
--- a/pkg/document/internal_document.go
+++ b/pkg/document/internal_document.go
@@ -257,17 +257,29 @@ func (d *InternalDocument) ApplyChanges(changes ...*change.Change) ([]DocEvent,
 		if c.PresenceChange() != nil {
 			clientID := c.ID().ActorID().String()
 			if _, ok := d.onlineClients.Load(clientID); ok {
-				event := DocEvent{
-					Type: PresenceChangedEvent,
-					Presences: map[string]innerpresence.Presence{
-						clientID: c.PresenceChange().Presence,
-					},
+				switch c.PresenceChange().ChangeType {
+				case innerpresence.Put:
+					eventType := PresenceChangedEvent
+					if !d.presences.Has(clientID) {
+						eventType = WatchedEvent
+					}
+					event := DocEvent{
+						Type: eventType,
+						Presences: map[string]innerpresence.Presence{
+							clientID: c.PresenceChange().Presence,
+						},
+					}
+					events = append(events, event)
+				case innerpresence.Clear:
+					event := DocEvent{
+						Type: UnwatchedEvent,
+						Presences: map[string]innerpresence.Presence{
+							clientID: d.Presence(clientID),
+						},
+					}
+					events = append(events, event)
+					d.RemoveOnlineClient(clientID)
 				}
-
-				if !d.presences.Has(clientID) {
-					event.Type = WatchedEvent
-				}
-				events = append(events, event)
 			}
 		}
 
@@ -283,34 +295,56 @@ func (d *InternalDocument) ApplyChanges(changes ...*change.Change) ([]DocEvent,
 
 // MyPresence returns the presence of the actor currently editing the document.
 func (d *InternalDocument) MyPresence() innerpresence.Presence {
-	p := d.presences.LoadOrStore(d.changeID.ActorID().String(), innerpresence.NewPresence())
+	if d.status != StatusAttached {
+		return innerpresence.NewPresence()
+	}
+	p := d.presences.Load(d.changeID.ActorID().String())
 	return p.DeepCopy()
 }
 
-// Presences returns the map of presences of the actors currently editing the document.
-func (d *InternalDocument) Presences() *innerpresence.Map {
-	return d.presences
-}
-
-// OnlinePresence returns the presence of the given client. If the client is not
-// online, it returns nil.
-func (d *InternalDocument) OnlinePresence(clientID string) innerpresence.Presence {
+// Presence returns the presence of the given client.
+// If the client is not online, it returns nil.
+func (d *InternalDocument) Presence(clientID string) innerpresence.Presence {
 	if _, ok := d.onlineClients.Load(clientID); !ok {
 		return nil
 	}
 
-	presence, _ := d.presences.Load(clientID)
-	return presence
+	return d.presences.Load(clientID).DeepCopy()
 }
 
-// Presence returns the presence of the given client.
-func (d *InternalDocument) Presence(clientID string) innerpresence.Presence {
-	presence, _ := d.presences.Load(clientID)
-	return presence
+// PresenceForTest returns the presence of the given client
+// regardless of whether the client is online or not.
+func (d *InternalDocument) PresenceForTest(clientID string) innerpresence.Presence {
+	return d.presences.Load(clientID).DeepCopy()
+}
+
+// Presences returns the presence map of online clients.
+func (d *InternalDocument) Presences() map[string]innerpresence.Presence {
+	presences := make(map[string]innerpresence.Presence)
+	d.onlineClients.Range(func(key, value interface{}) bool {
+		p := d.presences.Load(key.(string))
+		if p == nil {
+			return true
+		}
+		presences[key.(string)] = p.DeepCopy()
+		return true
+	})
+	return presences
+}
+
+// AllPresences returns the presence map of all clients
+// regardless of whether the client is online or not.
+func (d *InternalDocument) AllPresences() map[string]innerpresence.Presence {
+	presences := make(map[string]innerpresence.Presence)
+	d.presences.Range(func(key string, value innerpresence.Presence) bool {
+		presences[key] = value.DeepCopy()
+		return true
+	})
+	return presences
 }
 
-// SetOnlineClientSet sets the online client set.
-func (d *InternalDocument) SetOnlineClientSet(ids ...string) {
+// SetOnlineClients sets the online clients.
+func (d *InternalDocument) SetOnlineClients(ids ...string) {
 	d.onlineClients.Range(func(key, value interface{}) bool {
 		d.onlineClients.Delete(key)
 		return true
@@ -321,12 +355,12 @@ func (d *InternalDocument) SetOnlineClientSet(ids ...string) {
 	}
 }
 
-// AddOnlineClient adds the given client to the online client set.
+// AddOnlineClient adds the given client to the online clients.
 func (d *InternalDocument) AddOnlineClient(clientID string) {
 	d.onlineClients.Store(clientID, true)
 }
 
-// RemoveOnlineClient removes the given client from the online client set.
+// RemoveOnlineClient removes the given client from the online clients.
 func (d *InternalDocument) RemoveOnlineClient(clientID string) {
 	d.onlineClients.Delete(clientID)
 }
diff --git a/pkg/document/json/array.go b/pkg/document/json/array.go
index 7f7c0bca..5d13de46 100644
--- a/pkg/document/json/array.go
+++ b/pkg/document/json/array.go
@@ -214,7 +214,9 @@ func (p *Array) insertAfterInternal(
 		ticket,
 	))
 
-	p.InsertAfter(prevCreatedAt, value)
+	if err = p.InsertAfter(prevCreatedAt, value); err != nil {
+		panic(err)
+	}
 	p.context.RegisterElement(value)
 
 	return elem
@@ -223,7 +225,10 @@ func (p *Array) insertAfterInternal(
 func (p *Array) moveBeforeInternal(nextCreatedAt, createdAt *time.Ticket) {
 	ticket := p.context.IssueTimeTicket()
 
-	prevCreatedAt := p.FindPrevCreatedAt(nextCreatedAt)
+	prevCreatedAt, err := p.FindPrevCreatedAt(nextCreatedAt)
+	if err != nil {
+		panic(err)
+	}
 
 	p.context.Push(operations.NewMove(
 		p.Array.CreatedAt(),
@@ -232,5 +237,7 @@ func (p *Array) moveBeforeInternal(nextCreatedAt, createdAt *time.Ticket) {
 		ticket,
 	))
 
-	p.MoveAfter(prevCreatedAt, createdAt, ticket)
+	if err = p.MoveAfter(prevCreatedAt, createdAt, ticket); err != nil {
+		panic(err)
+	}
 }
diff --git a/pkg/document/operations/add.go b/pkg/document/operations/add.go
index d2e1ec58..9ea18d5d 100644
--- a/pkg/document/operations/add.go
+++ b/pkg/document/operations/add.go
@@ -64,7 +64,10 @@ func (o *Add) Execute(root *crdt.Root) error {
 	if err != nil {
 		return err
 	}
-	obj.InsertAfter(o.prevCreatedAt, value)
+
+	if err = obj.InsertAfter(o.prevCreatedAt, value); err != nil {
+		return err
+	}
 
 	root.RegisterElement(value)
 	return nil
diff --git a/pkg/document/operations/move.go b/pkg/document/operations/move.go
index 26500629..5207b8ec 100644
--- a/pkg/document/operations/move.go
+++ b/pkg/document/operations/move.go
@@ -60,9 +60,7 @@ func (o *Move) Execute(root *crdt.Root) error {
 		return ErrNotApplicableDataType
 	}
 
-	obj.MoveAfter(o.prevCreatedAt, o.createdAt, o.executedAt)
-
-	return nil
+	return obj.MoveAfter(o.prevCreatedAt, o.createdAt, o.executedAt)
 }
 
 // CreatedAt returns the creation time of the target element.
diff --git a/pkg/document/operations/remove.go b/pkg/document/operations/remove.go
index dce83903..4abf3cdc 100644
--- a/pkg/document/operations/remove.go
+++ b/pkg/document/operations/remove.go
@@ -53,7 +53,10 @@ func (o *Remove) Execute(root *crdt.Root) error {
 
 	switch parent := parentElem.(type) {
 	case crdt.Container:
-		elem := parent.DeleteByCreatedAt(o.createdAt, o.executedAt)
+		elem, err := parent.DeleteByCreatedAt(o.createdAt, o.executedAt)
+		if err != nil {
+			return err
+		}
 		if elem != nil {
 			root.RegisterRemovedElementPair(parent, elem)
 		}
diff --git a/pkg/index/tree.go b/pkg/index/tree.go
index d0b29ab6..468ff3a3 100644
--- a/pkg/index/tree.go
+++ b/pkg/index/tree.go
@@ -107,21 +107,6 @@ func postorderTraversal[V Value](node *Node[V], callback func(node *Node[V], dep
 	callback(node, depth)
 }
 
-// postorderTraversalAll traverses the whole tree (include tombstones) with postorder traversal.
-func postorderTraversalAll[V Value](node *Node[V], callback func(node *Node[V], depth int) error, depth int) error {
-	if node == nil {
-		return nil
-	}
-
-	for _, child := range node.children {
-		if err := postorderTraversalAll(child, callback, depth+1); err != nil {
-			return err
-		}
-	}
-
-	return callback(node, depth)
-}
-
 // nodesBetween iterates the nodes between the given range.
 // If the given range is collapsed, the callback is not called.
 // It traverses the tree with postorder traversal.
@@ -193,11 +178,6 @@ func Traverse[V Value](tree *Tree[V], callback func(node *Node[V], depth int)) {
 	postorderTraversal(tree.root, callback, 0)
 }
 
-// TraverseAll traverses the whole tree (include tombstones) with postorder traversal.
-func TraverseAll[V Value](tree *Tree[V], callback func(node *Node[V], depth int) error) error {
-	return postorderTraversalAll(tree.root, callback, 0)
-}
-
 // Value represents the data stored in the nodes of Tree.
 type Value interface {
 	IsRemoved() bool
diff --git a/server/backend/database/database.go b/server/backend/database/database.go
index 2d4922fe..894f89e9 100644
--- a/server/backend/database/database.go
+++ b/server/backend/database/database.go
@@ -47,9 +47,6 @@ var (
 	// ErrDocumentNotFound is returned when the document could not be found.
 	ErrDocumentNotFound = errors.New("document not found")
 
-	// ErrClientDocNotFound is returned when mapping between client and document could not be found.
-	ErrClientDocNotFound = errors.New("client document not found")
-
 	// ErrConflictOnUpdate is returned when a conflict occurs during update.
 	ErrConflictOnUpdate = errors.New("conflict on update")
 
diff --git a/server/backend/database/memory/database.go b/server/backend/database/memory/database.go
index 93882c31..1a880cf2 100644
--- a/server/backend/database/memory/database.go
+++ b/server/backend/database/memory/database.go
@@ -947,7 +947,7 @@ func (d *DB) CreateSnapshotInfo(
 	docID types.ID,
 	doc *document.InternalDocument,
 ) error {
-	snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.Presences())
+	snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.AllPresences())
 	if err != nil {
 		return err
 	}
diff --git a/server/backend/database/mongo/client.go b/server/backend/database/mongo/client.go
index bdcdc460..6465c72b 100644
--- a/server/backend/database/mongo/client.go
+++ b/server/backend/database/mongo/client.go
@@ -1031,7 +1031,7 @@ func (c *Client) CreateSnapshotInfo(
 	if err != nil {
 		return err
 	}
-	snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.Presences())
+	snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.AllPresences())
 	if err != nil {
 		return err
 	}
diff --git a/server/backend/database/project_info.go b/server/backend/database/project_info.go
index 2be73d0d..8c5fe7ec 100644
--- a/server/backend/database/project_info.go
+++ b/server/backend/database/project_info.go
@@ -81,22 +81,6 @@ func NewProjectInfo(name string, owner types.ID, clientDeactivateThreshold strin
 	}
 }
 
-// ToProjectInfo converts the given types.Project to ProjectInfo.
-func ToProjectInfo(project *types.Project) *ProjectInfo {
-	return &ProjectInfo{
-		ID:                        project.ID,
-		Name:                      project.Name,
-		Owner:                     project.Owner,
-		PublicKey:                 project.PublicKey,
-		SecretKey:                 project.SecretKey,
-		AuthWebhookURL:            project.AuthWebhookURL,
-		AuthWebhookMethods:        project.AuthWebhookMethods,
-		ClientDeactivateThreshold: project.ClientDeactivateThreshold,
-		CreatedAt:                 project.CreatedAt,
-		UpdatedAt:                 project.UpdatedAt,
-	}
-}
-
 // DeepCopy returns a deep copy of the ProjectInfo.
 func (i *ProjectInfo) DeepCopy() *ProjectInfo {
 	if i == nil {
diff --git a/server/packs/pushpull.go b/server/packs/pushpull.go
index 2eb0b95a..e3c5151e 100644
--- a/server/packs/pushpull.go
+++ b/server/packs/pushpull.go
@@ -157,7 +157,7 @@ func pullSnapshot(
 	}
 	cpAfterPull := cpAfterPush.NextServerSeq(docInfo.ServerSeq)
 
-	snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.Presences())
+	snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.AllPresences())
 	if err != nil {
 		return nil, err
 	}
diff --git a/server/rpc/admin_server.go b/server/rpc/admin_server.go
index 3487b1ee..340cff51 100644
--- a/server/rpc/admin_server.go
+++ b/server/rpc/admin_server.go
@@ -254,7 +254,7 @@ func (s *adminServer) GetSnapshotMeta(
 		return nil, err
 	}
 
-	snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.Presences())
+	snapshot, err := converter.SnapshotToBytes(doc.RootObject(), doc.AllPresences())
 	if err != nil {
 		return nil, err
 	}
diff --git a/server/users/users.go b/server/users/users.go
index cf158a0a..e24d383f 100644
--- a/server/users/users.go
+++ b/server/users/users.go
@@ -38,7 +38,7 @@ func SignUp(
 		return nil, fmt.Errorf("cannot hash password: %w", err)
 	}
 
-	info, err := be.DB.CreateUserInfo(ctx, username, string(hashed))
+	info, err := be.DB.CreateUserInfo(ctx, username, hashed)
 	if err != nil {
 		return nil, err
 	}


Test patch:
diff --git a/api/types/change_summary_test.go b/api/types/change_summary_test.go
index 7a9a729d..773a3b02 100644
--- a/api/types/change_summary_test.go
+++ b/api/types/change_summary_test.go
@@ -122,7 +122,7 @@ func TestChangeSummary(t *testing.T) {
 			PageSize:  3,
 			IsForward: true,
 		}, lastSeq)
-		assert.Equal(t, int64(lastSeq+1), from)
-		assert.Equal(t, int64(lastSeq+1), to)
+		assert.Equal(t, lastSeq+1, from)
+		assert.Equal(t, lastSeq+1, to)
 	})
 }
diff --git a/pkg/document/crdt/array_test.go b/pkg/document/crdt/array_test.go
index 805708e0..920422cd 100644
--- a/pkg/document/crdt/array_test.go
+++ b/pkg/document/crdt/array_test.go
@@ -32,11 +32,14 @@ func TestArray(t *testing.T) {
 
 		a := crdt.NewArray(crdt.NewRGATreeList(), ctx.IssueTimeTicket())
 
-		a.Add(crdt.NewPrimitive("1", ctx.IssueTimeTicket()))
+		err := a.Add(crdt.NewPrimitive("1", ctx.IssueTimeTicket()))
+		assert.NoError(t, err)
 		assert.Equal(t, `["1"]`, a.Marshal())
-		a.Add(crdt.NewPrimitive("2", ctx.IssueTimeTicket()))
+		err = a.Add(crdt.NewPrimitive("2", ctx.IssueTimeTicket()))
+		assert.NoError(t, err)
 		assert.Equal(t, `["1","2"]`, a.Marshal())
-		a.Add(crdt.NewPrimitive("3", ctx.IssueTimeTicket()))
+		err = a.Add(crdt.NewPrimitive("3", ctx.IssueTimeTicket()))
+		assert.NoError(t, err)
 		assert.Equal(t, `["1","2","3"]`, a.Marshal())
 	})
 }
diff --git a/pkg/document/crdt/rga_tree_list_test.go b/pkg/document/crdt/rga_tree_list_test.go
new file mode 100644
index 00000000..8f669f84
--- /dev/null
+++ b/pkg/document/crdt/rga_tree_list_test.go
@@ -0,0 +1,72 @@
+package crdt_test
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+
+	"github.com/yorkie-team/yorkie/pkg/document/crdt"
+	"github.com/yorkie-team/yorkie/test/helper"
+)
+
+func TestRGATreeList(t *testing.T) {
+	t.Run("rga_tree_list operations test", func(t *testing.T) {
+		root := helper.TestRoot()
+		ctx := helper.TextChangeContext(root)
+
+		elements := crdt.NewRGATreeList()
+
+		var err error
+		for _, v := range []string{"1", "2", "3"} {
+			err = elements.Add(crdt.NewPrimitive(v, ctx.IssueTimeTicket()))
+			assert.NoError(t, err)
+		}
+		assert.Equal(t, `["1","2","3"]`, elements.Marshal())
+
+		nodes := elements.Nodes()
+		assert.Equal(t, len(nodes), 3)
+
+		targetElement, err := elements.Get(1)
+		assert.NoError(t, err)
+		assert.Equal(t, `"2"`, targetElement.Element().Marshal())
+
+		prevCreatedAt, err := elements.FindPrevCreatedAt(targetElement.CreatedAt())
+		assert.NoError(t, err)
+		assert.Equal(t, prevCreatedAt.Compare(targetElement.CreatedAt()), -1)
+
+		err = elements.MoveAfter(targetElement.CreatedAt(), prevCreatedAt, ctx.IssueTimeTicket())
+		assert.NoError(t, err)
+		assert.Equal(t, `["2","1","3"]`, elements.Marshal())
+
+		_, err = elements.DeleteByCreatedAt(targetElement.CreatedAt(), ctx.IssueTimeTicket())
+		assert.NoError(t, err)
+		assert.Equal(t, `["1","3"]`, elements.Marshal())
+
+		_, err = elements.Delete(1, ctx.IssueTimeTicket())
+		assert.NoError(t, err)
+		assert.Equal(t, `["1"]`, elements.Marshal())
+
+	})
+
+	t.Run("invalid createdAt test", func(t *testing.T) {
+		root := helper.TestRoot()
+		ctx := helper.TextChangeContext(root)
+
+		validCreatedAt, invalidCreatedAt := ctx.IssueTimeTicket(), ctx.IssueTimeTicket()
+		elements := crdt.NewRGATreeList()
+		err := elements.Add(crdt.NewPrimitive("1", validCreatedAt))
+		assert.NoError(t, err)
+
+		_, err = elements.DeleteByCreatedAt(invalidCreatedAt, ctx.IssueTimeTicket())
+		assert.ErrorIs(t, err, crdt.ErrChildNotFound)
+
+		err = elements.MoveAfter(validCreatedAt, invalidCreatedAt, ctx.IssueTimeTicket())
+		assert.ErrorIs(t, err, crdt.ErrChildNotFound)
+
+		err = elements.MoveAfter(invalidCreatedAt, validCreatedAt, ctx.IssueTimeTicket())
+		assert.ErrorIs(t, err, crdt.ErrChildNotFound)
+
+		_, err = elements.FindPrevCreatedAt(invalidCreatedAt)
+		assert.ErrorIs(t, err, crdt.ErrChildNotFound)
+	})
+}
diff --git a/pkg/document/crdt/root_test.go b/pkg/document/crdt/root_test.go
index 64e5c77f..df8f69a6 100644
--- a/pkg/document/crdt/root_test.go
+++ b/pkg/document/crdt/root_test.go
@@ -36,15 +36,18 @@ func TestRoot(t *testing.T) {
 	t.Run("garbage collection for array test", func(t *testing.T) {
 		root := helper.TestRoot()
 		ctx := helper.TextChangeContext(root)
-		array := crdt.NewArray(crdt.NewRGATreeList(), ctx.IssueTimeTicket())
 
-		array.Add(crdt.NewPrimitive(0, ctx.IssueTimeTicket()))
-		array.Add(crdt.NewPrimitive(1, ctx.IssueTimeTicket()))
-		array.Add(crdt.NewPrimitive(2, ctx.IssueTimeTicket()))
+		array := crdt.NewArray(crdt.NewRGATreeList(), ctx.IssueTimeTicket())
+		var err error
+		for _, v := range []int{0, 1, 2} {
+			err = array.Add(crdt.NewPrimitive(v, ctx.IssueTimeTicket()))
+			assert.NoError(t, err)
+		}
 		assert.Equal(t, "[0,1,2]", array.Marshal())
 
 		targetElement, _ := array.Get(1)
-		array.DeleteByCreatedAt(targetElement.CreatedAt(), ctx.IssueTimeTicket())
+		_, err = array.DeleteByCreatedAt(targetElement.CreatedAt(), ctx.IssueTimeTicket())
+		assert.NoError(t, err)
 		root.RegisterRemovedElementPair(array, targetElement)
 		assert.Equal(t, "[0,2]", array.Marshal())
 		assert.Equal(t, 1, root.GarbageLen())
@@ -182,10 +185,14 @@ func TestRoot(t *testing.T) {
 
 		obj := root.Object()
 		obj.Set("1", crdt.NewPrimitive(1, ctx.IssueTimeTicket()))
-		arr := crdt.NewArray(crdt.NewRGATreeList(), ctx.IssueTimeTicket()).
-			Add(crdt.NewPrimitive(1, ctx.IssueTimeTicket())).
-			Add(crdt.NewPrimitive(2, ctx.IssueTimeTicket())).
-			Add(crdt.NewPrimitive(3, ctx.IssueTimeTicket()))
+
+		arr := crdt.NewArray(crdt.NewRGATreeList(), ctx.IssueTimeTicket())
+		var err error
+		for _, v := range []int{1, 2, 3} {
+			err = arr.Add(crdt.NewPrimitive(v, ctx.IssueTimeTicket()))
+			assert.NoError(t, err)
+		}
+
 		obj.Set("2", arr)
 		obj.Set("3", crdt.NewPrimitive(3, ctx.IssueTimeTicket()))
 		assert.Equal(t, `{"1":1,"2":[1,2,3],"3":3}`, root.Object().Marshal())
diff --git a/test/bench/grpc_bench_test.go b/test/bench/grpc_bench_test.go
index 05650a64..254bed7f 100644
--- a/test/bench/grpc_bench_test.go
+++ b/test/bench/grpc_bench_test.go
@@ -33,7 +33,6 @@ import (
 	"github.com/yorkie-team/yorkie/client"
 	"github.com/yorkie-team/yorkie/pkg/document"
 	"github.com/yorkie-team/yorkie/pkg/document/json"
-	"github.com/yorkie-team/yorkie/pkg/document/key"
 	"github.com/yorkie-team/yorkie/pkg/document/presence"
 	"github.com/yorkie-team/yorkie/server"
 	"github.com/yorkie-team/yorkie/server/backend/database"
@@ -105,7 +104,7 @@ func benchmarkUpdateProject(ctx context.Context, b *testing.B, cnt int, adminCli
 	for i := 0; i < cnt; i++ {
 		name := fmt.Sprintf("name%d", i)
 		authWebhookURL := fmt.Sprintf("http://authWebhookURL%d", i)
-		authWebhookMethods := []string{}
+		var authWebhookMethods []string
 		for _, m := range types.AuthMethods() {
 			authWebhookMethods = append(authWebhookMethods, string(m))
 		}
@@ -203,7 +202,7 @@ func BenchmarkRPC(b *testing.B) {
 
 		ctx := context.Background()
 
-		d1 := document.New(key.Key(helper.TestDocKey(b)))
+		d1 := document.New(helper.TestDocKey(b))
 		err := c1.Attach(ctx, d1)
 		assert.NoError(b, err)
 		testKey1 := "testKey1"
@@ -213,7 +212,7 @@ func BenchmarkRPC(b *testing.B) {
 		})
 		assert.NoError(b, err)
 
-		d2 := document.New(key.Key(helper.TestDocKey(b)))
+		d2 := document.New(helper.TestDocKey(b))
 		err = c2.Attach(ctx, d2)
 		assert.NoError(b, err)
 		testKey2 := "testKey2"
@@ -268,8 +267,8 @@ func BenchmarkRPC(b *testing.B) {
 				defer cleanupClients(b, clients)
 
 				ctx := context.Background()
-				doc1 := document.New(key.Key(helper.TestDocKey(b)))
-				doc2 := document.New(key.Key(helper.TestDocKey(b)))
+				doc1 := document.New(helper.TestDocKey(b))
+				doc2 := document.New(helper.TestDocKey(b))
 
 				err := doc1.Update(func(root *json.Object, p *presence.Presence) error {
 					text := root.SetNewText("k1")
diff --git a/test/integration/document_test.go b/test/integration/document_test.go
index 2a5981c3..c190fe9a 100644
--- a/test/integration/document_test.go
+++ b/test/integration/document_test.go
@@ -30,7 +30,6 @@ import (
 	"github.com/yorkie-team/yorkie/pkg/document"
 	"github.com/yorkie-team/yorkie/pkg/document/innerpresence"
 	"github.com/yorkie-team/yorkie/pkg/document/json"
-	"github.com/yorkie-team/yorkie/pkg/document/key"
 	"github.com/yorkie-team/yorkie/pkg/document/presence"
 	"github.com/yorkie-team/yorkie/test/helper"
 )
@@ -68,7 +67,7 @@ func TestDocument(t *testing.T) {
 		assert.True(t, doc2.IsAttached())
 		assert.Equal(t, `{"k1":"v2"}`, doc2.Marshal())
 
-		doc3 := document.New(key.Key("invalid$key"))
+		doc3 := document.New("invalid$key")
 		err = c1.Attach(ctx, doc3)
 		assert.Error(t, err)
 	})
diff --git a/test/integration/presence_test.go b/test/integration/presence_test.go
index 1ff65809..e2b37aa6 100644
--- a/test/integration/presence_test.go
+++ b/test/integration/presence_test.go
@@ -58,14 +58,14 @@ func TestPresence(t *testing.T) {
 			p.Set("updated", "true")
 			return nil
 		}))
-		encoded, err := gojson.Marshal(d1.Presences())
+		encoded, err := gojson.Marshal(d1.AllPresences())
 		assert.NoError(t, err)
 		assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"true"}}`, c1.ID()), string(encoded))
 
 		// 03 Sync documents and check that the presence is updated on the other client
 		assert.NoError(t, c1.Sync(ctx))
 		assert.NoError(t, c2.Sync(ctx))
-		encoded, err = gojson.Marshal(d2.Presences())
+		encoded, err = gojson.Marshal(d2.AllPresences())
 		assert.NoError(t, err)
 		assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"true"},"%s":{}}`, c1.ID(), c2.ID()), string(encoded))
 	})
@@ -88,14 +88,14 @@ func TestPresence(t *testing.T) {
 				return nil
 			}))
 		}
-		encoded, err := gojson.Marshal(d1.Presences())
+		encoded, err := gojson.Marshal(d1.AllPresences())
 		assert.NoError(t, err)
 		assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"9"}}`, c1.ID()), string(encoded))
 
 		// 03 Sync documents and check that the presence is updated on the other client
 		assert.NoError(t, c1.Sync(ctx))
 		assert.NoError(t, c2.Sync(ctx))
-		encoded, err = gojson.Marshal(d2.Presences())
+		encoded, err = gojson.Marshal(d2.AllPresences())
 		assert.NoError(t, err)
 		assert.Equal(t, fmt.Sprintf(`{"%s":{"updated":"9"},"%s":{}}`, c1.ID(), c2.ID()), string(encoded))
 	})
@@ -112,15 +112,15 @@ func TestPresence(t *testing.T) {
 		// 02. Check that the presence is updated on the other client.
 		assert.NoError(t, c1.Sync(ctx))
 		assert.Equal(t, innerpresence.Presence{"key": c1.Key()}, d1.MyPresence())
-		assert.Equal(t, innerpresence.Presence{"key": c2.Key()}, d1.Presence(c2.ID().String()))
+		assert.Equal(t, innerpresence.Presence{"key": c2.Key()}, d1.PresenceForTest(c2.ID().String()))
 		assert.Equal(t, innerpresence.Presence{"key": c2.Key()}, d2.MyPresence())
-		assert.Equal(t, innerpresence.Presence{"key": c1.Key()}, d2.Presence(c1.ID().String()))
+		assert.Equal(t, innerpresence.Presence{"key": c1.Key()}, d2.PresenceForTest(c1.ID().String()))
 
 		// 03. The first client detaches the document and check that the presence is updated on the other client.
 		assert.NoError(t, c1.Detach(ctx, d1))
 		assert.NoError(t, c2.Sync(ctx))
 		assert.Equal(t, innerpresence.Presence{"key": c2.Key()}, d2.MyPresence())
-		assert.Nil(t, d2.Presence(c1.ID().String()))
+		assert.Nil(t, d2.PresenceForTest(c1.ID().String()))
 	})
 
 	t.Run("presence-related events test", func(t *testing.T) {
@@ -249,7 +249,7 @@ func TestPresence(t *testing.T) {
 						})
 					}
 
-					if len(responsePairs) == 4 {
+					if len(responsePairs) == 3 {
 						return
 					}
 				}
@@ -285,23 +285,16 @@ func TestPresence(t *testing.T) {
 
 		// 05. Unwatch the second client's document.
 		expected = append(expected, watchResponsePair{
-			Type: client.PresenceChanged,
+			Type: client.DocumentUnwatched,
 			Presences: map[string]innerpresence.Presence{
-				c2.ID().String(): nil,
+				c2.ID().String(): d2.MyPresence(),
 			},
 		})
 		assert.NoError(t, c2.Detach(ctx, d2))
 		assert.NoError(t, c1.Sync(ctx, client.WithDocKey(helper.TestDocKey(t))))
+		wgEvents.Wait()
 
-		expected = append(expected, watchResponsePair{
-			Type: client.DocumentUnwatched,
-			Presences: map[string]innerpresence.Presence{
-				c2.ID().String(): nil,
-			},
-		})
 		cancel2()
-
-		wgEvents.Wait()
 		assert.Equal(t, expected, responsePairs)
 	})