Giter VIP home page Giter VIP logo

backend-pre-task's People

Contributors

elijah74 avatar humblem2 avatar

backend-pre-task's Issues

코드 정리

불필요한 코드정리
gitignore 내용 확인

  • **주의**: .env 패밀리 push 금지 (팀내 매뉴얼 문서로 리다이렉션하는 업무흐름)

페이징

페이징 위한 파일에 각 컨트롤러가 사용하는 페이지네이션 클래스 만들기

페이지네이션 클래스 mixin 고려해서 추상화 할지 말지 코드 적으면 모아두기 -> 체크 후 결정

DB 모델링 하기

주소록API

  • User 모델 AbstactUser 상속
  • 연락처, 라벨, 중간테이블
    • 요구사항 만족하도록
    • fat model
  • date 관련 mixin 만들기
  • 장고앱은 2개로 개발하기

테스트코드

목표: API 에 대해 커버리지 80% 이상
coverage report 활용하여 점진적으로 강화

[특정라벨의 연락처 목록 API] PathVariable인 label_id값의 잘못된 getter 문제 (--Fixed)

내용

정보

  • 발견일: 2023-10-06 09:30 (⚠ 과제마감인 9/30 자정 즉 release 이후 입니다.)
  • 발견자: 조수현(본인)
  • 상태:
    • release 이후에 발견하여 hotfix라고 판단하였고, 해결하여 PR 뒤 origin/master, origin/develop, tag/v1.0.1반영되었음.
    • 이전 버전(과제마감 시점)을 보시려면 tag/v1.0.0을 보시면 됩니다.

문제 상황

  1. /api/labels/{label_id}/contacts/ 경로를 통한 API 요청에서 label_id 경로변수가 제대로 Look-up 처리되지 않음
  2. 그 결과, 특정라벨의 연락처 리스트 쿼리 결과가 정상적이지 않음

자세한 설명

현재 LabelContactsViewSetget_queryset 메서드에서 label_id 값을 추출하는 로직이

  • ⭕ Path Varible(URL Captured value)
  • ❌ QueryString
    기반으로 되어 있음.

따라서 URL Path Varible로 전달된 label_id가 쿼리에 반영되지 않고 있음

코드위치

router.register(r'labels/(?P<label_id>\d+)/contacts', LabelContactsViewSet, basename='label-contacts')
class LabelContactsViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
    """`특정 라벨`에 대응되는 `연락처 리스트`"""

    queryset = m.Contact.objects.all()
    serializer_class = cs.ContactListSerializer
    pagination_class = pn.DefaultContactPagination
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated, pm.IsOwnerOfContact]

    def get_queryset(self):
        """@overide URL에서 `label` 쿼리 파라미터를 기반으로 특정 라벨에 해당하는 연락처리스트를 반환"""
        if not self.request.user.is_authenticated:
            return m.Contact.objects.none()

        queryset = m.Contact.objects.filter(user=self.request.user)

        label_id = self.request.query_params.get('label_id', None) //여기가 문제
        if label_id:
            contact_ids = m.ContactLabel.objects.filter(label=label_id).values_list('contact', flat=True)
            queryset = queryset.filter(id__in=contact_ids)
        return queryset

해결 방안

  • self.kwargs를 사용하여 Path Varible 인 label_id 값을 URL에서 올바르게 추출하도록 코드를 수정하는 것이 필요

향후 예방

  • 서버단에서 label_id 없는경우 예외처리하여 400/Bad Request 혹은 404 응답되도록 로직 개선
  • 이 문제가 미래에 재발하지 않도록, URL Path Varible getter로직에 대한 단위 테스트를 추가 필요하고 함께 커밋

브랜치 반영 계획

  • develop 브랜치에 반영하고
  • v1.0.1-hotfix tag 생성 릴리즈 노트
  • 안정화 이후 master 브랜치에 반영 할 계획
====================================

master -> hotfix-1.0.0 -> master 

hotfix-1.0.0 -> develop

release/tag v1.0.1

=====================================

Appendix.

@RequestParam

쿼리스트링인 경우

self.request.query_params
label_id = self.request.query_params.get('label_id', None)

Path variable 인 경우

label_id = self.kwargs['label_id']
label_id = self.kwargs.get('label_id', None)

정리 문서

  • 정리가 필요한 내용 문서로 정리해두기 -> 노션
  • db/~ 에 erd 위치 (요구사항)
  • uri action mapping
  • test
  • 폴더구조 및 파일 디렉터리 역할
  • 의존성설치
  • 기타 사용 명령어들
  • 서버 구동
  • 초기 ddl, dml 순서 방법 단축키 툴 (요구사항)
  • API-DOC 종류와 URL (관리포인트 1개로 갈지말지 여부는 마일스톤 이후 Fix. 우선은 Swagger, redoc 오픈)

개발환경 변경 시 Pycharm Debug 안되는 현상 (--Fixed)

🔨 Fixed 되었습니다.

  • 현상: Connection to Python debugger failed. Socket closed 토스트박스
  • 원인과 해결방안 찾아보기
  • 추정: decoupling 사용 .env.* 변경하고 나서 안되는 것 같음 / .idea 캐싱 등 ..

원인

  • 모르겠음. python 3.9 버전에서의 IDE 버그라는 소리도 있고. 확실치 않음. 우선 선해결 하는 방향으로.
  • Connection to Python debugger failed

참고

해결방법

  • Run/Debug Configurations > Environment > Environment variables에 PYDEVD_USE_CYTHON=NO 추가
PYTHONUNBUFFERED=1;DJANGO_SETTINGS_MODULE=conf.settings.development;PYDEVD_USE_CYTHON=NO

image

함께보기

  • Pycharm Django Support 설정
    image
  • Django settings 설정파일 모듈화 상태 & .env.* 환경변수파일 모듈화 상태 & manage.py 명령 파일 상태(decouple 패키지로 환경변수파일 주입하여 설정파일 자동 주입 후 장고서버 명령어로 실행
    image
  • manage.py
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
from typing import Dict

import pymysql
from decouple import Config, config, RepositoryEnv
from termcolor import colored

from conf.settings.base import BASE_DIR


def load_environment() -> Dict[str, str]:
    """환경변수 파일(`.env`)을 기반으로 배포환경을 고려한 Django 설정 모듈을 로드합니다."""

    # 배포환경에 따라 환경변수 파일 선택
    environment = config('ENVIRONMENT', default='development')
    env_file = f'.env.{environment}'
    selected_env_file = str(BASE_DIR / env_file)

    # 선택한 환경변수 파일로부터 설정 로드
    custom_config = Config(RepositoryEnv(selected_env_file))
    django_settings_module = custom_config('DJANGO_SETTINGS_MODULE', default='conf.settings.development')

    return {
        'environment': environment,
        'selected_env_file': selected_env_file,
        'django_settings_module': django_settings_module
    }


def pretty_print_colored(**kwargs):
    """중요 정보 색상화하여 출력"""
    box_width = 70
    print(colored("┌" + "─"*(box_width-2) + "┐", 'yellow'))
    for key, value in kwargs.items():
        formatted_key = key.replace("_", " ").capitalize()  # 'selected_env_file' -> 'Selected env file'
        key_width = len(formatted_key) + 2
        print(colored(f"│ {formatted_key}: {value:<{box_width - key_width - 2}}│", 'green'))
    print(colored("└" + "─"*(box_width-2) + "┘", 'yellow'))


def install_pymysql_as_mysqldb():
    """PyMySQL을 MySQLdb처럼 사용하기 위한 설정"""
    pymysql.install_as_MySQLdb()


def main():
    """Run administrative tasks."""
    install_pymysql_as_mysqldb()
    environment_data = load_environment()
    django_settings_module = environment_data['django_settings_module']
    pretty_print_colored(**environment_data)
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', django_settings_module)
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == '__main__':
    main()
  • .env
ENVIRONMENT=development
  • .env.development
DJANGO_SETTINGS_MODULE=conf.settings.development
RDBMS_NAME=addressbookdb
RDBMS_USER=root
RDBMS_PASSWORD=root
RDBMS_HOST=localhost
RDBMS_PORT=3306
TEST_RDBMS_NAME=test_addressbookdb
DJANGO_USE_PYMYSQL_AS_MYSQLDB=True

표준화된 폴더 구조 잡기

  • 의존성파일과 venv 잡기
  • 설정파일들은 dev와 prod 나누기
  • pip-tools 사용하여 의존성관리
  • 장고프로젝트, 장고앱 만들기
  • .env 사용하며 .env 나누기

mysqlclient 패키지 설치 이슈 (--Alternative Fixed)


Issue: mysqlclient 패키지 설치가 안되는 문제

상황 (최초 fork 상태 requirements.txt):

  • django-mysql 버전: 4.11.0
  • mysqlclient 버전: 2.2.0

원인:

  • Windows 빌드 이슈로 인해 현재 Python 버전, 빌드되는 Local 환경과 호환되지 않음.

임시 해결 방안:

PyMySQL 패키지를 사용하여 mysqlclient 대신 연동. 추후 특정 이슈로 인해 방법변경이 필요하다고 판단이 될 경우, mysqlclient를 적용하여 다시 검토할 예정 (우선순위: 낮음).

변경 사항

  • requirements.in

    PyMySQL==1.1.0
  • manage.py

    def install_pymysql_as_mysqldb():
        """PyMySQL을 MySQLdb처럼 사용하기 위한 설정"""
        import pymysql
        pymysql.install_as_MySQLdb()
    • 추가 정보: PyMySQL을 MySQLdb처럼 사용하기 위한 설정?
      • PyMySQL 및 MySQLdb는 모두 Mysql DB API 2.0 표준을 따르는 데이터베이스 커넥터다.
      • PyMySQL은 PyMySQL 패키지로 설치할 수 있다.
      • MySQLdb는 mysqlclient 패키지로 설치한다.
      • mysqlclient 패키지는 파이썬 버전에 따라 설치 호환 문제가 있을 수 있다.
      • PyMySQL은 MySQLdb보다 연동 속도가 느리다.
      • MySQLdb 라이브러리가 설치되지 않은 경우 PyMySQL을 MySQLdb처럼 사용할 수 있다.
      • MySQLdb가 설치되어 있으면 기본적으로 MySQLdb를 사용한다.
      • MySQLdb와 PyMySQL 모두 설치되지 않은 경우 예외가 발생한다.
      • PyMySQL.install_as_MySQLdb()를 호출하면 Django가 PyMySQL을 MySQLdb로 인식한다.
      • settings.py의 DATABASES 설정을 MySQLdb 스타일로 할 수 있다.

API-DOC 적용

스웨거는 옵션이나 기본(API개발)에 충실 하되 redoc 와 함께 사용가능하도록 확장성 있게

개발 중 테스트시에 있어도 편하긴 함 API 만들때 개발하거나 마지막에 하거나

SQL 쿼리

DDL 및 DML SQL
기타 개발 시 사용하는 유틸성 SQL
db/~ 보관
data.sql
schema.sql

연락처, 라벨, 연락처-라벨 API

  • ModelView, GenericViewSet .. 적절하게 가능한 짧은코드로 사용
  • Contact모델 목록 조회 (전시 필드 다름)
  • Contact모델 상세, 수정, 생성
  • Label모델 단독으로 생성, 상세, 목록, 수정, 삭제
  • 특정 라벨에 대응되는 연락처리스트
  • 우선 중간모델은 조회와 수정만 (추후 확장 될 수도 있음)
  • 적절하게 시리얼라이즈 분리
  • ...
  • 연락처 목록 전시 쿼리스트링 GET /api/contacts/ 정렬(필터링) ORM으로 메서드 오버라이딩
  • 연락처는 시리얼라이즈 2개
  • 연락처, 라벨 생성시 user 필드 setter

serializer 관련 Mixin 추가하기

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.