Claude를 위한 로컬 MCP 서버 구축 방법: 파일, 명령어, 스크린샷 및 앱 제어

@hrswatigupta
영어1개월 전 · 2026년 6월 04일
1.0M
72
16
2
224

TL;DR

이 가이드에서는 Python 기반의 로컬 MCP 서버를 생성하여 안전한 허용 목록(allowlist) 아키텍처를 통해 로컬 파일, 명령어 및 데스크탑 도구에 안전하고 제어된 방식으로 액세스하는 방법을 설명합니다.

Claude가 단순한 채팅 인터페이스를 넘어서면 훨씬 더 유용해집니다.

로컬 MCP 서버를 사용하면 Claude가 로컬 파일, 승인된 명령어, 스크린샷, 앱 실행 등 실제 컴퓨터와 상호작용할 수 있습니다. 중요한 것은 무제한적인 접근이 아니라 통제된 접근입니다.

이 가이드에서는 실제 워크플로우에서 사용할 수 있을 만큼 실용적이고, 범위가 좁으며, 안전한 로컬 MCP 서버를 구축해 보겠습니다.

시작하기 전에

이 글은 의도적으로 무책임한 "컴퓨터 제어" 방식을 피합니다.

Claude에 제한 없는 셸 접근 권한, 전체 파일 시스템 권한, 또는 가드레일 없이 시스템을 변경할 수 있는 권한을 부여하지 않을 것입니다. 잘못된 로컬 MCP 서버를 만드는 가장 빠른 방법은 거대한 run_anything() 도구를 만들고 이를 혁신이라고 부르는 것입니다.

더 나은 패턴은 다음과 같습니다:

  • 허용된 디렉토리
  • 허용된 명령어
  • 안전한 기본값
  • 사람이 읽을 수 있는 로그
  • 명확한 응답
  • 읽기 전용 도구와 실행 도구의 명확한 분리

Claude가 모든 것을 할 수 있다면, 그것은 데모에 불과합니다.

Claude가 안전하게 올바른 작업을 수행할 수 있다면, 그것은 사용 가능한 제품입니다.

이 아키텍처를 배워야 하는 이유

로컬 MCP 서버의 가치는 참신함이 아니라 마찰을 줄이는 데 있습니다.

로컬 도구 계층이 없으면 워크플로우는 다음과 같습니다:

  1. Claude에게 무엇을 할지 물어봅니다.
  2. 답변을 복사합니다.
  3. 직접 폴더를 엽니다.
  4. 직접 명령어를 실행합니다.
  5. 직접 스크린샷을 찍습니다.
  6. 결과를 다시 채팅에 붙여넣습니다.

로컬 MCP 서버를 사용하면 이 과정이 훨씬 빨라집니다. Claude는 필요한 컨텍스트를 확인하고, 범위가 좁은 도구를 사용하며, 실제 컴퓨터 상태에 기반한 답변을 제공할 수 있습니다.

이는 다음과 같은 작업에 유용합니다:

  • 개발 워크플로우
  • 로그 검사
  • 콘텐츠 운영
  • 연구 파이프라인
  • 데스크톱 자동화
  • 반복적인 관리 작업

그리고 도구 계층이 사용자의 것이기 때문에, 모델의 권한 범위를 정확히 설정할 수 있습니다.

AI를 배우고 커리어에서 앞서 나가고 싶으신가요?

다음과 같은 내용을 공유합니다:

1,000개 이상의 AI 프롬프트

AI 도구 및 자동화

커리어 및 LinkedIn 성장 팁

엄선된 기술 및 생산성 리소스

여기서 구독하세요:

https://bytebuilders.beehiiv.com/subscribe

더 많은 AI 팁을 보려면 저를 팔로우하세요

👇

구축할 설계

Swati Gupta - inline image

다섯 가지 도구를 갖춘 로컬 서버를 구축할 것입니다:

  1. list_files — 승인된 폴더 내의 파일 확인
  2. read_file — 안전한 텍스트 기반 파일 열기
  3. run_command — 승인된 소수의 로컬 명령어 실행
  4. take_screenshot — 알려진 위치에 스크린샷 저장
  5. open_target — 앱, 파일, 폴더 또는 URL 열기
Swati Gupta - inline image

이 범위는 의도적입니다.

안전하지 않은 범용 자동화로 빠지지 않으면서도 로컬 머신에서 Claude를 의미 있게 유용하게 만들기에 충분합니다.

개념적 모델은 다음과 같아야 합니다:

Claude → MCP 클라이언트 → 로컬 MCP 서버 → 좁은 도구 → 운영 체제

Claude는 운영 체제와 직접 통신해서는 안 됩니다. MCP 서버가 중간의 제어 평면 역할을 해야 합니다.

기술 스택

로컬 빌드의 경우, 공식 MCP SDK가 성숙하고 FastMCP 추상화가 간결하며 Python이 파일 시스템, 서브프로세스 및 데스크톱 스크립팅 작업에 가장 쉬운 언어이기 때문에 Python이 깔끔한 선택입니다 4 2.

다음을 사용할 것입니다:

  • Python 3.11+
  • mcp[cli] (MCP 서버 런타임)
  • mss (크로스 플랫폼 스크린샷)
  • 파일 액세스, 서브프로세스 호출 및 OS 처리를 위한 표준 라이브러리 모듈

새 프로젝트를 설정합니다:

bash
1mkdir local-mcp-server
2cd local-mcp-server
3uv init --python 3.11
4uv add "mcp[cli]>=1.0,<2.0" "mss>=9.0,<10.0"

데코레이터 기반 FastMCP 스타일은 프로토콜 레이어를 숨겨주므로 연결 작업 대신 도구 품질에 집중할 수 있습니다 4 5.

간단한 프로젝트 구조가 잘 작동합니다:

text
1mkdir local-mcp-server
2cd local-mcp-server
3uv init --python 3.11
4uv add "mcp[cli]>=1.0,<2.0" "mss>=9.0,<10.0"

v1을 위해 복잡한 아키텍처가 필요하지 않습니다. 필요한 것은 명확성입니다.

실제 서버

server.py를 만들고 정책 기반 구현으로 시작합니다.

python
1from __future__ import annotations
2
3import json
4import os
5import platform
6import shlex
7import subprocess
8from pathlib import Path
9from typing import Any
10
11import mss
12from mcp.server.fastmcp import FastMCP
13
14app = FastMCP("local-computer-control", json_response=True)
15
16HOME = Path.home()
17PROJECT_ROOT = Path(__file__).parent.resolve()
18CAPTURE_DIR = PROJECT_ROOT / "captures"
19CAPTURE_DIR.mkdir(exist_ok=True)
20
21ALLOWED_ROOTS = [
22 HOME / "Documents",
23 HOME / "Desktop",
24 PROJECT_ROOT,
25]
26
27ALLOWED_COMMANDS = {
28 "pwd",
29 "ls",
30 "git status",
31 "git diff --stat",
32 "python --version",
33 "node --version",
34 "npm --version",
35}
36
37READABLE_EXTENSIONS = {
38 ".txt",
39 ".md",
40 ".json",
41 ".py",
42 ".js",
43 ".ts",
44 ".tsx",
45 ".jsx",
46 ".yaml",
47 ".yml",
48 ".toml",
49 ".csv",
50 ".log",
51}
52
53def _resolve_path(raw_path: str) -> Path:
54 path = Path(raw_path).expanduser().resolve()
55 for root in ALLOWED_ROOTS:
56 root = root.resolve()
57 if path == root or root in path.parents:
58 return path
59 raise ValueError(f"Path not allowed: {path}")
60
61def _ensure_safe_command(command: str) -> str:
62 normalized = " ".join(shlex.split(command))
63 if normalized not in ALLOWED_COMMANDS:
64 raise ValueError(
65 "Command not allowed. Add it explicitly to ALLOWED_COMMANDS if you really need it."
66 )
67 return normalized
68
69@app.tool()
70def list_files(path: str = "~") -> dict[str, Any]:
71 """List files and folders inside an approved directory."""
72 target = _resolve_path(path)
73 if not target.is_dir():
74 raise ValueError(f"Not a directory: {target}")
75
76 items = []
77 for child in sorted(target.iterdir(), key=lambda p: (not p.is_dir(), p.name.lower())):
78 items.append(
79 {
80 "name": child.name,
81 "path": str(child),
82 "type": "directory" if child.is_dir() else "file",
83 }
84 )
85
86 return {
87 "path": str(target),
88 "count": len(items),
89 "items": items,
90 }
91
92@app.tool()
93def read_file(path: str, max_chars: int = 12000) -> dict[str, Any]:
94 """Read a safe text file from an approved location."""
95 target = _resolve_path(path)
96 if not target.is_file():
97 raise ValueError(f"Not a file: {target}")
98 if target.suffix.lower() not in READABLE_EXTENSIONS:
99 raise ValueError(f"Unsupported file type: {target.suffix}")
100
101 content = target.read_text(encoding="utf-8", errors="replace")
102 truncated = len(content) > max_chars
103 content = content[:max_chars]
104
105 return {
106 "path": str(target),
107 "truncated": truncated,
108 "content": content,
109 }
110
111@app.tool()
112def run_command(command: str, cwd: str | None = None, timeout: int = 15) -> dict[str, Any]:
113 """Run one allowlisted local command."""
114 safe_command = _ensure_safe_command(command)
115 working_dir = _resolve_path(cwd) if cwd else PROJECT_ROOT
116
117 completed = subprocess.run(
118 safe_command,
119 shell=True,
120 cwd=str(working_dir),
121 capture_output=True,
122 text=True,
123 timeout=timeout,
124 )
125
126 return {
127 "command": safe_command,
128 "cwd": str(working_dir),
129 "returncode": completed.returncode,
130 "stdout": completed.stdout.strip(),
131 "stderr": completed.stderr.strip(),
132 }
133
134@app.tool()
135def take_screenshot(name: str = "latest") -> dict[str, Any]:
136 """Capture a screenshot and save it locally."""
137 output_path = CAPTURE_DIR / f"{name}.png"
138
139 with mss.mss() as sct:
140 sct.shot(output=str(output_path))
141
142 return {
143 "saved": True,
144 "path": str(output_path),
145 }
146
147@app.tool()
148def open_target(target: str) -> dict[str, Any]:
149 """Open an approved file, folder, app, or URL using the local OS."""
150 system = platform.system().lower()
151
152 if target.startswith("http://") or target.startswith("https://"):
153 resolved = target
154 else:
155 resolved = str(_resolve_path(target))
156
157 if system == "darwin":
158 subprocess.run(["open", resolved], check=True)
159 elif system == "windows":
160 os.startfile(resolved) # type: ignore[attr-defined]
161 else:
162 subprocess.run(["xdg-open", resolved], check=True)
163
164 return {
165 "opened": True,
166 "target": resolved,
167 }
168
169if __name__ == "__main__":
170 app.run(transport="stdio")

이것은 간결한 서버이지만, 중요한 것은 길이가 아닙니다. 중요한 것은 인터페이스의 형태입니다:

  • 모든 도구는 매우 명확한 역할을 가집니다.
  • 모든 도구는 구조화된 데이터를 반환합니다.
  • 명령어 실행은 제한됩니다.
  • 파일 액세스는 루트 디렉토리 내로 제한됩니다.
  • 스크린샷은 지정된 폴더에 저장됩니다.

이것이 바로 로컬 MCP 서버에서 원하는 것입니다.

이 도구들이 이렇게 설계된 이유

에이전트 도구에 대한 고급 콘텐츠는 단순히 "코드를 제공"하는 데서 멈춰서는 안 됩니다. 도구의 설계 방식이 진정한 교훈입니다.

list_files

이 도구는 Claude에게 안전한 탐색 공간을 제공합니다. 다음과 같은 질문에 답할 수 있어야 합니다:

  • 이 프로젝트 폴더에는 무엇이 있나요?
  • Documents에 어떤 노트가 있나요?
  • 검사할 수 있는 로그 파일이 이미 있나요?

하지만 전체 디스크를 재귀적으로 탐색하는 도구가 되어서는 안 됩니다.

read_file

이것은 종종 가장 유용한 로컬 도구입니다. 실제 작업의 상당 부분은 여전히 로컬 Markdown 노트, 로그, CSV, 문서 및 프로젝트 파일에 숨겨져 있습니다.

max_chars 제한이 중요합니다. 큰 파일은 컨텍스트 및 지연 시간 문제를 야기합니다. 거대한 로그 파일의 전체 내용을 반환하는 것은 거의 유용하지 않습니다.

run_command

대부분의 사람들이 이 부분에서 실수합니다.

안전한 패턴은 "셸 액세스를 허용하고 최선을 바라는 것"이 아닙니다. 안전한 패턴은 "검토 가능한 아주 적은 수의 정확한 명령어만 허용하는 것"입니다. 이것이 예제에서 엄격한 허용 목록을 사용하는 이유입니다.

take_screenshot

스크린샷 도구는 Claude가 데스크톱 워크플로우에 참여할 수 있게 해주기 때문에 가치가 있습니다. 첫 번째 버전에서 이미지를 디스크에 저장하는 것만으로도 보고, UI 디버깅, 문서화 캡처 및 구조화된 전달에 이미 유용합니다.

open_target

앱 제어가 GUI 자동화로 시작될 필요는 없습니다. 많은 워크플로우에서 "올바른 폴더, 파일 또는 URL을 여는 것"만으로 충분합니다.

첫날부터 전체 커서 자동화가 필요하다고 가정하는 것보다 이것이 더 지속 가능한 v1입니다.

Claude에 서버 연결하기

로컬 MCP 서버는 일반적으로 stdio를 통해 실행됩니다. 즉, Claude가 프로세스를 로컬에서 시작하고 stdin/stdout을 통해 직접 통신합니다. 로컬 컴퓨터 제어 서버의 경우 불필요한 네트워크 노출을 피할 수 있으므로 올바른 기본값입니다 4 5.

Claude Desktop은 설정을 통해 로컬 MCP 서버를 지원하며, 서버 프로세스를 대신 시작합니다. 실제로는 인터프리터와 스크립트에 절대 경로를 사용하는 것이 가장 덜 취약한 설정입니다. 로컬 GUI 앱 환경이 터미널보다 더 엄격한 경우가 많기 때문입니다 2.

최소한의 설정은 다음과 같습니다:

json
1{
2 "mcpServers": {
3 "local-computer-control": {
4 "command": "/absolute/path/to/python",
5 "args": [
6 "/absolute/path/to/local-mcp-server/server.py"
7 ]
8 }
9 }
10}

uv를 선호한다면 그것도 괜찮습니다:

json
1{
2 "mcpServers": {
3 "local-computer-control": {
4 "command": "/absolute/path/to/uv",
5 "args": [
6 "--directory",
7 "/absolute/path/to/local-mcp-server",
8 "run",
9 "python",
10 "server.py"
11 ]
12 }
13 }
14}

설정을 저장하고 Claude를 다시 시작하면 서버 도구가 로컬 MCP 도구 목록에 나타납니다. Claude Desktop의 로컬 MCP 설정은 정확히 이 모델을 기반으로 구축되었습니다: 로컬 프로세스를 시작하고, stdio를 통해 연결하며, 도구를 모델에 노출합니다 2 3.

Swati Gupta - inline image

테스트에 실제로 유용한 프롬프트

서버가 연결되면 복잡한 오케스트레이션부터 시작하지 마세요. 직접적이고 기본적인 확인부터 시작하세요.

다음과 같은 프롬프트를 시도해 보세요:

  • "내 Desktop 폴더에 있는 파일들을 나열해 줘."
  • "~/Documents/todo.md를 읽고 상위 3개의 우선순위를 요약해 줘."
  • "내 로컬 프로젝트 폴더에서 git status를 실행하고 무엇이 변경되었는지 설명해 줘."
  • "workspace-check라는 이름으로 스크린샷을 찍어 줘."
  • "내 프로젝트 README를 열어 줘."

이러한 간단한 흐름이 일관되게 작동한다면 확장할 가치가 있는 서버를 갖춘 것입니다.

그렇지 않다면, 더 많은 도구를 추가해봐야 실제 문제만 가려질 뿐입니다.

로컬 MCP 서버가 진정한 가치를 발휘하는 곳

가장 확실한 사용 사례는 개발이지만, 그것이 유일한 분야는 아닙니다.

개발자 워크플로우

Claude는 다음을 수행할 수 있습니다:

  • 리포지토리 폴더 검사
  • 설정 파일 읽기
  • git status 실행
  • 버그 상태의 스크린샷 캡처
  • 프로젝트 디렉토리 열기

이것만으로도 많은 컨텍스트 전환을 없앨 수 있습니다.

연구 워크플로우

Claude는 다음을 수행할 수 있습니다:

  • 연구 폴더 나열
  • Markdown 노트 열기 및 요약
  • 구조화된 CSV 읽기
  • 도구 또는 대시보드에서 스크린샷 저장
  • 소스 파일 또는 브라우저 링크 열기

콘텐츠 워크플로우

Claude는 다음을 수행할 수 있습니다:

  • 초안 폴더 스캔
  • 기존 게시물 아이디어 읽기
  • 디자인 참고 자료의 스크린샷 캡처
  • 올바른 작성 파일 또는 URL 열기
  • 빌드 아티팩트 또는 초안 내보내기를 생성하는 제한된 명령어 실행

운영 워크플로우

Claude는 다음을 수행할 수 있습니다:

  • 승인된 디렉토리에서 로그 검사
  • 읽기 전용 진단 명령어 실행
  • 관련 폴더 또는 대시보드 링크 열기
  • 증거 스크린샷 저장

이것이 이 아키텍처의 진정한 목적입니다: 과시용 "컴퓨터 제어"가 아니라 워크플로우 압축입니다.

보안 계층이 곧 제품입니다

이 부분은 너무 많은 기술 문서에서 간과하는 부분입니다.

로컬 MCP의 위험한 부분은 프로토콜이 아니라 잘못된 권한 설계입니다.

이 서버를 데모 이상으로 사용하려면 안전 모델을 조기에 구축하세요.

Swati Gupta - inline image

디렉토리 허용 목록 사용

Claude는 사용자가 명시적으로 승인한 경로만 볼 수 있어야 합니다. 이것이 _resolve_path()가 파일 도구의 핵심인 이유입니다.

명령어 허용 목록 사용

첫 번째 버전에서는 임의의 셸 실행을 절대 노출하지 마세요. 한 줄씩 감사할 수 있는 정확한 명령어로 시작하세요.

읽기 도구와 실행 도구 분리

읽기 전용 도구를 기본값으로 설정해야 합니다. 실행 도구는 의도적으로 도입해야 합니다.

모든 것을 로깅

간단한 추가 전용 JSON 로그만으로도 디버깅과 신뢰도가 훨씬 향상됩니다.

쓰기 작업에 확인 레이어 추가

나중에 write_file, move_file 또는 delete_file을 추가하는 경우, 해당 도구는 두 번째 확인 토큰을 요구하거나 기본적으로 비활성화 상태를 유지해야 합니다.

드라이런 모드 고려

실행 도구의 경우 드라이런 모드는 과소평가됩니다. 이를 통해 Claude는 작업을 수행하기 전에 무엇을 할 것인지 설명할 수 있습니다.

가능하면 제한된 사용자로 실행

로컬 자동화를 진지하게 고려한다면 MCP 서버에 필요한 것보다 더 많은 OS 권한을 부여하지 마세요.

유용한 경험 법칙:

  • 레벨 1: 읽기 전용 도구
  • 레벨 2: 파일 열기/앱 열기와 같은 저위험 작업
  • 레벨 3: 확인된 쓰기 작업
  • 레벨 4: 가볍게 노출해서는 안 되는 파괴적인 작업

대부분의 사람들은 레벨 4가 필요하지 않습니다.

Swati Gupta - inline image

v1 이후 개선할 사항

견고한 첫 번째 버전은 더 강력해질 자격을 얻습니다.

기본 서버가 안정화되면 다음 합리적인 업그레이드는 다음과 같습니다:

  1. 중앙 집중식 정책 파일

config/policy.json으로 규칙을 옮겨 변경 사항을 선언적으로 만듭니다.

예시:

json
1{
2 "allowed_roots": [
3 "~/Documents",
4 "~/Desktop",
5 "./"
6 ],
7 "allowed_commands": [
8 "pwd",
9 "ls",
10 "git status",
11 "git diff --stat",
12 "python --version"
13 ]
14}
  1. 구조화된 로깅

도구 호출, 타임스탬프, 인수 및 결과를 logs/server.log 또는 JSONL 파일에 기록합니다.

  1. 더 안전한 명령어 실행

단일 일반 명령어 도구 대신 명령어를 다음과 같은 더 좁은 도구로 분할합니다:

  • git_status
  • show_current_directory
  • list_project_files

이렇게 하면 Claude가 도구를 더 쉽게 선택할 수 있고, 안전성도 향상됩니다.

  1. 더 나은 스크린샷 처리

"스크린샷을 디스크에 저장"하는 것에서 다음과 같이 발전시킬 수 있습니다:

  • 타임스탬프가 포함된 캡처
  • 활성 창 캡처
  • 영역 캡처
  • 파일 보존 규칙
  1. OS별 자동화 어댑터

macOS에서는 나중에 AppleScript 또는 Shortcuts을 추가할 수 있습니다. Windows에서는 PowerShell 또는 UI Automation을, Linux에서는 데스크톱별 런처 및 창 도구를 추가할 수 있습니다.

하지만 이는 기본적인 로컬 코어가 안정화된 후에 고려해야 합니다.

로컬 MCP 서버에서 사람들이 흔히 저지르는 실수

실수는 예측 가능합니다.

실수 1: 너무 많은 권한, 너무 이른 시점

사람들은 완전한 컴퓨터 제어라는 아이디어를 좋아합니다. 하지만 디버깅은 싫어합니다. 작게 시작하세요.

실수 2: 모호한 도구 이름

도구 이름이 모호하면 Claude가 제대로 사용하지 못합니다. 명확하게 하세요.

나쁨:

  • system_action
  • computer_control

더 나음:

  • list_files
  • read_file
  • run_command
  • take_screenshot
  • open_target

실수 3: 구조화되지 않은 출력

혼합된 텍스트 덩어리는 깔끔한 JSON 객체보다 Claude가 추론하기 어렵습니다.

실수 4: 로깅 부재

도구가 실패했는데 이유를 알 수 없으면 시스템은 추측에 의존하게 됩니다.

실수 5: 모델을 제어 계층으로 취급

Claude는 추론 계층입니다. 서버는 여전히 시행 계층이어야 합니다.

이 구분은 양보할 수 없습니다.

이 아키텍처가 일반 자동화보다 나은 점

전통적인 데스크톱 자동화는 일반적으로 다음 두 가지 중 하나입니다:

  • 깨지기 쉬운 GUI 스크립팅
  • 사용자가 정확히 언제 실행해야 하는지 알아야 하는 고립된 스크립트

로컬 MCP 서버는 사용자의 요청과 사용 가능한 컨텍스트에 따라 Claude가 어떤 도구를 사용할지 결정할 수 있기 때문에 이를 변경합니다.

즉, 단순히 명령어를 자동화하는 것이 아닙니다. 모델이 추론할 수 있는 로컬 기능 계층을 구축하는 것입니다.

이것이 MCP가 중요하게 느껴지는 이유입니다. 단순한 또 다른 통합 패턴이 아닙니다. 애플리케이션 계층에서 가능한 모든 워크플로우를 하드코딩하지 않고도 도구 사용을 모델에 노출하는 더 깔끔한 방법입니다.

존중해야 할 한계

좋은 로컬 MCP 서버라도 실제 한계가 있습니다.

  • 데스크톱 자동화는 운영 체제에 따라 불안정할 수 있습니다.
  • 스크린샷은 유용하지만 만능은 아닙니다.
  • 앱 실행은 쉽지만, 안정적인 UI 조작은 더 어렵습니다.
  • 일반적인 셸 액세스는 위험합니다.
  • 도구 출력이 너무 크면 컨텍스트가 커지는 문제가 발생합니다.
  • 중요한 작업에는 사람의 승인이 여전히 필요합니다.

즉, "모델이 작업을 수행할 수 있다"는 것을 "모델이 감독 없이 작업을 수행해야 한다"와 혼동하지 마십시오.

더 가치 있는 패턴은 맹목적인 자율성이 아닌 협업적 제어입니다.

원클릭 저장

YouMind로 바이럴 글을 AI 심층 읽기

소스를 저장하고, 핵심 질문을 던지고, 주장을 요약해 바이럴 글을 다시 활용할 수 있는 노트로 바꾸세요. 하나의 AI 워크스페이스에서 모두 할 수 있습니다.

YouMind 둘러보기
크리에이터를 위해

당신의 Markdown을 깔끔한 𝕏 글로

직접 쓴 장문을 올릴 때 이미지, 표, 코드 블록을 𝕏에 맞게 정리하는 일은 번거롭습니다. YouMind는 전체 Markdown 초안을 깔끔하고 바로 게시할 수 있는 𝕏 글로 바꿔 줍니다.

Markdown → 𝕏 사용해 보기

분석할 패턴 더 보기

최근 바이럴 아티클

더 많은 바이럴 아티클 보기