# 사용자 관리

## Content

**사용자 관리** 기능은 SpreadJS 기반 애플리케이션에서 사용자 정보를 관리하기 위한 중앙 집중식이며 확장 가능한 메커니즘을 제공합니다.
이 기능은 이름, ID, 아바타와 같은 사용자 데이터가 **스레드형 메모** 또는 **동시 편집** 도구와 같은 구성 요소 전반에서 어떻게 생성되고, 접근되며, 업데이트되는지를 정의합니다.

## 핵심 개념

**User**
**User**는 `IUser` 인터페이스를 구현하는 일반 JavaScript 객체입니다.
개별 사용자를 나타내며, 다음과 같은 주요 식별 속성을 포함합니다.

* `id` – 고유한 사용자 식별자
* `name` – 사용자의 표시 이름
* `email` *(선택)* – 연락 또는 참조 정보
* `avatar` *(선택)* – 다양한 이미지 소스 형식으로 정의할 수 있는 시각적 표현
* `color` *(선택)* – 사용자 라벨링 또는 시각적 구분에 사용되는 사용자 지정 색상

예시 사용자 객체는 다음과 같습니다.

```json
{
  id: "user1",
  name: "Alice",
  email: "alice@example.com",
  color: "#5B8FF9"
}
```

**UserManager**
`GC.Spread.Common` 네임스페이스에 포함된 `UserManager` 객체는 스프레드시트 환경 전반에서 사용자 정보에 접근하고 제어하기 위한 전역 기능을 제공합니다.

* **전역 싱글턴**으로, 인스턴스 생성이 필요하지 않습니다.
* 애플리케이션 전체에서 사용자 데이터의 저장, 조회, 업데이트 방식을 제어합니다.
* 등록된 모든 사용자와 관련 메타데이터를 관리합니다.
* 사용자 조회, 검색, 이벤트 바인딩을 위한 설정을 지원합니다.
* 런타임 중 현재 사용자 컨텍스트를 설정하거나 가져올 수 있습니다.
* 사용자 정보를 가져오기 위한 비동기 함수 `get`을 제공합니다.

`UserManager`는 SpreadJS 플랫폼 전체에서 사용자 관련 작업을 위한 공용 서비스 역할을 합니다.

## 설정 및 구성

사용자 관리를 활성화하려면 애플리케이션의 사용자 데이터를 정의하고, `UserManager`가 사용자 정보를 가져오는 방식을 구성해야 합니다.
이 구성 단계에서는 SpreadJS에 다음 내용을 알려줍니다.

* ID로 사용자 정보를 가져오는 방법
* 사용자 검색을 수행하는 방법
* (선택) 일관된 시각적 아이덴티티를 위한 색상 스킴 지정

**사용자(User) 정의**
**User**는 `IUser` 인터페이스를 따르는 JavaScript 객체로 표현됩니다.
각 사용자는 최소한 `id`와 `name`이 필요하며, `email`, `avatar`, `color`와 같은 다른 속성은 선택적으로 포함할 수 있습니다.

```json
// 예제
var users = [
  { id: "user1", name: "Alice", email: "alice@example.com" },
  { id: "user2", name: "Bob",   email: "bob@example.com" }
];
```

>type=note
> **참고:**
> avatar 속성은 URL, 데이터 URL, Blob, 로컬 파일 등 여러 이미지 소스 형식을 지원합니다.

**UserManager 구성**
`UserManager`는 일반적으로 애플리케이션 초기화 시 `configure()` 메서드를 사용하여 한 번만 구성하면 됩니다.
이 메서드는 사용자 데이터의 조회 및 검색 방식을 정의하는 구성 객체를 인수로 받습니다.

* **get(userId)** – 지정된 ID에 대한 사용자 객체를 반환하는 `Promise<IUser>`를 반환합니다. `get` 메서드는 `IUserManagerOptions`에 구성된 `get` 메서드를 호출합니다.
* **search(query)** – (선택) 사용자 선택 UI를 구현할 때 일치하는 사용자를 반환합니다.
* **colorScheme** – (선택) 아바타 또는 표시 색상이 자동 생성될 때 사용되는 사용자 지정 색상을 정의합니다.

```javascript
// 예제
const options = {
  get: async (userId) => {
        if (userId === undefined) {
            return;
        }
        return new Promise((resolve) => {
            const user = users.find(u => u.id === userId);
            resolve(user);
        });
    },
    search: async (query) => {
        return new Promise((resolve) => {
            resolve(users.filter(u => 
                        u.name.toLowerCase().includes(query.toLowerCase()) || 
                        u.email.toLowerCase().includes(query.toLowerCase())
                    ));
        });
    }
}

GC.Spread.Common.UserManager.configure(options);
```

구성이 완료되면 `UserManager`는 **전역 싱글턴**으로 동작하며, **스레드형 메모**와 같은 다른 SpreadJS 기능에서 사용자 ID를 통해 자동으로 사용자 데이터를 참조할 수 있습니다.

## UserManager 사용하기

`UserManager`가 구성된 이후에는 애플리케이션 내에서 현재 사용자 컨텍스트를 관리하고 이에 반응할 수 있습니다.
여기에는 현재 활성 사용자를 설정하거나 가져오고, 사용자 관련 이벤트를 처리하는 작업이 포함됩니다.

**현재 사용자(Current User) 설정**
`current()` 메서드는 현재 사용자의 ID를 가져오거나 설정하는 데 사용됩니다.
예시는 다음과 같습니다.

```auto
// 현재 사용자 설정
GC.Spread.Common.UserManager.current("user1");

// 현재 사용자 가져오기
var currentUserId = GC.Spread.Common.UserManager.current();
```

>type=note
> **참고:**
> 현재 사용자가 설정되지 않은 경우, `UserManager`는 기본 전역 사용자로 내장된 “**게스트(Guest)**” 사용자를 자동으로 유지합니다.

자동으로 생성된 경우, 게스트 사용자는 다음과 같은 속성을 가집니다.

* `id` – 임의로 생성된 UUID
* `name` – 문화권에 따른 표시 이름 (예: 영어 문화권에서는 “Guest”)

**사용자 이벤트 처리**
UserManager는 다음 메서드를 통해 이벤트 구독 및 관리를 지원합니다.

* `bind(event, handler)` – 이벤트에 함수를 구독합니다. 이벤트가 발생하면 핸들러가 호출됩니다.
* `unbind(event, handler?)` – 이벤트 구독을 해제합니다. 핸들러를 지정하지 않으면 해당 이벤트의 모든 핸들러가 제거됩니다.

지원되는 이벤트:

* `CurrentUserChanged` — 현재 사용자가 성공적으로 변경될 때마다 **비동기적으로 트리거**되는 이벤트입니다.

이 이벤트를 구독하면 UI 업데이트나 사용자별 데이터 새로 고침과 같이, 애플리케이션 로직에서 사용자 컨텍스트 변경에 대응할 수 있습니다.
예시는 다음과 같습니다.

```javascript
function onUserChanged(event, args) {
  console.log(`User changed: ${args.oldCurrentUser?.id} → ${args.newCurrentUser?.id}`);
}

// 이벤트 구독
GC.Spread.Common.UserManager.bind(GC.Spread.Common.Events.CurrentUserChanged, onUserChanged);

// 이벤트 구독 해제
GC.Spread.Common.UserManager.unbind(GC.Spread.Common.Events.CurrentUserChanged, onUserChanged);
```

## 아바타 처리 규칙

아바타 이미지가 제공되지 않은 경우:

* `user.name`의 첫 글자를 대문자로 변환하여 아바타 텍스트로 사용합니다.
* 아바타 배경 색상은 `user.color`가 정의되어 있으면 해당 값을 사용합니다.
* `user.color`가 지정되지 않은 경우, 사용자 ID를 기준으로 미리 정의된 색상 스킴에서 색상이 선택됩니다.

**기본 색상 스킴**
사용자 지정 `colorScheme`이 지정되지 않은 경우, SpreadJS는 다음의 20가지 기본 색상 팔레트를 사용합니다.

```auto
#3B82F6  #10B981  #8B5CF6  #F59E0B  #EF4444
#06B6D4  #EC4899  #84CC16  #6366F1  #14B8A6
#F97316  #A855F7  #22C55E  #EAB308  #F43F5E
#0EA5E9  #D946EF  #65A30D  #DC2626  #7C3AED
```

개발자는 `UserManager`를 구성할 때 `colorScheme` 배열을 지정하여 기본 스킴을 재정의할 수 있습니다.

>type=note
> **참고:**
> 색상 스킴의 색상 개수는 유동적이며, 알고리즘이 자동으로 사용자 ID를 사용 가능한 항목에 매핑합니다.

```javascript
// 예제
GC.Spread.Common.UserManager.configure({
  get: async (userId) => {
        if (userId === undefined) {
            return;
        }
        return new Promise((resolve) => {
            const user = users.find(u => u.id === userId);
            resolve(user);
        });
    },
    search: async (query) => {
        return new Promise((resolve) => {
            resolve(users.filter(u => 
                        u.name.toLowerCase().includes(query.toLowerCase()) || 
                        u.email.toLowerCase().includes(query.toLowerCase())
                    ));
        });
    }
  colorScheme: ["#5B8FF9", "#61DDAA", "#F6BD16"] // 사용자 지정 색상
});
```