All Articles

파이썬으로 살펴보는 디자인 패턴과 리팩터링

이 글은 글또 10기에서 모집된 디자인 패턴 스터디와, 외부 모임에서 리팩터링 2판 스터디를 따라하면 배운 점을 회고하는 글입니다.

코드 및 해당 내용의 상세한 부분은 아래 경로를 참조 부탁드립니다.

들어가며

좋은 코드에 대해 좋은 코드는 어떤 무언가 굉장히 고상하고 완성된 코드라는 환상이 있었습니다. 놀랍게도 그런 코드를 만드는 책을 살펴보자 이는 마치 백조처럼 생존을 위해 허덕이는 발버둥의 연속이었다는 걸1 알게 되었습니다.

??: 달리 그렇지도 않다는데요!?ㅋㅋ

그렇다면 효율적으로 프로젝트를 순항하기 위한 건강한 방안을 살펴봅시다.

건강한 코드베이스를 위해

건강한 코드베이스를 위해 어떤 것들이 필요할까요? 우선 마틴 파울러의 유명한 영상2을 가지고 이야기 해보고자 합니다.

리팩터링의 중요성

이는 “리팩터링의 중요성” 에 대한 내용이지요. 2014년 7월(영상 업로드 기준) 당시에만 해도 코드베이스의 높은 품질을 유지하기 위해 핵심기술로 자리잡았습니다. 그렇지만 저는 그러한 리팩터링의 워크플로우를 아직 이해하지 못하기도 했지요.

결국 리팩터링의 핵심은 지난 몇년의 실전경험에서 배웠던 것입니다.

돌아가는 소프트웨어가 제 1순위다.

그렇다고 해서 돌아만 가면 될까요? 우리의 소프트웨어는 고객에게 유의미한 가치를 계속 주길 원합니다. 그렇다면 계속해서 복잡함이 추가될 수 밖에 없습니다. 손님이 원하는 프로그램은 자꾸만 바뀔테니까요. 그래서 영상에서는 되는 코드를 만들고, 그 코드를 바꾸는 과정을 반복해서 수행하라는, 흔히 말하는 TDD의 방식을 먼저 이야기합니다. 다음 경우는 코드가 역겨울 때 ‘쓰레기 줍기 리팩터링’을 하길 바랍니다. 코드가 지속적으로 유지되기 위함이지요. 마지막 하나는 복잡한 로직이 뒤섞인 코드가 있습니다. 이 실타래를 풀어내는 것 또한 이하하기 쉽게 풀어야 합니다. 그것이 ‘이해를 위한 리팩터링’이지요.

리팩터링은 코드가 읽기 쉽고 고치기 쉬운(새 기능을 추가하든, 기존 기능을 수정하든) 코드를 만드는 방법입니다. 책의 내용을 발췌하자면:

좋은 코드를 가늠하는 확실한 방법은 ‘얼마나 수정하기 쉬운가’다.

라는 것입니다.

그걸 못하고있다고 생각이 들어, 대번에 스터디 신청을 하고 지원하게 되었네요.

디자인 패턴의 중요성

디자인 패턴은 물론 다른 쪽에 주안점을 둡니다. 객체지향 소프트웨어 설계는 근본적으로 힘듭니다. 이를 재사용 가능하게 설계하는 것도 마찬가지지요. 그리고 처음부터 제대로 설계하는 것은 저는 그냥 불가능하다고 봅니다. 경험있는 사람들도 손사래를 치는데, 하물며 업계 전반에서는 오죽할까요.

그래서 경험있는 설계자들은 이를 위해 이전에 좋았던 해결책을 “패턴화” 했습니다. 이럴 때 이게 맞을까? 하는 설계에 주안점을 두고 계속 살펴봅니다. 본문에서 설명하겠지만, 각 패턴이 쓰일 상황을 이해하고, 이를 해결하고, 완성품은 어떤 결과이며 이에 따른 트레이드오프는 무엇인지 알아봅니다.

크리스토퍼 알렉산더는 이런 말을 했습니다:

“패턴이란, 우리가 개발 환경에서 반복적으로 마주치는 문제를 설명하고, 그 문제의 핵심 해결책을 제시하는 것입니다. 이 해결책은 마치 템플릿처럼 백만 번이고 재사용할 수 있으나, 실제 상황에 맞추어 매번 다르게 적용할 수 있습니다.” [AIS+77]

Christopher Alexander, Sara Ishikawa, Murray Silverstein, Max Jacobson, Ingrid Fiksdahl-King, and Shlomo Angel. A Pattern Language. Oxford: Oxford University Press, 1977.

(Gemma, DeepL, Claude Sonnet 3.5 결과를 토대로 의역)

이 말은 아래와 같은 뜻이 아닌가 하고 생각했습니다.

  1. 반복되는 문제가 있는데
  2. 주요 “패턴”은 이를 해결하는 핵심 해결책을 제시한다. 어디까지나 기본 원칙으로서.
  3. 그리고 “백만 번(million times)”을 사용해도 상황에 맞추어 응용할 수 있음을 말한다

다시말해, 디자인 패턴은 특정 상황에서 일반적인 설계 고민을 해결하기 위한 방안인 것이지요.

1회차를 해본 후

헬스장도 그렇고 스터디도 그렇고 꾸준히 하는 게 중요하죠. 이런 건 하루이틀 해서 될 것도 아닙니다. 여기선 느낀 점을 위주로 이야기 해보려 합니다.

리팩터링 1장을 해본 후

작게 바꾸는 흐름과 소개해주는 패턴을 체화해보고 시간가는 줄 모르고 계속 짰습니다. 저는 아래 플로우로 짰습니다. 코드베이스는 실제 비즈니스 로직이 있는 코드를, 테스트코드는 우리가 아는 그 테스트코드를 의미합니다.

  1. 돌아가는 코드베이스를 짭니다
  2. 테스트코드를 구성하고 실행합니다.
  3. 테스트를 패스하게 코드베이스를 수정하고 커밋합니다.
  4. 리팩터 할 코드베이스를 살펴보고 작은단위의 리팩터를 수행합니다.
  5. 기존 테스트코드를 구동합니다
  6. 테스트를 패스하게 코드베이스를 수정하고 커밋합니다.

이 플로우대로 작은 범위를 계속 커밋하고나니 50개에 육박하는 커밋이 생겼더군요. 얼마나 잘게, 그리고 리팩터를 얼마나 신중하게 할 수 있는지 알게 되었습니다.

왜 이게 필요하고, 어떤 접근을 해야하는지 한번 해보게되어 매우 좋았습니다. 더 공유하고 싶은 내용이 있다면 블로그 글로 정제하여 공유드리겠습니다.

디자인패턴 1장, 2장을 살펴본 후

분량이 정말 많았습니다만 하나같이 중요해서 대충 넘어갈 수도 없었습니다…ㅠㅠ

자 그럼 책이 제시하는 예시를 통해 왜 코드를 이렇게 풀어내야하는지 보았습니다.

  • 해결해야 할 요구사항은 무엇이고
  • 도입 시 어떤 변화가 예상되며
  • 프로그램 내에 잠재적으로 변할 수 있는 것은 무엇인지

사실상 이것의 연속입니다. 그렇지만…

당신은 객체를 제대로 이해하고 있는가?

객체지향 프로그래밍의 핵심은 객체와 메시지, 그리고 객체 간의 협력에 있습니다. 객체는 데이터와 동작을 하나로 묶어 관리하며, 이러한 객체들이 서로 메시지를 주고받으며 협력하는 방식으로 시스템이 구성됩니다. 이러한 협력은 시스템의 유연성과 확장성을 높이는 데 필수적입니다.

다시 강조하자면 객체 간의 협력과 책임 분담을 명확하게 정의하는 것이 중요합니다. 이를 통해 시스템의 복잡성을 관리하고, 변경에 유연하게 대응할 수 있는 구조를 설계할 수 있습니다.

상속, 구현, 인터페이스, 위임을 넘어…

특히나 이런 것들이:

캡슐화(Encapsulation): 데이터와 메서드를 하나의 단위로 묶어 외부로부터 보호하는 개념입니다. 이를 통해 데이터의 무분별한 접근을 막고, 데이터의 일관성을 유지할 수 있습니다.

상속(Inheritance): 기존 클래스의 특성과 메서드를 새로운 클래스에서 재사용할 수 있게 하는 기능입니다. 이를 통해 코드의 재사용성을 높일 수 있습니다.

다형성(Polymorphism): 동일한 메서드가 다양한 방식으로 동작할 수 있게 하는 기능입니다. 이를 통해 코드의 유연성과 확장성을 높일 수 있습니다.

이러한 개념들을 이해하고 넘어가는 것부터 시작했습니다.

2장은?

각 개념을 Lexi 라는 이름의 텍스트 에디터를 설계하며 어떻게 풀어나갈 지 아는 내용입니다.

이런 개념을 보면 왜:

  • 상속보다 구현이 더 괜찮은 선택인지
  • 코드의 구조를 어떻게 잡을 수 있는지
  • 다형성을 어떤 식으로 쉽게 추가/수정하게 구성할지

를 알 수 있습니다.

요약하기 좀처럼 쉽지않지만, 추후 디자인패턴을 계속 공부하며 또 소개할 때 다시 언급해보겠습니다.

끝으로

2025년은 이런 스터디를 많이 하려 합니다. “이제는 해보자!” 하는 마인드로 하나씩 실현해보면 어떨까 합니다.

이 글을 읽어보시는 여러분들께도 한번 권해봅니다.

이제는 해봅시다!

Published Jan 19, 2025

Non scholæ sed vitæ discimus.

his/him