Skip to main content

횡단 관심사 처리

Copilot Chat는 코드가 있는 메서드나 함수의 핵심 관심사 이외의 우려 사항과 관련된 코드를 방지하는 데 도움이 될 수 있습니다.

횡단 관심사는 로깅, 보안, 데이터 유효성 검사, 오류 처리와 같은 시스템의 여러 부분에 영향을 주는 프로그램의 애스팩트입니다. 코드베이스 전체에 분산되어 코드 중복과 유지 관리 문제가 발생할 수 있습니다.

Copilot Chat는 AOP(측면 지향 프로그래밍) 구현을 제안하거나 데코레이터와 미들웨어 패턴을 사용하여 이러한 문제를 모듈식으로 유지 관리 가능한 방식으로 중앙 집중화하여 교차 절단 문제를 리팩터링하는 데 도움이 될 수 있습니다.

예제 시나리오

로깅이 발생하는 여러 서비스 파일이 포함된 Python 프로젝트가 있다고 상상해 보세요. 기록되는 정보는 개별 서비스 파일 내에서 정의됩니다. 나중에 애플리케이션을 수정하거나 확장하면 이 디자인으로 인해 로그 항목의 콘텐츠와 스타일이 일치하지 않을 수 있습니다. 로깅 동작을 통합하고 중앙 집중화하여 프로젝트 전체에 분산되지 않도록 할 수 있습니다.

다음은 예제 프로젝트의 세 가지 파일인 진입점 파일(main.py), 로그 메시지 구성 파일(logging_config.py) 및 서비스 파일(order_service.py) 중 하나입니다. 예제 서비스 파일은 애플리케이션의 특정 부분에 대한 비즈니스 논리와 함께 로그 정보가 정의되는 방법을 보여줍니다.

main.py

import logging
from logging_config import setup_logging
from payment_service import PaymentService
from order_service import OrderService
from shipping_service import ShippingService
from inventory_service import InventoryService
from notification_service import NotificationService

def main():
    setup_logging()
    payment_service = PaymentService()
    order_service = OrderService()
    shipping_service = ShippingService()
    inventory_service = InventoryService()
    notification_service = NotificationService()

    # Example usage
    payment_service.process_payment({"amount": 100, "currency": "USD"})
    order_service.place_order({"item": "Book", "quantity": 1})
    shipping_service.ship_order({"item": "Book", "quantity": 1})
    inventory_service.update_inventory("Book", -1)
    notification_service.send_notification("Order has been placed and shipped.")

if __name__ == "__main__":
    main()

logging_config.py

import logging

def setup_logging():
    logging.basicConfig(level=logging.INFO,
                  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

order_service.py

import logging

class OrderService:
    def __init__(self):
        self.logger = logging.getLogger(__name__)

    def place_order(self, order):
        self.logger.info("Entering place_order method.")
        try:
            # In here would be the business logic for placing an order
            # ...
            self.logger.info("Order placed successfully.")
        except Exception as e:
            self.logger.error(f"Error in place_order method: {e}")
            raise
        finally:
            self.logger.info("Exiting place_order method.")

예시 프롬프트

VS Code 또는 Visual Studio에서 프로젝트 작업 영역을 열고 Copilot Chat 보기에 다음 프롬프트를 입력합니다.

@workspace Refactor this project by using AOP to avoid logging that has cross-cutting concerns

Note

VS Code 및 Visual Studio의 채팅 참가자는 @workspace Copilot에게 작업 영역의 모든 코드에 대한 컨텍스트를 제공합니다. Copilot가 프로젝트의 구조, 코드의 여러 부분이 상호 작용을 하는 방식을 고려하도록 하려면 @workspace를 사용하세요.

예제 응답

Copilot는 다음과 같이 응답합니다.

Note

다음 응답이 예제입니다. Copilot Chat 응답은 비결정적이므로 여기에 표시된 응답과 다른 응답을 얻을 수 있습니다.

AOP(관점 지향 프로그래밍)를 사용하여 이 프로젝트를 리팩터링하여 횡단 관심사로 로깅을 처리하려면 aspectlib와 같은 라이브러리를 사용할 수 있습니다. 수행할 방법은 다음과 같습니다.

  1. aspectlib 설치:

    pip install aspectlib

  2. logging_aspect.py라는 새 파일에 로깅하기 위한 애스팩트를 만듭니다.

    import aspectlib
    import logging
    
    @aspectlib.Aspect(bind=True)
    def log_method_call(cutpoint, *args, **kwargs):
        logger = logging.getLogger()
        method_name = cutpoint.__name__
        logger.info(f"Entering method: {method_name}")
        try:
            result = yield aspectlib.Proceed
            logger.info(f"Method {method_name} executed successfully.")
            return result
        except Exception as e:
            logger.error(f"Error in method {method_name}: {e}")
            raise
        finally:
            logger.info(f"Exiting method: {method_name}")
    
  3. main.py의 서비스 메서드에 해당 애스팩트를 적용합니다.

    import logging
    from logging_config import setup_logging
    from payment_service import PaymentService
    from order_service import OrderService
    from shipping_service import ShippingService
    from inventory_service import InventoryService
    from notification_service import NotificationService
    from logging_aspect import log_method_call
    import aspectlib
    
    def main():
        setup_logging()
    
        # Apply logging aspect to service methods
        aspectlib.weave(PaymentService.process_payment, log_method_call)
        aspectlib.weave(OrderService.place_order, log_method_call)
        aspectlib.weave(ShippingService.ship_order, log_method_call)
        aspectlib.weave(InventoryService.update_inventory, log_method_call)
        aspectlib.weave(NotificationService.send_notification, log_method_call)
    
        payment_service = PaymentService()
        order_service = OrderService()
        shipping_service = ShippingService()
        inventory_service = InventoryService()
        notification_service = NotificationService()
    
        # Example usage
        payment_service.process_payment({"amount": 100, "currency": "USD"})
        order_service.place_order({"item": "Book", "quantity": 1})
        shipping_service.ship_order({"item": "Book", "quantity": 1})
        inventory_service.update_inventory("Book", -1)
        notification_service.send_notification("Order has been placed and shipped.")
    
    if __name__ == "__main__":
        main()
    

Copilot는 모든 서비스 클래스에서 로깅 코드를 제거하도록 지시하고 수정된 파일에 대한 코드를 제공합니다. 예를 들어 order_service.py의 수정된 버전은 다음과 같습니다.

class OrderService:
    def place_order(self, order):
        # In here would be the business logic for placing an order
        # ...
        pass

프로젝트의 리팩터링된 버전에서 동일한 로깅 작업이 수행되지만, 로깅 코드는 단일 파일에서 중앙 집중화됩니다.

추가 참고 자료