๐ŸํŒŒ์ด์ฌ ์ดˆ๋ณด์ž ๊ฐ€์ด๋“œ : Django CRUD ์™„์ „ ์ •๋ณต(๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ ํ•ต์‹ฌ ๊ฐ€์ด๋“œ)

 



์•ˆ๋…•ํ•˜์„ธ์š”! ์›น ๊ฐœ๋ฐœ์˜ ์„ธ๊ณ„์— ์˜ค์‹  ์—ฌ๋Ÿฌ๋ถ„์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ด์ฌ์˜ ๊ฐ•๋ ฅํ•œ ์›น ํ”„๋ ˆ์ž„์›Œํฌ์ธ Django๋Š” ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ž‘์—…์„ ์‰ฝ๊ณ  ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค. ํŠนํžˆ, ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ธ ๋ฐ์ดํ„ฐ๋ฅผ **์ƒ์„ฑ(Create), ์ฝ๊ธฐ(Read), ์ˆ˜์ •(Update), ์‚ญ์ œ(Delete)**ํ•˜๋Š” 4๊ฐ€์ง€ ๊ธฐ๋ณธ ์ž‘์—…, ์ฆ‰ CRUD๋Š” ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ์˜ ์•ŒํŒŒ์ด์ž ์˜ค๋ฉ”๊ฐ€๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ฐ€์ด๋“œ์—์„œ๋Š” Django๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๋‹ค๋ฃจ๋Š” CRUD์˜ ์›๋ฆฌ์™€ ๋ฐฉ๋ฒ•์„ ์ดˆ๋ณด์ž๋„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹จ๊ณ„๋ณ„๋กœ ์„ค๋ช…ํ•ด ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.


1. ๐Ÿ” CRUD๋ž€ ๋ฌด์—‡์ธ๊ฐ€์š”? (Create, Read, Update, Delete)

CRUD๋Š” ๋Œ€๋ถ€๋ถ„์˜ ์†Œํ”„ํŠธ์›จ์–ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์š”๊ตฌ๋˜๋Š” ๊ธฐ๋ณธ์ ์ธ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ์„ ๋‚˜ํƒ€๋‚ด๋Š” ์•ฝ์–ด์ž…๋‹ˆ๋‹ค.

์•ฝ์žํ’€๋„ค์ž„๊ธฐ๋ŠฅDjango์—์„œ์˜ ์—ญํ• 
CCreate์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ์†Œ์— ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.์‚ฌ์šฉ์ž ์ž…๋ ฅ(Form)์„ ๋ฐ›์•„ Model ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ  save() ๋˜๋Š” create() ๋ฉ”์„œ๋“œ๋กœ DB์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
RRead์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๊ณ  ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.Model.objects.all(), get(), filter() ๋“ฑ์˜ ๋ฉ”์„œ๋“œ๋กœ DB์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.
UUpdate๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€ ๊ฐ’์„ ๋ณ€๊ฒฝํ•œ ํ›„, save() ๋ฉ”์„œ๋“œ๋กœ DB์— ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค.
DDelete๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ์†Œ์—์„œ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€ delete() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ DB์—์„œ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

Django๋Š” **ORM (Object-Relational Mapping)**์ด๋ผ๋Š” ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ, ๋ณต์žกํ•œ SQL ์ฟผ๋ฆฌ ๋Œ€์‹  ํŒŒ์ด์ฌ ์ฝ”๋“œ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ์ด ORM์„ ํ†ตํ•ด ์šฐ๋ฆฌ๋Š” Model์ด๋ผ๋Š” ๊ฐœ๋…์„ ์‚ฌ์šฉํ•˜์—ฌ CRUD ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.


2. ๐Ÿงฑ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ(Model) ์ •์˜ํ•˜๊ธฐ

CRUD ์ž‘์—…์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์—, ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ• ์ง€ ์ •์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Django์—์„œ ์ด ์ •์˜๋Š” models.py ํŒŒ์ผ์— ํŒŒ์ด์ฌ ํด๋ž˜์Šค ํ˜•ํƒœ๋กœ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ๊ฐ„๋‹จํ•œ Todo ๋ฆฌ์ŠคํŠธ ์•ฑ์„ ๋งŒ๋“ ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ , ํ•  ์ผ์˜ **์ œ๋ชฉ(title)**๊ณผ **์™„๋ฃŒ ์—ฌ๋ถ€(completed)**๋ฅผ ์ €์žฅํ•˜๋Š” ๋ชจ๋ธ์„ ๋งŒ๋“ค์–ด ๋ด…์‹œ๋‹ค.

Python
# 

# crudapp/models.py
from django.db import models

class Todo(models.Model):
    # ํ•  ์ผ์˜ ์ œ๋ชฉ (์ตœ๋Œ€ 200์ž)
    title = models.CharField(max_length=200) 
    # ์™„๋ฃŒ ์—ฌ๋ถ€ (True/False)
    completed = models.BooleanField(default=False) 
    
    # ๊ฐ์ฒด๋ฅผ ์‚ฌ๋žŒ์ด ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์„œ๋“œ
    def __str__(self):
        return self.title

์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ ํ›„, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ด ๊ตฌ์กฐ๋ฅผ ๋ฐ˜์˜ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ ๋‘ ๊ฐ€์ง€ ๋ช…๋ น์–ด($$)๋ฅผ ํ„ฐ๋ฏธ๋„์— ์ž…๋ ฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • python manage.py makemigrations

  • python manage.py migrate


3. ✅ CRUD ํ•ต์‹ฌ ๊ธฐ๋Šฅ ๊ตฌํ˜„ (Views์—์„œ ORM ์‚ฌ์šฉ)

์ด์ œ views.py ํŒŒ์ผ์—์„œ ์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” 4๊ฐ€์ง€ CRUD ์ž‘์—…์„ Django ORM์„ ์ด์šฉํ•ด ๊ตฌํ˜„ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

3.1. ๐Ÿ†• ์ƒ์„ฑ (Create)

์ƒˆ๋กœ์šด Todo ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. create() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ์ฒด ์ƒ์„ฑ๊ณผ ์ €์žฅ ๊ณผ์ •์„ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด ํŽธ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

Python
# crudapp/views.py - ์ผ๋ถ€
from .models import Todo

def create_todo(request):
    # ์‹ค์ œ๋กœ๋Š” ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค. (์˜ˆ: request.POST)
    if request.method == 'POST':
        # title ํ•„๋“œ์— '์ƒˆ๋กœ์šด ํ•  ์ผ'์ด๋ผ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•œ๋‹ค๊ณ  ๊ฐ€์ •
        new_title = request.POST.get('title') 
        
        # 1. create() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ ์ƒ์„ฑ ๋ฐ ์ €์žฅ
        Todo.objects.create(
            title=new_title, 
            completed=False
        )
        # ์ €์žฅ์ด ์™„๋ฃŒ๋˜๋ฉด ๋ชฉ๋ก ํŽ˜์ด์ง€๋กœ ์ด๋™
        return redirect('list_url')
    
    # ... (ํ…œํ”Œ๋ฆฟ ๋ Œ๋”๋ง ์ฝ”๋“œ)

๋‹ค๋ฅธ ์ƒ์„ฑ ๋ฐฉ๋ฒ•: save() ๋ฉ”์„œ๋“œ

  1. new_task = Todo(title=new_title, completed=False) (๊ฐ์ฒด ์ƒ์„ฑ)

  2. new_task.save() (DB์— ์ €์žฅ)

์ด ๋ฐฉ๋ฒ•์€ ๊ฐ์ฒด๋ฅผ ๋จผ์ € ๋ฉ”๋ชจ๋ฆฌ์— ์ƒ์„ฑํ•œ ํ›„ ๋‚˜์ค‘์— ์ €์žฅํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

3.2. ๐Ÿ“š ์ฝ๊ธฐ (Read / Retrieve)

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. ์ „์ฒด ๋ชฉ๋ก์„ ๋ณด๊ฑฐ๋‚˜, ํŠน์ • ์กฐ๊ฑด์— ๋งž๋Š” ๋ฐ์ดํ„ฐ๋งŒ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ „์ฒด ๋ฐ์ดํ„ฐ ์กฐํšŒ: Todo.objects.all()

  • ํŠน์ • ์กฐ๊ฑด ๋ฐ์ดํ„ฐ ์กฐํšŒ: Todo.objects.filter(completed=False)

  • ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ ์กฐํšŒ: Todo.objects.get(id=1) (๊ฒฐ๊ณผ๊ฐ€ ์—†๊ฑฐ๋‚˜ 2๊ฐœ ์ด์ƒ์ด๋ฉด ์˜ค๋ฅ˜ ๋ฐœ์ƒ)

Python
# crudapp/views.py - ์ผ๋ถ€
def list_todos(request):
    # 2. ๋ชจ๋“  Todo ๊ฐ์ฒด๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.
    todos = Todo.objects.all() 
    
    # ์ด ๋ฐ์ดํ„ฐ๋ฅผ ํ…œํ”Œ๋ฆฟ์— ์ „๋‹ฌํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
    return render(request, 'todo/list_todos.html', {'todos': todos})

3.3. ✏️ ์ˆ˜์ • (Update)

๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์™€ ํ•„๋“œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

Python
# crudapp/views.py - ์ผ๋ถ€
def update_todo(request, todo_id):
    # 3. id๋ฅผ ์ด์šฉํ•ด ์ˆ˜์ •ํ•  Todo ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.
    todo_item = Todo.objects.get(id=todo_id) 
    
    if request.method == 'POST':
        new_title = request.POST.get('title')
        
        # ๋ถˆ๋Ÿฌ์˜จ ๊ฐ์ฒด์˜ ํ•„๋“œ ๊ฐ’์„ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.
        todo_item.title = new_title 
        
        # ๋ณ€๊ฒฝ๋œ ๋‚ด์šฉ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. (์ €์žฅํ•  ๋•Œ Update ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋จ)
        todo_item.save() 
        return redirect('list_url')
    
    # ... (ํ…œํ”Œ๋ฆฟ ๋ Œ๋”๋ง ์ฝ”๋“œ)

3.4. ❌ ์‚ญ์ œ (Delete)

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ํŠน์ • ๊ฐ์ฒด๋ฅผ ์™„์ „ํžˆ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

Python
# crudapp/views.py - ์ผ๋ถ€
def delete_todo(request, todo_id):
    # 4. id๋ฅผ ์ด์šฉํ•ด ์‚ญ์ œํ•  Todo ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค.
    todo_item = Todo.objects.get(id=todo_id) 
    
    if request.method == 'POST':
        # delete() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ฐ์ฒด๋ฅผ DB์—์„œ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.
        todo_item.delete() 
        return redirect('list_url')
    
    # ... (ํ…œํ”Œ๋ฆฟ ๋ Œ๋”๋ง ์ฝ”๋“œ)

4. ๐Ÿ”— URL ๋ฐ ํ…œํ”Œ๋ฆฟ ์—ฐ๊ฒฐ

์œ„์—์„œ ์ž‘์„ฑํ•œ View ํ•จ์ˆ˜๋“ค์ด ์›น์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๋ ค๋ฉด urls.py์— ๊ฒฝ๋กœ๋ฅผ ์—ฐ๊ฒฐํ•ด ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ์‚ฌ์šฉ์ž์—๊ฒŒ ์ž…๋ ฅ/์ถœ๋ ฅ ํ™”๋ฉด์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ View์— ํ•ด๋‹นํ•˜๋Š” HTML ํ…œํ”Œ๋ฆฟ์„ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

CRUD ์ž‘์—…View ํ•จ์ˆ˜URL ํŒจํ„ด (์˜ˆ์‹œ)HTTP ๋ฉ”์„œ๋“œ
Createcreate_todo/todo/new/GET (ํผ ํ‘œ์‹œ), POST (์ƒ์„ฑ)
Read (List)list_todos/todo/GET
Updateupdate_todo/todo/<int:todo_id>/edit/GET, POST
Deletedelete_todo/todo/<int:todo_id>/delete/POST

์ด์ฒ˜๋Ÿผ Django์˜ MVT (Model-View-Template) ํŒจํ„ด์„ ํ™œ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ์„ ์ •์˜ํ•˜๊ณ , View์—์„œ CRUD ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, Template์—์„œ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์™„์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

CRUD๋Š” ๋ชจ๋“  Django ํ”„๋กœ์ ํŠธ์˜ ๊ธฐ์ดˆ ์ฒด๋ ฅ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด ๋„ค ๊ฐ€์ง€ ๋™์ž‘์„ ์ˆ™๋‹ฌํ•˜๋ฉด ์–ด๋–ค ๋ณต์žกํ•œ ์›น ์„œ๋น„์Šค๋ผ๋„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋ฐ˜์„ ๋‹ค์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค!


์ด ์˜์ƒ์€ Django CRUD ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ Forms์™€ Models๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ•˜๋Š” ๊ณผ์ •์„ ๋‹จ๊ณ„๋ณ„๋กœ ์•ˆ๋‚ดํ•ด ์ค๋‹ˆ๋‹ค. Django CRUD Application Tutorial | Step-by-Step Guide to Master Create, Read, Update, Delete

๋Œ“๊ธ€