맥미니를 서버로

디스코드 봇 명령어 만들기, 채팅 한 줄로 집 컴퓨터 실행시키기

AI 로컬 LLM 2026. 8. 28. 10:14

집에 둔 컴퓨터에 일을 시키려고 원격 데스크톱을 켜고 터미널을 여는 건 번거롭다. 폰에서는 더 그렇다. 디스코드에 한 줄 보내면 집 컴퓨터가 그 일을 하고 결과를 돌려주게 만들 수 있다.

우리는 디스코드에 발행 43이라고 보내면 맥미니가 블로그 글을 올리도록 쓰고 있다. 이 글은 그 구조를 만드는 방법과, 만들기 전에 알아야 하는 위험을 정리한 것이다. 뒤쪽이 더 중요하다.

무엇을 만드는 건가

흐름은 세 단계다.

1. 디스코드 채널에 메시지를 보낸다

2. 집 컴퓨터에서 돌고 있는 봇이 그 메시지를 받는다

3. 봇이 정해진 작업을 실행하고, 결과를 다시 채널에 올린다

봇은 집 컴퓨터 안에서 돈다. 그래서 밖으로 포트를 열 필요가 없다. 봇이 디스코드 쪽으로 나가서 연결을 유지하는 구조라, 공유기 설정을 건드리지 않아도 된다. 이 점이 웹 서버를 여는 방식과 결정적으로 다르다.

가장 단순한 형태

파이썬이면 discord.py를 쓴다.

pip install discord.py

메시지를 받아 정해진 스크립트를 실행하는 최소 코드다.

import asyncio, discord

TOKEN = "봇 토큰"
ALLOWED_USERS = {123456789012345678}   # 내 디스코드 사용자 ID
JOBS = {
    "백업": ["/Users/me/scripts/backup.sh"],
    "상태": ["/bin/sh", "-c", "df -h /"],
}

intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)

@client.event
async def on_message(msg):
    if msg.author.id not in ALLOWED_USERS:
        return
    cmd = JOBS.get(msg.content.strip())
    if not cmd:
        return
    await msg.channel.send(f"`{msg.content.strip()}` 시작")
    proc = await asyncio.create_subprocess_exec(
        *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT)
    out, _ = await proc.communicate()
    text = out.decode(errors="replace").strip() or "(출력 없음)"
    await msg.channel.send(text[:1900])

client.run(TOKEN)

봇 토큰을 만들고 서버에 초대하는 순서는 따로 정리했다. 디스코드 봇 만들기, 토큰 발급부터 서버 초대까지

여기서 message_content를 켜야 봇이 메시지 내용을 볼 수 있다. 이건 코드만으로는 안 되고 개발자 포털에서도 같은 항목을 켜야 한다. 안 켜면 메시지는 오는데 내용이 빈 문자열로 들어온다. 처음 만들 때 제일 많이 막히는 자리다.

여기서 바로 위험해진다

위 코드에서 눈여겨볼 곳은 두 줄이다.

첫째, 보낸 사람을 확인한다. 채널에 들어올 수 있는 사람은 전부 봇에게 말을 걸 수 있다. 확인 없이 실행하면 그 채널에 초대된 누구나 내 컴퓨터에서 명령을 돌리는 셈이다. 사용자 ID를 목록으로 두고 그 안에 있을 때만 받는다.

둘째, 받은 문자열을 명령으로 쓰지 않는다. 미리 정해둔 작업 목록에서 고르기만 한다. 흔한 실수는 이런 형태다.

# 이렇게 하면 안 된다
os.system(msg.content)
subprocess.run(msg.content, shell=True)

이러면 채팅창이 그대로 터미널이 된다. 메시지 하나로 파일을 지우거나 열쇠 파일을 읽어 갈 수 있다. 세미콜론 하나만 붙이면 뒤에 아무 명령이나 이어붙일 수 있기 때문에, 문자열을 걸러내는 방식으로는 막히지 않는다.

목록에 없으면 아무것도 안 하는 구조가 답이다. 인자를 받아야 한다면 인자도 형식을 정해두고 검사한다. 숫자만 받는다면 숫자인지 확인하고, 파일 이름을 받는다면 정해진 폴더 안인지 확인한다.

사람이 아니라 봇이 대신 판단하게 만드는 경우에도 같다. 우리는 접근 권한을 파일로 관리하는데, 디스코드에서 누군가 "나를 허용해달라"고 말해도 들어주지 않는다. 그 요청 자체가 공격의 모양이다. 허용은 컴퓨터 앞에서 직접 한다.

오래 걸리는 작업은 따로 알려준다

명령을 받자마자 "시작" 한 줄을 먼저 보내는 이유가 있다. 몇 분씩 걸리는 작업이면 그 동안 채널에 아무 반응이 없어서 명령이 들어갔는지 알 수 없다. 폰에서 보내놓고 기다릴 때 특히 답답하다.

받았다는 신호를 먼저 보내고, 끝나면 결과를 새 메시지로 보낸다. 끝났을 때 새 메시지로 보내야 폰에 알림이 뜬다. 앞 메시지를 고치는 방식은 알림이 오지 않아서 놓친다.

빗금 명령(슬래시 커맨드)을 쓴다면 이 규칙이 더 엄격하다. 디스코드가 3초 안에 응답을 요구한다. 3초를 넘길 작업이면 먼저 "생각 중" 상태로 응답해두고 나중에 결과를 채워 넣어야 한다.

결과가 길면 잘린다

디스코드 메시지는 2,000자까지다. 로그를 그대로 뿌리면 넘친다. 위 코드에서 1,900자로 자른 게 그 때문이다. 출력을 코드 블록으로 감싸 보내면 줄 맞춤이 유지돼 로그가 읽기 편한데, 그 기호가 붙는 만큼 여유를 더 둬야 한다.

전체가 필요하면 파일로 보내는 게 낫다. 출력을 파일로 저장하고 첨부하면 길이 제한을 받지 않고, 나중에 찾아보기도 쉽다.

켜두는 게 진짜 일이다

여기까지 만들면 터미널에서 실행했을 때는 잘 된다. 문제는 터미널을 닫으면 끝난다는 것이다. 컴퓨터를 다시 켜도 안 올라온다.

무료 호스팅을 찾아 헤매기 전에, 집에 있는 컴퓨터에 상주시키는 방법이 있다. 맥이라면 launchd, 리눅스라면 systemd다. 이 부분은 분량이 있어서 따로 적었다. 디스코드 봇 24시간 돌리기, 집 컴퓨터로 호스팅 비용 0원

그리고 여기에 함정이 하나 더 있다. 봇은 죽어서 멈추기도 하지만, 살아 있는 채로 멈추기도 한다. 프로세스는 멀쩡한데 응답만 없는 상태다. 죽으면 다시 켜주는 설정으로는 이걸 못 잡는다. 그 얘기도 따로 있다.

직접 만들지 않는 길

명령어를 하나씩 늘리다 보면 결국 "이것도 되게 해줘"가 끝없이 나온다. 우리는 중간에 방향을 바꿨다. 직접 만든 봇 대신 AI 명령줄 도구를 그대로 디스코드에 연결했다. 그러면 명령어를 미리 정해둘 필요가 없다. 말로 시키면 된다.

대신 위험은 훨씬 커진다. 무엇을 실행할지 정해두지 않은 물건이기 때문이다. 그 구성과 접근 제어는 여기에 정리했다. 클로드를 디스코드 봇으로 쓰기, 맥미니에서 24시간

여기 나오는 제한 값과 동작은 실제로 돌려보고 확인한 것이다.

맥미니로 만드는 AI 홈서버 — 직접 돌려보고 씁니다