목차
-
- 서버 로그 시스템 구현
-
- 전역 예외 처리机制 구축
-
- 응답 포맷 통합 포장
-
- 데이터베이스 연결 및 설정
-
- 소프트웨어 개발 방법론
-
- 사용자 인증 모듈 구현
-
- Django 설정 파일 심층 분석
-
- 미디어 파일 서빙 설정
- 서버 로그 시스템 구현
대규모 프로젝트에서는 체계적인 로깅 시스템이 필수적입니다. 로그를 통해 애플리케이션의 상태를 모니터링하고, 오류 발생 시 원인을 파악할 수 있습니다.
로그 시스템의 핵심 기능
- 콘솔 출력 로그
- 파일 기반 로그 저장
- 데이터베이스 로그 저장
- 통합 로그 관리 시스템 (Sentry 등)
개발 환경에서는 print() 문을 사용해도 되지만, 운영 환경에서는 logger 객체를 활용한 로깅을 권장합니다. 로깅 레벨을 조정하면 불필요한 로그 출력을 효과적으로 제어할 수 있습니다.
Django 프로젝트 로그 설정
Django는 Python 내장 로깅 모듈을 기반으로 합니다. 설정 파일(dev.py)에 다음과 같이 로그 구성을 추가합니다.
코드 보기
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
},
'simple': {
'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
},
},
'filters': {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'WARNING',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'file': {
'level': 'ERROR',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(os.path.dirname(BASE_DIR), "logs", "app.log"),
'maxBytes': 300 * 1024 * 1024,
'backupCount': 10,
'formatter': 'verbose',
'encoding': 'utf-8'
},
},
'loggers': {
'django': {
'handlers': ['console', 'file'],
'propagate': True,
},
}
}
로거 객체 생성
utils 디렉토리에 custom_logger.py 파일을 생성합니다.
import logging
logger = logging.getLogger('django')
로그 활용 예시
# views.py
from utils.custom_logger import logger
from django.http import JsonResponse
def system_check(request):
logger.info('시스템 점검 로그')
logger.warning('경고 발생 로그')
logger.error('오류 발생 로그')
return JsonResponse({'status': 'running'})
- 전역 예외 처리机制 구축
RESTful API 개발 시 일관된 응답 형식은 매우 중요합니다. DRF(Django REST Framework)와 Django 자체 예외를 모두 처리하는 통합 예외 핸들러를 구현합니다.
IP 라우팅 차이점
| 주소 | 접근 범위 |
|---|---|
| 127.0.0.1 | 로컬 컴퓨터만 접근 가능 |
| 0.0.0.0 | 동일 네트워크 내 모든 장치 접근 가능 |
전역 예외 핸들러 구현
# utils/exception_handler.py
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.response import Response
from utils.custom_logger import logger
def global_exception_handler(exc, context):
request = context.get('request')
# 사용자 정보 추출
try:
user_id = request.user.pk if hasattr(request.user, 'pk') else '익명'
except:
user_id = '익명'
view = context.get('view')
# 오류 로깅
logger.error('사용자:[%s], 방법:[%s], 경로:[%s], 뷰:[%s], 오류:[%s]',
user_id, request.method, request.get_full_path(), str(view), str(exc)
)
# DRF 예외 처리
response = drf_exception_handler(exc, context)
if response:
response = Response({
'code': 999,
'msg': response.data.get('detail', '서버 오류 발생')
})
else:
response = Response({
'code': 888,
'msg': str(exc)
})
return response
DRF 설정 수정
# settings/dev.py
INSTALLED_APPS = [
'rest_framework',
'home',
'user',
]
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'utils.exception_handler.global_exception_handler',
}
예제 뷰
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.exceptions import APIException
class SystemTestView(APIView):
def get(self, request):
raise APIException('DRF 테스트 예외 발생')
return Response({'status': 'ok'})
- 응답 포맷 통합 포장
일관된 API 응답 구조를 위해 Response 클래스를 재정의합니다.
통합 응답 클래스
# utils/response_wrapper.py
from rest_framework.response import Response
class ApiResponse(Response):
def __init__(self, code=100, msg='success', status=None, headers=None, **kwargs):
data = {'code': code, 'msg': msg}
if kwargs:
data.update(kwargs)
super().__init__(data=data, status=status, headers=headers)
사용 예시
from utils.response_wrapper import ApiResponse
class UserView(APIView):
def get(self, request):
# 기본 응답
return ApiResponse()
def post(self, request):
# 추가 데이터 포함 응답
return ApiResponse(token='abc123', user={'name': '홍길동'})
def delete(self, request):
# 사용자 정의 코드
return ApiResponse(code=101, msg='deleted')
- 데이터베이스 연결 및 설정
데이터베이스 및 사용자 생성
-- MySQL 명령어
CREATE DATABASE myproject DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
-- 프로젝트용 사용자 생성
GRANT ALL PRIVILEGES ON myproject.* TO 'projectuser'@'%' IDENTIFIED BY 'SecurePass123!';
GRANT ALL PRIVILEGES ON myproject.* TO 'projectuser'@'localhost' IDENTIFIED BY 'SecurePass123!';
FLUSH PRIVILEGES;
Django 데이터베이스 설정
# settings/dev.py
import pymysql
pymysql.install_as_MySQLdb()
# 환경 변수 활용 (보안 강화)
import os
db_user = os.environ.get('DB_USER', 'projectuser')
db_pass = os.environ.get('DB_PASSWORD', 'SecurePass123!')
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'myproject',
'USER': db_user,
'PASSWORD': db_pass,
'HOST': '127.0.0.1',
'PORT': 3306
}
}
# 또는 mysqlclient 사용 시 (추천)
# pip install mysqlclient
mysqlclient 설치
pip install mysqlclient
# 또는 PyCharm 패키지 관리자 사용
- 소프트웨어 개발 방법론
폭포수 모델 (Waterfall Model)
요구사항 분석 → 설계 → 구현 → 테스트 → 배포
각 단계가 완료되어야 다음 단계로 진행하는 전통적 방식입니다.
애자일 개발 (Agile Development)
스프린트 (2-4주 단위)
계획 → 설계 → 개발 → 테스트 → 배포 → 회고
점진적 기능 개발과 빠른 피드백을 중시하는 현대적 방법론입니다.
- 사용자 인증 모듈 구현
Django 인증 시스템을 확장하여 사용자 정의 모델을 구현합니다.
필수 패키지 설치
pip install Pillow
사용자 모델 정의
# user/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
phone = models.CharField(max_length=11, unique=True)
avatar = models.ImageField(upload_to='avatars', default='avatars/default.png')
class Meta:
db_table = 'custom_user'
verbose_name = '사용자'
verbose_name_plural = verbose_name
def __str__(self):
return self.username
설정 업데이트
# settings/dev.py
AUTH_USER_MODEL = 'user.User'
마이그레이션 실행
python manage.py makemigrations
python manage.py migrate
- Django 설정 파일 심층 분석
# settings.py 핵심 설정 분석
from pathlib import Path
import os
import sys
# 프로젝트 루트 경로 설정
BASE_DIR = Path(__file__).resolve().parent.parent
# Python 경로에 앱 디렉토리 추가
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
# 보안 키 (중요: 운영 환경에서 변경 필요)
SECRET_KEY = 'your-secret-key-here'
# 디버그 모드 (운영에서는 False 필수)
DEBUG = False
ALLOWED_HOSTS = ['*']
# 설치된 앱
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'home',
'user',
]
# 미들웨어
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
]
# URL 설정
ROOT_URLCONF = 'project.urls'
# 템플릿 설정
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
],
},
},
]
# WSGI 애플리케이션
WSGI_APPLICATION = 'project.wsgi.application'
# 데이터베이스
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'myproject',
'USER': os.environ.get('DB_USER', 'projectuser'),
'PASSWORD': os.environ.get('DB_PASSWORD', 'SecurePass123!'),
'HOST': '127.0.0.1',
'PORT': 3306
}
}
# 국제화
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_TZ = False
# 정적 파일
STATIC_URL = '/static/'
# 미디어 파일
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 기본 자동 증가 필드
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
- 미디어 파일 서빙 설정
사용자 업로드 이미지에 접근할 수 있도록 라우팅을 설정합니다.
설정 추가
# settings/dev.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
URL 라우팅 등록
# urls.py
from django.urls import path
from django.views.static import serve
from django.conf import settings
urlpatterns = [
path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}),
]
이제 브라우저를 통해 http://localhost:media/avatars/user.png 형식으로 업로드된 이미지에 접근할 수 있습니다.
부록
RDBAC 프로젝트 참고 자료
암호화 관련 면접 질문
MD5는 대칭 암호화인가, 비대칭 암호화인가?
MD5는 암호화가 아닌 해시(摘要) 알고리즘입니다. 복호화가 불가능하며, 동일한 입력에 대해 항상 동일한 출력(해시값)을 생성합니다.
| 유형 | 특징 | 예시 |
|---|---|---|
| 대칭 암호화 | 암호/복호 동일 키 | AES, DES |
| 비대칭 암호화 | 공개키로 암호, 개인키로 복호 | RSA |
| 해시 알고리즘 | 단방향 변환 | MD5, SHA-256 |