Communitygithub.com

modal

Modal은 주문형 GPU를 포함한 Python 코드를 서버리스로 실행할 수 있는 클라우드 플랫폼입니다. AI/ML 모델 배포 및 서빙, GPU 가속 워크로드(학습, 미세 조정, 추론), 웹 엔드포인트 서빙, 배치 작업 예약, Modal SDK를 사용한 Python 코드 확장에 사용하세요.

modal란 무엇인가요?

이 스킬은 Modal 클라우드 플랫폼을 위한 것입니다. Modal은 Python 코드를 서버리스로 실행하는 데 특화되어 있으며, 특히 AI/ML 워크로드에 중점을 둡니다. 주요 기능으로는 주문형 GPU(T4, L4, A10, L40S, A100, H100, H200, B200 등) 지원, 0에서 수천 개의 컨테이너로 자동 확장되는 서버리스 함수, Python 코드로 완전히 빌드되는 사용자 정의 컨테이너 이미지, 모델 가중치 및 데이터 세트를 위한 Volume 기반 영구 스토리지, 모델 및 API 서빙을 위한 웹 엔드포인트(FastAPI, ASGI, WSGI, WebSocket 지원), cron 또는 고정 간격으로 예약된 작업, 그리고 짧은 대기 시간 추론을 위한 1초 미만의 콜드 스타트를 제공합니다. Claude Code, Cursor, Codex 등 다양한 AI 에이전트 도구와 호환됩니다. 이 스킬은 서버리스 AI/ML 모델 배포, GPU 가속 컴퓨팅, 배치 처리, 예약된 데이터 파이프라인, 그리고 동적 코드 실행을 위한 격리된 샌드박스 생성에 매우 유용합니다. Modal의 모든 것은 코드로 정의되며, YAML이나 Dockerfile이 필요하지 않습니다(둘 다 지원되기는 합니다).

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/K-Dense-AI/scientific-agent-skills/tree/main/skills/modal

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

modal은(는) 무엇을 하나요?

개요

Modal은 AI/ML 워크로드에 중점을 둔, Python 코드를 서버리스로 실행하기 위한 클라우드 플랫폼입니다. 주요 기능:

  • GPU 컴퓨팅 주문형 (T4, L4, A10, L40S, A100, H100, H200, B200)
  • 서버리스 함수 0에서 수천 개의 컨테이너로 자동 확장
  • 사용자 정의 컨테이너 이미지 Python 코드로 완전히 빌드
  • 영구 스토리지 모델 가중치 및 데이터 세트를 위한 Volume
  • 웹 엔드포인트 모델 및 API 서빙
  • 예약된 작업 cron 또는 고정 간격
  • 1초 미만의 콜드 스타트 짧은 대기 시간 추론

Modal의 모든 것은 코드로 정의됩니다 — YAML이나 Dockerfile이 필요하지 않습니다 (둘 다 지원되지만).

이 스킬을 사용해야 할 때

다음과 같은 경우 이 스킬을 사용하세요:

  • 클라우드에서 AI/ML 모델 배포 또는 서빙
  • GPU 가속 컴퓨팅 실행 (학습, 추론, 미세 조정)
  • 서버리스 웹 API 또는 엔드포인트 생성
  • 배치 처리 작업 병렬 확장
  • 반복 작업 예약 (데이터 파이프라인, 재학습, 스크래핑)
  • 모델 가중치 또는 데이터 세트를 위한 영구 클라우드 스토리지 필요
  • 사용자 정의 컨테이너 환경에서 코드 실행
  • 작업 큐 또는 비동기 작업 처리 시스템 구축

설치 및 인증

설치

uv pip install modal

Modal Python SDK는 Python 3.10–3.14를 지원합니다. 이 스킬은 안정적인 modal>=1.0 API를 대상으로 합니다 (현재 릴리스: 1.4.x).

인증

새 자격 증명을 만들기 전에 기존 자격 증명을 선호합니다. 아래 두 가지 Modal 관련 변수만 관련이 있습니다 — 다른 환경 변수나 .env 파일 내용을 읽거나, 로드하거나, 노출하지 마세요:

  1. MODAL_TOKEN_ID와 MODAL_TOKEN_SECRET이 현재 환경에 이미 설정되어 있는지 확인합니다.
  2. 그렇지 않은 경우, 로컬 .env 파일에서 해당 두 키만 찾아보고 (다른 모든 항목은 무시) 워크플로에 적합하면 로드합니다.
  3. 위 두 소스가 해당 두 값을 제공하지 않는 경우에만 대화형 modal setup 또는 새 토큰 생성을 대체 수단으로 사용합니다.
modal setup

그러면 브라우저가 인증을 위해 열립니다. CI/CD 또는 헤드리스 환경의 경우 환경 변수를 사용하세요:

export MODAL_TOKEN_ID=<your-token-id>
export MODAL_TOKEN_SECRET=<your-token-secret>

토큰이 환경이나 .env에 이미 없으면 https://modal.com/settings 에서 생성하세요.

Modal은 월 $30 크레딧의 무료 티어를 제공합니다.

참조: references/getting-started.md에서 자세한 설정 및 첫 번째 앱 설명을 참조하세요.

핵심 개념

앱 및 함수

Modal App은 관련 함수들을 그룹화합니다. @app.function()으로 데코레이트된 함수는 원격으로 클라우드에서 실행됩니다:

import modal

app = modal.App("my-app")

@app.function()
def square(x):
    return x ** 2

@app.local_entrypoint()
def main():
    # .remote()는 클라우드에서 실행됩니다
    print(square.remote(42))

modal run script.py로 실행합니다. modal deploy script.py로 배포합니다.

참조: references/functions.md에서 생명 주기 훅, 클래스, .map(), .spawn() 등에 대해 알아보세요.

컨테이너 이미지

Modal은 Python 코드로 컨테이너 이미지를 빌드합니다. 권장 패키지 설치 프로그램은 uv입니다:

image = (
    modal.Image.debian_slim(python_version="3.11")
    .uv_pip_install("torch==2.12.0", "transformers==5.9.0", "accelerate==1.13.0")
    .apt_install("git")
)

@app.function(image=image)
def inference(prompt):
    from transformers import pipeline
    pipe = pipeline("text-generation", model="meta-llama/Llama-3-8B")
    return pipe(prompt)

주요 이미지 메서드:

  • .uv_pip_install() — uv로 Python 패키지 설치 (권장)
  • .pip_install() — pip로 설치 (대체)
  • .apt_install() — 시스템 패키지 설치
  • .run_commands() — 빌드 중 셸 명령 실행
  • .run_function() — 빌드 중 Python 실행 (예: 모델 가중치 다운로드)
  • .add_local_python_source() — 로컬 모듈 추가
  • .env() — 환경 변수 설정

참조: references/images.md에서 Dockerfiles, micromamba, 캐싱, GPU 빌드 단계에 대해 알아보세요.

GPU 컴퓨팅

gpu 매개변수를 통해 GPU를 요청하세요:

@app.function(gpu="H100")
def train_model():
    import torch
    device = torch.device("cuda")
    # GPU 학습 코드

# 다중 GPU
@app.function(gpu="H100:4")
def distributed_training():
    ...

# GPU 대체 체인
@app.function(gpu=["H100", "A100-80GB", "A100-40GB"])
def flexible_inference():
    ...

사용 가능한 GPU: T4, L4, A10, L40S, A100-40GB, A100-80GB, RTX-PRO-6000, H100, H200, B200, B200+

  • GPU는 항상 문자열로 지정됩니다 (예: gpu="H100", gpu="H100:4"). 이전 modal.gpu.* 객체는 v0.73.31부터 사용 중단되었습니다.
  • 컨테이너당 최대 8개의 GPU (A10 제외: 최대 4개)
  • L40S는 추론에 권장됩니다 (비용/성능 균형, 48GB VRAM)
  • H100/A100은 추가 비용 없이 H200/A100-80GB로 자동 업그레이드될 수 있습니다.
  • 자동 업그레이드를 방지하려면 gpu="H100!"를 사용하세요.

참조: references/gpu.md에서 GPU 선택 지침 및 다중 GPU 학습에 대해 알아보세요.

Volume (영구 스토리지)

Volume은 분산된 영구 파일 스토리지를 제공합니다:

vol = modal.Volume.from_name("model-weights", create_if_missing=True)

@app.function(volumes={"/data": vol})
def save_model():
    # 마운트된 경로에 쓰기
    with open("/data/model.pt", "wb") as f:
        torch.save(model.state_dict(), f)

@app.function(volumes={"/data": vol})
def load_model():
    model.load_state_dict(torch.load("/data/model.pt"))
  • 한 번 쓰고 여러 번 읽는 워크로드에 최적화 (모델 가중치, 데이터 세트)
  • CLI 액세스: modal volume ls, modal volume put, modal volume get
  • 몇 초마다 백그라운드 자동 커밋
  • vol.with_mount_options(read_only=True, sub_path="subset")로 읽기 전용 마운트 또는 하위 디렉토리로 제한

참조: references/volumes.md에서 v2 volumes, 동시 쓰기 및 모범 사례에 대해 알아보세요.

시크릿

함수에 자격 증명을 안전하게 전달하세요:

@app.function(secrets=[modal.Secret.from_name("my-api-keys")])
def call_api():
    import os
    api_key = os.environ["API_KEY"]
    # 키 사용

CLI로 시크릿 생성: modal secret create my-api-keys API_KEY=sk-xxx

또는 .env 파일에서: modal.Secret.from_dotenv()

참조: references/secrets.md에서 대시보드 설정, 여러 시크릿 및 템플릿에 대해 알아보세요.

웹 엔드포인트

웹 엔드포인트로 모델 및 API를 서빙하세요:

@app.function()
@modal.fastapi_endpoint()
def predict(text: str):
    return {"result": model.predict(text)}
  • modal serve script.py — 핫 리로드가 있는 개발 및 임시 URL
  • modal deploy script.py — 영구 URL이 있는 프로덕션 배포
  • FastAPI, ASGI (Starlette, FastHTML), WSGI (Flask, Django), WebSockets 지원
  • 요청 본문 최대 4 GiB, 응답 크기 무제한

참조: references/web-endpoints.md에서 ASGI/WSGI 앱, 스트리밍, 인증 및 WebSockets에 대해 알아보세요.

예약된 작업

일정에 따라 함수를 실행하세요:

@app.function(schedule=modal.Cron("0 9 * * *"))  # 매일 오전 9시 UTC

def daily_pipeline():
    # ETL, 재학습, 스크래핑 등
    ...

@app.function(schedule=modal.Period(hours=6))
def periodic_check():
    ...

modal deploy script.py로 배포하여 일정을 활성화하세요.

  • modal.Cron("...") — 표준 cron 구문, 배포 간 안정적
  • modal.Period(hours=N) — 고정 간격, 재배포 시 재설정
  • Modal 대시보드에서 실행 모니터링

참조: references/scheduled-jobs.md에서 cron 구문 및 관리에 대해 알아보세요.

확장 및 동시성

Modal은 컨테이너를 자동으로 확장합니다. 제한 구성:

@app.function(
    max_containers=100,    # 상한
    min_containers=2,      # 짧은 대기 시간을 위해 웜 상태 유지
    buffer_containers=5,   # 용량 예약
    scaledown_window=300,  # 종료 전 유휴 시간 (초)
)
def process(data):
    ...

.map()으로 입력을 병렬로 처리하세요:

results = list(process.map([item1, item2, item3, ...]))

@modal.concurrent로 컨테이너당 동시 요청 처리를 활성화하세요. target_inputs (자동 확장기의 컨테이너 당 목표)를 max_inputs (하드 한도) 아래로 설정하여 확장 중 여유 공간을 확보하세요:

@app.function()
@modal.concurrent(max_inputs=10, target_inputs=8)
async def handle_request(req):
    ...

재배포 없이 호출 시 배포된 Function 또는 Cls를 재구성하려면 Function.with_options() / Function.with_concurrency() / Function.with_batching() (그리고 Cls.with_options())을 사용하세요:

Model = modal.Cls.from_name("my-app", "Model")
fast = Model.with_options(gpu="H200", max_containers=20)
fast().generate.remote(prompt)

참조: references/scaling.md에서 .map(), .starmap(), .spawn() 및 제한 사항에 대해 알아보세요.

리소스 구성

@app.function(
    cpu=4.0,              # 물리적 코어 (vCPU 아님)
    memory=16384,         # MiB
    ephemeral_disk=51200, # MiB (최대 3 TiB)
    timeout=3600,         # 초
)
def heavy_computation():
    ...

기본값: 0.125 CPU 코어, 128 MiB 메모리. 최대(요청, 사용량) 기준으로 청구됩니다.

참조: references/resources.md에서 제한 사항 및 청구 세부 정보에 대해 알아보세요.

생명 주기 훅이 있는 클래스

상태 저장 워크로드 (예: 모델을 한 번 로드하고 여러 요청 서빙)의 경우:

@app.cls(gpu="L40S", image=image)
class Predictor:
    @modal.enter()
    def load_model(self):
        self.model = load_heavy_model()  # 컨테이너 시작 시 한 번 실행

    @modal.method()
    def predict(self, text: str):
        return self.model(text)

    @modal.exit()
    def cleanup(self):
        ...  # 컨테이너 종료 시 실행

호출: Predictor().predict.remote("hello")

샌드박스

신뢰할 수 없거나 동적으로 생성된 코드 (예: AI 에이전트 출력 또는 코드 인터프리터)를 실행하려면 modal.Sandbox를 사용하세요 — 데코레이트된 Function이 아닌, 사용자가 생성하고 프로그래밍 방식으로 제어하는 격리된 컨테이너입니다:

app = modal.App.lookup("sandbox-demo", create_if_missing=True)

# 격리된 컨테이너; 신뢰할 수 없는 워크로드의 경우 송신 제한
sb = modal.Sandbox.create(
    app=app,
    image=modal.Image.debian_slim(),
    outbound_cidr_allowlist=["10.0.0.0/8"],
)

# 파일 시스템 API를 통해 파일 스트리밍 (베타)
sb.filesystem.write_text("print(2 ** 10)\n", "/tmp/job.py")
contents = sb.filesystem.read_text("/tmp/job.py")

sb.terminate()
  • 샌드박스 내에서 exec 메서드로 명령 실행 (예: python /tmp/job.py 실행)하고 반환된 프로세스 핸들에서 stdout 읽기 — references/api_reference.md 참조
  • outbound_cidr_allowlist=[...] / inbound_cidr_allowlist=[...]로 연결 제한
  • sb.snapshot_filesystem()으로 파일 시스템 스냅샷을 찍어 기본 이미지로 재사용
  • 코드 인터프리터, 에이전트 도구 실행 및 사용자별 격리에 이상적

일반적인 워크플로 패턴

GPU 모델 추론 서비스

import modal

app = modal.App("llm-service")

image = (
    modal.Image.debian_slim(python_version="3.11")
    .uv_pip_install("vllm")
)

@app.cls(gpu="H100", image=image, min_containers=1)
class LLMService:
    @modal.enter()
    def load(self):
        from vllm import LLM
        self.llm = LLM(model="meta-llama/Llama-3-70B")

    @modal.method()
    @modal.fastapi_endpoint(method="POST")
    def generate(self, prompt: str, max_tokens: int = 256):
        outputs = self.llm.generate([prompt], max_tokens=max_tokens)
        return {"text": outputs[0].outputs[0].text}

배치 처리 파이프라인

app = modal.App("batch-pipeline")
vol = modal.Volume.from_name("pipeline-data", create_if_missing=True)

@app.function(volumes={"/data": vol}, cpu=4.0, memory=8192)
def process_chunk(chunk_id: int):
    import pandas as pd
    df = pd.read_parquet(f"/data/input/chunk_{chunk_id}.parquet")
    result = heavy_transform(df)
    result.to_parquet(f"/data/output/chunk_{chunk_id}.parquet")
    return len(result)

@app.local_entrypoint()
def main():
    chunk_ids = list(range(100))
    results = list(process_chunk.map(chunk_ids))
    print(f"{sum(results)}개의 총 행 처리 완료")

예약된 데이터 파이프라인

app = modal.App("etl-pipeline")

@app.function(
    schedule=modal.Cron("0 */6 * * *"),  # 6시간마다
    secrets=[modal.Secret.from_name("db-credentials")],
)
def etl_job():
    import os
    db_url = os.environ["DATABASE_URL"]
    # 추출, 변환, 로드
    ...

CLI 참조

명령어설명
modal setupModal 인증
modal run script.py스크립트의 로컬 진입점 실행
modal serve script.py핫 리로드 개발 서버
modal deploy script.py프로덕션 배포
modal volume ls <name>볼륨의 파일 목록 표시
modal volume put <name> <file>볼륨에 파일 업로드
modal volume get <name> <file>볼륨에서 파일 다운로드
modal secret create <name> K=V시크릿 생성
modal secret list시크릿 목록 표시
modal app list배포된 앱 목록 표시
modal app stop <name>배포된 앱 중지

보안 참고 사항

  • 자격 증명: 인증에는 MODAL_TOKEN_ID와 MODAL_TOKEN_SECRET만 필요합니다. 다른 환경 변수나 .env 항목을 읽거나, 기록하거나, 전달하지 마세요.
  • 하위 프로세스 / 사용자 정의 서버: 일부 패턴 (다중 GPU 학습 실행기, @modal.web_server 앱)은 빌드 중 subprocess.run/subprocess.Popen 또는 셸 명령을 호출합니다. 인수 목록을 고정하고 하드코딩된 상태로 유지하세요. 하위 프로세스 또는 셸 인수를 삭제되지 않은 사용자 입력으로 구성하지 마세요 — 신뢰할 수 없는 값은 데이터 (파일, 환경 변수, stdin)로 전달하고 명령 인수로 전달하지 마세요.
  • 신뢰할 수 없는 코드: 사용자 또는 모델 생성 코드는 (위 참조) modal.Sandbox 내에서 실행하고, 일반 Function에서는 실행하지 마세요. CIDR 허용 목록으로 네트워크 액세스를 제한하세요.

참조 파일

각 주제에 대한 자세한 문서:

  • references/getting-started.md — 설치, 인증, 첫 번째 앱
  • references/functions.md — 함수, 클래스, 생명 주기 훅, 원격 실행
  • references/images.md — 컨테이너 이미지, 패키지 설치, 캐싱
  • references/gpu.md — GPU 유형

… (이하 번역 생략)

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

adaptyv

How to use the Adaptyv Bio Foundry API and Python SDK for protein experiment design, submission, and results retrieval. Use this skill whenever the user mentions Adaptyv, Foundry API, protein binding assays, protein screening experiments, BLI/SPR assays, thermostability assays, or wants to submit protein sequences for experimental characterization. Also trigger when code imports `adaptyv`, `adaptyv_sdk`, or `FoundryClient`, or references `foundry-api-public.adaptyvbio.com`.

aeon

This skill should be used for time series machine learning tasks including classification, regression, clustering, forecasting, anomaly detection, segmentation, and similarity search. Use when working with temporal data, sequential patterns, or time-indexed observations requiring specialized algorithms beyond standard ML approaches. Particularly suited for univariate and multivariate time series analysis with scikit-learn compatible APIs.

alphagenome

Look up precomputed AlphaGenome Atlas effects for any GRCh38 single-nucleotide variant (AVI score with Phred and 18 SHAP feature attributions, plus raw and quantile scores for RNA-seq, DNase, ATAC, ChIP-TF, ChIP-histone, CAGE, PRO-cap, splicing, polyadenylation and contact-map tracks), score variants or scan windows on demand with the AlphaGenome model for human and mouse (variant scoring, in silico mutagenesis, REF-versus-ALT track prediction), and build Atlas website deep links. Use when the user mentions AlphaGenome, AlphaGenome Atlas, AVI or AlphaGenome Variant Impact, DeepMind variant effect prediction, or wants to prioritise or mechanistically interpret non-coding, regulatory, splicing, enhancer, promoter, or chromatin-accessibility effects of SNVs from a VCF, credible set, or region. Research use only; not a clinical tool.

analytical-method-validation

Plan, execute, and document validation, verification, and transfer of analytical procedures under the governing framework - ICH Q2(R2) and Q14, USP <1220>/<1225>/<1226>, ICH M10 bioanalytical, CLSI EP, or ISO/IEC 17025. Use for HPLC, LC-MS/MS, GC, CE, ICP-MS, dissolution, qNMR, qPCR, NIR, and ligand binding or cell-based assays whenever the question is whether a procedure is fit for its intended purpose. Triggers include

anndata

Data structure for annotated matrices in single-cell analysis. Use when working with .h5ad files or integrating with the scverse ecosystem. This is the data format skill—for analysis workflows use scanpy; for probabilistic models use scvi-tools; for population-scale queries use cellxgene-census.

arbor

Autonomously improve a real artifact (code, training recipe, agent harness, data pipeline, prompt) against an objective and an evaluator, using Hypothesis Tree Refinement (HTR) from the Arbor paper. Use this whenever someone wants to iteratively optimize something over many experiments without overfitting — e.g.

arboreto

Infer gene regulatory networks (GRNs) from gene expression data using scalable algorithms (GRNBoost2, GENIE3). Use when analyzing transcriptomics data (bulk RNA-seq, single-cell RNA-seq) to identify transcription factor-target gene relationships and regulatory interactions. Supports distributed computation for large-scale datasets.

astropy

Core Python library for astronomy and astrophysics workflows that need Astropy APIs, including units/quantities, coordinates, FITS I/O, tables, time systems, WCS, and cosmology. Use when implementing or debugging astronomical data analysis code with Astropy.

autoskill

Observe the user

benchling-integration

Benchling Python SDK and REST API integration for registry entities, inventory, ELN entries, workflows, Benchling Apps, and Data Warehouse queries. Use when automating lab data with benchling-sdk or the v2 API.

bgpt-paper-search

Search scientific papers and retrieve structured experimental data extracted from full-text studies via the BGPT MCP server. Returns 25+ fields per paper including methods, results, sample sizes, quality scores, and conclusions. Use for literature reviews, evidence synthesis, and finding experimental details not available in abstracts alone.

bids

>

biopython

Comprehensive molecular biology toolkit. Use for sequence manipulation, file parsing (FASTA/GenBank/PDB), phylogenetics, and programmatic NCBI/PubMed access (Bio.Entrez). Best for batch processing, custom bioinformatics pipelines, BLAST automation. For quick lookups use gget; for multi-service integration use bioservices.

bioservices

Unified Python interface to 40+ bioinformatics services. Use when querying multiple databases (UniProt, KEGG, ChEMBL, Reactome) in a single workflow with consistent API. Best for cross-database analysis, ID mapping across services. For quick single-database lookups use gget; for sequence/file manipulation use biopython.

bulk-rnaseq

End-to-end bulk RNA-seq orchestrator — takes raw FASTQ reads through QC and trimming (FastQC, fastp/Trim Galore), alignment and quantification (STAR, Salmon, featureCounts), assembles a gene-level counts matrix, then hands off to differential expression (pydeseq2), pathway/GSEA enrichment (pathway-enrichment), and publication figures (scientific-visualization). Use whenever the user has bulk RNA-seq reads or quant output and wants a complete, reproducible differential-expression workflow — e.g.

cellxgene-census

Query the CZ CELLxGENE Census programmatically for versioned public single-cell and spatial transcriptomics data. Use when you need population-scale cell metadata, gene expression slices, Census summary counts, source H5AD URIs/downloads, embeddings, spatial Census data, or reference atlas comparisons across organisms, tissues, diseases, assays, and cell types. For analyzing your own local single-cell data use scanpy, anndata, or scvi-tools.

cirq

Google quantum computing framework. Use when targeting Google Quantum AI hardware, designing noise-aware circuits, or running quantum characterization experiments. Best for Google hardware, noise modeling, and low-level circuit design. For IBM hardware use qiskit; for quantum ML with autodiff use pennylane; for physics simulations use qutip.

citation-management

Comprehensive citation management for academic research. Search OpenAlex, PubMed, and Google Scholar for papers, extract accurate metadata, validate citations, and generate properly formatted BibTeX entries. This skill should be used when you need to find papers, verify citation information, convert DOIs to BibTeX, or ensure reference accuracy in scientific writing.

clinical-decision-support

Prepare and validate research-only clinical decision-support evaluation, evidence-profile, cohort, survival, biomarker/model, privacy, and governance artifacts. Use for aggregate or synthetic research documentation and traceability—not patient care or live clinical operation.

clinical-reports

Create safety-bounded draft structures and run local deterministic checks for clinical case, diagnostic, trial, safety, and aggregate research reports. Use only with synthetic, de-identified, or aggregate inputs and verified source-fact manifests; every output requires qualified review.

관련 스킬