AI 도구를 이용하여 디자인 패턴을 적용하는 리팩터링 방법을 실습해보겠습니다.
실습에 앞서, 디자인 패턴에 대해 간략히 살펴보겠습니다. 디자인 패턴은 소프트웨어 설계 중 발생하는 일반적인 문제들을 해결할 목적으로 재사용 가능한 형태로 정의한 규칙입니다. 이는 과거부터 자주 등장하는 문제들을 해결하는 과정에서 발전한 패턴을 체계화한 것으로, 개발자들이 효율적이고 유지보수가 용이한 코드를 작성할 수 있게 돕습니다.
디자인 패턴은 단순한 코드 조각이나 알고리즘이 아니라, 특정 상황에서 발생할 수 있는 문제에 대한 개념적인 해결책을 제공합니다. 적절한 디자인 패턴을 적용하면 코드의 가독성, 확장성, 재사용성이 향상되고, 검증된 설계 방법을 통해 개발 시간을 단축하고 버그 가능성을 줄이며 팀 간 의사소통도 개선할 수 있습니다. 따라서 복잡하고 큰 시스템에서는 디자인 패턴의 중요성이 더욱 커집니다.
디자인 패턴은 크게 생성 패턴, 구조 패턴, 행위 패턴으로 분류할 수 있습니다.
생성 패턴(Creational patterns)은 객체 생성 과정을 캡슐화하는 패턴으로, 객체 생성을 유연하게 하고 코드의 유지보수성 및 확장성을 향상시킵니다. 대표적인 예로는 싱글톤과 팩토리 메소드가 있습니다. 구조 패턴(Structural patterns)은 클래스나 객체를 조합하여 더 큰 구조를 만드는 방법을 제공하며, 애플리케이션의 전체 구조를 개선하고 인터페이스와 구현의 분리를 지원합니다. 어댑터 패턴과 데코레이터 패턴이 이에 해당합니다. 행위 패턴(Behavioral patterns)은 객체 간의 통신과 책임 분배에 초점을 맞추어 객체 간 협력과 태스크 분배를 효율적으로 하도록 돕습니다. 옵저버와 템플릿 메소드가 대표적인 예입니다.
디자인 패턴은 재사용성을 증대하고, 코드 중복을 줄이며 확장성을 높여 협업을 용이하게 합니다. 디자인 패턴을 사용하면 더 깔끔한 코드를 작성하고 팀원들과 효과적으로 협업할 수 있습니다. 이제 AI 도구를 이용해 다양한 디자인 패턴을 적용하는 방법을 배워보겠습니다.
01 이번에는 document_editor.py 예제를 사용하겠습니다. 워드와 엑셀과 문서를 편집하는 프로그래밍입니다.
class WordDocument :
def create(self) :
print("Word document created.")
def get_property(self) :
print("Font, size, color")
class ExcelDocument :
def create(self) :
print("Excel document created.")
def get_property(self) :
print("Row, column, cell")
class DocumentEditor :
def new_document(self, doc_type) :
if doc_type == "word" :
return WordDocument()
elif doc_type == "excel" :
return ExcelDocument()
else :
raise ValueError("Unknown document type")
editor = DocumentEditor()
word_doc = editor.new_document("word")
word_doc.create()
word_doc - get_property()
excel_doc = editor.new_document("excel")
excel_doc.create()
excel_doc - get_property()
new_document( )를 호출하여 워드 문서와 엑셀 문서를 생성합니다. Word Document 클래스는 font, size, color를 속성으로 가지며, ExcelDocument 클래스는 row, column, cell을 속성으로 갖습니다. 문서 편집기를 사용하여 새 문서를 생성할 때, 문서 유형을 문자열로 지정하면 해당 유형의 문서가 생성됩니다. 이제 이 과정을 실제로 실행해보겠습니다.
이 문서 편집기를 실행하면 먼저 워드 문서를 생성하고 그 속성을 출력한 후, 엑셀 문서를 생성하고 그 속성을 출력합니다.
Word document created.
Font, size, color
Excel document created.
Row, column, cell
위 코드에서 새로운 문서 타입, PowerPointDocument를 추가하는 요구 사항이 들어왔다고 가정해봅시다. 이 새로운 클래스에는 WordDocument, ExcelDocument와 같이 create 및 get_property 함수를 추가해야 합니다. 만약 함수 중 하나를 실수로 빼먹으면, 클래스를 사용하는 측에서 해당 함수를 호출할 때 에러가 발생할 수 있습니다. 이와 같이 코드 복사를 통한 확장은 개발자의 실수를 간과하게 하며, 결국 코드 확장에 대한 두려움을 초래할 수 있습니다.
02 어떻게 해결할 수 있는지 코파일럿에 질문해보겠습니다.
[REQUEST]
@workspace 코드에서 개선할 점이 보이나요? 디자인 패턴을 적용하고 싶습니다. 만약 Word나 Excel 외에 더 많은 문서 유형이 추가될 때마다 new_document 함수를 수정하는 것이 번거로운데, 더 좋은 방법이 있을까요?
[CHAT VIEW]
팩토리 디자인 패턴을 적용하면 코드의 유연성과 확장성이 향상됩니다. 팩토리 디자인 패턴은 객체 생성 로직을 메서드에 캡슐화하여, 클라이언트 코드가 특정 클래스의 인스턴스를 직접 생성하는 것을 방지합니다. 이를 통해 코드의 유연성과 재사용성을 높일 수 있습니다.
다음은 팩토리 패턴을 적용한 코드입니다.
class Document:
def create(self):
pass
class WordDocument(Document):
def create(self):
print("Word document created.")
class ExcelDocument(Document):
def create(self):
print("Excel document created.")
class DocumentFactory:
def new_document(self, doc_type):
documents = {
"word": WordDocument,
"excel": ExcelDocument
}
return documents.get(doc_type, Document)()
editor = DocumentFactory()
word_doc = editor.new_document("word")
word_doc.create()
excel_doc = editor.new_document("excel")
excel_doc.create()
이 코드에서 DocumentFactory 클래스의 new_document 메서드는 문서 타입을 인자로 받아 해당 타입의 문서 객체를 생성합니다. 문서 타입과 클래스의 매핑은 딕셔너리를 사용하여 관리합니다. 이렇게 하면 새로운 문서 타입이 추가될 때마다 딕셔너리에 새로운 항목을 추가하기만 하면 됩니다. new_document 메서드를 수정할 필요가 없습니다.
팩토리 디자인 패턴을 사용해 객체 생성 로직을 캡슐화하면 코드를 좀 더 유연하게 만들 수 있다고 합니다. 그러면 새로운 문서 타입이 추가될 때마다 new_document 메소드를 수정하지 않아도 된다고 합니다.
03 다른 패턴을 사용할 수 있는지 한 번 더 문의해보겠습니다.
[REQUEST]
@workspace 혹시 위 factory 패턴 이외에 다른 패턴으로 개선할수 있는 방법이 또 있을까요.
[CHAT VIEW]
네, “전략 패턴(Strategy Pattern)”을 적용할 수도 있습니다. 전략 패턴은 알고리즘을 캡슐화하여 동적으로 알고리즘을 교체할 수 있게 하는 디자인 패턴입니다. 이 패턴을 사용하면 실행 시간에 알고리즘(전략)을 선택할 수 있습니다.
다음은 전략 패턴을 적용한 코드입니다:
from abc import ABC, abstractmethod
class Document(ABC):
@abstractmethod
def create(self):
pass
class WordDocument(Document):
def create(self):
print("Word document created.")
class ExcelDocument(Document):
def create(self):
print("Excel document created.")
class DocumentEditor:
def __init__(self, strategy: Document):
self._strategy = strategy
def create_document(self):
self._strategy.create()
editor = DocumentEditor(WordDocument())
editor.create_document()
editor = DocumentEditor(ExcelDocument())
editor.create_document()
이 코드에서 DocumentEditor는 문서 생성 전략을 가지고 있으며, 이 전략은 실행 시간에 변경할 수 있습니다. 이렇게 하면 새로운 문서 유형이 추가될 때마다 DocumentEditor 클래스를 수정하지 않고도 새로운 문서를 생성할 수 있습니다. 이는 개방-폐쇄 원칙 (Open-Closed Principle)을 따르는 것으로, 소프트웨어의 유연성을 높이는 데 중요합니다.
Strategy 패턴을 사용하여 문서 생성 로직을 캡슐화하고, 이를 DocumentEditor 클래스 내에서 사용하도록 변경했습니다. Strategy 패턴은 특정 작업을 수행하는 여러 알고리즘을 정의하고, 이들을 상호 교환 가능하게 만듭니다.
위와 같이 Strategy 패턴을 사용하면, Word와 Excel뿐만 아니라 다양한 문서 형식이 추가될 때도 각 형식에 맞는 Strategy를 정의함으로써, 함수를 빠트리지 않고 안전하게 코드를 확장할 수 있습니다.
04 한 파일에 모든 코드가 포함되어 있어서, 확장할 때마다 파일 크기가 커지고, 이로 인해 가독성이 저하될 수 있습니다. 파일 분리를 요청해보겠습니다.
[REQUEST]
제시해준 코드가 한 파일에 있으니 너무 길고 가독성이 좋지 않은것 같습니다. 파일과 폴더를 적절히 나눈다면 어떻게 나눠볼 수 있을까요?
[CHAT VIEW]
네, 각 클래스를 별도의 파일로 분리하고, 이 파일들을 모아둘 폴더를 생성할 수 있습니다. 이렇게 하면 각 파일이 하나의 클래스를 정의하므로, 코드를 이해하고 관리하기가 더 쉬워집니다.
다음과 같이 파일과 폴더를 구성해볼 수 있습니다.
- documents_types/ 폴더 : 문서 관련 클래스를 담은 폴더
- document.py : Document 추상 베이스 클래스를 정의하는 파일
- word_document.py : WordDocument 클래스를 정의하는 파일
- excel_document.py : ExcelDocument 클래스를 정의하는 파일
- document_editor.py : DocumentEditor 클래스와 메인 실행 코드를 담은 파일
각 파일의 코드는 다음과 같습니다.
documents_types/document.py :
from abc import ABC, abstractmethod
class Document(ABC):
@abstractmethod
def create(self):
pass
documents_types/word_document.py :
from .document_types import Document
class WordDocument(Document):
def create(self):
print("Word document created.")
documents_types/excel_document.py :
from .document_types import Document
class ExcelDocument(Document):
def create(self):
print("Excel document created.")
document_editor.py :
from .document_types.word_document import WordDocument
from .document_types.excel_document import ExcelDocument
class DocumentEditor :
def __init__(self, strategy: Document):
self._strategy = strategy
def create_document(self):
self._strategy.create()
editor = DocumentEditor(WordDocument())
editor.create_document()
editor = DocumentEditor(ExcelDocument())
editor.create_document()
이렇게 하면, 각 파일은 하나의 클래스만을 정의하므로 코드의 가독성이 향상됩니다. 또한, 새로운 문서 유형을 추가하려면 ducment_types 폴더에 새 파일을 추가하기만 하면 됩니다. 이는 개방-폐쇄 원칙 (Open-Closed Principle)을 따르는 것으로, 소프트웨어의 유연성을 높이는 데 중요합니다.
‘document_types’라는 폴더 아래에 Word, Excel 등 확장 가능한 문서 형식을 파일별로 관리할 수 있게 폴더 구조를 제안했습니다.
이런 구조라면 PowerPoint와 같은 추가 문서 기능이 요구될 때 document_types 폴더에 필요한 파일과 코드를 추가함으로써 안전하게 기능을 확장할 수 있습니다. 또한, 코드 파일을 모두 열어보지 않아도 Word와 Excel을 지원하고 있음을 파일 구조만으로 파악할 수 있어 다른 사람들과의 협업, 신규 작업자 온보딩에 있어서도 유리합니다.
지금까지 깃허브 코파일럿으로 디자인 패턴을 적용하는 리팩터링 방법을 알아보았습니다. 반복적으로 사용하면서 실제 코드에 알맞는 디자인 패턴을 적용할 수 있는 방법을 연습하는 것이 중요합니다. AI 도구와 함께 페어 프로그래밍하세요.
서지연
카카오, 네이버, 페이스북 등에서 뉴스, 커머스, AI, NFT, 엔터테이먼트 등 여러 도메인을 탐험했습니다. 세상을 더 즐겁게 할 제품 만들기를 좋아하는 개발자로서 코드 리뷰 그리고 함께 성장하는 개발 문화에 관심이 많습니다. 데뷰, 인프콘 등에서 기술 발표, 팟캐스트 〈나는프로그래머다〉 공동 MC를 진행했습니다. 개발자 수다가 있는 곳이라면 ‘치즈🧀’라는 이름으로 어디든 나타납니다.