안녕하세요! Mojangcam입니다 😎
디스코드 봇을 만들다 보면 꼭 만나는 벽이 하나 있어요. "봇이 보내는 메시지가 너무 밋밋한데요?"
그냥 텍스트로 이벤트 안내: 7월 1일 ~ 7월 31일 이렇게 보내면 아무도 안 읽어요. 그런데 색깔 띠가 붙고, 제목이 굵게 나오고, 이미지랑 표처럼 정리된 항목이 들어간 임베드(Embed)로 보내면 완전히 달라집니다. 서버 공지, 유저 프로필 카드, 음악 봇 재생목록, 에러 리포트 — 우리가 예쁘다고 생각하는 봇 메시지는 사실상 전부 임베드예요.
지난 디스코드 봇 만들기 #8 - 임베드 이미지·필드로 꾸미기에서 기본기를 다뤘다면, 오늘은 임베드 하나만 파고드는 레퍼런스 글이에요. 메서드 전체 목록, 글자 수 제한값, 색상 지정법, 자주 나는 에러까지 이 글 하나로 끝내볼게요. 북마크 해두고 필요할 때 꺼내 쓰기 좋게 표로 정리했습니다! 🙌

📌 준비물
- Python 3.8 이상 (discord.py 공식 요구사항, 3.7 이하는 미지원)
- discord.py 2.7.1 (2026년 7월 기준 최신 안정 버전)
- 디스코드 개발자 포털에서 발급한 봇 토큰
pip install -U discord.py
설치된 버전이 궁금하면 이렇게 확인해요.
python -c "import discord; print(discord.__version__)"
⚠️ 이 글의 코드는 전부 discord.py 2.x 기준이에요. 1.x 시절 코드는 문법이 꽤 달라서 그대로 쓰면 에러가 납니다. 특히 Embed.Empty는 2.0에서 삭제됐어요 (뒤에서 자세히 설명할게요).
🧩 임베드는 어떤 구조인가요?
임베드는 레고 블록처럼 정해진 자리가 있어요. 아무 데나 텍스트를 넣는 게 아니라, 자리마다 이름이 정해져 있습니다.
| 자리 | 무엇이 들어가나요 | 지정 방법 |
|---|---|---|
| author | 맨 위 작은 이름 + 아이콘 | set_author() |
| title | 큰 제목 (링크 걸 수 있음) | Embed(title=..., url=...) |
| description | 제목 아래 본문 | Embed(description=...) |
| fields | 항목별 소제목 + 내용 (최대 25개) | add_field() |
| thumbnail | 오른쪽 위 작은 이미지 | set_thumbnail() |
| image | 아래쪽 큰 이미지 | set_image() |
| footer | 맨 아래 작은 글씨 + 아이콘 | set_footer() |
| timestamp | footer 옆 시간 표시 | Embed(timestamp=...) |
| colour | 왼쪽 세로 색깔 띠 | Embed(colour=...) |

📌 1단계 - 가장 기본 임베드 보내기
먼저 뼈대부터요. 제목·본문·색깔 세 개만 넣어도 훨씬 그럴싸해집니다.
import discord
embed = discord.Embed(
title="🎉 7월 이벤트 안내",
description="이번 달 출석 이벤트가 시작됐어요!\n아래 내용을 확인해주세요.",
colour=discord.Colour.blurple(),
)
await interaction.response.send_message(embed=embed)
하나씩 뜯어볼게요 💡
- title : 임베드 큰 제목. 최대 256자예요.
- description : 제목 아래 본문. 최대 4096자로 가장 넉넉합니다.
\n으로 줄바꿈되고 마크다운(**굵게**,`코드`)도 먹혀요. - colour : 왼쪽 세로 띠 색깔.
color로 써도 똑같이 동작합니다 (완전한 별칭이에요). embed=: 메시지를 보낼 때content대신embed인자로 넘겨요. 둘을 같이 넘겨서 "일반 텍스트 + 임베드"도 됩니다.
💡discord.Embed의 인자는 전부 키워드 전용이에요.discord.Embed("제목")처럼 위치 인자로 넣으면TypeError가 납니다. 꼭title="제목"형태로 쓰세요.
📌 2단계 - 필드(Field)로 항목 정리하기
임베드가 진짜 강해지는 지점이 여기예요. 필드는 소제목 + 내용 한 쌍이고, inline으로 가로 배치까지 정할 수 있어요.
embed.add_field(name="📅 기간", value="7월 1일 ~ 7월 31일", inline=True)
embed.add_field(name="🎁 보상", value="닉네임 색상 역할", inline=True)
embed.add_field(name="📌 참여 방법", value="`/출석` 명령어를 매일 입력하세요.", inline=False)
하나씩 뜯어볼게요 💡
- name : 필드 소제목. 최대 256자. 굵게 표시돼요.
- value : 필드 내용. 최대 1024자. 여기도 마크다운이 먹힙니다.
- inline :
True면 옆 필드와 가로로 나란히(한 줄에 최대 3개),False면 한 줄 통째로 차지해요. 기본값은True입니다. - 필드는 임베드당 최대 25개까지만 들어가요.
그리고 add_field()는 자기 자신을 반환해서 이렇게 체이닝도 됩니다.
embed = (
discord.Embed(title="서버 규칙", colour=discord.Colour.green())
.add_field(name="1️⃣", value="욕설 금지", inline=False)
.add_field(name="2️⃣", value="광고 금지", inline=False)
.set_footer(text="위반 시 경고 3회 → 밴")
)
필드를 나중에 손대야 할 때는 이 네 개를 씁니다.
| 메서드 | 하는 일 |
|---|---|
insert_field_at(index, *, name, value, inline=True) |
특정 위치에 끼워넣기 |
set_field_at(index, *, name, value, inline=True) |
특정 위치를 덮어쓰기 (없는 인덱스면 IndexError) |
remove_field(index) |
하나 삭제 |
clear_fields() |
전부 삭제 |
⚠️index만 위치 인자이고name·value·inline은 키워드 전용이에요.set_field_at(0, "제목", "내용")은 에러입니다.
📌 3단계 - 이미지·작성자·푸터로 마무리 꾸미기
embed.set_author(name="Mojangcam 봇", icon_url=client.user.display_avatar.url)
embed.set_thumbnail(url="https://example.com/thumb.png")
embed.set_image(url="https://example.com/banner.png")
embed.set_footer(text="문의는 /문의 명령어로", icon_url="https://example.com/icon.png")
하나씩 뜯어볼게요 💡
- set_author(*, name, url=None, icon_url=None) : 맨 위 작은 이름줄.
name은 필수, 최대 256자.url을 주면 이름이 링크가 돼요. - set_thumbnail(*, url) : 오른쪽 위 작은 이미지. 프로필 사진·아이템 아이콘에 잘 어울려요.
- set_image(*, url) : 아래쪽 큰 이미지(배너). 가로로 꽉 찹니다.
- set_footer(*, text=None, icon_url=None) : 맨 아래 작은 글씨.
text는 최대 2048자. - 지우고 싶으면
url=None/text=None을 넘기거나remove_author()·remove_footer()를 쓰면 돼요.
💡set_image와set_thumbnail의url은 키워드 전용이면서 기본값이 없는 필수 인자예요.set_image("주소")처럼 쓰면TypeError가 납니다. 꼭set_image(url="주소")로!
📌 임베드 메서드 총정리 (치트시트)
검색해서 이 글에 온 분들을 위해 한 표에 몰아넣었어요. discord.py 2.7.1 기준입니다.
| 메서드 | 시그니처 | 설명 |
|---|---|---|
| 생성자 | Embed(*, colour=None, color=None, title=None, type='rich', url=None, description=None, timestamp=None) |
전부 키워드 전용 |
add_field |
(*, name, value, inline=True) |
필드 추가 (최대 25개) |
insert_field_at |
(index, *, name, value, inline=True) |
지정 위치에 삽입 |
set_field_at |
(index, *, name, value, inline=True) |
지정 위치 교체 |
remove_field |
(index) |
필드 삭제 |
clear_fields |
() |
필드 전체 삭제 |
set_author |
(*, name, url=None, icon_url=None) |
상단 작성자 |
remove_author |
() |
작성자 제거 |
set_footer |
(*, text=None, icon_url=None) |
하단 푸터 |
remove_footer |
() |
푸터 제거 |
set_image |
(*, url) |
큰 이미지 |
set_thumbnail |
(*, url) |
작은 썸네일 |
to_dict |
() |
dict로 변환 (저장용) |
from_dict |
(data) |
dict → 임베드 (클래스메서드) |
copy |
() |
임베드 복사 |
len(embed) |
— | 글자 수 합계 반환 |
위 메서드들은 전부 자기 자신을 반환해서 점(.)으로 계속 이어 쓸 수 있어요.
📌 임베드 제한값 총정리 (이거 넘으면 400 에러!)
디스코드 공식 문서 기준 제한값이에요. 봇이 갑자기 Bad Request를 뱉는다면 십중팔구 여기 걸린 겁니다.
| 항목 | 최대값 |
|---|---|
title |
256자 |
description |
4096자 |
fields 개수 |
25개 |
field.name |
256자 |
field.value |
1024자 |
footer.text |
2048자 |
author.name |
256자 |
| 메시지 하나당 임베드 개수 | 10개 |
| 메시지 내 전체 임베드 글자 합계 | 6000자 |
마지막 6000자가 함정이에요. 임베드 하나당이 아니라 메시지에 붙은 모든 임베드를 합쳐서 6000자입니다. 임베드 3개를 한 번에 보낼 거면 3개 합계로 계산해야 해요.
그래서 미리 체크하는 습관이 중요합니다.
if len(embed) > 6000:
await interaction.response.send_message("내용이 너무 길어요! 나눠서 보낼게요.")
return
len(embed)는 title + description + 모든 field의 name·value + footer.text + author.name 글자 수를 더한 값을 돌려줘요. 이미지 URL이나 링크 주소는 안 세니까 참고하세요.
📌 색상(Colour) 지정하는 3가지 방법
discord.Colour와 discord.Color는 완전히 같은 클래스예요. 철자는 편한 쪽으로 쓰면 됩니다.
# 1) 이름으로 (가장 간단)
discord.Colour.blurple() # 디스코드 대표 보라색
discord.Colour.green()
discord.Colour.red()
discord.Colour.gold()
# 2) 직접 값 지정
discord.Colour.from_rgb(88, 101, 242)
discord.Colour.from_str("#5865F2") # "0x5865F2", "rgb(88, 101, 242)"도 OK
discord.Colour(0x5865F2) # 16진수 정수 그대로
# 3) 랜덤
discord.Colour.random()
- blurple : 디스코드 공식 브랜드 컬러예요. 봇 공지에 제일 무난합니다.
- from_str :
0x5865F2/#5865F2/rgb(88, 101, 242)형식을 전부 받아줘요. 2.0에서 추가됐습니다. - random(*, seed=None) : 매번 다른 색.
seed를 주면 같은 값엔 같은 색이 나와서 유저별 고정 색상 만들 때 좋아요.
색깔 이름은 이 외에도 teal, magenta, orange, fuchsia, yellow, dark_grey, brand_green, brand_red 등 30가지 넘게 준비돼 있어요.
📌 전체 코드 (복붙 OK)
# discord.py 2.7.1 / Python 3.8+
# 설치: pip install -U discord.py
import datetime
import discord
from discord import app_commands
TOKEN = "여기에_봇_토큰_붙여넣기"
intents = discord.Intents.default()
client = discord.Client(intents=intents)
tree = app_commands.CommandTree(client)
@client.event
async def on_ready():
await tree.sync()
print(f"로그인 완료: {client.user}")
@tree.command(name="공지", description="임베드 공지를 보냅니다")
async def notice(interaction: discord.Interaction):
# 1) 뼈대 만들기
embed = discord.Embed(
title="🎉 7월 출석 이벤트 안내",
description="이번 달 출석 이벤트가 시작됐어요!\n아래 내용을 꼭 확인해주세요.",
colour=discord.Colour.blurple(),
url="https://mojangcam.tistory.com/",
timestamp=datetime.datetime.now(datetime.timezone.utc),
)
# 2) 상단 작성자 + 오른쪽 썸네일
embed.set_author(name="Mojangcam 봇", icon_url=client.user.display_avatar.url)
embed.set_thumbnail(url=client.user.display_avatar.url)
# 3) 필드로 항목 정리
embed.add_field(name="📅 기간", value="7월 1일 ~ 7월 31일", inline=True)
embed.add_field(name="🎁 보상", value="닉네임 색상 역할", inline=True)
embed.add_field(name="👥 대상", value="서버 전체 멤버", inline=True)
embed.add_field(
name="📌 참여 방법",
value="매일 `/출석` 명령어를 입력하면 자동 기록돼요.",
inline=False,
)
# 4) 하단 푸터 (글자 수도 같이 표시)
embed.set_footer(text=f"현재 임베드 길이 {len(embed)}자 / 최대 6000자")
# 5) 6000자 안전장치
if len(embed) > 6000:
await interaction.response.send_message("내용이 너무 길어요! 😥", ephemeral=True)
return
await interaction.response.send_message(embed=embed)
client.run(TOKEN)
TOKEN 자리에 본인 봇 토큰만 넣으면 바로 돌아가요. 슬래시 명령어라 서버에 반영되는 데 잠깐 걸릴 수 있습니다.
📌 실행 결과
봇을 켜고 디스코드에서 /공지를 입력하면, 왼쪽에 보라색 띠가 있는 카드가 뜹니다. 상단엔 봇 이름과 아이콘, 그 아래 제목(클릭하면 블로그로 이동), 본문, 그리고 기간·보상·대상 3개가 가로로 나란히 붙고 참여 방법만 한 줄을 통째로 차지해요. 오른쪽 위엔 썸네일, 맨 아래엔 글자 수와 시간이 표시됩니다.

inline=True인 필드가 3개까지 한 줄에 붙는다는 걸 눈으로 확인해보세요. 4번째 필드를 inline=True로 바꾸면 아래 줄로 내려갑니다.

📌 자주 나는 에러
1. TypeError: add_field() takes 1 positional argument but 3 were given
- 원인 :
add_field("제목", "내용")처럼 위치 인자로 넘겼어요. 임베드 메서드는 대부분 키워드 전용입니다. - 해결 :
add_field(name="제목", value="내용")형태로 바꿔주세요.set_image,set_footer,set_author도 마찬가지예요.
2. AttributeError: type object 'Embed' has no attribute 'Empty'
- 원인 :
discord.Embed.Empty는 2.0에서 삭제됐어요. 인터넷에 떠도는 1.x 시절 예제를 그대로 복사하면 이 에러가 납니다. - 해결 : 전부
None으로 바꾸면 돼요.if embed.footer.text is not None:이런 식으로요.
3. discord.errors.HTTPException: 400 Bad Request (error code: 50035)
- 원인 : 제한값 초과. description 4096자, field.value 1024자, 전체 6000자 중 하나를 넘겼을 확률이 높아요.
- 해결 : 보내기 전에
print(len(embed))로 찍어보세요. DB에서 가져온 긴 텍스트를 넣을 땐value=text[:1024]처럼 잘라주는 게 안전합니다.
4. 이미지가 안 뜨고 빈 칸으로 나와요
- 원인 :
set_image(url="C:/images/banner.png")처럼 내 컴퓨터 경로를 넣었어요. 디스코드는 인터넷에서 접근 가능한 http(s) 주소만 읽습니다. - 해결 : 인터넷에 올라간 이미지 URL을 쓰거나, 아래 보너스의
attachment://방식을 쓰세요.
5. TypeError: unhashable type: 'Embed'
- 원인 : 임베드를
set에 넣거나dict의 키로 썼어요. 2.0에서__eq__가 생기면서 임베드는 해시 불가 객체가 됐습니다. - 해결 :
list에 담아 관리하세요. 중복 제거가 필요하면to_dict()결과를 비교하면 됩니다.
💡 보너스 - 내 컴퓨터 이미지를 임베드에 넣기
로컬 이미지 파일을 쓰고 싶다면 discord.File로 같이 올리고 attachment://파일명으로 참조하면 돼요.
file = discord.File("images/banner.png", filename="banner.png")
embed = discord.Embed(title="오늘의 배너", colour=discord.Colour.gold())
embed.set_image(url="attachment://banner.png")
await interaction.response.send_message(file=file, embed=embed)
- filename : 여기 적은 이름과
attachment://뒤 이름이 정확히 같아야 해요. 대소문자도 구분합니다. - 이 방식은
set_image,set_thumbnail,set_footer(icon_url=...),set_author(icon_url=...)네 군데 모두 사용할 수 있어요.
💡 보너스 - 임베드를 파일로 저장했다가 다시 불러오기
공지 템플릿을 여러 개 관리할 때 유용해요.
import json
# 저장
with open("notice.json", "w", encoding="utf-8") as f:
json.dump(embed.to_dict(), f, ensure_ascii=False, indent=2)
# 불러오기
with open("notice.json", encoding="utf-8") as f:
embed = discord.Embed.from_dict(json.load(f))
to_dict()로 dict를 뽑아 JSON에 저장하고, from_dict()로 되살리는 구조예요. 봇을 껐다 켜도 공지 양식이 그대로 남습니다.
📌 마무리
오늘은 디스코드 임베드를 처음부터 끝까지 정리해봤어요! 요약하면 이렇습니다 💬
- 임베드는 author / title / description / fields / thumbnail / image / footer 자리가 정해져 있어요
- 메서드 인자는 거의 다 키워드 전용 —
name=,value=,url=꼭 붙이기 - 필드 25개 / 전체 6000자가 가장 자주 걸리는 제한값,
len(embed)로 미리 체크 Embed.Empty는 2.0에서 사라졌어요 →None사용- 로컬 이미지는
discord.File+attachment://파일명
다음 편에서는 디스코드 봇 데이터 저장 - JSON vs SQLite를 다뤄볼게요. 출석 기록이나 유저 포인트처럼 "봇을 껐다 켜도 남아야 하는 데이터"를 어디에 어떻게 저장할지 비교해볼 예정이에요. 기대해주세요! 🙌
궁금한 점은 댓글로 남겨주세요. 임베드 관련해서 막히는 부분 있으면 같이 풀어봐요 😎
📚 디스코드 봇 만들기 시리즈
- #1 ~ #3 - 봇 생성·슬래시 커맨드 기초
- #4 - Slash Command로 임베드 전송하기
- #5 - 버튼(Button) 상호작용 구현하기
- #6 - 드롭다운(Select Menu) 메뉴 구현하기
- #7 - 모달(Modal)로 입력 받기
- #8 - 임베드(Embed) 이미지·필드로 꾸미기
- #9 - AI(Claude·ChatGPT API) 연동하기
- #10 - 다국어(i18n) 지원하기
- #11 - 플라스크(Flask)로 웹 대시보드 만들기
- #12 - 명령어 쿨다운(Cooldown)·권한(Permission) 관리하기
- #13 - 역할·채널 자동화로 서버 관리하기
- 임베드 완전정복 ← 지금 이 글!
함께 보면 좋은 글
'디스코드 봇 만들기' 카테고리의 다른 글
| 디스코드 봇 만들기 #15 - 슬래시 명령어 그룹(Group)으로 명령어 정리하기! (파이썬 예제) (0) | 2026.08.01 |
|---|---|
| 디스코드 봇 만들기 #14 - 데이터 저장하기! JSON vs SQLite (파이썬 예제) (0) | 2026.07.30 |
| 디스코드 봇 만들기 #13 - 역할·채널 자동화로 서버 관리하기! (파이썬 예제) (0) | 2026.07.25 |
| 디스코드 봇 만들기 #12 - 명령어 쿨다운(Cooldown)·권한(Permission) 관리하기! (파이썬 예제) (0) | 2026.07.21 |
| 디스코드 봇 만들기 #11 - 플라스크(Flask)로 웹 대시보드 만들기! (파이썬 예제) (0) | 2026.07.15 |