파이썬 프레임워크나 라이브러리에서 데코레이터는 자주 쓰이는데, 막상 일반 비즈니스 로직을 짤 때는 별로 안 쓰는 경우가 많다. 데코레이터 패턴이 어떻게 코드를 깔끔하게 만들고 유지보수하기 쉽게 해주는지 공유하고자 한다.

 

 

데코레이터 패턴 안 쓴 경우

 

우선 아래의 데코레이터를 사용하기 전 중복(복붙)이 많은 코드를 보라.

모든 메소드마다 로그인이 되어 있는지 체크하는 로직이 있고, 자기 본연의 비지니스 로직 외 로그인 중복을 체크하는 로직이 뒤섞여 있다. 이는 유지보수 시 굉장한 문제가 된다. 데코레이터 패턴을 사용해 깔끔한 코드를 만들어보자.

import os
from cloud_storage_client import CloudStorageClient
from exceptions import CloudStorageLoginFailedException

class CloudStorageManager:
    def __init__(self, access_key: str, secret_key: str, bucket_name: str):
        self.access_key = access_key
        self.secret_key = secret_key
        self.bucket_name = bucket_name
        self._is_logged_in = False
        self.client: CloudStorageClient | None = None

    @property
    def is_logged_in(self) -> bool:
        return self._is_logged_in

    def sign_in(self):
        if self.is_logged_in:
            return True

        try:
            self.client = CloudStorageClient(self.access_key, self.secret_key, self.bucket_name)
            self._is_logged_in = True
        except Exception as e:
            self._is_logged_in = False
            return False
        return True

    def upload_file(self, file_path: str, object_name: str = None):
        if not self.is_logged_in:
            raise CloudStorageLoginFailedException("로그인이 필요합니다.")
        if not object_name:
            object_name = os.path.basename(file_path)
        return self.client.upload_file(file_path, self.bucket_name, object_name)

    def download_file(self, object_name: str, download_path: str):
        if not self.is_logged_in:
            raise CloudStorageLoginFailedException("로그인이 필요합니다.")
        return self.client.download_file(self.bucket_name, object_name, download_path)

    def list_files(self):
        if not self.is_logged_in:
            raise CloudStorageLoginFailedException("로그인이 필요합니다.")
        return self.client.list_files(self.bucket_name)

    def delete_file(self, object_name: str):
        if not self.is_logged_in:
            raise CloudStorageLoginFailedException("로그인이 필요합니다.")
        return self.client.delete_file(self.bucket_name, object_name)
 

 

데코레이터 패턴 적용 후

 

require_sign_in 데코레이터를 사용함으로서 로그인 상태 확인 로직을 각 메소드에서 제거할 수 있다.

이렇게 하면 각 메소드가 본연의 기능에만 집중할 수 있어서 코드 읽기도 편해지고 관리하기도 편해진다.

import os
from functools import wraps
from cloud_storage_client import CloudStorageClient
from exceptions import CloudStorageLoginFailedException

def require_sign_in(func):
    @wraps(func)
    def wrapper(self: 'CloudStorageManager', *args, **kwargs):
        if not self.is_logged_in:
            if not self.sign_in():
                raise CloudStorageLoginFailedException("클라우드 스토리지 서비스에 로그인 할 수 없습니다.")
        return func(self, *args, **kwargs)
    return wrapper

class CloudStorageManager:
    def __init__(self, access_key: str, secret_key: str, bucket_name: str):
        self.access_key = access_key
        self.secret_key = secret_key
        self.bucket_name = bucket_name
        self._is_logged_in = False
        self.client: CloudStorageClient | None = None

    @property
    def is_logged_in(self) -> bool:
        return self._is_logged_in

    def sign_in(self):
        if self.is_logged_in:
            return True

        try:
            self.client = CloudStorageClient(self.access_key, self.secret_key, self.bucket_name)
            self._is_logged_in = True
        except Exception as e:
            self._is_logged_in = False
            return False
        return True

    @require_sign_in
    def upload_file(self, file_path: str, object_name: str = None):
        if not object_name:
            object_name = os.path.basename(file_path)
        return self.client.upload_file(file_path, self.bucket_name, object_name)

    @require_sign_in
    def download_file(self, object_name: str, download_path: str):
        return self.client.download_file(self.bucket_name, object_name, download_path)

    @require_sign_in
    def list_files(self):
        return self.client.list_files(self.bucket_name)

    @require_sign_in
    def delete_file(self, object_name: str):
        return self.client.delete_file(self.bucket_name, object_name)
 

 

 

'python' 카테고리의 다른 글

파이썬 데코레이터 활용(로깅)  (1) 2021.09.02
python oracle 데이터 json serialize(django)  (0) 2020.02.25
python super  (0) 2019.12.26
python 메모리 관리(memory allocation)  (4) 2019.11.15
클래스 관계 IS-A 상속  (0) 2019.11.07

+ Recent posts