Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/ISSUE_TEMPLATE/🏷-이슈해결.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
name: "\U0001F3F7 이슈해결"
about: 이슈 해결 관련 템플릿입니다
title: "[fix]"
labels: "\U0001F3F7️ Fix"
assignees: ''

---

### 🚀 Summary

<!-- A brief description of the issue. -->

---

### 📝 To Do

<!-- Write what you need to do -->

- [ ]
- [ ]
- [ ]

---

### 🏞️ images

<!-- Capture related images -->
28 changes: 28 additions & 0 deletions .github/ISSUE_TEMPLATE/🚀-기능-구현.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
name: "\U0001F680 기능 구현"
about: 기능 구현 이슈 템플릿입니다.
title: "[feat]"
labels: "\U0001F680 Feat"
assignees: ''

---

### 🚀 Summary

<!-- A brief description of the issue. -->

---

### 📝 To Do

<!-- Write what you need to do -->

- [ ]
- [ ]
- [ ]

---

### 🏞️ images

<!-- Capture related images -->
19 changes: 19 additions & 0 deletions .github/ISSUE_TEMPLATE/🧭-환경설정.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
name: "\U0001F9ED 환경설정"
about: 환경설정 관련 템플릿입니다.
title: "[chore]"
labels: "\U0001F9ED Chore"
assignees: ''

---

## 🔍 환경 설정 관련 수정 사항 설명

수정하려는 환경 설정 사항에 대해 작성해주세요.
(Ex. eslint/prettier 를 세팅한다. )

<br>

### ✅ 참고 사항

참고 레퍼런스, 스크린샷 등 issue와 관련된 참고 자료가 있다면 첨부해주세요.
28 changes: 28 additions & 0 deletions .github/ISSUE_TEMPLATE/🧹-리팩토링.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
name: "\U0001F9F9 리팩토링"
about: 기능 리팩토링 관련 이슈 템플릿입니다.
title: "[refactor]"
labels: "\U0001F9F9 Refactor"
assignees: ''

---

### 🚀 Summary

<!-- A brief description of the issue. -->

---

### 📝 To Do

<!-- Write what you need to do -->

- [ ]
- [ ]
- [ ]

---

### 🏞️ images

<!-- Capture related images -->
6 changes: 6 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## 변경 사항
ex) 로그인 시, 구글 소셜 로그인 기능을 추가했습니다.


## 테스트 결과 (테스트를 했으면)
ex) 베이스 브랜치에 포함되기 위한 코드는 모두 정상적으로 동작해야 합니다. 결과물에 대한 스크린샷, GIF, 혹은 라이브 데모가 가능하도록 샘플API를 첨부할 수도 있습니다.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
*.log
*.pot
*.pyc
__pycache__/
__pycache__/
static
18 changes: 11 additions & 7 deletions backend/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@
SECRET_KEY = os.getenv("SECRET_KEY")

# DEBUG 설정
DEBUG = os.getenv("DEBUG", "True").lower() == "true"
# DEBUG = os.getenv("DEBUG", "True").lower() == "true"
DEBUG = True

# ALLOWED_HOSTS 설정
ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "localhost,127.0.0.1").split(",")

# ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS", "localhost,127.0.0.1").split(",")
ALLOWED_HOSTS = ["*"]
# Application definition
INSTALLED_APPS = [
# "django.contrib.admin",
# "django.contrib.auth",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
Expand All @@ -41,7 +42,7 @@
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
# "django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
Expand Down Expand Up @@ -73,7 +74,7 @@
'NAME': 'capstone', # docker-compose에서 설정한 MYSQL_DATABASE 값
'USER': 'sa', # docker-compose에서 설정한 MYSQL_USER 값
'PASSWORD': '1234', # docker-compose에서 설정한 MYSQL_PASSWORD 값
'HOST': 'mysqldb', # docker-compose의 서비스 이름 (컨테이너 내부에서 접속할 때 사용)
'HOST': 'localhost', # docker-compose의 서비스 이름 (컨테이너 내부에서 접속할 때 사용)
'PORT': 3306, # 기본 MySQL 포트
'OPTIONS': {
'charset': 'utf8mb4', # UTF-8 문자 인코딩 설정 (이모지 지원 포함)
Expand Down Expand Up @@ -121,3 +122,6 @@
},
"USE_SESSION_AUTH": False,
}


CORS_ORIGIN_ALLOW_ALL = True
32 changes: 30 additions & 2 deletions backend/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,36 @@
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from django.urls import path, include, re_path
from rest_framework.permissions import AllowAny
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from django.http import JsonResponse

# 간단한 홈 페이지 뷰 추가
def home(request):
return JsonResponse({"message": "Welcome to the API"})


schema_view = get_schema_view(
openapi.Info(
title="Title",
default_version='v1',
description="Test description",
terms_of_service="<https://www.google.com/policies/terms/>",
contact=openapi.Contact(email="[email protected]"),
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=[AllowAny],
)


urlpatterns = [
# path("admin/", admin.site.urls),
path("", home, name="home"), # ✅ 루트 경로 추가
path('admin/', admin.site.urls),
path('api/v1/',include('post.urls')),
re_path(r'^swagger(?P<format>\\.json|\\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
re_path(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]
34 changes: 17 additions & 17 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ services:
ports:
- "3306:3306"

backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: backend
ports:
- "8000:8000"
volumes:
- ./:/app
restart: always
depends_on:
- mysqldb
command: |
bash -c "python wait_mysql.py &&
python manage.py makemigrations &&
python manage.py migrate &&
python manage.py runserver 0.0.0.0:8000"
# backend:
# build:
# context: ./backend
# dockerfile: Dockerfile
# container_name: backend
# ports:
# - "8000:8000"
# volumes:
# - ./:/app
# restart: always
# depends_on:
# - mysqldb
# command: |
# bash -c "python wait_mysql.py &&
# python manage.py makemigrations &&
# python manage.py migrate &&
# python manage.py runserver 0.0.0.0:8000"
12 changes: 11 additions & 1 deletion post/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
from django.db import models

# DB에 어떤 데이터를 저장할지 스키마를 정의
# Create your models here.

class Vehicle(models.Model):
plate_number = models.CharField(max_length=20)
created_at = models.DateTimeField(auto_now_add=True)

class SpeedViolation(models.Model):
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE) #외래키로 설정
speed = models.IntegerField()
location = models.CharField(max_length=255)
timestamp = models.DateTimeField()
18 changes: 18 additions & 0 deletions post/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from rest_framework import serializers
from .models import SpeedViolation, Vehicle

#REST Framework에서 데이터를 직렬화하거나 역직렬화하는 역할 (Python -> JSON, JSON -> Python)

class SpeedViolationSerializer(serializers.ModelSerializer):
plate_number = serializers.CharField(write_only=True) #번호판을 기반으로

class Meta:
model = SpeedViolation
fields = ['id', 'plate_number', 'speed', 'location', 'timestamp']

#POST 요청 시 차량 번호판을 기반으로 Vehicle 객체를 생성하거나 조회한 후,
#새 SpeedViolation 객체를 생성
def create(self, validated_data):
plate_number = validated_data.pop('plate_number')
vehicle, _ = Vehicle.objects.get_or_create(plate_number=plate_number)
return SpeedViolation.objects.create(vehicle=vehicle, **validated_data)
11 changes: 11 additions & 0 deletions post/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from backend.urls import path, include
from .views import SpeedViolationViewSet
#ViewSet을 사용하여 curd 엔드포인트 자동 생성
from rest_framework.routers import DefaultRouter
from . import views

router = DefaultRouter()
router.register(r'violations', SpeedViolationViewSet, basename='violation')
urlpatterns = [
path("", include(router.urls)),
]
77 changes: 77 additions & 0 deletions post/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,80 @@
from django.shortcuts import render
from django.http import HttpResponse
from drf_yasg import openapi
from drf_yasg.openapi import Response
from drf_yasg.utils import swagger_auto_schema
from rest_framework import viewsets
from rest_framework.decorators import api_view, action

from .models import SpeedViolation
from .serializers import SpeedViolationSerializer

def index(request):
return HttpResponse("설정이 완료되었습니다.")
# Create your views here.

class SpeedViolationViewSet(viewsets.ModelViewSet):
#ModelViewSet : 자동으로 CREATE, READ, UPDATE, DELETE 기능을 모두 제공
queryset = SpeedViolation.objects.all().order_by('-timestamp') #최신순 정렬
serializer_class = SpeedViolationSerializer

# API 문서화
@swagger_auto_schema(
operation_description="과속 차량 목록 조회",
responses={200: SpeedViolationSerializer(many=True)}
)
def list(self, request, *args, **kwargs):
#GET /api/v1/violations/
return super().list(request, *args, **kwargs)

@swagger_auto_schema(
operation_description="과속 차량 상세 조회",
responses={200: SpeedViolationSerializer} #단일 객체라 many=True가 없음
)
def retrieve(self, request, *args, **kwargs):
#GET /api/v1/violations/{id}/
return super().retrieve(request, *args, **kwargs)

@swagger_auto_schema(
request_body=SpeedViolationSerializer,
operation_description="과속 차량 생성",
responses={
201: openapi.Response(description="과속 차량 생성 성공", schema=SpeedViolationSerializer),
400: "잘못된 요청"
}
)
def create(self, request, *args, **kwargs):
#POST /api/v1/violations/
return super().create(request, *args, **kwargs)

@swagger_auto_schema(
request_body=SpeedViolationSerializer,
operation_description="과속 차량 수정",
responses={
200: openapi.Response(description="과속 차량 수정 성공", schema=SpeedViolationSerializer),
400: "잘못된 요청"
}
)
def update(self, request, *args, **kwargs):
#PUT /api/v1/violations/{id}/
return super().update(request, *args, **kwargs)

@swagger_auto_schema(
request_body=SpeedViolationSerializer,
operation_description="과속 차량 부분 수정",
responses={
200: openapi.Response(description="부분 수정 성공", schema=SpeedViolationSerializer),
400: "잘못된 요청"
}
)
def partial_update(self, request, *args, **kwargs):
#PATCH /api/v1/violations/{id}/
return super().partial_update(request, *args, **kwargs)

@swagger_auto_schema(
operation_description="과속 차량 삭제",
responses={204: "삭제 성공"}
)
def destroy(self, request, *args, **kwargs):
#DELETE /api/v1/violations/{id}/
return super().destroy(request, *args, **kwargs)
18 changes: 18 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
asgiref==3.8.1
cffi==1.17.1
click==8.1.8
cryptography==44.0.2
Django==5.1.7
djangorestframework==3.15.2
drf-yasg==1.21.10
inflection==0.5.1
mysqlclient==2.2.7
packaging==24.2
pycparser==2.22
PyMySQL==1.1.1
python-dotenv==1.0.1
pytz==2025.1
PyYAML==6.0.2
sqlparse==0.5.3
typing_extensions==4.12.2
uritemplate==4.1.1