InfoGrab DocsInfoGrab Docs

튜토리얼: GitLab Duo로 Python 쇼핑 애플리케이션 만들기

요약

당신은 온라인 서점의 개발자로 채용되었습니다. 직원이 입고되는 새 도서를 추가할 수 있도록 합니다. 음수 가격이나 수량과 같은 일반적인 데이터 입력 오류를 방지합니다. 향후 고객 대상 기능을 위한 기반을 마련합니다. 이 튜토리얼은 시리즈의 1부로, 이러한 요구사항을 충족하는 데이터베이스 백엔드를 갖춘 Python 웹 애플리케이션을 생성하고 디버깅하는 과정을 안내합니다.

당신은 온라인 서점의 개발자로 채용되었습니다. 현재 재고 관리 시스템은 스프레드시트와 수동 프로세스가 혼재되어 있어 재고 오류와 업데이트 지연이 발생하고 있습니다. 팀에서는 다음 기능을 갖춘 웹 애플리케이션을 만들어야 합니다:

  • 실시간으로 도서 재고를 추적합니다.

  • 직원이 입고되는 새 도서를 추가할 수 있도록 합니다.

  • 음수 가격이나 수량과 같은 일반적인 데이터 입력 오류를 방지합니다.

  • 향후 고객 대상 기능을 위한 기반을 마련합니다.

이 튜토리얼은 시리즈의 1부로, 이러한 요구사항을 충족하는 데이터베이스 백엔드를 갖춘 Python 웹 애플리케이션을 생성하고 디버깅하는 과정을 안내합니다.

GitLab Duo Agentic ChatGitLab Duo Code Suggestions를 사용하여 다음 작업을 수행합니다:

  • 표준 디렉터리와 필수 파일로 구성된 체계적인 Python 프로젝트를 설정합니다.

  • Python 가상 환경을 구성합니다.

  • 웹 애플리케이션의 기반인 Flask 프레임워크를 설치합니다.

  • 필요한 의존성을 설치하고 개발을 위한 프로젝트를 준비합니다.

  • Flask 애플리케이션 개발을 위한 Python 구성 파일과 환경 변수를 설정합니다.

  • 아티클 모델, 데이터베이스 작업, API 라우트, 재고 관리 기능 등 핵심 기능을 구현합니다.

  • 애플리케이션이 의도한 대로 작동하는지 테스트하고, 코드를 예제 코드 파일과 비교합니다.

시작하기 전에#

  • 시스템에 최신 버전의 Python을 설치하세요. 운영 체제에 맞는 설치 방법을 Chat에 물어볼 수 있습니다.

  • 관리자, 그룹 Owner, 또는 프로젝트 Owner에게 GitLab Duo에 대한 접근 권한이 있는지 확인하세요.

  • 사용하는 IDE에 확장 프로그램을 설치하세요:

Web IDE: GitLab 인스턴스를 통해 접근

GitLab Duo Chat과 Code Suggestions 사용하기#

이 튜토리얼에서는 Chat과 Code Suggestions를 사용하여 Python 웹 애플리케이션을 만듭니다. 이러한 기능을 사용하는 방법은 여러 가지가 있습니다.

GitLab Duo Chat 사용하기#

구독 추가 기능에 따라 GitLab UI, Web IDE, 또는 IDE에서 Chat을 사용할 수 있습니다.

GitLab UI에서 Chat 사용하기#

상단 바에서 Search or go to를 선택하고 프로젝트를 찾으세요.

GitLab Duo 사이드바에서 Add new chat ( pencil-square )을 선택하세요.

드롭다운 목록에서 에이전트를 선택하세요.

화면 오른쪽의 GitLab Duo 사이드바에 Chat 대화가 열립니다.

Chat 텍스트 상자에 질문을 입력하고 Enter를 누르거나 Send를 선택하세요. 대화형 AI 채팅이 답변을 생성하는 데 몇 초가 걸릴 수 있습니다.

Web IDE에서 Chat 사용하기#

  • Web IDE 열기:

GitLab UI의 상단 바에서 Search or go to를 선택하고 프로젝트를 찾으세요.

  • 파일을 선택하세요. 그런 다음 오른쪽 상단에서 Edit > Open in Web IDE를 선택하세요.

  • 다음 방법 중 하나를 사용하여 Chat을 여세요:

왼쪽 사이드바에서 GitLab Duo Chat을 선택하세요.

  • 에디터에서 열려 있는 파일에서 일부 코드를 선택하세요.

마우스 오른쪽 버튼을 클릭하고 GitLab Duo Chat을 선택하세요.

  • Explain selected code, Generate Tests, 또는 Refactor를 선택하세요.

  • 키보드 단축키 사용: Windows 및 Linux에서는 ALT+d, Mac에서는 Option+d.

  • 메시지 상자에 질문을 입력하세요. Enter를 누르거나 Send를 선택하세요.

IDE에서 Chat 사용하기#

IDE에서 Chat을 사용하는 방법은 사용하는 IDE에 따라 다릅니다.

VS Code

  • VS Code에서 파일을 여세요. Git 리포지터리의 파일일 필요는 없습니다.

  • 왼쪽 사이드바에서 GitLab Duo Chat ( duo-chat )을 선택하세요.

  • 메시지 상자에 질문을 입력하세요. Enter를 누르거나 Send를 선택하세요.

  • Chat 창의 오른쪽 상단에서 Show Status를 선택하여 Command Palette에서 정보를 확인하세요.

코드의 일부를 작업하는 동안에도 GitLab Duo Chat과 상호 작용할 수 있습니다.

  • VS Code에서 파일을 여세요. Git 리포지터리의 파일일 필요는 없습니다.

  • 파일에서 일부 코드를 선택하세요.

  • 마우스 오른쪽 버튼을 클릭하고 GitLab Duo Chat을 선택하세요.

  • 옵션을 선택하거나, Open Quick Chat을 선택하고 Can you simplify this code?와 같은 질문을 입력한 후 Enter를 누르세요.

자세한 내용은 VS Code에서 GitLab Duo Chat 사용하기를 참조하세요.

JetBrains IDEs

Code Suggestions 사용하기#

Code Suggestions를 사용하려면:

지원되는 IDE에서 Git 프로젝트를 여세요.

로컬 프로젝트에는 GitLab의 리포지터리를 가리키는 Git 리모트가 구성되어 있어야 합니다. 아직 설정되지 않은 경우, git remote add를 사용하여 프로젝트를 연결하세요.

코드를 작성하세요. 입력하는 동안 제안이 표시됩니다. Code Suggestions는 커서 위치에 따라 코드 스니펫을 제공하거나 현재 줄을 완성합니다.

자연어로 요구사항을 설명하세요. Code Suggestions는 제공된 컨텍스트를 기반으로 함수와 코드 스니펫을 생성합니다.

제안을 받으면 다음 중 하나를 수행할 수 있습니다:

제안을 수락하려면 Tab을 누르세요.

  • 부분 제안을 수락하려면 Control+Right arrow 또는 Command+Right arrow를 누르세요.

  • 제안을 거부하려면 Esc를 누르세요.

  • 제안을 무시하려면 평소처럼 계속 입력하세요.

자세한 내용은 Code Suggestions 문서를 참조하세요.

이제 Chat과 Code Suggestions를 사용하는 방법을 알았으니, 웹 애플리케이션 구축을 시작합니다. 먼저 체계적인 Python 프로젝트 구조를 만듭니다.

프로젝트 구조 만들기#

시작하기 위해 Python 모범 사례를 따르는 잘 정리된 프로젝트 구조가 필요합니다. 올바른 구조는 코드를 유지 관리하기 쉽고, 테스트하기 쉬우며, 다른 개발자가 이해하기 쉽게 만듭니다.

Chat을 사용하여 Python 프로젝트 구성 규칙을 이해하고 적절한 파일을 생성할 수 있습니다. 이를 통해 모범 사례를 조사하는 시간을 절약하고 중요한 구성 요소를 빠뜨리지 않을 수 있습니다.

IDE에서 Chat을 열고 다음을 입력하세요:

What is the recommended project structure for a Python web application? Include
common files, and explain the purpose of each file.

이 프롬프트는 파일을 만들기 전에 Python 프로젝트 구성을 이해하는 데 도움이 됩니다.

Python 프로젝트를 위한 새 폴더를 만들고 Chat의 응답을 바탕으로 디렉터리 및 파일 구조를 만드세요. 아마 다음과 유사할 것입니다:

python-shop-app/
├── LICENSE
├── README.md
├── requirements.txt
├── setup.py
├── .gitignore
├── .env
├── app/
│   ├── __init__.py
│   ├── models/
│   │   ├── __init__.py
│   │   └── article.py
│   ├── routes/
│   │   ├── __init__.py
│   │   └── shop.py
│   └── database.py
└── tests/
    ├── __init__.py
    └── test_shop.py

.gitignore 파일을 채워야 합니다. Chat에 다음을 입력하세요:

Generate a .gitignore file for a Python project that uses Flask, SQLite, and
virtual environments. Include common IDE files.

응답을 .gitignore 파일에 복사하세요.

README 파일을 위해 Chat에 다음을 입력하세요:

Generate a README.md file for a Python web application that manages a bookstore
inventory. Make sure that it includes all sections for requirements, setup, and usage.

이제 업계 모범 사례를 따르는 체계적인 Python 프로젝트를 만들었습니다. 이 구성 덕분에 코드를 더 쉽게 유지 관리하고 테스트할 수 있습니다. 다음으로, 코드 작성을 시작하기 위한 개발 환경을 설정합니다.

개발 환경 설정하기#

올바르게 격리된 개발 환경은 의존성 충돌을 방지하고 애플리케이션을 배포 가능하게 만듭니다.

Chat을 사용하여 Python 가상 환경을 설정하고 올바른 의존성이 포함된 requirements.txt 파일을 만들 것입니다. 이를 통해 개발을 위한 안정적인 기반을 확보할 수 있습니다.

   python-shop-app/
   ├── LICENSE
   ├── README.md
   ├── requirements.txt <= File you are updating
   ├── setup.py
   ├── .gitignore
   ├── .env
   ├── app/
   │   ├── __init__.py
   │   ├── models/
   │   │   ├── __init__.py
   │   │   └── article.py
   │   ├── routes/
   │   │   ├── __init__.py
   │   │   └── shop.py
   │   └── database.py
   └── tests/
       ├── __init__.py
       └── test_shop.py

선택 사항. Python과 Flask가 함께 웹 애플리케이션을 생성하는 방법에 대해 Chat에 물어보세요.

Chat을 사용하여 Python 환경 설정 모범 사례를 이해하세요:

What are the recommended steps for setting up a Python virtual environment with
Flask? Include information about requirements.txt and pip.

필요한 추가 질문을 하세요. 예를 들어:

What does the requirements.txt do in a Python web app?

응답을 바탕으로 먼저 가상 환경을 만들고 활성화하세요 (예: Homebrew의 python3 패키지를 사용하는 macOS의 경우):

python3 -m venv myenv
source myenv/bin/activate

requirements.txt 파일도 만들어야 합니다. Chat에 다음을 물어보세요:

What should be included in requirements.txt for a Flask web application with
SQLite database and testing capabilities? Include specific version numbers.

응답을 requirements.txt 파일에 복사하세요.

requirements.txt 파일에 나열된 의존성을 설치하세요:

pip install -r requirements.txt

이제 개발 환경이 구성되어 모든 필요한 의존성이 가상 환경에 격리되어 충돌을 방지합니다. 다음으로, 프로젝트의 패키지 및 환경 설정을 구성합니다.

프로젝트 구성하기#

환경 변수를 포함한 올바른 구성은 다양한 환경에서 애플리케이션이 일관되게 실행되도록 합니다.

Code Suggestions를 사용하여 구성을 생성하고 다듬을 것입니다. 그런 다음 Chat에게 각 설정의 목적을 설명하도록 요청하여 무엇을 구성하고 있는지, 그 이유를 이해할 수 있습니다.

프로젝트 폴더에 이미 setup.py라는 Python 구성 파일을 만들었습니다:

   python-shop-app/
   ├── LICENSE
   ├── README.md
   ├── requirements.txt
   ├── setup.py <= File you are updating
   ├── .gitignore
   ├── .env
   ├── app/
   │   ├── __init__.py
   │   ├── models/
   │   │   ├── __init__.py
   │   │   └── article.py
   │   ├── routes/
   │   │   ├── __init__.py
   │   │   └── shop.py
   │   └── database.py
   └── tests/
       ├── __init__.py
       └── test_shop.py

이 파일을 열고 파일 맨 위에 다음 주석을 입력하세요:

# Populate this setup.py configuration file for a Flask web application
# Include dependencies for Flask, testing, and database functionality
# Use semantic versioning

Code Suggestions가 구성을 생성해 줍니다.

선택 사항. 생성된 구성 코드를 선택하고 다음 슬래시 명령어를 사용하세요:

/explain을 사용하여 각 구성 설정이 하는 일을 이해하세요.

  • /refactor를 사용하여 구성 구조에서 잠재적인 개선 사항을 식별하세요.

생성된 코드를 검토하고 필요에 따라 조정하세요.

구성 파일에서 무엇을 조정할 수 있는지 확실하지 않으면 Chat에 물어보세요.

Chat에 무엇을 조정해야 하는지 물어보려면, GitLab UI가 아닌 IDE의 setup.py 파일에서 하세요. 이렇게 하면 방금 만든 setup.py 파일을 포함하여 작업 중인 컨텍스트가 Chat에 제공됩니다.

You have used Code Suggestions to generate a Python configuration file, `setup.py`,
for a Flask web application. This file includes dependencies for Flask, testing,
and database functionality. If I were to review this file, what might I want
to change and adjust?

파일을 저장하세요.

환경 변수 설정하기#

다음으로, Chat과 Code Suggestions를 모두 사용하여 환경 변수를 설정합니다.

Chat에서 다음을 물어보세요:

In a Python project, what environment variables should be set for a Flask application in development mode? Include database configuration.

환경 변수를 저장하기 위해 이미 .env 파일을 만들었습니다.

   python-shop-app/
   ├── LICENSE
   ├── README.md
   ├── requirements.txt
   ├── setup.py
   ├── .gitignore
   ├── .env <= File you are updating
   ├── app/
   │   ├── __init__.py
   │   ├── models/
   │   │   ├── __init__.py
   │   │   └── article.py
   │   ├── routes/
   │   │   ├── __init__.py
   │   │   └── shop.py
   │   └── database.py
   └── tests/
       ├── __init__.py
       └── test_shop.py

이 파일을 열고 Chat이 권장한 환경 변수를 포함하여 파일 맨 위에 다음 주석을 입력하세요:

# Populate this .env file to store environment variables
# Include the following
# ...
# Use semantic versioning

생성된 코드를 검토하고 필요에 따라 조정한 후 파일을 저장하세요.

프로젝트를 구성하고 환경 변수를 설정했습니다. 이를 통해 다양한 환경에서 애플리케이션을 일관되게 배포할 수 있습니다. 다음으로, 재고 시스템을 위한 애플리케이션 코드를 만듭니다.

애플리케이션 코드 만들기#

Flask 웹 프레임워크에는 세 가지 핵심 구성 요소가 있습니다:

  • 모델: 데이터와 비즈니스 로직 및 데이터베이스 모델을 포함합니다. article.py 파일에 지정됩니다.

  • 뷰: HTTP 요청과 응답을 처리합니다. shop.py 파일에 지정됩니다.

  • 컨트롤러: 데이터 저장 및 검색을 관리합니다. database.py 파일에 지정됩니다.

Chat과 Code Suggestions를 사용하여 Python 프로젝트 구조의 세 파일에서 이 세 가지 구성 요소를 각각 정의합니다:

  • article.py는 모델 구성 요소, 특히 데이터베이스 모델을 정의합니다.

  • shop.py는 뷰 구성 요소, 특히 API 라우트를 정의합니다.

  • database.py는 컨트롤러 구성 요소를 정의합니다.

데이터베이스 모델을 정의하는 아티클 파일 만들기#

서점에는 재고를 효과적으로 관리하기 위한 데이터베이스 모델과 작업이 필요합니다.

서점 재고 시스템을 위한 애플리케이션 코드를 만들기 위해 아티클 파일을 사용하여 아티클의 데이터베이스 모델을 정의합니다.

Code Suggestions를 사용하여 코드를 생성하고, Chat을 사용하여 데이터 모델링 및 데이터베이스 관리 모범 사례를 구현합니다.

이미 article.py 파일을 만들었습니다:

   python-shop-app/
   ├── LICENSE
   ├── README.md
   ├── requirements.txt
   ├── setup.py
   ├── .gitignore
   ├── .env
   ├── app/
   │   ├── __init__.py
   │   ├── models/
   │   │   ├── __init__.py
   │   │   └── article.py <= File you are updating
   │   ├── routes/
   │   │   ├── __init__.py
   │   │   └── shop.py
   │   └── database.py
   └── tests/
       ├── __init__.py
       └── test_shop.py

이 파일에서 Code Suggestions를 사용하여 다음을 입력하세요:

# Create an Article class for a bookstore inventory system
# Include fields for: name, price, quantity
# Add data validation for each field
# Add methods to convert to/from dictionary format

선택 사항. 다음 슬래시 명령어를 사용하세요:

/explain을 사용하여 아티클 클래스의 작동 방식과 설계 패턴을 이해하세요.

  • /refactor를 사용하여 클래스 구조와 메서드에서 잠재적인 개선 사항을 식별하세요.

생성된 코드를 검토하고 필요에 따라 조정한 후 파일을 저장하세요.

다음으로 API 라우트를 정의합니다.

API 라우트를 정의하는 쇼핑 파일 만들기#

데이터베이스 모델을 정의하는 아티클 파일을 만들었으니, 이제 API 라우트를 만듭니다.

API 라우트는 웹 애플리케이션에서 다음 이유로 중요합니다:

  • 클라이언트가 애플리케이션과 상호 작용할 수 있는 공개 API를 정의합니다.

  • HTTP 요청을 애플리케이션의 적절한 코드에 매핑합니다.

  • 입력 유효성 검사와 오류 응답을 처리합니다.

  • 내부 모델과 API 클라이언트가 기대하는 JSON 형식 사이에서 데이터를 변환합니다.

서점 재고 시스템의 경우, 이러한 라우트를 통해 직원이 다음을 수행할 수 있습니다:

  • 재고의 모든 도서를 조회합니다.

  • ID로 특정 도서를 검색합니다.

  • 입고되는 새 도서를 추가합니다.

  • 가격 또는 수량과 같은 도서 정보를 업데이트합니다.

  • 더 이상 필요하지 않은 도서를 삭제합니다.

Flask에서 라우트는 특정 URL 엔드포인트에 대한 요청을 처리하는 함수입니다. 예를 들어, GET /books에 대한 라우트는 모든 도서 목록을 반환하고, POST /books는 재고에 새 도서를 추가합니다.

프로젝트 구조에 이미 설정한 shop.py 파일에서 Chat과 Code Suggestions를 사용하여 이러한 라우트를 만듭니다:

python-shop-app/
├── LICENSE
├── README.md
├── requirements.txt
├── setup.py
├── .gitignore
├── .env
├── app/
│   ├── __init__.py
│   ├── models/
│   │   ├── __init__.py
│   │   └── article.py
│   ├── routes/
│   │   ├── __init__.py
│   │   └── shop.py <= File you are updating
│   └── database.py
└── tests/
   ├── __init__.py
   └── test_shop.py

Flask 애플리케이션과 라우트 만들기#

shop.py 파일을 여세요. Code Suggestions를 사용하기 위해 파일 맨 위에 다음 주석을 입력하세요:

# Create Flask routes for a bookstore inventory system
# Include routes for:
# - Getting all books (GET /books)
# - Getting a single book by ID (GET /books/<id>)
# - Adding a new book (POST /books)
# - Updating a book (PUT /books/<id>)
# - Deleting a book (DELETE /books/<id>)
# Use the Article class from models.article and database from database.py
# Include proper error handling and HTTP status codes

생성된 코드를 검토하세요. 다음이 포함되어야 합니다:

Flask, request, jsonify에 대한 import 문.

  • Article 클래스와 데이터베이스 모듈에 대한 import 문.

  • 모든 CRUD 작업(Create, Read, Update, Delete)에 대한 라우트 정의.

  • 적절한 오류 처리 및 HTTP 상태 코드.

선택 사항. 다음 슬래시 명령어를 사용하세요:

/explain을 사용하여 Flask 라우팅 작동 방식을 이해하세요.

  • /refactor를 사용하여 잠재적인 개선 사항을 식별하세요.

생성된 코드가 요구사항을 완전히 충족하지 못하거나 개선 방법을 알고 싶다면, shop.py 파일 내에서 Chat에 물어볼 수 있습니다:

Can you suggest improvements for my Flask routes in this shop.py file?
I want to ensure that:
1. The routes follow RESTful API design principles
2. Responses include appropriate HTTP status codes
3. Input validation is handled properly
4. The code follows Flask best practices

또한 app 디렉터리 내의 __init__.py 파일에 Flask 애플리케이션 인스턴스를 만들어야 합니다. 이 파일을 열고 Code Suggestions를 사용하여 적절한 코드를 생성하세요:

# Create a Flask application factory
# Configure the app with settings from environment variables
# Register the shop blueprint
# Return the configured app

두 파일을 모두 저장하세요.

데이터 저장 및 검색을 관리하는 데이터베이스 파일 만들기#

마지막으로 데이터베이스 작업 코드를 만듭니다. 이미 database.py 파일을 만들었습니다:

   python-shop-app/
   ├── LICENSE
   ├── README.md
   ├── requirements.txt
   ├── setup.py
   ├── .gitignore
   ├── .env
   ├── app/
   │   ├── __init__.py
   │   ├── models/
   │   │   ├── __init__.py
   │   │   └── article.py
   │   ├── routes/
   │   │   ├── __init__.py
   │   │   └── shop.py
   │   └── database.py <= File you are updating
   └── tests/
       ├── __init__.py
       └── test_shop.py

Chat에 다음을 입력하세요:

Generate a Python class that manages SQLite database operations for a bookstore inventory. Include:
- Context manager for connections
- Table creation
- CRUD operations
- Error handling
Show the complete code with comments.

생성된 코드를 검토하고 필요에 따라 조정한 후 파일을 저장하세요.

재고 관리 시스템의 기반 코드를 성공적으로 만들었고 Flask 프레임워크를 사용하여 구축된 Python 웹 애플리케이션의 핵심 구성 요소를 정의했습니다.

다음으로 생성한 코드를 예제 코드 파일과 비교합니다.

예제 코드 파일과 코드 비교하기#

다음 예제는 튜토리얼을 따른 후 완성될 코드와 유사한 완전하고 작동하는 코드를 보여줍니다.

.gitignore

이 파일은 표준 Python 프로젝트 제외 항목을 보여줍니다:
# Virtual Environment
myenv/
venv/
ENV/
env/
.venv/

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# SQLite database files
*.db
*.sqlite
*.sqlite3

# Environment variables
.env
.env.local
.env.*.local

# IDE specific files
.idea/
.vscode/
*.swp
*.swo
.DS_Store

README.md

설정 및 사용 지침이 포함된 포괄적인 `README` 파일.
# Bookstore Inventory Management System

A Python web application for managing bookstore inventory, built with Flask and SQLite.

## Features

- Track book inventory in real time.
- Add, update, and remove books.
- Data validation to prevent common errors.
- RESTful API for inventory management.

## Requirements

- Python 3.8 or higher.
- Flask 2.2.0 or higher.
- SQLite 3.

## Installation

1. Clone the repository:

   ```shell
   git clone https://gitlab.com/your-username/python-shop-app.git
   cd python-shop-app
  1. Create and activate a virtual environment:

    python -m venv myenv
    source myenv/bin/activate  # On Windows: myenv\Scripts\activate
    
  2. Install dependencies:

    pip install -r requirements.txt
    
  3. Set up environment variables:

    Copy .env.example to .env and modify as needed.

Usage#

  1. Start the Flask application:

    flask run
    
  2. The API will be available at http://localhost:5000/

API Endpoints#

  • GET /books - Get all books
  • GET /books/<id> - Get a specific book
  • POST /books - Add a new book
  • PUT /books/<id> - Update a book
  • DELETE /books/<id> - Delete a book

Testing#

Run tests with pytest:

python -m pytest

  requirements.txt
  
    
    
    
    버전이 포함된 필수 Python 패키지 목록.

  

Flask==2.2.3 pytest==7.3.1 pytest-flask==1.2.0 Flask-SQLAlchemy==3.0.3 SQLAlchemy==2.0.9 python-dotenv==1.0.0 Werkzeug==2.2.3 requests==2.28.2


  setup.py
  
    
    
    
    패키징을 위한 프로젝트 구성.

  

from setuptools import setup, find_packages

setup( name="bookstore-inventory", version="0.1.0", packages=find_packages(), include_package_data=True, install_requires=[ "Flask>=2.2.0", "Flask-SQLAlchemy>=3.0.0", "SQLAlchemy>=2.0.0", "pytest>=7.0.0", "pytest-flask>=1.2.0", "python-dotenv>=1.0.0", ], python_requires=">=3.8", author="Your Name", author_email="your.email@example.com", description="A Flask web application for managing bookstore inventory", keywords="flask, inventory, bookstore", url="https://gitlab.com/your-username/python-shop-app", classifiers=[ "Development Status :: 3 - Alpha", "Environment :: Web Environment", "Framework :: Flask", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", ], )


  .env
  
    
    
    
    애플리케이션의 환경 변수를 포함합니다.

  

Flask configuration#

FLASK_APP=app FLASK_ENV=development FLASK_DEBUG=1 SECRET_KEY=your-secret-key-change-in-production

Database configuration#

DATABASE_URL=sqlite:///bookstore.db TEST_DATABASE_URL=sqlite:///test_bookstore.db

Application settings#

BOOK_TITLE_MAX_LENGTH=100 MAX_PRICE=1000.00 MAX_QUANTITY=1000


  app/models/article.py
  
    
    
    
    전체 유효성 검사가 포함된 Article 클래스.

  

class Article: """Article class for a bookstore inventory system."""

def __init__(self, name, price, quantity, article_id=None):
    """
    Initialize an article with validation.

    Args:
        name (str): The name/title of the book
        price (float): The price of the book
        quantity (int): The quantity in stock
        article_id (int, optional): The unique identifier for the article

    Raises:
        ValueError: If any of the fields fail validation
    """
    self.id = article_id
    self.set_name(name)
    self.set_price(price)
    self.set_quantity(quantity)

def set_name(self, name):
    """
    Set the name with validation.

    Args:
        name (str): The name/title of the book

    Raises:
        ValueError: If name is empty or too long
    """
    if not name or not isinstance(name, str):
        raise ValueError("Book title cannot be empty and must be a string")

    if len(name) > 100:  # Max length validation
        raise ValueError("Book title cannot exceed 100 characters")

    self.name = name.strip()

def set_price(self, price):
    """
    Set the price with validation.

    Args:
        price (float): The price of the book

    Raises:
        ValueError: If price is negative or not a number
    """
    try:
        price_float = float(price)
    except (ValueError, TypeError):
        raise ValueError("Price must be a number")

    if price_float < 0:
        raise ValueError("Price cannot be negative")

    if price_float > 1000:  # Max price validation
        raise ValueError("Price cannot exceed 1000")

    # Ensure price has at most 2 decimal places
    self.price = round(price_float, 2)

def set_quantity(self, quantity):
    """
    Set the quantity with validation.

    Args:
        quantity (int): The quantity in stock

    Raises:
        ValueError: If quantity is negative or not an integer
    """
    try:
        quantity_int = int(quantity)
    except (ValueError, TypeError):
        raise ValueError("Quantity must be an integer")

    if quantity_int < 0:
        raise ValueError("Quantity cannot be negative")

    if quantity_int > 1000:  # Max quantity validation
        raise ValueError("Quantity cannot exceed 1000")

    self.quantity = quantity_int

def to_dict(self):
    """
    Convert the article to a dictionary.

    Returns:
        dict: Dictionary representation of the article
    """
    return {
        "id": self.id,
        "name": self.name,
        "price": self.price,
        "quantity": self.quantity
    }

@classmethod
def from_dict(cls, data):
    """
    Create an article from a dictionary.

    Args:
        data (dict): Dictionary with article data

    Returns:
        Article: New article instance
    """
    article_id = data.get("id")
    return cls(
        name=data["name"],
        price=data["price"],
        quantity=data["quantity"],
        article_id=article_id
    )

  app/routes/shop.py
  
    
    
    
    오류 처리가 포함된 완전한 API 엔드포인트.

  

from flask import Blueprint, request, jsonify, current_app from app.models.article import Article from app import database import logging

Create a blueprint for the shop routes#

shop_bp = Blueprint('shop', name, url_prefix='/books')

Set up logging#

logger = logging.getLogger(name)

@shop_bp.route('', methods=['GET']) def get_all_books(): """Get all books from the inventory.""" try: books = database.get_all_articles() return jsonify([book.to_dict() for book in books]), 200 except Exception as e: logger.error(f"Error getting all books: {str(e)}") return jsonify({"error": "Failed to retrieve books"}), 500

@shop_bp.route('/int:book_id', methods=['GET']) def get_book(book_id): """Get a specific book by ID.""" try: book = database.get_article_by_id(book_id) if book: return jsonify(book.to_dict()), 200 return jsonify({"error": f"Book with ID {book_id} not found"}), 404 except Exception as e: logger.error(f"Error getting book {book_id}: {str(e)}") return jsonify({"error": f"Failed to retrieve book {book_id}"}), 500

@shop_bp.route('', methods=['POST']) def add_book(): """Add a new book to the inventory.""" data = request.get_json()

if not data:
    return jsonify({"error": "No data provided"}), 400

required_fields = ['name', 'price', 'quantity']
for field in required_fields:
    if field not in data:
        return jsonify({"error": f"Missing required field: {field}"}), 400

try:
    # Validate data by creating an Article object
    new_book = Article(
        name=data['name'],
        price=data['price'],
        quantity=data['quantity']
    )

    # Save to database
    book_id = database.add_article(new_book)

    # Return the created book
    created_book = database.get_article_by_id(book_id)
    return jsonify(created_book.to_dict()), 201

except ValueError as e:
    return jsonify({"error": str(e)}), 400
except Exception as e:
    logger.error(f"Error adding book: {str(e)}")
    return jsonify({"error": "Failed to add book"}), 500

@shop_bp.route('/int:book_id', methods=['PUT']) def update_book(book_id): """Update an existing book.""" data = request.get_json()

if not data:
    return jsonify({"error": "No data provided"}), 400

try:
    # Check if book exists
    existing_book = database.get_article_by_id(book_id)
    if not existing_book:
        return jsonify({"error": f"Book with ID {book_id} not found"}), 404

    # Update book properties
    if 'name' in data:
        existing_book.set_name(data['name'])
    if 'price' in data:
        existing_book.set_price(data['price'])
    if 'quantity' in data:
        existing_book.set_quantity(data['quantity'])

    # Save updated book
    database.update_article(existing_book)

    # Return the updated book
    updated_book = database.get_article_by_id(book_id)
    return jsonify(updated_book.to_dict()), 200

except ValueError as e:
    return jsonify({"error": str(e)}), 400
except Exception as e:
    logger.error(f"Error updating book {book_id}: {str(e)}")
    return jsonify({"error": f"Failed to update book {book_id}"}), 500

@shop_bp.route('/int:book_id', methods=['DELETE']) def delete_book(book_id): """Delete a book from the inventory.""" try: # Check if book exists existing_book = database.get_article_by_id(book_id) if not existing_book: return jsonify({"error": f"Book with ID {book_id} not found"}), 404

    # Delete the book
    database.delete_article(book_id)

    return jsonify({"message": f"Book with ID {book_id} deleted successfully"}), 200

except Exception as e:
    logger.error(f"Error deleting book {book_id}: {str(e)}")
    return jsonify({"error": f"Failed to delete book {book_id}"}), 500

  app/database.py
  
    
    
    
    연결 관리가 포함된 데이터베이스 작업.

  

import sqlite3 import os import logging from contextlib import contextmanager from app.models.article import Article

Set up logging#

logger = logging.getLogger(name)

Get database path from environment variable or use default#

DATABASE_PATH = os.environ.get('DATABASE_PATH', 'bookstore.db')

@contextmanager def get_db_connection(): """ Context manager for database connections. Automatically handles connection opening, committing, and closing.

Yields:
    sqlite3.Connection: Database connection object
"""
conn = None
try:
    conn = sqlite3.connect(DATABASE_PATH)
    # Configure connection to return rows as dictionaries
    conn.row_factory = sqlite3.Row
    yield conn
    conn.commit()
except sqlite3.Error as e:
    if conn:
        conn.rollback()
    logger.error(f"Database error: {str(e)}")
    raise
finally:
    if conn:
        conn.close()

def initialize_database(): """ Initialize the database by creating the articles table if it doesn't exist. """ try: with get_db_connection() as conn: cursor = conn.cursor()

        # Create articles table
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS articles (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                price REAL NOT NULL,
                quantity INTEGER NOT NULL
            )
        ''')

        logger.info("Database initialized successfully")
except sqlite3.Error as e:
    logger.error(f"Failed to initialize database: {str(e)}")
    raise

def add_article(article): """ Add a new article to the database.

Args:
    article (Article): Article object to add

Returns:
    int: ID of the newly added article
"""
try:
    with get_db_connection() as conn:
        cursor = conn.cursor()

        cursor.execute(
            "INSERT INTO articles (name, price, quantity) VALUES (?, ?, ?)",
            (article.name, article.price, article.quantity)
        )

        # Get the ID of the newly inserted article
        article_id = cursor.lastrowid
        logger.info(f"Added article with ID {article_id}")
        return article_id
except sqlite3.Error as e:
    logger.error(f"Failed to add article: {str(e)}")
    raise

def get_article_by_id(article_id): """ Get an article by its ID.

Args:
    article_id (int): ID of the article to retrieve

Returns:
    Article: Article object if found, None otherwise
"""
try:
    with get_db_connection() as conn:
        cursor = conn.cursor()

        cursor.execute("SELECT * FROM articles WHERE id = ?", (article_id,))
        row = cursor.fetchone()

        if row:
            return Article(
                name=row['name'],
                price=row['price'],
                quantity=row['quantity'],
                article_id=row['id']
            )
        return None
except sqlite3.Error as e:
    logger.error(f"Failed to get article {article_id}: {str(e)}")
    raise

def get_all_articles(): """ Get all articles from the database.

Returns:
    list: List of Article objects
"""
try:
    with get_db_connection() as conn:
        cursor = conn.cursor()

        cursor.execute("SELECT * FROM articles")
        rows = cursor.fetchall()

        articles = []
        for row in rows:
            article = Article(
                name=row['name'],
                price=row['price'],
                quantity=row['quantity'],
                article_id=row['id']
            )
            articles.append(article)

        return articles
except sqlite3.Error as e:
    logger.error(f"Failed to get all articles: {str(e)}")
    raise

def update_article(article): """ Update an existing article in the database.

Args:
    article (Article): Article object with updated values

Returns:
    bool: True if successful, False if article not found
"""
try:
    with get_db_connection() as conn:
        cursor = conn.cursor()

        cursor.execute(
            "UPDATE articles SET name = ?, price = ?, quantity = ? WHERE id = ?",
            (article.name, article.price, article.quantity, article.id)
        )

        # Check if an article was actually updated
        if cursor.rowcount == 0:
            logger.warning(f"No article found with ID {article.id}")
            return False

        logger.info(f"Updated article with ID {article.id}")
        return True
except sqlite3.Error as e:
    logger.error(f"Failed to update article {article.id}: {str(e)}")
    raise

def delete_article(article_id): """ Delete an article from the database.

Args:
    article_id (int): ID of the article to delete

Returns:
    bool: True if successful, False if article not found
"""
try:
    with get_db_connection() as conn:
        cursor = conn.cursor()

        cursor.execute("DELETE FROM articles WHERE id = ?", (article_id,))

        # Check if an article was actually deleted
        if cursor.rowcount == 0:
            logger.warning(f"No article found with ID {article_id}")
            return False

        logger.info(f"Deleted article with ID {article_id}")
        return True
except sqlite3.Error as e:
    logger.error(f"Failed to delete article {article_id}: {str(e)}")
    raise

  app/__init__.py
  
    
    
    
    Flask 애플리케이션 팩토리.

  

import os from flask import Flask from dotenv import load_dotenv

def create_app(test_config=None): """ Application factory for creating the Flask app.

Args:
    test_config (dict, optional): Test configuration to override default config

Returns:
    Flask: Configured Flask application
"""
# Load environment variables from .env file
load_dotenv()

# Create and configure the app
app = Flask(__name__, instance_relative_config=True)

# Set default configuration
app.config.from_mapping(
    SECRET_KEY=os.environ.get('SECRET_KEY', 'dev'),
    DATABASE_PATH=os.environ.get('DATABASE_URL', 'bookstore.db'),
    BOOK_TITLE_MAX_LENGTH=int(os.environ.get('BOOK_TITLE_MAX_LENGTH', 100)),
    MAX_PRICE=float(os.environ.get('MAX_PRICE', 1000.00)),
    MAX_QUANTITY=int(os.environ.get('MAX_QUANTITY', 1000))
)

# Override config with test config if provided
if test_config:
    app.config.update(test_config)

# Ensure the instance folder exists
os.makedirs(app.instance_path, exist_ok=True)

# Initialize database
from app import database
database.initialize_database()

# Register blueprints
from app.routes.shop import shop_bp
app.register_blueprint(shop_bp)

# Add a simple index route
@app.route('/')
def index():
    return {
        "message": "Welcome to the Bookstore Inventory API",
        "endpoints": {
            "books": "/books",
            "book_by_id": "/books/<id>"
        }
    }

return app

- 
작성한 코드 파일을 이 예제와 비교하세요.

- 
코드가 작동하는지 확인하기 위해 Chat에게 로컬 애플리케이션 서버를 시작하는 방법을 물어보세요:

  

How do I start a local application server for my Python web application?


- 
지침을 따르고 애플리케이션이 작동하는지 확인하세요.

애플리케이션이 작동한다면 축하드립니다! GitLab Duo Chat과 Code Suggestions를 성공적으로 사용하여 작동하는 온라인 쇼핑 애플리케이션을 구축했습니다.

작동하지 않는다면 원인을 파악해야 합니다. Chat과 Code Suggestions를 사용하여 애플리케이션이 예상대로 작동하는지 확인하는 테스트를 만들고 수정이 필요한 문제를 식별할 수 있습니다.

자세한 내용은 [GitLab Duo /fix 사용하기](https://youtu.be/agTqx__j6Ko?si=vpLfVvmFVcBivB1g)를 참조하세요.

## 관련 주제

- [GitLab Duo 사용 사례](/19.1/user/gitlab_duo/use_cases/)

- [GitLab Duo 시작하기](/19.1/user/get_started/getting_started_gitlab_duo/).

- 블로그 포스트: [GitLab Duo로 DevSecOps 엔지니어링 워크플로 간소화하기](https://about.gitlab.com/blog/streamline-devsecops-engineering-workflows-with-gitlab-duo/)

- 
[GitLab Duo Chat (에이전틱)](https://youtu.be/uG9-QLAJrrg?si=c25SR7DoRAep7jvQ)

- 
[GitLab Duo Chat (비에이전틱)](https://youtu.be/ZQBAuf-CTAY?si=0o9-xJ_ATTsL1oew)

- 
[GitLab Duo Code Suggestions](https://youtu.be/ds7SG1wgcVM?si=MfbzPIDpikGhoPh7)

- 
[GitLab Duo를 사용한 애플리케이션 현대화 (C++에서 Java로)](https://youtu.be/FjoAmt5eeXA?si=SLv9Mv8eSUAVwW5Z)

튜토리얼: GitLab Duo로 Python 쇼핑 애플리케이션 만들기

GitLab v19.1
원문 보기
요약

당신은 온라인 서점의 개발자로 채용되었습니다. 직원이 입고되는 새 도서를 추가할 수 있도록 합니다. 음수 가격이나 수량과 같은 일반적인 데이터 입력 오류를 방지합니다. 향후 고객 대상 기능을 위한 기반을 마련합니다. 이 튜토리얼은 시리즈의 1부로, 이러한 요구사항을 충족하는 데이터베이스 백엔드를 갖춘 Python 웹 애플리케이션을 생성하고 디버깅하는 과정을 안내합니다.

당신은 온라인 서점의 개발자로 채용되었습니다. 현재 재고 관리 시스템은 스프레드시트와 수동 프로세스가 혼재되어 있어 재고 오류와 업데이트 지연이 발생하고 있습니다. 팀에서는 다음 기능을 갖춘 웹 애플리케이션을 만들어야 합니다:

  • 실시간으로 도서 재고를 추적합니다.

  • 직원이 입고되는 새 도서를 추가할 수 있도록 합니다.

  • 음수 가격이나 수량과 같은 일반적인 데이터 입력 오류를 방지합니다.

  • 향후 고객 대상 기능을 위한 기반을 마련합니다.

이 튜토리얼은 시리즈의 1부로, 이러한 요구사항을 충족하는 데이터베이스 백엔드를 갖춘 Python 웹 애플리케이션을 생성하고 디버깅하는 과정을 안내합니다.

GitLab Duo Agentic ChatGitLab Duo Code Suggestions를 사용하여 다음 작업을 수행합니다:

  • 표준 디렉터리와 필수 파일로 구성된 체계적인 Python 프로젝트를 설정합니다.

  • Python 가상 환경을 구성합니다.

  • 웹 애플리케이션의 기반인 Flask 프레임워크를 설치합니다.

  • 필요한 의존성을 설치하고 개발을 위한 프로젝트를 준비합니다.

  • Flask 애플리케이션 개발을 위한 Python 구성 파일과 환경 변수를 설정합니다.

  • 아티클 모델, 데이터베이스 작업, API 라우트, 재고 관리 기능 등 핵심 기능을 구현합니다.

  • 애플리케이션이 의도한 대로 작동하는지 테스트하고, 코드를 예제 코드 파일과 비교합니다.

시작하기 전에#

  • 시스템에 최신 버전의 Python을 설치하세요. 운영 체제에 맞는 설치 방법을 Chat에 물어볼 수 있습니다.

  • 관리자, 그룹 Owner, 또는 프로젝트 Owner에게 GitLab Duo에 대한 접근 권한이 있는지 확인하세요.

  • 사용하는 IDE에 확장 프로그램을 설치하세요:

Web IDE: GitLab 인스턴스를 통해 접근

GitLab Duo Chat과 Code Suggestions 사용하기#

이 튜토리얼에서는 Chat과 Code Suggestions를 사용하여 Python 웹 애플리케이션을 만듭니다. 이러한 기능을 사용하는 방법은 여러 가지가 있습니다.

GitLab Duo Chat 사용하기#

구독 추가 기능에 따라 GitLab UI, Web IDE, 또는 IDE에서 Chat을 사용할 수 있습니다.

GitLab UI에서 Chat 사용하기#

상단 바에서 Search or go to를 선택하고 프로젝트를 찾으세요.

GitLab Duo 사이드바에서 Add new chat ( pencil-square )을 선택하세요.

드롭다운 목록에서 에이전트를 선택하세요.

화면 오른쪽의 GitLab Duo 사이드바에 Chat 대화가 열립니다.

Chat 텍스트 상자에 질문을 입력하고 Enter를 누르거나 Send를 선택하세요. 대화형 AI 채팅이 답변을 생성하는 데 몇 초가 걸릴 수 있습니다.

Web IDE에서 Chat 사용하기#

  • Web IDE 열기:

GitLab UI의 상단 바에서 Search or go to를 선택하고 프로젝트를 찾으세요.

  • 파일을 선택하세요. 그런 다음 오른쪽 상단에서 Edit > Open in Web IDE를 선택하세요.

  • 다음 방법 중 하나를 사용하여 Chat을 여세요:

왼쪽 사이드바에서 GitLab Duo Chat을 선택하세요.

  • 에디터에서 열려 있는 파일에서 일부 코드를 선택하세요.

마우스 오른쪽 버튼을 클릭하고 GitLab Duo Chat을 선택하세요.

  • Explain selected code, Generate Tests, 또는 Refactor를 선택하세요.

  • 키보드 단축키 사용: Windows 및 Linux에서는 ALT+d, Mac에서는 Option+d.

  • 메시지 상자에 질문을 입력하세요. Enter를 누르거나 Send를 선택하세요.

IDE에서 Chat 사용하기#

IDE에서 Chat을 사용하는 방법은 사용하는 IDE에 따라 다릅니다.

VS Code

  • VS Code에서 파일을 여세요. Git 리포지터리의 파일일 필요는 없습니다.

  • 왼쪽 사이드바에서 GitLab Duo Chat ( duo-chat )을 선택하세요.

  • 메시지 상자에 질문을 입력하세요. Enter를 누르거나 Send를 선택하세요.

  • Chat 창의 오른쪽 상단에서 Show Status를 선택하여 Command Palette에서 정보를 확인하세요.

코드의 일부를 작업하는 동안에도 GitLab Duo Chat과 상호 작용할 수 있습니다.

  • VS Code에서 파일을 여세요. Git 리포지터리의 파일일 필요는 없습니다.

  • 파일에서 일부 코드를 선택하세요.

  • 마우스 오른쪽 버튼을 클릭하고 GitLab Duo Chat을 선택하세요.

  • 옵션을 선택하거나, Open Quick Chat을 선택하고 Can you simplify this code?와 같은 질문을 입력한 후 Enter를 누르세요.

자세한 내용은 VS Code에서 GitLab Duo Chat 사용하기를 참조하세요.

JetBrains IDEs

Code Suggestions 사용하기#

Code Suggestions를 사용하려면:

지원되는 IDE에서 Git 프로젝트를 여세요.

로컬 프로젝트에는 GitLab의 리포지터리를 가리키는 Git 리모트가 구성되어 있어야 합니다. 아직 설정되지 않은 경우, git remote add를 사용하여 프로젝트를 연결하세요.

코드를 작성하세요. 입력하는 동안 제안이 표시됩니다. Code Suggestions는 커서 위치에 따라 코드 스니펫을 제공하거나 현재 줄을 완성합니다.

자연어로 요구사항을 설명하세요. Code Suggestions는 제공된 컨텍스트를 기반으로 함수와 코드 스니펫을 생성합니다.

제안을 받으면 다음 중 하나를 수행할 수 있습니다:

제안을 수락하려면 Tab을 누르세요.

  • 부분 제안을 수락하려면 Control+Right arrow 또는 Command+Right arrow를 누르세요.

  • 제안을 거부하려면 Esc를 누르세요.

  • 제안을 무시하려면 평소처럼 계속 입력하세요.

자세한 내용은 Code Suggestions 문서를 참조하세요.

이제 Chat과 Code Suggestions를 사용하는 방법을 알았으니, 웹 애플리케이션 구축을 시작합니다. 먼저 체계적인 Python 프로젝트 구조를 만듭니다.

프로젝트 구조 만들기#

시작하기 위해 Python 모범 사례를 따르는 잘 정리된 프로젝트 구조가 필요합니다. 올바른 구조는 코드를 유지 관리하기 쉽고, 테스트하기 쉬우며, 다른 개발자가 이해하기 쉽게 만듭니다.

Chat을 사용하여 Python 프로젝트 구성 규칙을 이해하고 적절한 파일을 생성할 수 있습니다. 이를 통해 모범 사례를 조사하는 시간을 절약하고 중요한 구성 요소를 빠뜨리지 않을 수 있습니다.

IDE에서 Chat을 열고 다음을 입력하세요:

What is the recommended project structure for a Python web application? Include
common files, and explain the purpose of each file.

이 프롬프트는 파일을 만들기 전에 Python 프로젝트 구성을 이해하는 데 도움이 됩니다.

Python 프로젝트를 위한 새 폴더를 만들고 Chat의 응답을 바탕으로 디렉터리 및 파일 구조를 만드세요. 아마 다음과 유사할 것입니다:

python-shop-app/
├── LICENSE
├── README.md
├── requirements.txt
├── setup.py
├── .gitignore
├── .env
├── app/
│   ├── __init__.py
│   ├── models/
│   │   ├── __init__.py
│   │   └── article.py
│   ├── routes/
│   │   ├── __init__.py
│   │   └── shop.py
│   └── database.py
└── tests/
    ├── __init__.py
    └── test_shop.py

.gitignore 파일을 채워야 합니다. Chat에 다음을 입력하세요:

Generate a .gitignore file for a Python project that uses Flask, SQLite, and
virtual environments. Include common IDE files.

응답을 .gitignore 파일에 복사하세요.

README 파일을 위해 Chat에 다음을 입력하세요:

Generate a README.md file for a Python web application that manages a bookstore
inventory. Make sure that it includes all sections for requirements, setup, and usage.

이제 업계 모범 사례를 따르는 체계적인 Python 프로젝트를 만들었습니다. 이 구성 덕분에 코드를 더 쉽게 유지 관리하고 테스트할 수 있습니다. 다음으로, 코드 작성을 시작하기 위한 개발 환경을 설정합니다.

개발 환경 설정하기#

올바르게 격리된 개발 환경은 의존성 충돌을 방지하고 애플리케이션을 배포 가능하게 만듭니다.

Chat을 사용하여 Python 가상 환경을 설정하고 올바른 의존성이 포함된 requirements.txt 파일을 만들 것입니다. 이를 통해 개발을 위한 안정적인 기반을 확보할 수 있습니다.

   python-shop-app/
   ├── LICENSE
   ├── README.md
   ├── requirements.txt <= File you are updating
   ├── setup.py
   ├── .gitignore
   ├── .env
   ├── app/
   │   ├── __init__.py
   │   ├── models/
   │   │   ├── __init__.py
   │   │   └── article.py
   │   ├── routes/
   │   │   ├── __init__.py
   │   │   └── shop.py
   │   └── database.py
   └── tests/
       ├── __init__.py
       └── test_shop.py

선택 사항. Python과 Flask가 함께 웹 애플리케이션을 생성하는 방법에 대해 Chat에 물어보세요.

Chat을 사용하여 Python 환경 설정 모범 사례를 이해하세요:

What are the recommended steps for setting up a Python virtual environment with
Flask? Include information about requirements.txt and pip.

필요한 추가 질문을 하세요. 예를 들어:

What does the requirements.txt do in a Python web app?

응답을 바탕으로 먼저 가상 환경을 만들고 활성화하세요 (예: Homebrew의 python3 패키지를 사용하는 macOS의 경우):

python3 -m venv myenv
source myenv/bin/activate

requirements.txt 파일도 만들어야 합니다. Chat에 다음을 물어보세요:

What should be included in requirements.txt for a Flask web application with
SQLite database and testing capabilities? Include specific version numbers.

응답을 requirements.txt 파일에 복사하세요.

requirements.txt 파일에 나열된 의존성을 설치하세요:

pip install -r requirements.txt

이제 개발 환경이 구성되어 모든 필요한 의존성이 가상 환경에 격리되어 충돌을 방지합니다. 다음으로, 프로젝트의 패키지 및 환경 설정을 구성합니다.

프로젝트 구성하기#

환경 변수를 포함한 올바른 구성은 다양한 환경에서 애플리케이션이 일관되게 실행되도록 합니다.

Code Suggestions를 사용하여 구성을 생성하고 다듬을 것입니다. 그런 다음 Chat에게 각 설정의 목적을 설명하도록 요청하여 무엇을 구성하고 있는지, 그 이유를 이해할 수 있습니다.

프로젝트 폴더에 이미 setup.py라는 Python 구성 파일을 만들었습니다:

   python-shop-app/
   ├── LICENSE
   ├── README.md
   ├── requirements.txt
   ├── setup.py <= File you are updating
   ├── .gitignore
   ├── .env
   ├── app/
   │   ├── __init__.py
   │   ├── models/
   │   │   ├── __init__.py
   │   │   └── article.py
   │   ├── routes/
   │   │   ├── __init__.py
   │   │   └── shop.py
   │   └── database.py
   └── tests/
       ├── __init__.py
       └── test_shop.py

이 파일을 열고 파일 맨 위에 다음 주석을 입력하세요:

# Populate this setup.py configuration file for a Flask web application
# Include dependencies for Flask, testing, and database functionality
# Use semantic versioning

Code Suggestions가 구성을 생성해 줍니다.

선택 사항. 생성된 구성 코드를 선택하고 다음 슬래시 명령어를 사용하세요:

/explain을 사용하여 각 구성 설정이 하는 일을 이해하세요.

  • /refactor를 사용하여 구성 구조에서 잠재적인 개선 사항을 식별하세요.

생성된 코드를 검토하고 필요에 따라 조정하세요.

구성 파일에서 무엇을 조정할 수 있는지 확실하지 않으면 Chat에 물어보세요.

Chat에 무엇을 조정해야 하는지 물어보려면, GitLab UI가 아닌 IDE의 setup.py 파일에서 하세요. 이렇게 하면 방금 만든 setup.py 파일을 포함하여 작업 중인 컨텍스트가 Chat에 제공됩니다.

You have used Code Suggestions to generate a Python configuration file, `setup.py`,
for a Flask web application. This file includes dependencies for Flask, testing,
and database functionality. If I were to review this file, what might I want
to change and adjust?

파일을 저장하세요.

환경 변수 설정하기#

다음으로, Chat과 Code Suggestions를 모두 사용하여 환경 변수를 설정합니다.

Chat에서 다음을 물어보세요:

In a Python project, what environment variables should be set for a Flask application in development mode? Include database configuration.

환경 변수를 저장하기 위해 이미 .env 파일을 만들었습니다.

   python-shop-app/
   ├── LICENSE
   ├── README.md
   ├── requirements.txt
   ├── setup.py
   ├── .gitignore
   ├── .env <= File you are updating
   ├── app/
   │   ├── __init__.py
   │   ├── models/
   │   │   ├── __init__.py
   │   │   └── article.py
   │   ├── routes/
   │   │   ├── __init__.py
   │   │   └── shop.py
   │   └── database.py
   └── tests/
       ├── __init__.py
       └── test_shop.py

이 파일을 열고 Chat이 권장한 환경 변수를 포함하여 파일 맨 위에 다음 주석을 입력하세요:

# Populate this .env file to store environment variables
# Include the following
# ...
# Use semantic versioning

생성된 코드를 검토하고 필요에 따라 조정한 후 파일을 저장하세요.

프로젝트를 구성하고 환경 변수를 설정했습니다. 이를 통해 다양한 환경에서 애플리케이션을 일관되게 배포할 수 있습니다. 다음으로, 재고 시스템을 위한 애플리케이션 코드를 만듭니다.

애플리케이션 코드 만들기#

Flask 웹 프레임워크에는 세 가지 핵심 구성 요소가 있습니다:

  • 모델: 데이터와 비즈니스 로직 및 데이터베이스 모델을 포함합니다. article.py 파일에 지정됩니다.

  • 뷰: HTTP 요청과 응답을 처리합니다. shop.py 파일에 지정됩니다.

  • 컨트롤러: 데이터 저장 및 검색을 관리합니다. database.py 파일에 지정됩니다.

Chat과 Code Suggestions를 사용하여 Python 프로젝트 구조의 세 파일에서 이 세 가지 구성 요소를 각각 정의합니다:

  • article.py는 모델 구성 요소, 특히 데이터베이스 모델을 정의합니다.

  • shop.py는 뷰 구성 요소, 특히 API 라우트를 정의합니다.

  • database.py는 컨트롤러 구성 요소를 정의합니다.

데이터베이스 모델을 정의하는 아티클 파일 만들기#

서점에는 재고를 효과적으로 관리하기 위한 데이터베이스 모델과 작업이 필요합니다.

서점 재고 시스템을 위한 애플리케이션 코드를 만들기 위해 아티클 파일을 사용하여 아티클의 데이터베이스 모델을 정의합니다.

Code Suggestions를 사용하여 코드를 생성하고, Chat을 사용하여 데이터 모델링 및 데이터베이스 관리 모범 사례를 구현합니다.

이미 article.py 파일을 만들었습니다:

   python-shop-app/
   ├── LICENSE
   ├── README.md
   ├── requirements.txt
   ├── setup.py
   ├── .gitignore
   ├── .env
   ├── app/
   │   ├── __init__.py
   │   ├── models/
   │   │   ├── __init__.py
   │   │   └── article.py <= File you are updating
   │   ├── routes/
   │   │   ├── __init__.py
   │   │   └── shop.py
   │   └── database.py
   └── tests/
       ├── __init__.py
       └── test_shop.py

이 파일에서 Code Suggestions를 사용하여 다음을 입력하세요:

# Create an Article class for a bookstore inventory system
# Include fields for: name, price, quantity
# Add data validation for each field
# Add methods to convert to/from dictionary format

선택 사항. 다음 슬래시 명령어를 사용하세요:

/explain을 사용하여 아티클 클래스의 작동 방식과 설계 패턴을 이해하세요.

  • /refactor를 사용하여 클래스 구조와 메서드에서 잠재적인 개선 사항을 식별하세요.

생성된 코드를 검토하고 필요에 따라 조정한 후 파일을 저장하세요.

다음으로 API 라우트를 정의합니다.

API 라우트를 정의하는 쇼핑 파일 만들기#

데이터베이스 모델을 정의하는 아티클 파일을 만들었으니, 이제 API 라우트를 만듭니다.

API 라우트는 웹 애플리케이션에서 다음 이유로 중요합니다:

  • 클라이언트가 애플리케이션과 상호 작용할 수 있는 공개 API를 정의합니다.

  • HTTP 요청을 애플리케이션의 적절한 코드에 매핑합니다.

  • 입력 유효성 검사와 오류 응답을 처리합니다.

  • 내부 모델과 API 클라이언트가 기대하는 JSON 형식 사이에서 데이터를 변환합니다.

서점 재고 시스템의 경우, 이러한 라우트를 통해 직원이 다음을 수행할 수 있습니다:

  • 재고의 모든 도서를 조회합니다.

  • ID로 특정 도서를 검색합니다.

  • 입고되는 새 도서를 추가합니다.

  • 가격 또는 수량과 같은 도서 정보를 업데이트합니다.

  • 더 이상 필요하지 않은 도서를 삭제합니다.

Flask에서 라우트는 특정 URL 엔드포인트에 대한 요청을 처리하는 함수입니다. 예를 들어, GET /books에 대한 라우트는 모든 도서 목록을 반환하고, POST /books는 재고에 새 도서를 추가합니다.

프로젝트 구조에 이미 설정한 shop.py 파일에서 Chat과 Code Suggestions를 사용하여 이러한 라우트를 만듭니다:

python-shop-app/
├── LICENSE
├── README.md
├── requirements.txt
├── setup.py
├── .gitignore
├── .env
├── app/
│   ├── __init__.py
│   ├── models/
│   │   ├── __init__.py
│   │   └── article.py
│   ├── routes/
│   │   ├── __init__.py
│   │   └── shop.py <= File you are updating
│   └── database.py
└── tests/
   ├── __init__.py
   └── test_shop.py

Flask 애플리케이션과 라우트 만들기#

shop.py 파일을 여세요. Code Suggestions를 사용하기 위해 파일 맨 위에 다음 주석을 입력하세요:

# Create Flask routes for a bookstore inventory system
# Include routes for:
# - Getting all books (GET /books)
# - Getting a single book by ID (GET /books/<id>)
# - Adding a new book (POST /books)
# - Updating a book (PUT /books/<id>)
# - Deleting a book (DELETE /books/<id>)
# Use the Article class from models.article and database from database.py
# Include proper error handling and HTTP status codes

생성된 코드를 검토하세요. 다음이 포함되어야 합니다:

Flask, request, jsonify에 대한 import 문.

  • Article 클래스와 데이터베이스 모듈에 대한 import 문.

  • 모든 CRUD 작업(Create, Read, Update, Delete)에 대한 라우트 정의.

  • 적절한 오류 처리 및 HTTP 상태 코드.

선택 사항. 다음 슬래시 명령어를 사용하세요:

/explain을 사용하여 Flask 라우팅 작동 방식을 이해하세요.

  • /refactor를 사용하여 잠재적인 개선 사항을 식별하세요.

생성된 코드가 요구사항을 완전히 충족하지 못하거나 개선 방법을 알고 싶다면, shop.py 파일 내에서 Chat에 물어볼 수 있습니다:

Can you suggest improvements for my Flask routes in this shop.py file?
I want to ensure that:
1. The routes follow RESTful API design principles
2. Responses include appropriate HTTP status codes
3. Input validation is handled properly
4. The code follows Flask best practices

또한 app 디렉터리 내의 __init__.py 파일에 Flask 애플리케이션 인스턴스를 만들어야 합니다. 이 파일을 열고 Code Suggestions를 사용하여 적절한 코드를 생성하세요:

# Create a Flask application factory
# Configure the app with settings from environment variables
# Register the shop blueprint
# Return the configured app

두 파일을 모두 저장하세요.

데이터 저장 및 검색을 관리하는 데이터베이스 파일 만들기#

마지막으로 데이터베이스 작업 코드를 만듭니다. 이미 database.py 파일을 만들었습니다:

   python-shop-app/
   ├── LICENSE
   ├── README.md
   ├── requirements.txt
   ├── setup.py
   ├── .gitignore
   ├── .env
   ├── app/
   │   ├── __init__.py
   │   ├── models/
   │   │   ├── __init__.py
   │   │   └── article.py
   │   ├── routes/
   │   │   ├── __init__.py
   │   │   └── shop.py
   │   └── database.py <= File you are updating
   └── tests/
       ├── __init__.py
       └── test_shop.py

Chat에 다음을 입력하세요:

Generate a Python class that manages SQLite database operations for a bookstore inventory. Include:
- Context manager for connections
- Table creation
- CRUD operations
- Error handling
Show the complete code with comments.

생성된 코드를 검토하고 필요에 따라 조정한 후 파일을 저장하세요.

재고 관리 시스템의 기반 코드를 성공적으로 만들었고 Flask 프레임워크를 사용하여 구축된 Python 웹 애플리케이션의 핵심 구성 요소를 정의했습니다.

다음으로 생성한 코드를 예제 코드 파일과 비교합니다.

예제 코드 파일과 코드 비교하기#

다음 예제는 튜토리얼을 따른 후 완성될 코드와 유사한 완전하고 작동하는 코드를 보여줍니다.

.gitignore

이 파일은 표준 Python 프로젝트 제외 항목을 보여줍니다:
# Virtual Environment
myenv/
venv/
ENV/
env/
.venv/

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# SQLite database files
*.db
*.sqlite
*.sqlite3

# Environment variables
.env
.env.local
.env.*.local

# IDE specific files
.idea/
.vscode/
*.swp
*.swo
.DS_Store

README.md

설정 및 사용 지침이 포함된 포괄적인 `README` 파일.
# Bookstore Inventory Management System

A Python web application for managing bookstore inventory, built with Flask and SQLite.

## Features

- Track book inventory in real time.
- Add, update, and remove books.
- Data validation to prevent common errors.
- RESTful API for inventory management.

## Requirements

- Python 3.8 or higher.
- Flask 2.2.0 or higher.
- SQLite 3.

## Installation

1. Clone the repository:

   ```shell
   git clone https://gitlab.com/your-username/python-shop-app.git
   cd python-shop-app
  1. Create and activate a virtual environment:

    python -m venv myenv
    source myenv/bin/activate  # On Windows: myenv\Scripts\activate
    
  2. Install dependencies:

    pip install -r requirements.txt
    
  3. Set up environment variables:

    Copy .env.example to .env and modify as needed.

Usage#

  1. Start the Flask application:

    flask run
    
  2. The API will be available at http://localhost:5000/

API Endpoints#

  • GET /books - Get all books
  • GET /books/<id> - Get a specific book
  • POST /books - Add a new book
  • PUT /books/<id> - Update a book
  • DELETE /books/<id> - Delete a book

Testing#

Run tests with pytest:

python -m pytest

  requirements.txt
  
    
    
    
    버전이 포함된 필수 Python 패키지 목록.

  

Flask==2.2.3 pytest==7.3.1 pytest-flask==1.2.0 Flask-SQLAlchemy==3.0.3 SQLAlchemy==2.0.9 python-dotenv==1.0.0 Werkzeug==2.2.3 requests==2.28.2


  setup.py
  
    
    
    
    패키징을 위한 프로젝트 구성.

  

from setuptools import setup, find_packages

setup( name="bookstore-inventory", version="0.1.0", packages=find_packages(), include_package_data=True, install_requires=[ "Flask>=2.2.0", "Flask-SQLAlchemy>=3.0.0", "SQLAlchemy>=2.0.0", "pytest>=7.0.0", "pytest-flask>=1.2.0", "python-dotenv>=1.0.0", ], python_requires=">=3.8", author="Your Name", author_email="your.email@example.com", description="A Flask web application for managing bookstore inventory", keywords="flask, inventory, bookstore", url="https://gitlab.com/your-username/python-shop-app", classifiers=[ "Development Status :: 3 - Alpha", "Environment :: Web Environment", "Framework :: Flask", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", ], )


  .env
  
    
    
    
    애플리케이션의 환경 변수를 포함합니다.

  

Flask configuration#

FLASK_APP=app FLASK_ENV=development FLASK_DEBUG=1 SECRET_KEY=your-secret-key-change-in-production

Database configuration#

DATABASE_URL=sqlite:///bookstore.db TEST_DATABASE_URL=sqlite:///test_bookstore.db

Application settings#

BOOK_TITLE_MAX_LENGTH=100 MAX_PRICE=1000.00 MAX_QUANTITY=1000


  app/models/article.py
  
    
    
    
    전체 유효성 검사가 포함된 Article 클래스.

  

class Article: """Article class for a bookstore inventory system."""

def __init__(self, name, price, quantity, article_id=None):
    """
    Initialize an article with validation.

    Args:
        name (str): The name/title of the book
        price (float): The price of the book
        quantity (int): The quantity in stock
        article_id (int, optional): The unique identifier for the article

    Raises:
        ValueError: If any of the fields fail validation
    """
    self.id = article_id
    self.set_name(name)
    self.set_price(price)
    self.set_quantity(quantity)

def set_name(self, name):
    """
    Set the name with validation.

    Args:
        name (str): The name/title of the book

    Raises:
        ValueError: If name is empty or too long
    """
    if not name or not isinstance(name, str):
        raise ValueError("Book title cannot be empty and must be a string")

    if len(name) > 100:  # Max length validation
        raise ValueError("Book title cannot exceed 100 characters")

    self.name = name.strip()

def set_price(self, price):
    """
    Set the price with validation.

    Args:
        price (float): The price of the book

    Raises:
        ValueError: If price is negative or not a number
    """
    try:
        price_float = float(price)
    except (ValueError, TypeError):
        raise ValueError("Price must be a number")

    if price_float < 0:
        raise ValueError("Price cannot be negative")

    if price_float > 1000:  # Max price validation
        raise ValueError("Price cannot exceed 1000")

    # Ensure price has at most 2 decimal places
    self.price = round(price_float, 2)

def set_quantity(self, quantity):
    """
    Set the quantity with validation.

    Args:
        quantity (int): The quantity in stock

    Raises:
        ValueError: If quantity is negative or not an integer
    """
    try:
        quantity_int = int(quantity)
    except (ValueError, TypeError):
        raise ValueError("Quantity must be an integer")

    if quantity_int < 0:
        raise ValueError("Quantity cannot be negative")

    if quantity_int > 1000:  # Max quantity validation
        raise ValueError("Quantity cannot exceed 1000")

    self.quantity = quantity_int

def to_dict(self):
    """
    Convert the article to a dictionary.

    Returns:
        dict: Dictionary representation of the article
    """
    return {
        "id": self.id,
        "name": self.name,
        "price": self.price,
        "quantity": self.quantity
    }

@classmethod
def from_dict(cls, data):
    """
    Create an article from a dictionary.

    Args:
        data (dict): Dictionary with article data

    Returns:
        Article: New article instance
    """
    article_id = data.get("id")
    return cls(
        name=data["name"],
        price=data["price"],
        quantity=data["quantity"],
        article_id=article_id
    )

  app/routes/shop.py
  
    
    
    
    오류 처리가 포함된 완전한 API 엔드포인트.

  

from flask import Blueprint, request, jsonify, current_app from app.models.article import Article from app import database import logging

Create a blueprint for the shop routes#

shop_bp = Blueprint('shop', name, url_prefix='/books')

Set up logging#

logger = logging.getLogger(name)

@shop_bp.route('', methods=['GET']) def get_all_books(): """Get all books from the inventory.""" try: books = database.get_all_articles() return jsonify([book.to_dict() for book in books]), 200 except Exception as e: logger.error(f"Error getting all books: {str(e)}") return jsonify({"error": "Failed to retrieve books"}), 500

@shop_bp.route('/int:book_id', methods=['GET']) def get_book(book_id): """Get a specific book by ID.""" try: book = database.get_article_by_id(book_id) if book: return jsonify(book.to_dict()), 200 return jsonify({"error": f"Book with ID {book_id} not found"}), 404 except Exception as e: logger.error(f"Error getting book {book_id}: {str(e)}") return jsonify({"error": f"Failed to retrieve book {book_id}"}), 500

@shop_bp.route('', methods=['POST']) def add_book(): """Add a new book to the inventory.""" data = request.get_json()

if not data:
    return jsonify({"error": "No data provided"}), 400

required_fields = ['name', 'price', 'quantity']
for field in required_fields:
    if field not in data:
        return jsonify({"error": f"Missing required field: {field}"}), 400

try:
    # Validate data by creating an Article object
    new_book = Article(
        name=data['name'],
        price=data['price'],
        quantity=data['quantity']
    )

    # Save to database
    book_id = database.add_article(new_book)

    # Return the created book
    created_book = database.get_article_by_id(book_id)
    return jsonify(created_book.to_dict()), 201

except ValueError as e:
    return jsonify({"error": str(e)}), 400
except Exception as e:
    logger.error(f"Error adding book: {str(e)}")
    return jsonify({"error": "Failed to add book"}), 500

@shop_bp.route('/int:book_id', methods=['PUT']) def update_book(book_id): """Update an existing book.""" data = request.get_json()

if not data:
    return jsonify({"error": "No data provided"}), 400

try:
    # Check if book exists
    existing_book = database.get_article_by_id(book_id)
    if not existing_book:
        return jsonify({"error": f"Book with ID {book_id} not found"}), 404

    # Update book properties
    if 'name' in data:
        existing_book.set_name(data['name'])
    if 'price' in data:
        existing_book.set_price(data['price'])
    if 'quantity' in data:
        existing_book.set_quantity(data['quantity'])

    # Save updated book
    database.update_article(existing_book)

    # Return the updated book
    updated_book = database.get_article_by_id(book_id)
    return jsonify(updated_book.to_dict()), 200

except ValueError as e:
    return jsonify({"error": str(e)}), 400
except Exception as e:
    logger.error(f"Error updating book {book_id}: {str(e)}")
    return jsonify({"error": f"Failed to update book {book_id}"}), 500

@shop_bp.route('/int:book_id', methods=['DELETE']) def delete_book(book_id): """Delete a book from the inventory.""" try: # Check if book exists existing_book = database.get_article_by_id(book_id) if not existing_book: return jsonify({"error": f"Book with ID {book_id} not found"}), 404

    # Delete the book
    database.delete_article(book_id)

    return jsonify({"message": f"Book with ID {book_id} deleted successfully"}), 200

except Exception as e:
    logger.error(f"Error deleting book {book_id}: {str(e)}")
    return jsonify({"error": f"Failed to delete book {book_id}"}), 500

  app/database.py
  
    
    
    
    연결 관리가 포함된 데이터베이스 작업.

  

import sqlite3 import os import logging from contextlib import contextmanager from app.models.article import Article

Set up logging#

logger = logging.getLogger(name)

Get database path from environment variable or use default#

DATABASE_PATH = os.environ.get('DATABASE_PATH', 'bookstore.db')

@contextmanager def get_db_connection(): """ Context manager for database connections. Automatically handles connection opening, committing, and closing.

Yields:
    sqlite3.Connection: Database connection object
"""
conn = None
try:
    conn = sqlite3.connect(DATABASE_PATH)
    # Configure connection to return rows as dictionaries
    conn.row_factory = sqlite3.Row
    yield conn
    conn.commit()
except sqlite3.Error as e:
    if conn:
        conn.rollback()
    logger.error(f"Database error: {str(e)}")
    raise
finally:
    if conn:
        conn.close()

def initialize_database(): """ Initialize the database by creating the articles table if it doesn't exist. """ try: with get_db_connection() as conn: cursor = conn.cursor()

        # Create articles table
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS articles (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                price REAL NOT NULL,
                quantity INTEGER NOT NULL
            )
        ''')

        logger.info("Database initialized successfully")
except sqlite3.Error as e:
    logger.error(f"Failed to initialize database: {str(e)}")
    raise

def add_article(article): """ Add a new article to the database.

Args:
    article (Article): Article object to add

Returns:
    int: ID of the newly added article
"""
try:
    with get_db_connection() as conn:
        cursor = conn.cursor()

        cursor.execute(
            "INSERT INTO articles (name, price, quantity) VALUES (?, ?, ?)",
            (article.name, article.price, article.quantity)
        )

        # Get the ID of the newly inserted article
        article_id = cursor.lastrowid
        logger.info(f"Added article with ID {article_id}")
        return article_id
except sqlite3.Error as e:
    logger.error(f"Failed to add article: {str(e)}")
    raise

def get_article_by_id(article_id): """ Get an article by its ID.

Args:
    article_id (int): ID of the article to retrieve

Returns:
    Article: Article object if found, None otherwise
"""
try:
    with get_db_connection() as conn:
        cursor = conn.cursor()

        cursor.execute("SELECT * FROM articles WHERE id = ?", (article_id,))
        row = cursor.fetchone()

        if row:
            return Article(
                name=row['name'],
                price=row['price'],
                quantity=row['quantity'],
                article_id=row['id']
            )
        return None
except sqlite3.Error as e:
    logger.error(f"Failed to get article {article_id}: {str(e)}")
    raise

def get_all_articles(): """ Get all articles from the database.

Returns:
    list: List of Article objects
"""
try:
    with get_db_connection() as conn:
        cursor = conn.cursor()

        cursor.execute("SELECT * FROM articles")
        rows = cursor.fetchall()

        articles = []
        for row in rows:
            article = Article(
                name=row['name'],
                price=row['price'],
                quantity=row['quantity'],
                article_id=row['id']
            )
            articles.append(article)

        return articles
except sqlite3.Error as e:
    logger.error(f"Failed to get all articles: {str(e)}")
    raise

def update_article(article): """ Update an existing article in the database.

Args:
    article (Article): Article object with updated values

Returns:
    bool: True if successful, False if article not found
"""
try:
    with get_db_connection() as conn:
        cursor = conn.cursor()

        cursor.execute(
            "UPDATE articles SET name = ?, price = ?, quantity = ? WHERE id = ?",
            (article.name, article.price, article.quantity, article.id)
        )

        # Check if an article was actually updated
        if cursor.rowcount == 0:
            logger.warning(f"No article found with ID {article.id}")
            return False

        logger.info(f"Updated article with ID {article.id}")
        return True
except sqlite3.Error as e:
    logger.error(f"Failed to update article {article.id}: {str(e)}")
    raise

def delete_article(article_id): """ Delete an article from the database.

Args:
    article_id (int): ID of the article to delete

Returns:
    bool: True if successful, False if article not found
"""
try:
    with get_db_connection() as conn:
        cursor = conn.cursor()

        cursor.execute("DELETE FROM articles WHERE id = ?", (article_id,))

        # Check if an article was actually deleted
        if cursor.rowcount == 0:
            logger.warning(f"No article found with ID {article_id}")
            return False

        logger.info(f"Deleted article with ID {article_id}")
        return True
except sqlite3.Error as e:
    logger.error(f"Failed to delete article {article_id}: {str(e)}")
    raise

  app/__init__.py
  
    
    
    
    Flask 애플리케이션 팩토리.

  

import os from flask import Flask from dotenv import load_dotenv

def create_app(test_config=None): """ Application factory for creating the Flask app.

Args:
    test_config (dict, optional): Test configuration to override default config

Returns:
    Flask: Configured Flask application
"""
# Load environment variables from .env file
load_dotenv()

# Create and configure the app
app = Flask(__name__, instance_relative_config=True)

# Set default configuration
app.config.from_mapping(
    SECRET_KEY=os.environ.get('SECRET_KEY', 'dev'),
    DATABASE_PATH=os.environ.get('DATABASE_URL', 'bookstore.db'),
    BOOK_TITLE_MAX_LENGTH=int(os.environ.get('BOOK_TITLE_MAX_LENGTH', 100)),
    MAX_PRICE=float(os.environ.get('MAX_PRICE', 1000.00)),
    MAX_QUANTITY=int(os.environ.get('MAX_QUANTITY', 1000))
)

# Override config with test config if provided
if test_config:
    app.config.update(test_config)

# Ensure the instance folder exists
os.makedirs(app.instance_path, exist_ok=True)

# Initialize database
from app import database
database.initialize_database()

# Register blueprints
from app.routes.shop import shop_bp
app.register_blueprint(shop_bp)

# Add a simple index route
@app.route('/')
def index():
    return {
        "message": "Welcome to the Bookstore Inventory API",
        "endpoints": {
            "books": "/books",
            "book_by_id": "/books/<id>"
        }
    }

return app

- 
작성한 코드 파일을 이 예제와 비교하세요.

- 
코드가 작동하는지 확인하기 위해 Chat에게 로컬 애플리케이션 서버를 시작하는 방법을 물어보세요:

  

How do I start a local application server for my Python web application?


- 
지침을 따르고 애플리케이션이 작동하는지 확인하세요.

애플리케이션이 작동한다면 축하드립니다! GitLab Duo Chat과 Code Suggestions를 성공적으로 사용하여 작동하는 온라인 쇼핑 애플리케이션을 구축했습니다.

작동하지 않는다면 원인을 파악해야 합니다. Chat과 Code Suggestions를 사용하여 애플리케이션이 예상대로 작동하는지 확인하는 테스트를 만들고 수정이 필요한 문제를 식별할 수 있습니다.

자세한 내용은 [GitLab Duo /fix 사용하기](https://youtu.be/agTqx__j6Ko?si=vpLfVvmFVcBivB1g)를 참조하세요.

## 관련 주제

- [GitLab Duo 사용 사례](/19.1/user/gitlab_duo/use_cases/)

- [GitLab Duo 시작하기](/19.1/user/get_started/getting_started_gitlab_duo/).

- 블로그 포스트: [GitLab Duo로 DevSecOps 엔지니어링 워크플로 간소화하기](https://about.gitlab.com/blog/streamline-devsecops-engineering-workflows-with-gitlab-duo/)

- 
[GitLab Duo Chat (에이전틱)](https://youtu.be/uG9-QLAJrrg?si=c25SR7DoRAep7jvQ)

- 
[GitLab Duo Chat (비에이전틱)](https://youtu.be/ZQBAuf-CTAY?si=0o9-xJ_ATTsL1oew)

- 
[GitLab Duo Code Suggestions](https://youtu.be/ds7SG1wgcVM?si=MfbzPIDpikGhoPh7)

- 
[GitLab Duo를 사용한 애플리케이션 현대화 (C++에서 Java로)](https://youtu.be/FjoAmt5eeXA?si=SLv9Mv8eSUAVwW5Z)