> ## Documentation Index
> Fetch the complete documentation index at: https://docs.iru.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Iru Query Language

> Iru Query Language(IQL)는 매핑된 값을 구성하기 위한 Iru의 표현식 언어로, 필드, 연산자, 문자열 함수, 조건문 및 null 안전 도우미를 제공합니다.

애플리케이션이 로그인 시 수신하는 항목([SAML 앱](/ko/identity/applications/saml-applications)의
**Subject (NameID)** 또는 [OIDC 앱](/ko/identity/applications/oidc-applications)의 **subject
및 클레임**)을 매핑할 때 각 값은 **IQL 표현식**입니다. IQL을 사용하면 사용자 프로필에서 값을
가져와 전송되기 전에 변형할 수 있습니다.

<Info>
  **IQL**(Iru Query Language)은 **매핑**을 위한 Iru의 표현식 언어로, 여러 곳에서 사용됩니다.

  * 애플리케이션이 로그인 시 수신하는 속성 및 클레임.
  * 연결된 소스가 디렉터리에 매핑하는 필드
    ([속성 매핑](/ko/identity/directory/attribute-mapping) 참조).
  * [Auto Group](/ko/identity/directory/auto-groups) 규칙 및 목록 필터.

  IQL이 사용되는 모든 곳에서 동일한 구문이 작동합니다.

  IQL은 개방형 표현식 언어 표준인 [CEL(Common Expression Language)](https://cel.dev)을
  기반으로 하므로 CEL에서 유효한 모든 표현식은 IQL에서도 유효합니다.
</Info>

입력하는 동안 매핑 편집기는 사용 가능한 필드, 함수 및 연산자를 제안하고 결과의 실시간
**미리 보기**를 표시합니다. 표현식은 편집기에서, 그리고 저장할 때 다시 **유형 검사**를
통과해야 하므로 실수가 로그인이나 동기화에 도달하기 전에 드러납니다.

## 데이터 참조

참조하는 항목은 표현식이 실행되는 위치에 따라 다릅니다.

* **애플리케이션 매핑**은 `user` 객체에서 시작하며, 이 객체의 필드는 디렉터리의
  [프로필 속성](/ko/identity/directory/schema)입니다.

  ```
  user.email
  user.username
  user.id
  ```

  기본값도 이를 반영합니다. OIDC 앱의 subject 기본값은 `user.id`이고, SAML 앱의 Subject
  기본값은 `user.username`입니다.

* **Directory Sync 매핑**은 **소스의 필드 이름**을 참조합니다. 유효한 IQL 식별자는 연결된
  시스템에서 오는 그대로 직접 작성합니다. ASCII 문자 또는 밑줄로 시작하고 나머지는 ASCII
  문자, 숫자, 밑줄로 구성됩니다.

  ```
  email_work
  first_name
  ```

  다른 이름은 정확한 이름으로 `__source`에서 읽습니다. 공백, 기호, 단일 필드 이름 안의 점,
  ASCII 이외의 문자 및 IQL 키워드가 포함됩니다. 예: `__source["Email - Work"]`.
  [IQL로 값 변환](/ko/identity/directory/directory-sync#transform-values-with-iql)을
  참조하세요.

필드 이름은 **대/소문자를 구분**합니다. 중첩된 값은 점(`user.profile.team`)으로, 목록이나
맵은 `[...]`로 접근합니다(아래에서 설명).

## 기본 구성 요소

| 형식 | 예 | 설명 |
| - | - | - |
| 필드 / 변수 | `first_name`, `user.email` | 이름으로 지정된 값 또는 값의 필드. |
| 메서드 호출 | `user.email.upperAscii()` | 값에 **대해** 호출되는 함수. |
| 인덱싱 | `parts[0]` | 0부터 시작하는 위치로 지정된 목록 요소. |
| 맵 키 | `entry["label"]` | 문자열 키에 저장된 값. |
| 그룹화 | `(a + b) * c` | 평가 순서를 제어하는 괄호. |

## 리터럴

```
"hello"                             // string (double quotes)
'hello'                             // string (single quotes)
42                                  // whole number
3.14                                // decimal number
true   false                        // booleans
null                                // null
["a", "b", "c"]                     // list
{"label": "work", "primary": true}  // map (string keys)
```

문자열은 `\n`, `\t`, `\\`, `\"`, `\uXXXX`와 같은 일반적인 이스케이프를 지원합니다.

## 연산자

<Tabs>
  <Tab title="산술">
    | 연산자 | 예 | 기능 |
    | - | - | - |
    | `+` | `first_name + " " + last_name` | 숫자를 더하거나 **문자열을 연결**합니다. |
    | `-` `*` `/` | `a * b` | 빼기, 곱하기, 나누기. |
    | `%` | `a % b` | 나머지(모듈로). |
  </Tab>

  <Tab title="비교">
    | 연산자 | 예 | 기능 |
    | - | - | - |
    | `==` `!=` | `user.domain == "acme.com"` | 같음, 같지 않음. |
    | `<` `<=` `>` `>=` | `user.level >= 3` | 순서 비교. |
  </Tab>

  <Tab title="논리">
    | 연산자 | 예 | 기능 |
    | - | - | - |
    | `&&` | `a && b` | 그리고(And). |
    | <code>\|\|</code> | <code>a \|\| b</code> | 또는(Or). |
    | `!` | `!a` | 부정(Not). |
  </Tab>

  <Tab title="멤버십 및 선택">
    | 연산자 | 예 | 기능 |
    | - | - | - |
    | `in` | `user.domain in ["acme.com", "acme.io"]` | 값이 목록에 있거나 키가 맵에 있으면 true입니다. |
    | `? :` | `name != "" ? name : "unknown"` | 조건부: 두 값 중 하나를 선택합니다. |
  </Tab>
</Tabs>

### 우선순위

가장 강하게 결합하는 것부터 가장 약한 것 순입니다. 확실하지 않으면 괄호를 추가하세요.

```
1) a.b   a()   a[i]   ()      // member access, calls, indexing, grouping
2) !a    -a                   // unary not, negation
3) *  /  %                    // multiply, divide, remainder
4) +  -                       // add, subtract (and string +)
5) <  <=  >  >=  in           // comparison and membership
6) ==  !=                     // equality
7) &&                         // and
8) ||                         // or
9) ? :                        // conditional
```

## 문자열 함수

문자열 연산은 `value.method(...)` 형식으로 값에 대한 **메서드**로 작성합니다.

| 메서드 | 예 | 기능 |
| - | - | - |
| `upperAscii` / `lowerAscii` | `user.email.lowerAscii()` | 값을 대문자 또는 소문자로 변환합니다. |
| `trim` | `first_name.trim()` | 앞뒤 공백을 제거합니다. |
| `split` | `user.email.split("@")` | 구분 기호로 목록으로 분할합니다. `[n]`으로 인덱싱합니다. |
| `replace` | `phone.replace("-", "")` | 하위 문자열의 모든 항목을 다른 문자열로 바꿉니다. |
| `contains` | `user.email.contains("+")` | 값에 하위 문자열이 포함되어 있으면 true입니다. |
| `startsWith` / `endsWith` | `user.email.endsWith("@acme.com")` | 값의 시작 또는 끝을 테스트합니다. |
| `indexOf` | `username.indexOf(".")` | 하위 문자열의 위치이며, 없으면 `-1`입니다. |
| `matches` | `user.email.matches("^[a-z]+@acme\\.com$")` | 값이 정규식과 일치하면 true입니다. |
| `size` | `user.email.split("@").size()` | 문자열 또는 목록의 길이입니다. `size(value)`로도 사용할 수 있습니다. |

## 조건문 및 기본값

조건 연산자는 값이 비어 있을 수 있을 때 대체 값을 제공하는 가장 간단한 방법입니다.

```
// Use the first name, or fall back to the username when it's empty
user.firstName != "" ? user.firstName : user.username
```

더 풍부한 null 처리를 위해 IQL에는 아래의 **optional**(null 안전) 도우미도 있습니다.

## 선택적 값

Optional 도우미를 사용하면 오류를 일으키지 않고 **누락될 수 있는** 값을 다룰 수 있습니다.
*optional*은 값을 보유하거나 비어 있습니다.

<Tabs>
  <Tab title="생성">
    ```
    optional.of(value)            // wrap a value as an optional
    optional.ofNonZeroValue(value) // empty if the value is "zero" (see below)
    optional.none()               // an empty optional
    ```

    `ofNonZeroValue`는 빈 문자열 `""`, `0`, `false`, 빈 목록 `[]`, 빈 맵 `{}`, `null`을
    비어 있는 것으로 처리합니다.
  </Tab>

  <Tab title="안전하게 접근">
    ```
    value.?field     // optional field access (yields an optional)
    list[?i]         // optional index (empty instead of an error if out of range)
    ```
  </Tab>

  <Tab title="값 꺼내기">
    ```
    opt.hasValue()          // true if the optional holds a value
    opt.value()             // the inner value (errors if empty)
    opt.orValue(fallback)   // the value, or the fallback if empty
    a.or(b)                 // the first of two optionals that has a value
    ```
  </Tab>

  <Tab title="변환">
    ```
    opt.optMap(x, expr)      // transform the inner value if present, else stay empty
    opt.optFlatMap(x, expr)  // like optMap, when expr itself returns an optional
    ```
  </Tab>
</Tabs>

```
// A default when a field is empty
optional.ofNonZeroValue(first_name).orValue("anon")

// Split an email and safely take the domain, falling back to ""
optional.ofNonZeroValue(email_work).optMap(e, e.split("@"))[?1].orValue("")
```

## 주석 및 공백

공백은 의미가 없으며, `//`는 줄 끝까지 이어지는 주석을 시작합니다.

```
user.firstName + " " + user.lastName   // build a display name
```

## 예제

```
// A field, as-is
user.email

// Upper-case the value
user.email.upperAscii()

// Build a display name from two fields
user.firstName + " " + user.lastName

// The local part of an email, before the "@"
user.email.split("@")[0]

// A field from the user's first address
user.addresses[0].city

// Choose a fallback when a field is blank
user.firstName != "" ? user.firstName : user.username

// A structured value, built as a map
{"email": user.email, "primary": true}
```

<Tip>
  게시하기 전에 매핑 편집기의 **미리 보기**를 사용하여 표현식이 예상한 결과를 생성하는지
  확인하세요. 미리 보기는 Iru가 전송하거나 저장할 정확한 값을 표시합니다.
</Tip>

## 다음 단계

<CardGroup cols={2}>
  <Card title="애플리케이션 매핑" icon="arrows-left-right" href="/ko/identity/applications/application-mapping">
    IQL 값을 애플리케이션이 수신하는 어설션 또는 토큰에 매핑합니다.
  </Card>

  <Card title="속성 매핑" icon="code" href="/ko/identity/directory/attribute-mapping">
    동일한 IQL을 사용하여 연결된 소스를 디렉터리에 매핑합니다.
  </Card>

  <Card title="자동 그룹" icon="wand-magic-sparkles" href="/ko/identity/directory/auto-groups">
    프로필 속성에 대한 IQL 규칙으로 그룹 멤버십을 구동합니다.
  </Card>

  <Card title="프로필 속성" icon="table-list" href="/ko/identity/directory/schema">
    표현식이 값을 가져오는 `user` 필드입니다.
  </Card>
</CardGroup>
