YouMind
로그인

하룻밤 만에 Grok Bot 구축하기: 전체 설정 + 복사-붙여넣기 러그 필터

@0xboan
영어2026년 10월 17일
126K
139
19
20
439

TL;DR

이 가이드는 실행 전 사기 토큰을 제거하는 엄격한 14단계 러그 필터에 중점을 두어 처음부터 Grok Bot 트레이딩 데스크를 구축하는 방법을 상세히 설명합니다. '코드가 실행하고, Grok이 분석하며, 사용자가 서명한다'는 워크플로우와 페이퍼 트레이딩 안전장치를 강조합니다.

대부분의 사람들은 다른 사람의 수익률(PnL) 스크린샷을 보고 Grok Bot 을 처음 접한다. 앱을 열고, 차트에 대고, 사기도 전에 이미 죽어버린 토큰에 첫 50 달러를 날린다. 봇이 문제였던 게 아니다. 아무도 봇에게 '죽은 토큰'이 어떤 건지 알려주지 않았을 뿐이다.

다시 처음부터 시작해야 한다면 오늘 밤 당장 똑같이 세팅할 구성이다. 봇 하나, 폴더 하나, 작은 파일 열 개. 100 배짜리를 쫓아다니지 않는다. 주된 임무는 토큰을 걸러내는 것이고, 살아남은 몇 안 되는 토큰조차 단 1 달러가 움직이기 전에 Grok 이 X 를 읽게 만든다.

01 부터 07 까지는 첫날 저녁 작업이다: 봇, 키, 제한 조건, 청산. 08 부터 12 까지는 러그풀 필터로, 14 가지 검사를 전부 코드로 적어둔 것이다. 13 과 14 는 자동 조종 모드를 켜는데, 일단은 페이퍼 트레이딩으로만 돌린다.

내 8 에이전트 데스크 글을 읽었다면, 이게 바로 그 데스크가 서 있는 바닥이다.

https://x.com/0xboan/status/2092226208234647626

사용법: Grok Bot 을 열고 이 글 전체를 붙여넣은 뒤 "/workspace/desk 에 이 데스크를 구축해 줘, 페이퍼 모드, 지갑에 손대는 건 뭐든 나한테 먼저 물어봐"라고 말해라. 봇이 자기 컴퓨터에서 모든 파일을 직접 작성한다.

꼭 기억해야 할 역할 분담: 코드가 죽이고, Grok 이 X 를 읽고, 네가 서명한다. 데스크에서 남의 일을 대신 해주는 녀석은 없다.

01. 시작하기 전에 네 가지를 준비하라

네 가지다. 각각 그게 뭔지, 오늘 밤 뭘 해야 하는지, 데스크에 왜 필요한지 설명한다.

1. Grok Bot

뭔가. 이름 있는 봇들을 고용하는 앱이다. 각 봇은 브라우저, 파일, 터미널이 갖춰진 클라우드 컴퓨터에서 일하며, 노트북 뚜껑을 닫아도 계속 돌아간다.

뭘 하나. x.ai/bot 에서 다운로드하고 로그인한 뒤 Create your own 을 고른다.

왜. 데스크 전체가 그 컴퓨터 안에서 산다. 파일, 루틴, 알림 전부 폰에서도 열 수 있는 한 곳에 모여 있다.

2. xAI API 키

뭔가. 코드에서 Grok 을 유료로 쓰는 권한이며, X 검색 기능이 내장되어 있다.

뭘 하나. console.x.ai 로 가서 API Keys 에서 하나 만든다. 아직 어디에도 붙여넣지 마라. 03 단계에서 어디에 넣는지 알려준다.

왜. 루틴이 채팅창에 앉아서 토큰 40 개를 고민할 수는 없다. 토큰당 호출 한 번씩 하는 스크립트는 가능하고, 모든 답변을 같은 형식으로 기록한다.

3. Rugcheck 와 DexScreener

뭔가. 무료 공개 API 두 개다. Rugcheck 은 컨트랙트를 읽는다: 권한, LP, 홀더, 내부자, 개발자 지갑. DexScreener 는 풀을 읽는다: 깊이, 생성 기간, 매수 대비 매도.

뭘 하나. 없다. 가입도 필요 없고 조회용 키도 없다.

왜. 의견보다 사실이 먼저다. 민트 권한이 열려 있다는 건 판단의 문제가 아니라 사실이고, 사실이 가장 먼저 토큰을 죽인다.

4. 소각용 지갑(단, 오늘 밤은 아님)

뭔가. 다른 걸 담아본 적 없는 새 Solana 지갑이다.

뭘 하나. 아직은 없다. 첫 주는 페이퍼로 돌린다.

왜. 데스크는 절대 키를 쥐지 않는다. 실전에 투입되면 뭘 살지 알려주기만 하고, 서명은 네가 직접 한다.

02. 봇은 여덟 개 말고 하나만 고용하라

내 데스크는 지금 봇 8 개를 굴린다. 처음부터 그랬던 건 아니고, 너도 그래선 안 된다. 첫날 봇 8 개는 동시에 디버깅할 대상 8 개다. 일 하나만 맡은 봇 하나가 나중에 나머지 7 개에 무엇이 필요한지 보여준다.

Create your own 을 선택하고 다음 내용을 토씨 하나 틀리지 않게 넣어라:

text
1이름: RUG DESK
2업무: Solana 밈코인 데스크, 우선 페이퍼 모드
3설명: 내가 붙여넣는 글에 따라 /workspace/desk 에 데스크를 구축하고 실행해라.
4모든 토큰은 예외 없이, 지름길 없이 check.py 를 거쳐야 한다.
5절대 자금을 매수, 매도, 서명, 이동시키지 마라. 내가 이 채팅에서 요청하지 않는 한 config.py 를 수정하지 마라.
6토큰을 통과시키기 위해 필터 수치를 절대 낮추지 마라.
7토큰에 대한 네 의견이 아니라, 걸러낸 이유와 함께 모든 탈락을 보고해라.
8소스가 다운되면 어느 것인지 말하고 건너뛰어라. 누락된 수치는 절대 추측하지 마라.

마지막 두 줄이 앞의 다섯 줄보다 중요하다. 누락된 수치를 추측하는 봇은 결국 네 돈을 날릴 수치를 추측하게 된다.

boan - inline image

03. 키는 채팅창 밖으로 빼둬라

봇에게 입력한 모든 내용은 기록으로 남는다. 그래서 키는 봇의 컴퓨터에서 네가 직접 타이핑해서 파일에 넣어야 한다.

봇의 컴퓨터를 열고 Take over 를 클릭한 뒤 터미널을 열어 아래 명령어를 실행해라. 화면에 키가 표시되지 않게 입력받으므로 히스토리에도 남지 않는다:

bash
1mkdir -p /workspace/desk && cd /workspace/desk
2read -rs -p "xai key: " K && printf 'XAI_API_KEY=%s\n' "$K" > .env && unset K
3chmod 600 .env
4pip install requests

그다음 다른 걸 하기 전에 키가 작동하는지 증명해라. 호출 한 번이면 나중에 잘못된 키와 잘못된 프롬프트를 헷갈릴 일이 없다:

bash
1curl -s https://api.x.ai/v1/responses \
2 -H "Authorization: Bearer $(cut -d= -f2 .env)" \
3 -H "Content-Type: application/json" \
4 -d '{"model":"grok-4.7","input":"reply with OK"}'
5
6-> ... "type":"output_text","text":"OK" ...

I'm done 을 클릭하고 컴퓨터를 봇에게 돌려줘라. 이제부터 봇이 데스크를 굴릴 수 있고, 여전히 채팅창에서는 키를 볼 수 없다.

boan - inline image

04. 만들기 전에 퍼널 전체를 파악하라

8 단계 통과. 무료 단계가 먼저 돈다. 무료 단계를 살아남은 것만 Grok 호출 비용을 쓰고, Grok 이 BUY 라고 부른 것만 실제 돈을 쓴다. 이번 주는 그것조차 아니다.

text
10. LEAD 어디서든 CA 확보: 콜, 게시물, scan.py -> 하나의 민트
21. LIMITS book.py: 일일 손실, 오픈 슬롯, 쿨다운 -> 무료, 네트워크 사용 없음
32. FACTS rugcheck.py: 하드 킬 10개 -> 민트 권한 개방, LP 잠금 해제, 상위 10% 집중 여기서 죽음
43. POOL market.py: 깊이, 생성 기간, 매도 -> 얇은 풀과 허니팟 여기서 죽음
54. JUDGE grok.py + X 검색, 호출 1회 -> BUY 또는 SKIP, 이유 1개, 리스크 1개
65. FILL 페이퍼: book.py 실시간 가격 기준 -> 라이브: 네가 직접 서명
76. WATCH watch.py 10분마다 -> 스탑, 래더, 트레일링, 타임 스탑
87. REPORT report.py 21:30 -> 뭐가 죽었나, 뭐가 벌었나, 뭐를 의심해야 하나

민트 권한이 열려 있다는 건 판단의 문제가 아니다. 사실이고, 사실은 공짜다. Grok 은 스크립트로 할 수 있는 모든 검사를 이미 통과한 토큰만 본다.

boan - inline image

05. 모든 수치는 한 파일에 넣어라

config.py 로 저장해라. 데스크의 모든 수치가 여기에 있으니, 튜닝할 때 이 파일 하나만 수정하면 된다. 이건 내 수치로, 25 달러 티켓에 맞춰져 있다. 추천값이 아니라 네가 앞으로 바꿔나갈 출발점이다.

python
1# config.py
2# 데스크의 모든 수치는 여기에 있다. 튜닝할 때는 이 파일만 수정하고 다른 건 건드리지 마라.
3
4MODEL = "grok-4.7" # API 가 거부하면 console.x.ai -> Models 확인
5PAPER = True # 첫 주: 실제 가격으로 가상 체결. 로그를 다 읽어본 후에만 전환해라
6
7TICKET_USD = 25 # 고정. 직전 수익에 따라 물량을 조절하지 마라
8MAX_OPEN = 4 # 4 x $25 = 시장에 최대 $100
9MAX_ENTRIES_PER_DAY = 6
10DAILY_LOSS_HALT_USD = 50 # 오늘 실현 손실이 이 수치에 도달 -> 내일까지 신규 진입 금지
11COOLDOWN_HOURS = 24 # 방금 빠져나온 토큰 재진입 금지
12
13FACTS = { # 러그 필터. 모델 없이 코드만
14 "max_risk_score": 40, # rugcheck score_normalised, 0-100, 높을수록 위험
15 "min_lp_locked_pct": 90, # 가장 큰 풀: LP 잠금 또는 소각 여부
16 "max_top10_pct": 30, # 상위 10 홀더, 풀 및 락커 제외
17 "max_dev_pct": 5, # 크리에이터 지갑에 남아있는 공급량
18 "max_insiders": 0, # 내부자 플래그 홀더 + 내부자 그래프 적중
19 "min_liquidity_usd": 30000,
20 "max_ticket_share": 0.01, # 네 티켓은 풀의 1% 를 넘지 않음
21 "min_age_min": 30, # 이보다 짧으면 모든 리포트가 아직 추측 수준
22 "max_age_hours": 72,
23}
24
25MIN_CONFIDENCE = 0.7 # Grok 이 이 수치 미만에서 BUY 라고 하면 -> SKIP 처리
26
27EXIT = {
28 "stop_pct": -35, # 진입가 기준 하드 스탑
29 "ladder": [(100, 0.50), (200, 0.25)], # +100% 에서 절반 매도, +200% 에서 1/4 매도
30 "trail_pct": 30, # 나머지: 고점 대비 30% 하락 시 전량 청산
31 "time_stop_hours": 6, # 6 시간 동안 +20% 못 가면 -> 청산, 묶인 자금
32 "time_stop_min_pct": 20,
33 "liq_drop_pct": 40, # 풀 유동성이 진입가 대비 40% 감소 -> 즉시 청산
34}
35
36LOG = "checks.csv"
37POSITIONS = "positions.json"
38TRADES = "trades.csv"
text
1고정 티켓. 최근 세 번 이겼다고 물량이 늘어나진 않는다.
2봇은 config.py 를 읽기만 하지 절대 쓰지 않는다. 수치는 네가 바꿀 때만 움직인다.
3필터를 완화하려면 글로 남겨라. 수치를 바꾸면 같은 날 채팅에 왜 바꿨는지 적어라.

06. 다른 무엇보다 제한 조건부터 돌려라

book.py 로 저장해라. 데스크에서 가장 저렴한 검사는 API 를 아예 호출하지 않는 검사다. 오늘 이미 50 달러를 잃었다면 데스크는 토큰을 쳐다보지도 않는다. 포지션이 4 개 열려 있어도 마찬가지다. 이 파일은 데스크의 기억이기도 하다: 네가 직접 열 수 있는 positions.json 과 trades.csv 두 파일이다.

python
1# book.py
2# 포지션, 거래, 제한 조건. 데스크의 기억은 네가 직접 열 수 있는 두 파일에 산다.
3import csv, json, os, sys, time
4from config import (POSITIONS, TRADES, TICKET_USD, MAX_OPEN, MAX_ENTRIES_PER_DAY,
5 DAILY_LOSS_HALT_USD, COOLDOWN_HOURS)
6
7def load() -> dict:
8 return json.load(open(POSITIONS)) if os.path.exists(POSITIONS) else {}
9
10def save(book: dict):
11 json.dump(book, open(POSITIONS, "w"), indent=2)
12
13def trades() -> list[dict]:
14 return list(csv.DictReader(open(TRADES))) if os.path.exists(TRADES) else []
15
16def record(mint, symbol, action, usd, pnl, reason):
17 new = not os.path.exists(TRADES)
18 with open(TRADES, "a", newline="") as f:
19 w = csv.DictWriter(f, fieldnames=["time", "mint", "symbol", "action", "usd", "pnl", "reason"])
20 if new:
21 w.writeheader()
22 w.writerow({"time": time.strftime("%Y-%m-%d %H:%M"), "mint": mint, "symbol": symbol,
23 "action": action, "usd": round(usd, 2), "pnl": round(pnl, 2), "reason": reason})
24
25def today_pnl() -> float:
26 day = time.strftime("%Y-%m-%d")
27 return sum(float(t["pnl"]) for t in trades() if t["time"].startswith(day))
28
29def guard(mint: str) -> str | None:
30 """제한 조건은 네트워크 호출 전에 실행된다. 차단된 진입은 비용이 들지 않는다."""
31 day = time.strftime("%Y-%m-%d")
32 book = load()
33 if today_pnl() <= -DAILY_LOSS_HALT_USD: return "halted_daily_loss"
34 if mint in book: return "already_open"
35 if len(book) >= MAX_OPEN: return "max_open"
36 opens = [t for t in trades() if t["action"] == "open" and t["time"].startswith(day)]
37 if len(opens) >= MAX_ENTRIES_PER_DAY: return "max_entries_today"
38 cutoff = time.strftime("%Y-%m-%d %H:%M", time.localtime(time.time() - COOLDOWN_HOURS * 3600))
39 if any(t["mint"] == mint and t["action"] == "close" and t["time"] >= cutoff for t in trades()):
40 return "cooldown"
41 return None
42
43def open_position(mint, symbol, price, liq_usd, usd=TICKET_USD):
44 book = load()
45 book[mint] = {"symbol": symbol, "entry": price, "qty0": usd / price, "qty": usd / price,
46 "peak": price, "liq0": liq_usd, "opened": time.time(), "steps": 0}
47 save(book)
48 record(mint, symbol, "open", usd, 0, "entry")
49
50def sell(mint, frac_of_original, price, reason) -> float:
51 """원래 물량의 일부 비율을 매도한다(1.0 = 남은 전량). 실현 손익을 반환한다."""
52 book = load()
53 p = book[mint]
54 q = min(p["qty"], p["qty0"] * frac_of_original)
55 pnl = q * (price - p["entry"])
56 p["qty"] -= q
57 action = "close" if p["qty"] <= p["qty0"] * 1e-6 else "partial"
58 record(mint, p["symbol"], action, q * price, pnl, reason)
59 if action == "close":
60 del book[mint]
61 save(book)
62 return pnl
63
64if __name__ == "__main__":
65 # 라이브 모드: 본인 지갑에서 매수한 후 -> python book.py add <mint> <usd> <price> <liq_usd>
66 # 수동으로 매도한 후 -> python book.py close <mint> <price>
67 cmd = sys.argv[1]
68 if cmd == "add":
69 mint, usd, price, liq = sys.argv[2], float(sys.argv[3]), float(sys.argv[4]), float(sys.argv[5])
70 open_position(mint, mint[:6], price, liq, usd)
71 elif cmd == "close":
72 print(round(sell(sys.argv[2], 1.0, float(sys.argv[3]), "manual"), 2))

07. 진입보다 청산을 먼저 짜라

대부분의 사람들은 초록색 캔들을 뚫어져라 보면서 언제 팔지 결정한다. 뭔가를 결정하기에 최악의 순간이다. 이 데스크에서는 토큰을 사기 전에 모든 청산 조건이 존재하며, 봇은 10 분마다 그걸 확인할 뿐이다:

  • 유동성이 진입가 대비 40% 빠짐: 질문 없이 전량 즉시 청산
  • 35% 하락: 청산
  • 100% 상승: 절반 매도, 나머지는 시장 돈
  • 200% 상승: 추가로 1/4 매도
  • 첫 번째 익절 이후, 나머지는 고점 대비 30% 하락 시 청산
  • 6 시간 동안 +20% 달성 실패: 청산, 그 슬롯은 희망보다 가치가 크다

watch.py 로 저장해라:

python
1# watch.py
2# 열린 모든 포지션을 10 분마다 확인. 변동 없으면 아무것도 출력하지 않는다.
3import time
4from config import EXIT, PAPER
5from market import pool
6import book
7
8def watch() -> list[str]:
9 events = []
10 for mint, p in list(book.load().items()):
11 try:
12 m = pool(mint)
13 except Exception as e:
14 events.append(f"{p['symbol']}: 가격 피드 다운 ({e}), 보유 중, 재시도 예정")
15 continue
16 if not m or m["price"] <= 0:
17 events.append(f"{p['symbol']}: 풀 증발. 러그풀로 간주, 가능한 만큼 매도")
18 book.sell(mint, 1.0, 0.0, "pool_gone")
19 continue
20
21 price = m["price"]
22 b = book.load(); s = b[mint]
23 s["peak"] = max(s["peak"], price); book.save(b)
24 pct = (price / s["entry"] - 1) * 100
25 hours = (time.time() - s["opened"]) / 3600
26
27 def out(frac, why):
28 pnl = book.sell(mint, frac, price, why)
29 verb = "sold" if PAPER else "SELL NOW in your wallet"
30 size = "the rest" if frac >= 1 else f"{int(frac * 100)}%"
31 events.append(f"{s['symbol']}: {verb} {size} at {pct:+.0f}% ({why}), pnl ${pnl:+.2f}")
32
33 if m["liq_usd"] < s["liq0"] * (1 - EXIT["liq_drop_pct"] / 100):
34 out(1.0, "liquidity_pulled"); continue
35 if pct <= EXIT["stop_pct"]:
36 out(1.0, "stop"); continue
37 ladder = EXIT["ladder"]
38 if s["steps"] < len(ladder) and pct >= ladder[s["steps"]][0]:
39 out(ladder[s["steps"]][1], f"take_profit_{ladder[s['steps']][0]}")
40 b = book.load()
41 if mint in b:
42 b[mint]["steps"] += 1; book.save(b)
43 continue
44 if s["steps"] > 0 and price <= s["peak"] * (1 - EXIT["trail_pct"] / 100):
45 out(1.0, "trailing_stop"); continue
46 if hours >= EXIT["time_stop_hours"] and pct < EXIT["time_stop_min_pct"]:
47 out(1.0, "time_stop")
48 return events
49
50if __name__ == "__main__":
51 for e in watch():
52 print(e)
text
1유동성이 최우선. 풀이 빠져나가면 수익 중인 포지션이라도 다른 모든 규칙을 덮어쓴다.
2트레일링 스탑은 첫 익절 이후에 작동한다. 그전엔 -35% 가 유일한 하한선이다.
3묶인 자금도 손실이다. 6 시간 횡보하면 그 슬롯을 비워 다음 토큰에 써라.
4침묵도 상태다. watch.py 는 변동 없으면 아무것도 출력하지 않으므로, 채팅창이 조용하면 굳이 들여다볼 필요가 없다.
boan - inline image

08. 토큰이 죽는 14 가지 방법을 익혀라

위의 모든 걸 버리고 하나만 남겨야 한다면 이걸 택하겠다. 봇 없이도 작동한다: 다음 매수 전에 rugcheck.xyz 와 dexscreener 에서 같은 14 가지 검사를 수동으로 돌려보면, 곧 후회하게 될 대부분의 토큰을 피할 수 있다.

13 개 검사는 코드다. 비교 연산이지 모델이나 의견이 끼어들 자리가 없다. 14 번째만이 유일하게 Grok 을 필요로 한다.

#

검사 항목

탈락 조건

이유

1

이미 러그풀

Rugcheck 가 러그풀로 표시

누군가 이미 해치웠다

2

민트 권한

아직 열려 있음

개발자가 네 물량 위로 새 토큰을 찍어낼 수 있다

3

동결 권한

아직 열려 있음

개발자가 네 토큰을 동결시켜 팔 수 없게 만든다

4

위험 플래그

danger 레벨 리스크 하나라도 존재

Rugcheck 가 네가 반박해야 할 무언가를 이미 발견했다

5

리스크 점수

40/100 초과

작은 경고들이 쌓인다

6

LP 잠금

가장 큰 풀의 잠금 또는 소각 비율이 90% 미만

잠기지 않은 유동성은 출금 버튼이다

7

상위 10 홀더

30% 초과 (풀 및 락커 제외)

지갑 10 개가 합심해 네 거래를 끝낼 수 있다

8

개발자 지갑

크리에이터가 아직 5% 이상 보유

개발자가 첫 번째 매도자다

9

내부자 / 번들

내부자 홀더 또는 내부자 그래프 적중

함께 자금을 받은 지갑들은 함께 매도한다

10

풀 깊이

유동성 3 만 달러 미만

네가 누군가의 출구 물량이 된다

11

티켓 비중

네 25 달러가 풀의 1% 초과

네 매수세 자체가 가격을 움직인다

12

생성 기간

30 분 미만이거나 72 시간 초과

리포트를 믿기엔 너무 이르거나, 이미 움직임이 끝났다

13

매도 존재 여부

지난 1 시간 매수 20 건 이상, 매도 0 건

허니팟의 전형적인 형태다

14

X 판독 (Grok)

게시물 없음, 카피캣 CA, 과대광고 매수, 러그 이력

어떤 API 도 반환해주지 않는 영역

boan - inline image

09. 뻔한 놈들은 코드가 죽이게 둬라

1 번부터 9 번까지의 검사다. 리포트 하나를 가져와서 데스크가 읽는 필드만 남긴다. Grok 은 원본 리포트를 절대 보지 않는다. 무거운 페이로드는 토큰 비용과 정확도를 모두 갉아먹는다.

사람들이 놓치는 디테일 하나: 풀 자체의 볼트와 락커가 상위 홀더 목록에 들어 있다. 이걸 포함하면 모든 토큰이 고래 토큰처럼 보인다. 코드는 상위 10 개를 합산하기 전에 이들을 제외한다.

python
1# rugcheck.py
2# 토큰당 리포트 하나, 데스크가 읽는 필드만 남긴다. Grok 은 원본 리포트를 절대 보지 않는다.
3import requests
4from config import FACTS
5
6RC = "https://api.rugcheck.xyz/v1/tokens"
7
8def report(mint: str) -> dict:
9 r = requests.get(f"{RC}/{mint}/report", timeout=20)
10 r.raise_for_status()
11 d = r.json()
12 token = d.get("token") or {}
13 known = d.get("knownAccounts") or {}
14
15 def is_pool(h): # AMM 볼트와 락커는 상위 홀더에 있지만 고래가 아니다
16 tag = known.get(h.get("owner")) or known.get(h.get("address")) or {}
17 return str(tag.get("type", "")).upper() in ("AMM", "LOCKER")
18
19 holders = [h for h in (d.get("topHolders") or []) if not is_pool(h)]
20 markets = sorted(d.get("markets") or [],
21 key=lambda m: ((m.get("lp") or {}).get("baseUSD") or 0)
22 + ((m.get("lp") or {}).get("quoteUSD") or 0),
23 reverse=True)
24 lp = (markets[0].get("lp") or {}) if markets else {}
25 supply = token.get("supply") or 0
26 risks = d.get("risks") or []
27
28 return {
29 "mint": mint,
30 "symbol": (d.get("tokenMeta") or {}).get("symbol"),
31 "rugged": bool(d.get("rugged")),
32 "risk_score": d.get("score_normalised"),
33 "danger": [x.get("name") for x in risks if x.get("level") == "danger"],
34 "warn": [x.get("name") for x in risks if x.get("level") == "warn"],
35 "mint_authority": token.get("mintAuthority", d.get("mintAuthority")),
36 "freeze_authority": token.get("freezeAuthority", d.get("freezeAuthority")),
37 "lp_locked_pct": lp.get("lpLockedPct", d.get("lpLockedPct")),
38 "top10_pct": round(sum(h.get("pct") or 0 for h in holders[:10]), 2) if holders else None,
39 "dev_pct": round((d.get("creatorBalance") or 0) / supply * 100, 2) if supply else None,
40 "insiders": sum(1 for h in holders if h.get("insider")) + (d.get("graphInsidersDetected") or 0),
41 "holders": d.get("totalHolders"),
42 }
43
44def fact_kill(t: dict) -> str | None:
45 """단순 비교. 첫 번째 실패가 판정을 결정하고, 이유는 기록된다."""
46 if t["risk_score"] is None: return "no_report"
47 if t["rugged"]: return "already_rugged"
48 if t["mint_authority"]: return "mint_open"
49 if t["freeze_authority"]: return "freeze_open"
50 if t["danger"]: return "danger:" + t["danger"][0]
51 if t["risk_score"] > FACTS["max_risk_score"]: return "risk_score"
52 if t["lp_locked_pct"] is None or t["lp_locked_pct"] < FACTS["min_lp_locked_pct"]:
53 return "lp_unlocked"
54 if t["top10_pct"] is None or t["top10_pct"] > FACTS["max_top10_pct"]:
55 return "top10_heavy"
56 if t["dev_pct"] is None or t["dev_pct"] > FACTS["max_dev_pct"]:
57 return "dev_holds"
58 if t["insiders"] > FACTS["max_insiders"]: return "insiders"
59 return None
boan - inline image

10. 컨트랙트 말고 풀을 확인하라

10 번부터 13 번까지의 검사다. 컨트랙트가 완벽해도 풀이 널 내보내주지 않을 수 있다. 해당 토큰의 가장 깊은 풀을 읽어 지루한 질문 4 개를 던진다: 잔고가 충분한가, 내 티켓 규모는 그에 비해 어떤가, 얼마나 오래됐는가, 실제로 파는 사람이 있는가.

python
1# market.py
2# 풀 자체: 깊이, 생성 기간, 가격, 그리고 실제로 누군가 매도할 수 있는지.
3import time
4import requests
5from config import FACTS, TICKET_USD
6
7DS = "https://api.dexscreener.com/tokens/v1/solana"
8
9def pool(mint: str) -> dict | None:
10 r = requests.get(f"{DS}/{mint}", timeout=15)
11 r.raise_for_status()
12 data = r.json()
13 pairs = data if isinstance(data, list) else (data.get("pairs") or [])
14 pairs = [p for p in pairs if (p.get("baseToken") or {}).get("address") == mint]
15 if not pairs:
16 return None
17 p = max(pairs, key=lambda x: (x.get("liquidity") or {}).get("usd") or 0)
18 h1 = (p.get("txns") or {}).get("h1") or {}
19 born = p.get("pairCreatedAt")
20 return {
21 "symbol": (p.get("baseToken") or {}).get("symbol"),
22 "price": float(p.get("priceUsd") or 0),
23 "liq_usd": (p.get("liquidity") or {}).get("usd") or 0,
24 "age_min": round((time.time() * 1000 - born) / 60000) if born else None,
25 "buys_1h": h1.get("buys", 0),
26 "sells_1h": h1.get("sells", 0),
27 "vol_1h": (p.get("volume") or {}).get("h1"),
28 "url": p.get("url"),
29 }
30
31def market_kill(m: dict | None) -> str | None:
32 if not m: return "no_pool"
33 if m["price"] <= 0: return "no_price"
34 if m["liq_usd"] < FACTS["min_liquidity_usd"]: return "thin_pool"
35 if TICKET_USD / m["liq_usd"] > FACTS["max_ticket_share"]: return "ticket_too_big"
36 if m["age_min"] is None or m["age_min"] < FACTS["min_age_min"]:
37 return "too_fresh"
38 if m["age_min"] > FACTS["max_age_hours"] * 60: return "too_old"
39 if m["buys_1h"] >= 20 and m["sells_1h"] == 0: return "nobody_can_sell"
40 return None

11. 코드가 못하는 건 Grok 이 읽게 둬라

14 번 검사. 이 데스크가 다른 모델이 아닌 Grok 위에서 돌아가는 이유다: 생각하면서 X 를 검색할 수 있기 때문이다. 토큰이 13 개 검사를 다 통과해도 컨트랙트 주소만 바꾼 카피캣이거나, 지난주에 만든 계정 40 개가 같은 분에 같은 문장을 올린 론칭일 수 있다. 어떤 API 도 그걸 반환해주지 않는다. X 는 해준다.

grok.py 로 저장해라. 키를 읽는 유일한 파일이자 데스크가 xAI 와 대화하는 유일한 장소다:

python
1# grok.py
2# 키를 보관하는 유일한 파일이자, 데스크가 xAI와 통신하는 유일한 장소입니다.
3import os, json, datetime, requests
4from config import MODEL
5
6URL = "https://api.x.ai/v1/responses"
7
8def _key() -> str:
9 k = os.environ.get("XAI_API_KEY")
10 if not k and os.path.exists(".env"):
11 for line in open(".env"):
12 if line.startswith("XAI_API_KEY="):
13 k = line.split("=", 1)[1].strip().strip('"')
14 if not k:
15 raise RuntimeError("환경 변수나 .env에 XAI_API_KEY가 없습니다")
16 return k
17
18def _text(resp: dict) -> str:
19 if isinstance(resp.get("output_text"), str):
20 return resp["output_text"]
21 parts = []
22 for item in resp.get("output") or []:
23 if item.get("type") == "message":
24 for c in item.get("content") or []:
25 if c.get("type") in ("output_text", "text"):
26 parts.append(c.get("text") or "")
27 return "\n".join(parts)
28
29def ask(system: str, payload: dict, read_x: bool = False) -> dict:
30 """한 번의 호출로 JSON을 넣고 JSON을 받습니다. JSON이 아니면 예외가 발생하고, 예외는 상위 단계에서 SKIP(건너뛰기) 처리됩니다."""
31 body = {"model": MODEL,
32 "input": [{"role": "system", "content": system},
33 {"role": "user", "content": json.dumps(payload)}]}
34 if read_x:
35 since = (datetime.date.today() - datetime.timedelta(days=3)).isoformat()
36 body["tools"] = [{"type": "x_search", "from_date": since}]
37 body["max_turns"] = 2 # 두 차례 검색 후 반드시 답변해야 함
38 r = requests.post(URL, timeout=180, json=body,
39 headers={"Authorization": f"Bearer {_key()}"})
40 r.raise_for_status()
41 text = _text(r.json())
42 start, end = text.find("{"), text.rfind("}")
43 if start == -1:
44 raise ValueError(f"답변에 JSON이 없습니다: {text[:200]}")
45 return json.loads(text[start:end + 1])

prompts.py 로 저장하세요. 실제로 다시 읽어볼 파일은 바로 이것입니다:

python
1# prompts.py
2# 데스크에 있는 단 하나의 Grok 프롬프트입니다. 실제로 다시 읽어볼 파일은 바로 이것입니다.
3
4JUDGE_PROMPT = """
5당신은 RUG DESK입니다. 솔라나 밈코인이 코드로 된 모든 하드 체크를 통과했습니다:
6권한(authorities), LP, 홀더, 개발자 지갑, 내부자, 풀 깊이, 생성 기간.
7해당 수치를 다시 확인하지 마세요. 단일 플랫 티켓 한 장을 넣을지 당신이 결정합니다.
8당신은 절대 물량을 조절하거나 청산 시점을 고르지 않습니다.
9
10입력: {"token": {...rugcheck 사실...}, "pool": {...dexscreener 사실...}}
11
121단계. 지난 72시간 동안 X에서 티커와 컨트랙트 주소를 검색하세요.
132단계. 코드가 볼 수 없는 것만 판단하세요:
14 - 경고(warn) 수준의 리스크들이 합쳐져 더 심각한 문제가 되는가
15 - 누가 먼저 게시했는가: 개발자인가, 이력이 있는 실제 계정인가, 아니면 새로 만든 계정의 물량 공세인가
16 - 자연스러운 반응인가 매수된 것인가: 같은 문구, 같은 시간대, 과거 이력이 없는 계정들
17 - 해당 게시물들의 컨트랙트 주소가 입력(INPUT)과 동일한가
18 - 이 티커명이나 이 개발자가 이전에 러그풀과 연관된 적이 있는가
19
20JSON만 반환하세요:
21{"verdict": "BUY" | "SKIP", "confidence": 0.0-1.0,
22 "why": "최대 두 줄", "x_read": "X 상황이 어떤지 한 줄",
23 "main_risk": "한 줄"}
24
25X에 게시물이 없는 것도 하나의 답입니다: 아무도 존재를 모릅니다. SKIP.
26X에서 같은 티커로 다른 컨트랙트 주소가 보인다면: 카피캣 리스크. SKIP.
27필요한 필드가 null일 때는 절대 BUY라고 답하지 마세요.
28절대 목표가를 언급하지 마세요. 당신은 그것을 알지 못합니다.
29"""

12. 하나의 명령어로 연결하기

컨트랙트 주소 하나가 들어가면 판정 하나가 나옵니다. 제한, 사실, 풀, Grok 순서대로 진행되며, 가장 먼저 '아니오'를 말하는 단계에서 종료됩니다:

python
1# check.py
2# 컨트랙트 주소 하나가 들어가면 판정 하나가 나옵니다. 토큰이 데스크를 거칠 때마다 실행됩니다.
3import csv, json, os, sys, time
4from config import LOG, MIN_CONFIDENCE, PAPER, TICKET_USD
5from rugcheck import report, fact_kill
6from market import pool, market_kill
7from grok import ask
8from prompts import JUDGE_PROMPT
9import book
10
11def log(row: dict):
12 new = not os.path.exists(LOG)
13 with open(LOG, "a", newline="") as f:
14 w = csv.DictWriter(f, fieldnames=["time", "mint", "symbol", "stage", "verdict",
15 "confidence", "reason"])
16 if new:
17 w.writeheader()
18 w.writerow({k: row.get(k) for k in w.fieldnames})
19
20def check(mint: str) -> dict:
21 if (g := book.guard(mint)): # 제한, 무료
22 return {"stage": "limits", "verdict": "SKIP", "reason": g}
23
24 try:
25 token = report(mint) # 러그 필터, 무료
26 except Exception as e:
27 return {"stage": "facts", "verdict": "SKIP", "reason": f"rugcheck 실패: {e}"}
28 if (k := fact_kill(token)):
29 return {"stage": "facts", "verdict": "SKIP", "reason": k, "symbol": token["symbol"]}
30
31 try:
32 m = pool(mint) # 풀, 무료
33 except Exception as e:
34 return {"stage": "pool", "verdict": "SKIP", "reason": f"dexscreener 실패: {e}"}
35 if (k := market_kill(m)):
36 return {"stage": "pool", "verdict": "SKIP", "reason": k, "symbol": token["symbol"]}
37
38 try:
39 ans = ask(JUDGE_PROMPT, {"token": token, "pool": m}, read_x=True) # 유일한 유료 단계
40 except Exception as e:
41 return {"stage": "grok", "verdict": "SKIP", "reason": f"grok 실패: {e}"}
42
43 verdict, conf = ans.get("verdict"), ans.get("confidence") or 0
44 if verdict == "BUY" and conf < MIN_CONFIDENCE:
45 verdict = "SKIP"
46 ans["why"] = f"낮은 신뢰도 {conf}"
47 return {**ans, "stage": "grok", "verdict": verdict, "symbol": m["symbol"] or token["symbol"],
48 "reason": ans.get("why"), "price": m["price"], "liq_usd": m["liq_usd"], "url": m["url"]}
49
50if __name__ == "__main__":
51 mint = sys.argv[1].strip()
52 v = check(mint)
53 log({"time": time.strftime("%Y-%m-%d %H:%M"), "mint": mint, **v})
54 if v["verdict"] == "BUY":
55 if PAPER:
56 book.open_position(mint, v["symbol"], v["price"], v["liq_usd"])
57 v["action"] = f"PAPER BUY ${TICKET_USD} at {v['price']}"
58 else:
59 v["action"] = (f"승인 필요: 본인 지갑에서 {v['symbol']} ${TICKET_USD} 어치를 매수한 뒤, "
60 f"다음 명령어를 실행하세요: python book.py add {mint} {TICKET_USD} <체결가> {v['liq_usd']}")
61 print(json.dumps(v, indent=2))

이것을 사용하는 데 스캐너는 필요 없습니다. 콜 채널의 CA, 게시물, 친구의 메시지 등에서 얻은 CA를 봇에게 "check <CA>" 라고 말하면 한 줄이 실행됩니다. 돌아오는 결과는 다음과 같습니다(실제 토큰이 아닌 예시 형태):

json
1{
2 "verdict": "SKIP",
3 "confidence": 0.78,
4 "why": "모든 하드 체크를 통과했지만, 처음 30개의 게시물이 이번 주에 생성된 계정들에서 나왔고 문구가 동일함.",
5 "x_read": "실제 계정 하나, 새로 만든 계정들의 물량 공세, CA 불일치 없음",
6 "main_risk": "매수된 과장 광고, 게시가 멈추면 매수자들이 떠남",
7 "stage": "grok",
8 "symbol": "EXAMPLE",
9 "reason": "모든 하드 체크를 통과했지만, 처음 30개의 게시물이 이번 주에 생성된 계정들에서 나왔고 문구가 동일함."
10}
boan - inline image
text
1의견보다 사실이 먼저다. Grok은 민트가 열려 있는지 여부를 절대 판단하지 않는다.
2실패한 호출은 SKIP이다. Rugcheck 다운, DexScreener 다운, Grok 다운, 잘못된 JSON: 모두 SKIP.
3NULL은 안전하다는 뜻이 아니다. 값이 돌아오지 않은 필드는 토큰을 탈락시키며, 절대 통과시키지 않는다.
4토큰당 판정은 하나다. BUY가 나올 때까지 절대 다시 실행하지 마라. 동의할 수 없다면 config.py에 동의할 수 없는 것이다.
5매수가 아니라 탈락 사유를 읽어라. 매수는 기분이 좋다. 탈락 사유가 필터가 제대로 작동하는지 알려준다.

13. 세 가지 루틴, 각각 한 문장씩

워크플로우 빌더는 없습니다. 봇에게 각 루틴을 한 문장으로 한 번만 알려주면 다음 실행 결과를 보여줍니다. 제가 쓰는 문장은 토씨 하나 틀리지 않고 다음과 같습니다:

text
110분마다 /workspace/desk에서 python watch.py를 실행해.
2아무것도 출력하지 않으면 말하지 마. 무언가 출력하면 여기에 토씨 하나 안 틀리고 그대로 올려줘.
text
130분마다 /workspace/desk에서 python scan.py를 실행해.
2BUY 판정만 올려줘: 심볼, 신뢰도, x_read.
3Rugcheck나 DexScreener가 다운되면 매번 말고 한 번만 어느 쪽인지 말해줘.
text
1매일 21:30에 /workspace/desk에서 python report.py를 실행하고 결과를 여기에 올려줘.
2오늘 잘 통했던 것 하나와 config.py에서 의심되는 숫자 하나를 이유와 함께 추가해.
3그 숫자를 네가 직접 바꾸지는 마.

이 루틴들이 실행하는 두 개의 파일입니다. report.py 로 저장하세요:

python
1# report.py
2# 저녁 보고서. 모든 내용은 checks.csv와 trades.csv에서 가져오며 기억에는 의존하지 않습니다.
3import csv, os, time
4from collections import Counter
5from config import LOG, PAPER
6import book
7
8day = time.strftime("%Y-%m-%d")
9checks = [r for r in csv.DictReader(open(LOG))] if os.path.exists(LOG) else []
10checks = [r for r in checks if r["time"].startswith(day)]
11done = [t for t in book.trades() if t["time"].startswith(day) and t["action"] != "open"]
12
13by_stage = Counter(r["stage"] for r in checks if r["verdict"] == "SKIP")
14buys = [r for r in checks if r["verdict"] == "BUY"]
15wins = [t for t in done if float(t["pnl"]) > 0]
16reasons = Counter(r["reason"] for r in checks if r["stage"] in ("facts", "pool"))
17
18print(f"저녁 보고서 {day} {'(PAPER)' if PAPER else '(LIVE)'}")
19print(f"검사 수: {len(checks)}")
20print(f"러그 필터 탈락: {by_stage['facts']} | 풀 탈락: {by_stage['pool']} | "
21 f"Grok 탈락: {by_stage['grok']} | 제한 탈락: {by_stage['limits']}")
22print(f"매수: {len(buys)} | 현재 보유: {len(book.load())}")
23print(f"오늘 실현 손익: ${book.today_pnl():+.2f}")
24if done:
25 print(f"수익 청산: {len(wins)}/{len(done)}")
26 best = max(done, key=lambda t: float(t["pnl"]))
27 worst = min(done, key=lambda t: float(t["pnl"]))
28 print(f"최고: {best['symbol']} ${float(best['pnl']):+.2f} | 최악: {worst['symbol']} ${float(worst['pnl']):+.2f}")
29if reasons:
30 top, n = reasons.most_common(1)[0]
31 print(f"가장 흔한 탈락 사유: {top} ({n}회)")

그리고 선택적 리드 소스인 scan.py 입니다. 방금 DexScreener 프로필 비용을 지불한 새로운 솔라나 토큰들을 가져옵니다. 유료 프로필이 품질을 의미하는 것은 아니며, 단지 개발자가 돈을 썼다는 뜻일 뿐입니다. 나머지는 필터가 결정합니다:

python
1# scan.py
2# 선택적 리드 소스: 방금 DexScreener 프로필 비용을 지불한 새로운 솔라나 토큰들.
3# 유료 프로필이 품질을 의미하는 것은 아니며, 단지 개발자가 돈을 썼다는 뜻일 뿐입니다. 필터가 결정합니다.
4import csv, os, subprocess, sys
5import requests
6from config import LOG
7
8seen = {r["mint"] for r in csv.DictReader(open(LOG))} if os.path.exists(LOG) else set()
9r = requests.get("https://api.dexscreener.com/token-profiles/latest/v1", timeout=15)
10r.raise_for_status()
11fresh = [t["tokenAddress"] for t in r.json()
12 if t.get("chainId") == "solana" and t.get("tokenAddress") not in seen]
13
14for mint in fresh[:5]: # 실행당 5개씩이라 바쁜 한 시간에도 API 예산이 바닥나지 않음
15 subprocess.run([sys.executable, "check.py", mint])

오늘 밤 마지막 할 일: Settings, General, Auto-review로 이동하세요. "Ask first(먼저 물어보기)" 규칙 두 개를 추가합니다. 이 규칙들은 루틴이 어떻게 생각하든 상관없이 동작을 멈추고 당신을 기다리게 만듭니다:

text
1지갑 연결, 서명, 스왑 또는 전송 전에는 항상 먼저 물어봐.
2config.py라는 이름의 파일을 수정하기 전에는 항상 먼저 물어봐.
boan - inline image
boan - inline image

14. 단 1달러 쓰기 전에 페이퍼 트레이딩으로 일주일

PAPER = True 는 데스크가 돈만 제외하고 모든 것을 실전처럼 수행한다는 뜻입니다. 실제 토큰, 실제 가격, 실제 청산, 가짜 체결. 7일 후 checks.csv 를 열어 남이 쓴 것처럼 읽어보세요.

확인해야 할 것: 어떤 검사가 가장 많이 탈락시키는지, BUY 판정이 나온 것들이 정말 내가 샀을 것들인지, SKIP 사유 중 틀린 것이 없는지. 페이퍼 체결은 화면상의 가격에서 일어나지만 실전 체결은 절대 그렇지 않으므로, 페이퍼 결과는 예측치가 아니라 상한선으로 취급하세요.

실전으로 넘어갈 때는 PAPER 를 False 로 바꾸면 되며, 마지막 단계를 제외하고는 바뀌는 것이 없습니다. check.py 가 매수를 멈추고 대신 승인 안내문을 출력합니다: 본인 지갑에서 $25 어치를 직접 매수한 뒤, 명령어 하나로 장부에 기록하면 watch.py 가 이를 보호하기 시작합니다.

문제가 생겼을 때

돈이 걸린 일이므로, 모든 오류에 대한 답은 문제가 발생하기 전에 미리 적어둬야 합니다.

text
1xAI에서 401 발생 -> 잘못된 키. 직접 개입해서 .env를 다시 쓰고 03단계의 curl을 다시 실행하세요.
2xAI에서 400 / 404 발생 -> 모델 이름 변경됨. config.py의 MODEL만 수정하세요.
3xAI에서 429 발생 -> 속도 제한. 해당 토큰은 SKIP이고 다음 실행으로 넘어갑니다. 절대 반복하지 마세요.
4x_search 관련 오류 -> 사용 중인 MODEL이 X 검색을 지원하지 않습니다. console.x.ai -> Models에서 지원되는 모델을 고르세요.
5Grok이 줄글로 답변함 -> ValueError 발생, 로그 기록, SKIP. 절대 문단 하나를 매매로 바꾸지 마세요.
6rugcheck 404 또는 빈 값 -> 인덱싱되기에 너무 이른 토큰. SKIP. 새것이 곧 안전하다는 뜻은 아닙니다.
7모든 것에 lp_unlocked 표시 -> 일부 풀 유형에는 LP 토큰 자체가 없습니다. 그 수치를 건드리기 전에 rugcheck에서 직접 하나 열어보고, 문서로 남겨서 변경하세요.
8no_pool -> 아직 DEX에서 거래되지 않거나 솔라나가 아님. SKIP.
9루틴이 조용해짐 -> 오랫동안 자리를 비우면 Grok Bot이 루틴을 일시 중지합니다. Routines를 열어 다시 켜주세요.

비용

판정 이전의 모든 과정은 무료입니다: 제한, Rugcheck, DexScreener. 유일한 유료 단계는 13가지 검사를 모두 통과한 토큰당 한 번의 Grok 호출이며, 이때 X 검색이 켜집니다.

text
1cost_per_token = input_tokens / 1_000_000 * price_per_M_input
2 + output_tokens / 1_000_000 * price_per_M_output
3 + posts_fetched / 1_000 * 5 # X 검색: 1,000건 조회당 $5
4
5max_turns = 2로 설정하면 검색이 두 차례로 제한되어 posts_fetched가 작게 유지됩니다.
6모든 응답은 자체적으로 x_posts_fetched를 보고하므로 첫날부터 실제 수치를 알 수 있습니다.
7
8가격: [console.x.ai의 가격 참조]

사람들이 잘못 이해하는 부분: 비용은 스캐너가 아니라 살아남은 토큰 수에 비례합니다. scan.py 가 하루에 240개의 토큰을 데스크에 공급할 수 있지만, 대부분은 Grok이 알기도 전에 무료로 탈락합니다.

boan - inline image

중요한 부분

이것은 제 세팅이지 당신의 전략이 아닙니다.

config.py 의 모든 숫자는 $25 티켓과 가치가 0이 되는 것을 지켜보는 저만의 허용 범위에 맞춰 제가 정한 것입니다. 대부분의 밈코인은 0이 되고, 이를 거래하는 대부분의 사람은 돈을 잃습니다. 필터가 토큰을 좋게 만들어주지는 않습니다. 뻔하게 돈을 잃을 경로를 제거하고, 모든 토큰이 왜 죽었는지 그 이유를 읽게 만들어줄 뿐입니다.

페이퍼로 일주일 돌려보세요. 탈락 사유를 읽어보세요. 동의할 수 없는 숫자는 문서로 남겨서 옮기세요. 상수를 복사하지 말고 구조를 복사하세요. 투자 조언이 아닙니다.

코드가 탈락시키고, Grok이 X를 읽고, 당신이 서명합니다. 스크립트는 절대 재량권을 갖지 못하고, Grok은 절대 계산기를 갖지 못하며, 당신 외에는 누구도 지갑을 갖지 못합니다.

다음 매수 전에 이 글을 저장하세요. 똑같이 돌릴 계획이라면 팔로우하세요. 이런 내용이 더 올 예정입니다 🫶

내 텔레그램 - t.me/+8wAXS0nHWgA2MGEy

내 깃허브 - github.com/0xboan

여기서 거래하기 - fomo.family/r/0xboan

원클릭 저장

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

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

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

당신의 Markdown을 깔끔한 𝕏 글로

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

Markdown → 𝕏 사용해 보기

분석할 패턴 더 보기

최근 바이럴 아티클

더 많은 바이럴 아티클 보기