1 – 배포 전에 코드 정리하기
1.1 – 비밀값과 설정을 환경변수로 빼기
- 편집기로
config/settings.py파일을 엽니다. SECRET_KEY,DEBUG,ALLOWED_HOSTS세 줄을 찾아 아래로 바꿉니다.
import os
# 배포 서버에서는 환경변수로 넣어 줍니다. 없으면 개발용 값을 씁니다.
SECRET_KEY = os.environ.get("SECRET_KEY", "dev-only-key")
# 환경변수 DEBUG 가 "True" 일 때만 켭니다. 배포 서버에서는 넣지 않습니다.
DEBUG = os.environ.get("DEBUG", "True") == "True"
# 어떤 주소로 들어오는 요청을 받을지 정합니다.
# Render 가 넣어 주는 주소를 읽고, 없으면 내 컴퓨터 주소를 씁니다.
RENDER_HOST = os.environ.get("RENDER_EXTERNAL_HOSTNAME")
ALLOWED_HOSTS = [RENDER_HOST] if RENDER_HOST else ["127.0.0.1", "localhost"]- 앞서 적은
CORS_ALLOWED_ORIGINS부분을 아래로 바꿉니다.
# 배포한 화면 주소를 환경변수로 받습니다.
FRONTEND_URL = os.environ.get("FRONTEND_URL")
CORS_ALLOWED_ORIGINS = ["http://localhost:5173"]
if FRONTEND_URL:
CORS_ALLOWED_ORIGINS.append(FRONTEND_URL)- 파일을 저장합니다.
배포(deploy)는 내 컴퓨터에서만 돌던 것을 인터넷 서버에 올려 누구나 접속하게 하는 일입니다. 다 그린 그림을 전시장에 거는 것과 같은 단계입니다.

Render에 올리는 것은 세 덩어리입니다. React 빌드 결과는 Static Site로, Django 서버는 Web Service로, 데이터는 PostgreSQL로 올립니다. 지금 코드는 내 컴퓨터를 전제로 적혀 있어 그대로 올리면 동작하지 않으므로, 바꿔야 할 자리부터 하나씩 고칩니다.

방금 고친 세 가지 가운데 DEBUG가 가장 중요합니다. 오류가 났을 때 코드 내용을 화면에 보여 주는 설정이며, 켠 채로 배포하면 접속한 누구나 그 내용을 볼 수 있습니다.
os.environ.get의 두 번째 값은 환경변수가 없을 때 쓸 값입니다. 이렇게 적어 두면 내 컴퓨터에서는 아무것도 설정하지 않아도 그대로 돌고, 배포 서버에서만 값을 넣어 바뀝니다. 코드를 두 벌로 나누지 않아도 되는 이유가 이것입니다.
ALLOWED_HOSTS는 어떤 주소로 들어온 요청을 받아 줄지 적는 목록입니다. DEBUG를 끄면 이 목록에 없는 주소로 들어온 요청은 거부됩니다.
DEBUG를 끈 뒤 로컬에서 시험하려면 .env에 DEBUG=False를 넣고 ALLOWED_HOSTS에 127.0.0.1이 들어 있는지 확인합니다. 목록이 비면 접속할 때 DisallowedHost 오류가 나타납니다.
1.2 – 데이터베이스와 정적 파일 설정 바꾸기
- 필요한 도구를 설치합니다.
pip install gunicorn psycopg2-binary dj-database-url whitenoise
config/settings.py의DATABASES부분을 찾아 그 아래에 이어 적습니다.
import dj_database_url
# Render 가 넣어 주는 접속 주소가 있으면 그쪽을 씁니다.
# 없으면 위에 적힌 SQLite 설정이 그대로 쓰입니다.
DATABASE_URL = os.environ.get("DATABASE_URL")
if DATABASE_URL:
DATABASES["default"] = dj_database_url.parse(DATABASE_URL)MIDDLEWARE목록에 한 줄을 더합니다. CORS 줄 바로 아래에 둡니다.
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware",
"django.middleware.security.SecurityMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware", # 추가
# ... 나머지는 그대로 둡니다
]- 파일 맨 아래에 정적 파일을 모을 자리를 적습니다.
# 관리자 화면의 CSS 같은 파일을 한곳에 모을 폴더입니다.
STATIC_ROOT = BASE_DIR / "staticfiles"
STORAGES = {
"default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
"staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"},
}- 파일을 저장합니다.
- 편집기에서 새 파일을 만들고 아래 내용을 적습니다.
Django
djangorestframework
django-cors-headers
python-dotenv
requests
gunicorn
psycopg2-binary
dj-database-url
whitenoisechatbot-app폴더 안에"requirements.txt"라는 이름으로 저장합니다.
내 컴퓨터에서는 SQLite 파일 하나로 데이터를 다뤘습니다. Render의 서버는 다시 켜질 때마다 파일이 초기화되므로, 데이터를 따로 떨어진 PostgreSQL에 두어야 합니다.
requirements.txt는 어떤 도구를 어느 버전으로 설치할지 적은 목록입니다. Render는 이 파일을 보고 서버에 같은 도구를 설치합니다. 이 파일이 없으면 Django조차 설치되지 않아 배포가 실패합니다.
WhiteNoise는 정적 파일을 Django가 직접 내보내게 해 주는 도구입니다. DEBUG를 끄면 Django는 정적 파일을 스스로 내보내지 않으므로, 이 도구가 없으면 관리자 화면의 모양이 깨집니다.
Render의 무료 데이터베이스는 만든 지 30일이 지나면 만료되고, 그 뒤 14일 안에 유료로 올리지 않으면 데이터까지 지워집니다. 만든 것을 오래 두려면 다른 무료 데이터베이스로 옮기거나 유료로 올려야 합니다.
도구를 새로 설치할 때마다 이 파일에 이름을 한 줄 더 적습니다. 내 컴퓨터에서만 설치하고 목록에 넣지 않으면, 배포한 서버에서만 ModuleNotFoundError가 나타납니다.
1.3 – 코드를 GitHub에 올리기
- 브라우저에서
https://github.com/에 접속해 로그인합니다. - 오른쪽 위 [+] → [New repository]를 클릭합니다.
Repository name칸에chatbot-app을 입력하고,Public을 고른 뒤 [Create repository]를 클릭합니다.- 터미널에서
chatbot-app폴더로 이동합니다. - 아래 명령을 차례로 입력합니다. 마지막 줄의 주소는 GitHub 화면에 나온 주소로 바꿉니다.
git init
git add .
git commit -m "챗봇 앱 첫 등록"
git branch -M main
git remote add origin https://github.com/내계정/chatbot-app.git
git push -u origin main- GitHub 저장소 화면을 새로고침합니다.
Render는 GitHub 저장소를 보고 코드를 가져갑니다. 저장소가 없으면 올릴 방법이 없습니다.
파일 목록이 나타납니다. 목록에 .env가 없는지 눈으로 확인합니다. 앞에서 만든 .gitignore가 걸러 준 것입니다.
목록에 .env가 보이면 토큰이 공개된 상태입니다. 파일을 지우는 것만으로는 기록에 남으므로, 허깅페이스 설정 화면에서 그 토큰을 지우고 새로 발급받습니다.
실습 코칭 프롬프트
이 부분을 혼자 해 보다 막히면 아래 프롬프트를 대화형 AI(ChatGPT·Claude·Gemini)에 붙여 넣어 한 단계씩 도움받습니다.
실습에 쓰던 대화창이 아니라 새 대화창에 붙여 넣습니다. 실습 대화창에는 앞서 시킨 다른 지시가 남아 있어, 그 위에 얹으면 답이 뒤섞입니다.
아래는 배포 전 코드 정리하기 실습을 혼자 해 보다 막혀서 도움을 받으려는 요청입니다.
지금 하려는 것: 비밀값과 DEBUG 를 환경변수로 빼고, PostgreSQL 과 정적 파일 설정을 넣은 뒤 GitHub 저장소에 올리는 것입니다.
지금까지 한 것: {지금까지 한 것}
막힌 지점: {막힌 지점}
화면에 나온 메시지: {에러 메시지}
다음 규칙을 지켜 주세요.
- 정답 코드를 통째로 주지 마세요. 다음 한 단계만 알려 주세요.
- 먼저 왜 막혔는지 짚어 주고, 제가 직접 해 보게 하세요.
- 제가 해 본 결과를 말하면 그때 확인 질문을 던지고 다음 단계로 넘어가세요.
- 제가 놓치기 쉬운 부분이 보이면 마지막에 한 줄로 짚어 주세요.
- requirements.txt 에 설치한 도구가 모두 적혀 있는지, .gitignore 에 .env 가 들어 있는지 먼저 확인해 주세요.
아래는 배포 전 코드 정리하기 실습을 혼자 해 보다 막혀서 도움을 받으려는 요청입니다.
지금 하려는 것: 비밀값과 DEBUG 를 환경변수로 빼고, PostgreSQL 과 정적 파일 설정을 넣은 뒤 GitHub 저장소에 올리는 것입니다.
지금까지 한 것: settings.py 를 고치고 git push 까지 마쳤습니다
막힌 지점: DEBUG 를 끄니 로컬에서 접속이 안 됩니다
화면에 나온 메시지: DisallowedHost at /
다음 규칙을 지켜 주세요.
- 정답 코드를 통째로 주지 마세요. 다음 한 단계만 알려 주세요.
- 먼저 왜 막혔는지 짚어 주고, 제가 직접 해 보게 하세요.
- 제가 해 본 결과를 말하면 그때 확인 질문을 던지고 다음 단계로 넘어가세요.
- 제가 놓치기 쉬운 부분이 보이면 마지막에 한 줄로 짚어 주세요.
- requirements.txt 에 설치한 도구가 모두 적혀 있는지, .gitignore 에 .env 가 들어 있는지 먼저 확인해 주세요.
{지금까지 한 것}— 어느 단계까지 마쳤는지 적습니다. 예:django-admin startproject config . 까지 실행했습니다{막힌 지점}— 무엇을 하려다 안 됐는지 적습니다. 예:python manage.py runserver 를 실행하면 서버가 바로 꺼집니다{에러 메시지}— 화면에 나온 글을 그대로 붙여 넣습니다. 없으면없음이라고 적습니다.
AI가 고친 코드 전체를 한 번에 내놓으면 “코드 전체 말고 어느 줄을 왜 고쳐야 하는지 한 단계만 알려 주세요”라고 되물으십시오. 에러 메시지를 요약해 적으면 원인을 못 짚습니다. 화면에 나온 글을 그대로 붙여 넣습니다.
2 – Render에 올리기
2.1 – 데이터베이스 만들기
- 브라우저에서
https://render.com/에 접속해 GitHub 계정으로 로그인합니다. - 왼쪽 위 [+ New] → [Postgres]를 클릭합니다.
Name칸에chatbot-db를 입력합니다.Instance Type에서 [Free]를 고릅니다.- 맨 아래 [Create Database]를 클릭합니다.
세 덩어리 가운데 데이터베이스를 먼저 만드는 이유는 서버가 켜질 때 접속 주소가 이미 있어야 하기 때문입니다. 만들어지는 데 약 1분이 걸립니다. 화면 상태 표시가 Available로 바뀌면 준비된 것입니다.
- 화면을 아래로 내려
Internal Database URL항목의 복사 버튼을 클릭합니다.
이 주소를 다음에 서버에 넣습니다. Internal은 Render 안에서만 통하는 주소이며, 밖에서 접속할 때 쓰는 External보다 빠릅니다.
2.2 – Django 서버 올리기
- 왼쪽 위 [+ New] → [Web Service]를 클릭합니다.
- 앞서 만든
chatbot-app저장소를 고르고 [Connect]를 클릭합니다. - 아래 값을 채웁니다.
| 칸 | 넣을 값 |
|---|---|
| Name | chatbot-api |
| Language | Python 3 |
| Build Command | pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate |
| Start Command | gunicorn config.wsgi:application |
| Instance Type | Free |
Environment Variables항목에서 [Add Environment Variable]을 눌러 아래를 하나씩 넣습니다.
| 이름 | 값 |
|---|---|
DATABASE_URL | 앞 절에서 복사한 Internal Database URL |
SECRET_KEY | 아무도 모르는 긴 글자 |
DEBUG | False |
HF_TOKEN | 허깅페이스에서 발급받은 토큰 |
HF_MODEL | Qwen/Qwen2.5-7B-Instruct |
- 맨 아래 [Deploy Web Service]를 클릭합니다.
화면에 설치 기록이 줄줄이 흐릅니다. 약 3분에서 8분 뒤 맨 위에 Live 표시가 붙고 https://chatbot-api.onrender.com처럼 주소가 나타납니다.
- 그 주소 뒤에
/api/conversations/를 붙여 브라우저로 접속합니다.
앞서 로컬에서 보던 회색 화면이 나타납니다. 목록은 비어 있습니다. 새 데이터베이스라 아직 아무것도 없는 상태입니다.
여기서 넣은 환경변수가 앞서 코드에서 빼 둔 자리에 들어갑니다.
Build Command에 세 명령을 이어 붙인 이유가 있습니다. 서버가 켜지기 전에 도구를 설치하고, 정적 파일을 모으고, 데이터베이스에 표를 만들어야 하기 때문입니다. migrate를 빼면 표가 없어 접속하는 순간 오류가 나타납니다.
기록 맨 아래에 Build failed가 뜨면 붉은 줄을 위로 거슬러 읽습니다. ModuleNotFoundError면 requirements.txt에 그 도구가 빠진 것이고, ImproperlyConfigured면 환경변수 하나가 빠진 것입니다.
2.3 – React 화면 올리기
- 왼쪽 위 [+ New] → [Static Site]를 클릭합니다.
- 같은
chatbot-app저장소를 고르고 [Connect]를 클릭합니다. - 아래 값을 채웁니다.
| 칸 | 넣을 값 |
|---|---|
| Name | chatbot-web |
| Root Directory | frontend |
| Build Command | npm install && npm run build |
| Publish Directory | dist |
Environment Variables에 아래를 넣습니다. 값은 앞 절에서 받은 서버 주소로 바꿉니다.
| 이름 | 값 |
|---|---|
VITE_API_BASE | https://chatbot-api.onrender.com/api |
- [Deploy Static Site]를 클릭합니다.
약 1분에서 3분 뒤 https://chatbot-web.onrender.com처럼 주소가 나타납니다.
React는 빌드하면 HTML·CSS·JS 파일이 되므로 서버가 필요 없고 Static Site로 올립니다.
Root Directory에 frontend를 적는 이유가 있습니다. 저장소 맨 위에는 Django 코드도 함께 있어, 그 자리에서 npm install을 실행하면 package.json을 찾지 못합니다.
2.4 – 두 주소를 서로 알려 주기
- Render 화면에서
chatbot-api서비스를 클릭합니다. - 왼쪽 [Environment]를 클릭합니다.
- [Add Environment Variable]을 눌러 아래를 넣습니다.
| 이름 | 값 |
|---|---|
FRONTEND_URL | https://chatbot-web.onrender.com |
- [Save, rebuild, and deploy]를 클릭합니다.
- 다시 올라가기를 기다린 뒤
https://chatbot-web.onrender.com에 접속합니다. - [새 대화방]을 만들고 방을 열어 질문을 보냅니다.
모델이 쓴 문장이 화면에 나타납니다. 서버와 화면이 각각 올라간 뒤에도 화면 주소를 서버의 허용 목록에 넣어야 브라우저가 요청을 통과시킵니다. 여기까지 되면 화면·서버·모델·데이터베이스가 인터넷 위에서 서로 이어진 것입니다.
주소를 끝에 빗금 없이 적는 것에 주의합니다. CORS_ALLOWED_ORIGINS는 주소를 글자 그대로 맞춰 보므로, 빗금이 하나 더 붙으면 다른 주소로 봅니다.
무료 서버는 요청이 15분간 없으면 잠들고, 다시 깨어나는 데 30초에서 60초가 걸립니다. 첫 접속에서 화면이 비어 있으면 잠시 기다렸다 새로고침합니다. 남에게 보여 주기 전에 미리 한 번 접속해 깨워 둡니다.
2.5 – 배포한 뒤 코드를 고치는 방법
- 로컬에서 코드를 고치고 저장합니다.
- 아래 명령을 차례로 입력합니다.
git add .
git commit -m "고친 내용을 한 줄로 적습니다"
git push- Render 화면의 해당 서비스를 열고 [Events] 탭을 봅니다.
Deploy started라는 줄이 새로 생기고, 몇 분 뒤 Deploy live로 바뀝니다. 코드를 고칠 때 Render 화면에서 손댈 것은 없습니다. GitHub에 올리면 Render가 그것을 보고 다시 배포합니다. 서버와 화면은 각각 다시 배포되므로, 양쪽을 함께 고쳤으면 두 서비스 모두 끝나기를 기다립니다.
고친 뒤 화면이 그대로면 브라우저가 예전 파일을 갖고 있는 경우입니다. Windows는 Ctrl+Shift+R, macOS는 ⌘+Shift+R을 눌러 저장된 파일을 무시하고 다시 받습니다.
실습 코칭 프롬프트
이 부분을 혼자 해 보다 막히면 아래 프롬프트를 대화형 AI(ChatGPT·Claude·Gemini)에 붙여 넣어 한 단계씩 도움받습니다.
실습에 쓰던 대화창이 아니라 새 대화창에 붙여 넣습니다. 실습 대화창에는 앞서 시킨 다른 지시가 남아 있어, 그 위에 얹으면 답이 뒤섞입니다.
아래는 Render에 세 덩어리 올리기 실습을 혼자 해 보다 막혀서 도움을 받으려는 요청입니다.
지금 하려는 것: PostgreSQL 과 Web Service 와 Static Site 를 만들고, 두 주소를 서로 알려 줘 이어 붙이는 것입니다.
지금까지 한 것: {지금까지 한 것}
막힌 지점: {막힌 지점}
화면에 나온 메시지: {에러 메시지}
다음 규칙을 지켜 주세요.
- 정답 코드를 통째로 주지 마세요. 다음 한 단계만 알려 주세요.
- 먼저 왜 막혔는지 짚어 주고, 제가 직접 해 보게 하세요.
- 제가 해 본 결과를 말하면 그때 확인 질문을 던지고 다음 단계로 넘어가세요.
- 제가 놓치기 쉬운 부분이 보이면 마지막에 한 줄로 짚어 주세요.
- 빌드 기록의 붉은 줄을 위에서부터 읽고, 환경변수가 하나라도 빠지지 않았는지 먼저 확인해 주세요.
아래는 Render에 세 덩어리 올리기 실습을 혼자 해 보다 막혀서 도움을 받으려는 요청입니다.
지금 하려는 것: PostgreSQL 과 Web Service 와 Static Site 를 만들고, 두 주소를 서로 알려 줘 이어 붙이는 것입니다.
지금까지 한 것: 데이터베이스와 Web Service 를 만들고 환경변수를 넣었습니다
막힌 지점: 배포가 끝까지 안 갑니다
화면에 나온 메시지: ModuleNotFoundError: No module named 'gunicorn'
다음 규칙을 지켜 주세요.
- 정답 코드를 통째로 주지 마세요. 다음 한 단계만 알려 주세요.
- 먼저 왜 막혔는지 짚어 주고, 제가 직접 해 보게 하세요.
- 제가 해 본 결과를 말하면 그때 확인 질문을 던지고 다음 단계로 넘어가세요.
- 제가 놓치기 쉬운 부분이 보이면 마지막에 한 줄로 짚어 주세요.
- 빌드 기록의 붉은 줄을 위에서부터 읽고, 환경변수가 하나라도 빠지지 않았는지 먼저 확인해 주세요.
{지금까지 한 것}— 어느 단계까지 마쳤는지 적습니다. 예:django-admin startproject config . 까지 실행했습니다{막힌 지점}— 무엇을 하려다 안 됐는지 적습니다. 예:python manage.py runserver 를 실행하면 서버가 바로 꺼집니다{에러 메시지}— 화면에 나온 글을 그대로 붙여 넣습니다. 없으면없음이라고 적습니다.
AI가 고친 코드 전체를 한 번에 내놓으면 “코드 전체 말고 어느 줄을 왜 고쳐야 하는지 한 단계만 알려 주세요”라고 되물으십시오. 에러 메시지를 요약해 적으면 원인을 못 짚습니다. 화면에 나온 글을 그대로 붙여 넣습니다.