Django에서 재사용 가능한 템플릿 구성 요소를 만드는 간단한 방법.
먼저 구성 요소를 등록해야합니다
from django_web_components import component
@ component . register ( "card" )
class Card ( component . Component ):
template_name = "components/card.html"구성 요소의 템플릿 :
# components/card.html
{% load components %}
< div class =" card " >
< div class =" card-header " >
{% render_slot slots.header %}
</ div >
< div class =" card-body " >
< h5 class =" card-title " >
{% render_slot slots.title %}
</ h5 >
{% render_slot slots.inner_block %}
</ div >
</ div >이제이 구성 요소를 다음과 같이 렌더링 할 수 있습니다.
{% load components %}
{% card %}
{% slot header %} Featured {% endslot %}
{% slot title %} Card title {% endslot %}
< p > Some quick example text to build on the card title and make up the bulk of the card's content. </ p >
< a href =" # " class =" btn btn-primary " > Go somewhere </ a >
{% endcard %}이로 인해 다음 HTML이 렌더링됩니다.
< div class =" card " >
< div class =" card-header " >
Featured
</ div >
< div class =" card-body " >
< h5 class =" card-title " >
Card title
</ h5 >
< p > Some quick example text to build on the card title and make up the bulk of the card's content. </ p >
< a href =" # " class =" btn btn-primary " > Go somewhere </ a >
</ div >
</ div > pip install django-web-components
그런 다음 django_web_components INSTALLED_APPS 에 추가하십시오.
INSTALLED_APPS = [
...,
"django_web_components" ,
] 각 템플릿에서 {% load components %} 사용하지 않으려면 설정 내부의 builtins 목록에 태그를 추가 할 수 있습니다.
TEMPLATES = [
{
...,
"OPTIONS" : {
"context_processors" : [
...
],
"builtins" : [
"django_web_components.templatetags.components" ,
],
},
},
]라이브러리는 Python 3.8+ 및 Django 3.2+를 지원합니다.
| 파이썬 버전 | 장고 버전 |
|---|---|
3.12 | 5.0 , 4.2 |
3.11 | 5.0 , 4.2 , 4.1 |
3.10 | 5.0 , 4.2 , 4.1 , 4.0 , 3.2 |
3.9 | 4.2 , 4.1 , 4.0 , 3.2 |
3.8 | 4.2 , 4.1 , 4.0 , 3.2 |
구성 요소 작성에는 클래스 기반 구성 요소 및 기능 기반 구성 요소의 두 가지 접근 방식이 있습니다.
from django_web_components import component
@ component . register ( "alert" )
class Alert ( component . Component ):
# You may also override the get_template_name() method instead
template_name = "components/alert.html"
# Extra context data will be passed to the template context
def get_context_data ( self , ** kwargs ) -> dict :
return {
"dismissible" : False ,
} 구성 요소는 render(context) 메소드를 호출하여 렌더링되며, 기본적으로 템플릿 파일을로드하여 렌더링합니다.
작은 구성 요소의 경우 구성 요소 클래스와 구성 요소의 템플릿을 모두 관리하는 것이 번거 롭을 수 있습니다. 이러한 이유로 render 메소드에서 템플릿을 직접 정의 할 수 있습니다.
from django_web_components import component
from django_web_components . template import CachedTemplate
@ component . register ( "alert" )
class Alert ( component . Component ):
def render ( self , context ) -> str :
return CachedTemplate (
"""
<div class="alert alert-primary" role="alert">
{% render_slot slots.inner_block %}
</div>
""" ,
name = "alert" ,
). render ( context ) 구성 요소는 context 를 수락하고 문자열을 반환하는 단일 함수로 정의 될 수 있습니다.
from django_web_components import component
from django_web_components . template import CachedTemplate
@ component . register
def alert ( context ):
return CachedTemplate (
"""
<div class="alert alert-primary" role="alert">
{% render_slot slots.inner_block %}
</div>
""" ,
name = "alert" ,
). render ( context )이 안내서의 예제는 구성 요소 코드와 템플릿이 같은 위치에 있기 때문에 예시를 예시하기가 쉽기 때문에 대부분 기능 기반 구성 요소를 사용하지만 원하는 방법을 자유롭게 선택할 수 있기 때문입니다.
라이브러리는 일반 Django 템플릿을 사용하여 파일에서 템플릿을로드하거나 템플릿 문자열을 사용하여 직접 템플릿 개체를 만들 수 있습니다. 두 방법 모두 지원되며 두 가지 방법 모두 장단점이 있습니다.
캐싱과 관련하여 라이브러리는 CachedTemplate 제공합니다. 이는 캐시 키로 사용되는 name 제공하는 한 Template 객체를 캐시하고 재사용 할 수 있습니다.
from django_web_components import component
from django_web_components . template import CachedTemplate
@ component . register
def alert ( context ):
return CachedTemplate (
"""
<div class="alert alert-primary" role="alert">
{% render_slot slots.inner_block %}
</div>
""" ,
name = "alert" ,
). render ( context ) 따라서 CachedTemplate 템플릿 파일이있는 캐시 로드더를 사용하는 것만 큼 빠르기 때문에 실제로 템플릿 문자열을 사용할 때 캐싱이 문제가되지 않아야합니다.
서식 지원 및 구문 강조 표시와 관련하여 템플릿 문자열에 대한 좋은 솔루션은 없습니다. Pycharm은 템플릿 문자열 전에 # language=html 주석을 추가하고 구문 강조 표시를 할 수있는 언어 주입을 지원하지만 Django 태그가 아닌 HTML 만 강조 표시되며 여전히 서식 지원이 누락됩니다. 아마도 편집자는 앞으로 이것에 대해 더 나은 지원을 더할 것이지만,이 경로를 가면 구문 강조 표시 및 서식이 누락 될 것입니다. django-components Repo에서 이에 대한 공개 대화가 있습니다. VSCODE 팀과의 대화를 전진한 Emilstenstrom에 대한 크레딧.
결국, 그것은 트레이드 오프입니다. 가장 적합한 방법을 사용하십시오.
신호와 마찬가지로 구성 요소는 어디에서나 살 수 있지만 Django가 시작시 Django가 수령해야합니다. 이 작업을 수행하는 가장 쉬운 방법은 components.py 에서 구성 요소를 정의하는 것입니다. 관련 응용 프로그램의 서브 모듈이 응용 프로그램 구성 클래스의 ready() 메소드 내에 연결하는 것입니다.
from django . apps import AppConfig
from django_web_components import component
class MyAppConfig ( AppConfig ):
...
def ready ( self ):
# Implicitly register components decorated with @component.register
from . import components # noqa
# OR explicitly register a component
component . register ( "card" , components . Card ) 기존 구성 요소를 unregister 하거나 레지스트리에서 구성 요소를 얻을 수도 있습니다.
from django_web_components import component
# Unregister a component
component . registry . unregister ( "card" )
# Get a component
component . registry . get ( "card" )
# Remove all components
component . registry . clear ()
# Get all components as a dict of name: component
component . registry . all ()등록 된 각 구성 요소에는 템플릿에서 사용할 수있는 두 개의 태그가 있습니다.
{% card %} ... {% endcard %}{% #user_profile %} . 이것은 반드시 신체가 필요하지 않은 구성 요소에 유용 할 수 있습니다.기본적으로 구성 요소는 다음 태그를 사용하여 등록됩니다.
{% <component_name> %}{% end<component_name> %}{% #<component_name> %} 이 동작은 설정에서 사용자 정의 태그 포맷터를 제공하여 변경 될 수 있습니다. 예를 들어, 블록 태그를 {% #card %} ... {% /card %} 로, 인라인 태그를 {% card %} (슬리퍼와 유사)로 변경하려면 다음 Formatter를 사용할 수 있습니다.
class ComponentTagFormatter :
def format_block_start_tag ( self , name ):
return f"# { name } "
def format_block_end_tag ( self , name ):
return f"/ { name } "
def format_inline_tag ( self , name ):
return name
# inside your settings
WEB_COMPONENTS = {
"DEFAULT_COMPONENT_TAG_FORMATTER" : "path.to.your.ComponentTagFormatter" ,
}하드 코딩 된 값 또는 변수를 허용하는 키워드 인수를 사용하여 데이터를 구성 요소로 전달할 수 있습니다.
{% with error_message="Something bad happened!" %}
{% #alert type="error" message=error_message %}
{% endwith %} 모든 속성은 템플릿 컨텍스트에서 사용할 수있는 attributes DICT에 추가됩니다.
{
"attributes" : {
"type" : " error " ,
"message" : " Something bad happened! "
}
}그런 다음 구성 요소의 템플릿에서 액세스 할 수 있습니다.
< div class =" alert alert-{{ attributes.type }} " >
{{ attributes.message }}
</ div > {{ attributes }} 사용하여 모든 속성을 직접 렌더링 할 수도 있습니다. 예를 들어 다음 구성 요소가있는 경우
{% alert id="alerts" class="font-bold" %} ... {% endalert %}모든 속성을 사용하여 렌더링 할 수 있습니다
< div {{ attributes }} >
<!-- Component content -->
</ div >이로 인해 다음 HTML이 렌더링됩니다.
< div id =" alerts " class =" font-bold " >
<!-- Component content -->
</ div > 또한 특수 문자 ( [@:_-.] )로 속성을 전달할 수도 있고 가치가없는 속성을 전달할 수도 있습니다.
{% button @click="handleClick" data-id="123" required %} ... {% endbutton %}컨텍스트에서 다음 dict를 사용할 수 있습니다.
{
"attributes" : {
"@click" : "handleClick" ,
"data-id" : "123" ,
"required" : True ,
}
} {{ attributes }} as @click="handleClick" data-id="123" required 에 의해 렌더링됩니다.
때로는 속성의 기본값을 지정하거나 추가 값을 일부 구성 요소의 속성에 병합해야 할 수도 있습니다. 라이브러리는 이에 도움이되는 merge_attrs 태그를 제공합니다.
< div {% merge_attrs attributes class =" alert " role =" alert " %} >
<!-- Component content -->
</ div >이 구성 요소가 그렇게 사용한다고 가정하면 :
{% alert class="mb-4" %} ... {% endalert %}구성 요소의 최종 렌더링 HTML은 다음과 같습니다.
< div class =" alert mb-4 " role =" alert " >
<!-- Component content -->
</ div > class 속성이 아닌 속성을 병합 할 때 merge_attrs 태그에 제공된 값은 속성의 "기본값"값으로 간주됩니다. 그러나 class 속성과 달리 이러한 속성은 주입 된 속성 값과 병합되지 않습니다. 대신, 그들은 덮어 쓸 것입니다. 예를 들어, button 구성 요소의 구현은 다음과 같습니다.
< button {% merge_attrs attributes type =" button " %} >
{% render_slot slots.inner_block %}
</ button > 버튼 구성 요소를 사용자 정의 type 으로 렌더링하려면 구성 요소를 소비 할 때 지정할 수 있습니다. 유형이 지정되지 않으면 button 유형이 사용됩니다.
{% button type="submit" %} Submit {% endbutton %} 이 예제에서 button 구성 요소의 렌더링 된 HTML은 다음과 같습니다.
< button type =" submit " >
Submit
</ button > += 연산자를 사용하여 다른 속성을 "부록"으로 취급 할 수도 있습니다.
< div {% merge_attrs attributes data-value+ =" some-value " %} >
<!-- Component content -->
</ div >이 구성 요소가 그렇게 사용한다고 가정하면 :
{% alert data-value="foo" %} ... {% endalert %}렌더링 된 HTML은 다음과 같습니다.
< div data-value =" foo some-value " >
<!-- Component content -->
</ div > 기본적으로 모든 속성은 컨텍스트 내부의 attributes 에 추가됩니다. 그러나 이것이 항상 우리가 원하는 것이 아닐 수도 있습니다. 예를 들어, 우리는 해산 할 수있는 alert 구성 요소를 갖고 싶지만 동시에 id 또는 class 와 같이 추가 속성을 루트 요소로 전달할 수 있다고 상상해보십시오. 이상적으로는 다음과 같은 구성 요소를 렌더링 할 수 있기를 원합니다.
{% alert id="alerts" dismissible %} Something went wrong! {% endalert %}이 구성 요소를 구현하는 순진한 방법은 다음과 같습니다.
< div {{ attributes }} >
{% render_slot slots.inner_block %}
{% if attributes.dismissible %}
< button type =" button " class =" btn-close " data-bs-dismiss =" alert " aria-label =" Close " > </ button >
{% endif %}
</ div > 그러나 이로 인해 dismissible 속성이 루트 요소에 포함되며, 이는 우리가 원하는 것이 아닙니다.
< div id =" alerts " dismissible >
Something went wrong!
< button type =" button " class =" btn-close " data-bs-dismiss =" alert " aria-label =" Close " > </ button >
</ div > 이상적으로 우리는 논리에서만 사용하기를 원하지만 반드시 구성 요소로 렌더링 할 필요는 없기 때문에 dismissible 속성이 attributes 과 분리되기를 원합니다.
이를 달성하려면 구성 요소를 사용하기위한 더 나은 API를 제공하기 위해 구성 요소의 컨텍스트를 조작 할 수 있습니다. 이를 수행하는 방법에는 여러 가지가 있습니다. 예를 들어 가장 의미가있는 방법을 선택하십시오.
get_context_data 무시하고 attributes 에서 dismissible 속성을 제거하고 대신 컨텍스트에서 반환 할 수 있습니다. from django_web_components import component
@ component . register ( "alert" )
class Alert ( component . Component ):
template_name = "components/alert.html"
def get_context_data ( self , ** kwargs ):
dismissible = self . attributes . pop ( "dismissible" , False )
return {
"dismissible" : dismissible ,
}render 메소드를 무시하고 컨텍스트를 조작 할 수 있습니다. from django_web_components import component
@ component . register ( "alert" )
class Alert ( component . Component ):
template_name = "components/alert.html"
def render ( self , context ):
context [ "dismissible" ] = context [ "attributes" ]. pop ( "dismissible" , False )
return super (). render ( context )위의 두 솔루션 모두 작동하며 기능 기반 구성 요소에 대해 동일한 작업을 수행 할 수 있습니다. 그런 다음 구성 요소의 템플릿이 다음과 같습니다.
< div {{ attributes }} >
{% render_slot slots.inner_block %}
{% if dismissible %}
< button type =" button " class =" btn-close " data-bs-dismiss =" alert " aria-label =" Close " > </ button >
{% endif %}
</ div >올바른 HTML을 렌더링해야합니다.
< div id =" alerts " >
Something went wrong!
< button type =" button " class =" btn-close " data-bs-dismiss =" alert " aria-label =" Close " > </ button >
</ div > 종종 "슬롯"을 통해 추가 컨텐츠를 구성 요소에 전달해야합니다. slots 컨텍스트 변수는 구성 요소로 전달되며, 이는 슬롯 이름이 키로, 슬롯을 값으로 사용하는 DITT로 구성됩니다. 그런 다음 render_slot 태그를 사용하여 구성 요소 내부의 슬롯을 렌더링 할 수 있습니다.
이 개념을 탐색하려면 alert 구성 요소에 일부 내용을 전달하고 싶다고 상상해 봅시다.
{% alert %}
< strong > Whoops! </ strong > Something went wrong!
{% endalert %} 기본적으로 해당 컨텐츠는 inner_block 이라고 불리는 기본 슬롯의 구성 요소에 사용할 수 있습니다. 그런 다음 구성 요소 내부의 render_slot 태그를 사용 하여이 슬롯을 렌더링 할 수 있습니다.
{% load components %}
< div class =" alert alert-danger " >
{% render_slot slots.inner_block %}
</ div >다음 HTML이 렌더링됩니다.
< div class =" alert alert-danger " >
< strong > Whoops! </ strong > Something went wrong!
</ div >설정에서 기본 슬롯을 지정하여 기본 슬롯의 이름을 바꿀 수도 있습니다.
# inside your settings
WEB_COMPONENTS = {
"DEFAULT_SLOT_NAME" : "inner_block" ,
}때로는 구성 요소가 구성 요소 내의 다른 위치에서 여러 다른 슬롯을 렌더링해야 할 수도 있습니다. "제목"슬롯의 주입을 허용하도록 경고 구성 요소를 수정합시다.
{% load components %}
< div class =" alert alert-danger " >
< span class =" alert-title " >
{% render_slot slots.title %}
</ span >
{% render_slot slots.inner_block %}
</ div > slot 태그를 사용하여 명명 된 슬롯의 내용을 정의 할 수 있습니다. 명시 적 slot 태그 내에없는 컨텐츠는 기본 inner_block 슬롯에 추가됩니다.
{% load components %}
{% alert %}
{% slot title %} Server error {% endslot %}
< strong > Whoops! </ strong > Something went wrong!
{% endalert %}이 예에서 렌더링 된 HTML은 다음과 같습니다.
< div class =" alert alert-danger " >
< span class =" alert-title " >
Server error
</ span >
< strong > Whoops! </ strong > Something went wrong!
</ div >동일한 이름의 슬롯을 여러 번 정의 할 수 있습니다.
{% unordered_list %}
{% slot item %} First item {% endslot %}
{% slot item %} Second item {% endslot %}
{% slot item %} Third item {% endslot %}
{% endunordered_list %}그런 다음 구성 요소 내부의 슬롯을 반복 할 수 있습니다.
< ul >
{% for item in slots.item %}
< li > {% render_slot item %} </ li >
{% endfor %}
</ ul >다음 HTML이 발생합니다.
< ul >
< li > First item </ li >
< li > Second item </ li >
< li > Third item </ li >
</ ul > 슬롯 컨텐츠는 또한 구성 요소의 컨텍스트에 액세스 할 수 있습니다. 이 개념을 탐색하려면 사물 목록을 나타내는 entries 속성을 수락하는 목록 구성 요소를 상상해 보면 각 항목에 대해 inner_block 슬롯을 반복하여 렌더링합니다.
from django_web_components import component
from django_web_components . template import CachedTemplate
@ component . register
def unordered_list ( context ):
context [ "entries" ] = context [ "attributes" ]. pop ( "entries" , [])
return CachedTemplate (
"""
<ul>
{% for entry in entries %}
<li>
{% render_slot slots.inner_block %}
</li>
{% endfor %}
</ul>
""" ,
name = "unordered_list" ,
). render ( context )그런 다음 구성 요소를 다음과 같이 렌더링 할 수 있습니다.
{% unordered_list entries=entries %}
I like {{ entry }}!
{% endunordered_list %} 이 예에서는 entry 변수가 구성 요소의 컨텍스트에서 나옵니다. entries = ["apples", "bananas", "cherries"] 를 가정하면 결과 HTML은 다음과 같습니다.
< ul >
< li > I like apples! </ li >
< li > I like bananas! </ li >
< li > I like cherries! </ li >
</ ul > 또한 render_slot 에 두 번째 인수를 명시 적으로 전달할 수도 있습니다.
< ul >
{% for entry in entries %}
< li >
<!-- We are passing the `entry` as the second argument to render_slot -->
{% render_slot slots.inner_block entry %}
</ li >
{% endfor %}
</ ul > 구성 요소를 호출 할 때 특수 속성을 사용할 수 있습니다 :let render_slot 에 전달 된 값을 가져 와서 변수에 바인딩하십시오.
{% unordered_list :let="fruit" entries=entries %}
I like {{ fruit }}!
{% endunordered_list %}이것은 위와 동일한 HTML을 렌더링합니다.
구성 요소와 유사하게 슬롯에 추가 속성을 할당 할 수 있습니다. 아래는 속성이있는 여러 이름의 슬롯을 보여주는 테이블 구성 요소입니다.
from django_web_components import component
from django_web_components . template import CachedTemplate
@ component . register
def table ( context ):
context [ "rows" ] = context [ "attributes" ]. pop ( "rows" , [])
return CachedTemplate (
"""
<table>
<tr>
{% for col in slots.column %}
<th>{{ col.attributes.label }}</th>
{% endfor %}
</tr>
{% for row in rows %}
<tr>
{% for col in slots.column %}
<td>
{% render_slot col row %}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
""" ,
name = "table" ,
). render ( context )다음과 같은 구성 요소를 호출 할 수 있습니다.
{% table rows=rows %}
{% slot column :let="user" label="Name" %}
{{ user.name }}
{% endslot %}
{% slot column :let="user" label="Age" %}
{{ user.age }}
{% endslot %}
{% endtable %} 우리 rows = [{ "name": "Jane", "age": "34" }, { "name": "Bob", "age": "51" }] 를 가정하면 다음 HTML이 렌더링됩니다.
< table >
< tr >
< th > Name </ th >
< th > Age </ th >
</ tr >
< tr >
< td > Jane </ td >
< td > 34 </ td >
</ tr >
< tr >
< td > Bob </ td >
< td > 51 </ td >
</ tr >
</ table >더 복잡한 요소를 달성하기 위해 구성 요소를 중첩 할 수도 있습니다. 다음은 Bootstrap을 사용하여 아코디언 구성 요소를 구현하는 방법의 예입니다.
from django_web_components import component
from django_web_components . template import CachedTemplate
import uuid
@ component . register
def accordion ( context ):
context [ "accordion_id" ] = context [ "attributes" ]. pop ( "id" , str ( uuid . uuid4 ()))
context [ "always_open" ] = context [ "attributes" ]. pop ( "always_open" , False )
return CachedTemplate (
"""
<div class="accordion" id="{{ accordion_id }}">
{% render_slot slots.inner_block %}
</div>
""" ,
name = "accordion" ,
). render ( context )
@ component . register
def accordion_item ( context ):
context [ "id" ] = context [ "attributes" ]. pop ( "id" , str ( uuid . uuid4 ()))
context [ "open" ] = context [ "attributes" ]. pop ( "open" , False )
return CachedTemplate (
"""
<div class="accordion-item" id="{{ id }}">
<h2 class="accordion-header" id="{{ id }}-header">
<button
class="accordion-button {% if not open %}collapsed{% endif %}"
type="button"
data-bs-toggle="collapse"
data-bs-target="#{{ id }}-collapse"
aria-expanded="{% if open %}true{% else %}false{% endif %}"
aria-controls="{{ id }}-collapse"
>
{% render_slot slots.title %}
</button>
</h2>
<div
id="{{ id }}-collapse"
class="accordion-collapse collapse {% if open %}show{% endif %}"
aria-labelledby="{{ id }}-header"
{% if accordion_id and not always_open %}
data-bs-parent="#{{ accordion_id }}"
{% endif %}}
>
<div class="accordion-body">
{% render_slot slots.body %}
</div>
</div>
</div>
""" ,
name = "accordion_item" ,
). render ( context )그런 다음 다음과 같이 사용할 수 있습니다.
{% accordion %}
{% accordion_item open %}
{% slot title %} Accordion Item #1 {% endslot %}
{% slot body %}
< strong > This is the first item's accordion body. </ strong > It is shown by default.
{% endslot %}
{% endaccordion_item %}
{% accordion_item %}
{% slot title %} Accordion Item #2 {% endslot %}
{% slot body %}
< strong > This is the second item's accordion body. </ strong > It is hidden by default.
{% endslot %}
{% endaccordion_item %}
{% accordion_item %}
{% slot title %} Accordion Item #3 {% endslot %}
{% slot body %}
< strong > This is the third item's accordion body. </ strong > It is hidden by default.
{% endslot %}
{% endaccordion_item %}
{% endaccordion %} 이 프로젝트는 poetry 사용하여 종속성을 관리합니다. 시를 설치하는 방법에 대한 문서를 확인하십시오. https://python-poetry.org/docs/#installation
종속성을 설치하십시오
poetry install환경을 활성화하십시오
poetry shell이제 테스트를 실행할 수 있습니다
python runtests.py이 프로젝트는 다른 언어 / 프레임 워크가 구성 요소를 어떻게 다루는지를보고 그 아이디어 중 일부를 Django로 가져오고 싶어했습니다.
django-components 라이브러리는 이미 훌륭하고이 프로젝트가 가지고있는 대부분의 기능을 지원하지만 구문은 덜 장황한 느낌을 느끼고 기능 기반 구성 요소 및 템플릿 문자열에 대한 지원과 같은 몇 가지 추가 사항을 추가 할 수 있다고 생각했습니다.<x-alert type="error">Server Error</x-alert> 와 매우 흡사하게 보였지만 솔루션은 훨씬 더 복잡했고 django-components 에 대한 비슷한 접근 방식을 사용하여 Django에서 더 의미가 있다는 결론에 도달했습니다.다른 많은 언어 / 프레임 워크는 구성 요소 (슬롯, 속성)에 동일한 개념을 사용하고 있으므로 많은 지식이 전송 가능하며 이미 기존 구성 요소 라이브러리가 이미 있습니다 (예 : 부트 스트랩, 테일 윈드, 자료 설계 등). 구성 요소를 구축 / 구조화하는 방법에 영감을 얻으려면 일부를 살펴 보는 것이 좋습니다. 몇 가지 예는 다음과 같습니다.