feat: Presigned URL API 및 OpenTelemetry trace 추가
Some checks failed
Build And Test / build-and-push (push) Has been cancelled

- Presigned URL API 추가 (MinIO/S3 private 버킷 지원)
- OpenTelemetry trace 설정 추가 (DEBUG=False 시 활성화)
- requirements.txt에 opentelemetry 패키지 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-24 02:08:21 +09:00
parent dfecaa7654
commit 63c126077a
7 changed files with 158 additions and 5 deletions

16
CLAUDE.md Normal file
View File

@ -0,0 +1,16 @@
# 자동 실행 지시사항
다음 작업을 순서대로 실행해주세요:
1. venv가 없으면 생성하고 의존성 설치:
```bash
python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt
```
2. Django 개발 서버 실행:
```bash
source venv/bin/activate && python3 manage.py runserver 0.0.0.0:8800
```
# 연계프로젝트
- 'msa-fe' : 프론트엔드

View File

@ -6,7 +6,8 @@ from .views import (
PostListView, PostListCreateView, PostDetailView,
CommentViewSet, TagListView,
AttachmentListCreateView, AttachmentDeleteView,
TempAttachmentUploadView, TempAttachmentDeleteView
TempAttachmentUploadView, TempAttachmentDeleteView,
PresignedUrlView
)
# 댓글 라우터
@ -36,4 +37,8 @@ urlpatterns = [
TempAttachmentUploadView.as_view(), name='temp-attachment-upload'),
path('attachments/<int:pk>/',
TempAttachmentDeleteView.as_view(), name='temp-attachment-delete'),
# Presigned URL (MinIO/S3)
path('presigned/<path:object_key>/',
PresignedUrlView.as_view(), name='presigned-url'),
]

View File

@ -2,6 +2,9 @@
import os
import re
import boto3
from botocore.client import Config
from django.conf import settings
from rest_framework import generics, viewsets, permissions, status
from rest_framework.exceptions import PermissionDenied
from rest_framework.response import Response
@ -371,4 +374,40 @@ class AttachmentDeleteView(APIView):
attachment.delete()
logger.info(f"Attachment '{original_name}' deleted from post {post_pk} by {username}.")
return Response(status=status.HTTP_204_NO_CONTENT)
return Response(status=status.HTTP_204_NO_CONTENT)
class PresignedUrlView(APIView):
"""Presigned URL 생성 (MinIO/S3)"""
permission_classes = [permissions.AllowAny]
def get_s3_client(self):
"""S3 클라이언트 생성"""
return boto3.client(
's3',
endpoint_url=settings.AWS_S3_ENDPOINT_URL,
aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
config=Config(signature_version='s3v4'),
region_name=settings.AWS_S3_REGION_NAME,
)
def get(self, request, object_key):
"""GET presigned URL 생성"""
try:
s3_client = self.get_s3_client()
presigned_url = s3_client.generate_presigned_url(
'get_object',
Params={
'Bucket': settings.AWS_STORAGE_BUCKET_NAME,
'Key': object_key,
},
ExpiresIn=3600, # 1시간 유효
)
return Response({'url': presigned_url})
except Exception as e:
logger.error(f"Presigned URL 생성 실패: {e}")
return Response(
{'detail': 'Presigned URL 생성 실패'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)

View File

@ -37,6 +37,11 @@ else:
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('SECRET_KEY', 'django-insecure-ec9me^z%x7-2vwee5#qq(kvn@^cs!!22_*f-im(320_k5-=0j5')
# OpenTelemetry Trace 설정
SERVICE_PLATFORM = os.getenv("SERVICE_PLATFORM", "none")
TRACE_SERVICE_NAME = os.getenv("TRACE_SERVICE_NAME", "msa-django-blog")
TRACE_ENDPOINT = os.getenv("TRACE_ENDPOINT", "none")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = int(os.environ.get('DEBUG', 1))

View File

@ -9,8 +9,76 @@ https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
import os
from django.core.wsgi import get_wsgi_application
# Django 설정을 미리 불러온다
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'blog_prj.settings')
from django.conf import settings
from django.core.wsgi import get_wsgi_application
# DEBUG 모드 아닐 때만 OpenTelemetry 활성
if not settings.DEBUG:
import grpc
from opentelemetry import trace
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.django import DjangoInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.instrumentation.logging import LoggingInstrumentor
from opentelemetry.instrumentation.dbapi import trace_integration
import MySQLdb
trace.set_tracer_provider(
TracerProvider(
resource=Resource.create({
"service.platform": settings.SERVICE_PLATFORM,
"service.name": settings.TRACE_SERVICE_NAME,
})
)
)
# TRACE_CA_CERT 설정에 따른 gRPC credentials 구성
credentials = None
ca_cert_path = os.getenv('TRACE_CA_CERT', '').strip()
if ca_cert_path and os.path.exists(ca_cert_path):
with open(ca_cert_path, 'rb') as f:
ca_cert = f.read()
credentials = grpc.ssl_channel_credentials(root_certificates=ca_cert)
insecure = False
else:
insecure = True
otlp_exporter = OTLPSpanExporter(
endpoint=settings.TRACE_ENDPOINT,
insecure=insecure,
credentials=credentials,
headers={
"x-scope-orgid": settings.SERVICE_PLATFORM,
"x-service": settings.TRACE_SERVICE_NAME
}
)
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(otlp_exporter)
)
# Django 요청/응답 추적
DjangoInstrumentor().instrument()
# HTTP 클라이언트 요청 추적 (requests 라이브러리)
RequestsInstrumentor().instrument()
# 로그와 Trace 연동 (trace_id, span_id를 로그에 자동 추가)
LoggingInstrumentor().instrument(set_logging_format=True)
# MySQL DB 쿼리 추적
trace_integration(
MySQLdb,
"connect",
"mysql",
capture_parameters=True,
)
application = get_wsgi_application()

View File

@ -35,3 +35,23 @@ sqlparse==0.5.3
typing_extensions==4.13.2
uritemplate==4.1.1
urllib3==2.4.0
# OpenTelemetry Trace
googleapis-common-protos==1.70.0
grpcio==1.72.1
opentelemetry-api==1.34.0
opentelemetry-exporter-otlp==1.34.0
opentelemetry-exporter-otlp-proto-common==1.34.0
opentelemetry-exporter-otlp-proto-grpc==1.34.0
opentelemetry-exporter-otlp-proto-http==1.34.0
opentelemetry-instrumentation==0.55b0
opentelemetry-instrumentation-dbapi==0.55b0
opentelemetry-instrumentation-django==0.55b0
opentelemetry-instrumentation-logging==0.55b0
opentelemetry-instrumentation-requests==0.55b0
opentelemetry-instrumentation-wsgi==0.55b0
opentelemetry-proto==1.34.0
opentelemetry-sdk==1.34.0
opentelemetry-semantic-conventions==0.55b0
opentelemetry-util-http==0.55b0
protobuf==5.29.5
wrapt==1.17.2

View File

@ -1 +1 @@
v0.0.12
v0.0.13