티스토리 뷰

디자인 패턴의 아름다움 - 왕정 지음

간략 목차

책의 목적

  • 고품질의 코드를 작성할 수 있도록 돕는다.

Chater 1 _ 개요

코드 설계 방법론을 배우기 전에 '고품질의 코드란 무엇인가' 같은 코드 품질과 관련되어 확실히 정의를 한다.

1.1 코드 설계를 배우는 이유

코드 설계에 대한 지식은 확장성과 가독성이 높아 유지 보수가 용이한 고품질의 코드를 작성할 때 필요하다.

1.1.1 고품질의 코드 작성

소프트웨어 엔지니어라면 코드 품질을 매우 중요하게 생각한다. 나쁜 코드를 작성하고 싶은 엔지니어는 없을 것이다.
하지만 많은 개발자가 만족스럽지 못한 코드를 작성하고 있는데, 어떤 코드가 고품질의 코드인지 알지 못하기 때문이다.

 

표준을 벗어난 네이밍 규칙, 불합리한 클래스 설계, 불문명한 계층화, 모듈화되지 않은, 혼란스러운 코드 구조, 높은 결합성 - 을 가진 너덜너덜해진 코드가 만들어진다. 이러한 코드를 유지 보수하는 것은 매우 힘든 일이다.

 

그렇다면 어떻게 높은 품질의 코드를 작성할 수 있도록 스킬을 향상할 수 있을까?

  • 코드 설계에 대한 이론을 갖춰야 한다. 이론적 지식이란 선지자로부터 이어온 지혜의 결정체이며 문제를 해결하는 도구이다.
  • 기본적인 이론이 없으면 강력한 '무기와 장비'를 갖추지 못하고 게임을하는 것과 같다.

1.1.2 복잡한 코드 개발 다루기

개발 시 만나는 어려움은 2가지이다.

  1. 매우 높은 수준의 기술이 필요한 경우.
    • 코드의 양보단 자율주행, 비디오 인식 등 상대적으로 고도의 기술이 필요한 경우.
  2. 높은 수준의 기술이나 최신 기술이 필요한 경우는 아니지만 복잡한 비니지스를 갖춘 대규모 프로젝트.
    • 단순하지만 코드의 양이 매우 많은 물류 시스템, 금융 시스템, 대규모 ERP 시스템 등이 있다.

여기서는 첫 번째 '기술적 어려움'은 세분화된 전문 분야에 대한 지식이 요구되므로, 두 번째 '소프트웨어 개발의 복잡성'을 다루는 방법에 대해 얘기한다.

 

대부분의 개발자는 hello world 출력과 수천줄의 코드 유지 보수 능력이 있다. 하지만 코드가 수만, 수십만, 수백만 줄로 늘어난다면 소프트웨어의 복잡성은 기하급수적으로 증가하며 작성된 코드를 이해하고 유지 보수할 수 있어야 한다.

이때 코드 설계와 관련된 지식이 활용되게 된다.

 

복잡한 코드 설계와 개발이 필요하게 되면 어디서부터 무엇을 시작해야할 지 모르는 막막한 상태가 되며 무력감을 느끼게 된다.

단순 기능 구현과 사용 가능한 코드를 만드는 것은 쉬울 수 있으나 사용하기 쉬운 코드를 작성하는 것은 어렵다.

 

아래 질문들을 생각해본 적이 있는지 생각해보자.

  • 계층화와 하위 모듈화 방법은 무엇인가.
  • 클래스를 어떻게 나누는게 좋은가.
  • 각 클래스에는 어떤 속성과 메서드가 있는가.
  • 클래스 간의 상호 작용을 설계하는 방법은 무엇인가.
  • 상속이나 연관을 사용하는 것이 옳은가.
  • 인터페이스나 추상 클래스를 사용하는 것이 옳은가.
  • 결합도가 높은 코드와 낮은 코드는 무엇인가.
  • 디커플링을 달성하는 방법은 무엇인가.
  • 싱글톤 패턴이나 정적 메서드를 사용하는게 옳은가.
  • 객체를 생성할 때 팩토리 메서드를 사용하는게 옳은가.
  • 가독성을 유지하면서 확장성을 유지하기 위해 디자인 패턴을 도입하는 방법은 무엇인가.

저자도 처음에는 위 문제에 대해 생각하지 않았다.

코드 설계에 대한 어려움에 헤메고 있을 때, 코드 설계의 중요성을 깨닫고 의도적으로 코드 설계를 의식하고 개발하게 되었다.
그 결과 복잡한 코드 설계와 개발을 할 때도 점점 더 쉽게 문제를 해결해 나가게 되었다.

1.1.3 프로그래머의 기본 능력

소스 코드를 읽으면서 종종 그것이 어떤 의미인지 이해하지 못하는 경우가 있다. 이런 문제의 원인은 간단하다. 코드를 완벽하게 이해하기 위해 필요한 기본적인 기술적 소양과 능력이 부족하기 때문이다.

 

우수한 오픈소스 프로젝트, 프레임워크, 미들웨어는 코드와 클래스의 양이 많고, 클래스 구조와 클래스 간의 관계와 호출 관계도 복잡하다. 따라서 코드의 확장성, 유연성, 유지 보수성을 보장하기 위해 더 많은 디자인 패턴과 설계 원칙이 코드에 사용된다.
이러한 디자인 패턴과 설계 원칙을 이해하지 못한다면 작성자의 설계 의도를 완전히 이해하지 못하거나, 이해하는데 오랜 시간이 걸릴 수 있다.


반대로 디자인 패턴과 설계 원칙에 대해 확실히 이해하면서 코드가 설계된 이유를 한눈에 파악할 수 있다면 코드는 쉽게 읽힌다.

 

우수한 오픈소스 프로젝트, 프레임워크 등은 다양하고 정교한 기술을 갖춘 최신형 스텔스기와 같다.

우리가 그 원리를 분석하고 기술을 내것으로 만들고 싶어도 기초적인 기술의 깊은 이해가 없다면 최신형 스텔스기가 눈 앞에 있어도 본질을 이해하지 못하며 수박 겉핥기만 하게 되는 것이다.

1.1.4 경력 개발에 필요한 기술

평생 주니어가 아닌 수석 엔지니어로 성장하고, 직장에서 더 높은 성과와 발전을 달성하기 원한다면 기본적인 능력 향상과 기본적인 지식 습득에 꾸준히 신경써야 한다.

 

프레임워크를 잘 사용하고 아키텍처에 대해 이야기하면서도 정작 코드가 나쁘면 결코 좋은 개발자가 될 수 없다.


1.2 코드 품질 평가 방법

코드 리뷰를 할 때마다 가장 많이 듣는 이야기는 '코드가 제대로 작성되지 않았다', '코드가 잘 작성되었다'이다. 코드의 품질을 설명하기 위해 '좋은' 또는 '나쁜'과 같은 수식어를 사용하는게 일반적이다.


하지만 코드의 작성자가 어떤 부분이 '나쁜지' 또는 '좋은지' 물으면, 대부분 단순히 몇 가지 나쁜 측면 또는 좋은 측면을 나열하는 수준에 그치거나 충분한 답변이 되지 않는 경우가 많다.

 

좋은 코드는 확장하기 쉽고, 읽기 쉽고, 단순하고, 유지 보수가 쉽다고 생각하는 등 코드 품질 평가 방법을 알고 있는 개발자도 있지만 명쾌하게 대답하는 이는 드믈다.

이러한 평가에 대한 이해는 아래 질문에 대한 명확한 인식이 필요하다.

  • 어떤 코드가 높은 가독성을 가지는 코드인가.
  • 어떤 종류의 코드가 확장과 유지 관리에 용이한가.
  • 가독성, 확장성, 유지 보수 사이의 관계는 무엇인가.
  • 유지 보수는 정확히 어떤 것을 의미하는가.

코드 품질을 평가하는 단어는 매우 많지만 (유연성, 확장성, 재사용성, 모듈성, 응집성 등등) 이 단어들은 서로 다른 관점에서의 코드 품질을 평가하기에 종합적으로 평가하기가 어렵다.

 

우리가 사람을 평가할 때 단편적이지 않고 종합적으로 평가하는 것과 마찬가지이다. (성격, 능력, 외모 등)

코드 품질에 대한 평가 역시 다양한 측면에서 각 요소를 평가해야 하며 하나의 관점에서만 평가해서는 안된다.

 

예를 들어 확장성이 좋은데 가독성이 좋지 않은 코드의 경우 이 코드는 고품질의 코드라고 일방적으로 가정할 수는 없을 것이다.

이처럼 서로 다른 평가의 관점들은 완전히 독립적이지 않으며 일부는 포괄 관계, 중첩 관계이거나 서로 영향을 줄 수 있다.

가독성이 높으면서 확장성이 좋은 코드라면 유지 보수성이 높다라고 할 수 있다. 또한 평가는 흑/백이 아닌 0~100점과 같은 연속적인 값으로 평가되어야 한다.

 

평가는 매우 주관적이지만 개발자의 경험이 많을 수록 평가가 정확해지는 경향이 있다. 반대로 경험이 부족한 개발자는 정량화 할 수 있는 기준이 없으면 코드 품질을 정확하게 판단하기 어려워 한다.

  • 코드가 잘 작성되었는지 여부를 판별할 수 없다면 아무리 많은 코드를 작성해도 코딩 능력은 향상되지 않을 것이다.

 

이 책에서는 다양한 평가 기준들에서 7가지 중요한 평가 기준인

유지 보수성, 가독성, 확장성, 유연성, 간결성, 재사용성, 테스트 용이성에 대해 집중적으로 살펴본다.

  • maintainability
  • readability
  • extensibility
  • flexibility
  • simplicity
  • reusability
  • testablilty

1.2.1 유지 보수성 - maintainability

코드 개발에서 유지 보수란 버그의 수정, 이전 코드의 수정 또는 새로운 코드의 추가 정도이다.
코드의 유지 보수성이 높다라는 표현은 '기존 코드 설계를 손상시키거나 새로운 버그를 발생시키지 않고도 빠르게 코드를 수정하거나 추가할 수 있는 상태'를 말한다.

 

유지 보수, 쉬운 유지 보수, 어려운 유지 보수 3가지 개념을 이해하는 것은 어렵지 않고, 오히려 코드의 유지 보수성을 판단하는 방법을 아는것이 훨씬 더 중요하다.
-> 즉, 이 코드의 유지 보수성이 낮은지 / 높은지 판단하는 방법.

 

코드가 간결하고 가독성이 높고 확정성이 높으면 코드의 유지 보수도 쉬워진다.
즉, 코드가 명확하게 계층화되어 있으며, 높은 모듈성, 높은 응집도와 낮은 결합도를 가지고 구현보다 인터페이스 기반의 설계 원칙을 고수한다면 유지 보수가 쉽다는 의미이다.

1.2.2 가독성 - readability

컴퓨터가 이해할 수 있는 코드는 바보라도 작성할 수 있다. 훌륭한 개발자는 사람이 이해할 수 있는 코드를 작성한다. - Martin Fowler

코드의 가독성은 매우 중요하므로 코드를 작성할 때 이해하기 쉽고 읽기 쉬운 코드인지 고려해야 한다. 버그 수정이든, 기능 코드 추가 및 수정이든 먼저 코드를 이해해야 가능하기 때문이다.

코드의 가독성은 어떻게 판단할 수 있을까?

  • 코드의 명명이 의미가 있는지
  • 주석이 자세히 기술되어 있는지
  • 함수 길이는 적절한지
  • 모듈 구분이 명확한지
  • 코드가 높은 응집도와 낮은 결합도를 갖는지

등 모두 확인해야 한다.

 

코드 리뷰도 코드의 가독성을 판단하는 좋은 수단이다. 읽는이가 쉽게 읽는다면 가독성이 좋은거고, 코드에 질문이 많다면 가독성에 문제가 있다는 것이다.

1.2.3 확장성 - extensibility

확장성은 - 기존 코드를 약간 수정하거나 전혀 수정하지 않고도 확장을 통해 새로운 기능을 추가하는 것이다.

 

즉, 코드의 확장성은 처음 코드를 작성할 때 부터 새로운 기능을 추가할 수 있는 여지가 설계 당시에 고려되어 있어서 확장용 인터페이스가 이미 존재하는 것을 의미한다.

1.2.4 유연성 - flexibility

코드의 품질을 설명할 때 '코드는 유연하게 작성되었습니다.'라는 설명을 들을 수 있는데, '유연하게'라는 단어는 어떻게 이해할 수 있을까?

유연하다는 것은 추상적인 평가 기준이며, 어떤것이 유연한 것인지 한 마디로 정의하기는 쉽지 않다. 그래도 유연성에 대해 생각해 볼 수는 있다.


코드의 유연성이 무엇인지 이해하는데 도움이 되는 세 가지 시나리오를 봐보자.

  1. 기존 코드에 확장을 위한 인터페이스가 준비되어 있어서 기존 코드의 수정 없이 새로운 코드를 추가하기만 하면된다. 이때 코드는 확장서잉 높으며 유연하다고 할 수 있다.
  2. 함수를 구현할 때 기본적으로 재사용 가능한 많은 모듈과 클래드 등이 기존 코드에 추상화된 형태로 이미 제공되어 이를 상속받아 사용할 수 있다. 이때 코드는 재사용성이 높으며 유연하다고 할 수 있다.
  3. 클래스를 사용할 때 클래스가 다양한 시나리오에 대응하고, 다양한 요구를 충족할 수 있다면 클래스의 사용성이 높으며 유연하다고 할 수 있다.

즉, 코드가 확장재사용이 용이하고 사용성이 높을 경우 일반적으로 코드가 유연하다고 할 수 있다.

1.2.5 간결성 - simplicity

잘 알려진 설계 원칙으로 바로 'Keep It Simple, Stupid', 일명 'KISS 원칙'이다. 이 원칙은 코드를 가능한 단순하게 유지하려는 간결성을 의미한다.

 

단순한 코드와 명확한 논리는 코드의 가독성이 높고, 유지 보수성이 높다는 것을 의미한다.

 

코드가 최대한 간결하게 작성되어야 하고 KISS 원칙에 부합되어야 한다는 것도 알겠는데, 어떤 코드를 간결한 코드라고 할 수 있으며, 어떤 종류의 코드가 KISS 원칙을 준수하는 것으로 간주 할 수 있을까.

이 부분은 Chapter 3에서 KISS 원리 소개와 함께 예제를 통해 알아보겠다.

1.2.6 재사용성 - reusability

코드의 재사용성은 반복적인 코드 작성을 최소화하고 기존 코드를 재사용하는 것으로 이해할 수 있다.

재사용성은 코드 평가 기준으로 언급되는데,

  • 객체지향을 도입할 때 상속과 다형성의 목적 중 하나가 재사용성을 향상시키는 것이다.
  • 설계 원칙을 도입할 때 단일 책임 원칙과 코드 재사용성을 언급한다.
  • 리팩토링 기술을 도입할 때 디커플링, 높은 응집도, 모듈화가 코드 재사용성을 향상시킨다.

즉, 재사용성은 중요한 코드 평가 기준이면서 다양한 설계 원칙, 설계 사상, 디자인 패턴에 의해 달성되는 최종 효과이다.

특히 DRY 패턴과 밀접한 관련이 있다.

이부분은 Chapter 3장에서 DRY 원칙을 소개할 때 다루게 된다.

1.2.7 테스트 용이성 - testablilty

코드 테스트 용이성 수준이 코드 품질 수준 측면을 정확하게 반영할 수 있다. 코드의 테스트 용이성이 낮으면 단위 테스트를 작성하기 어렵다는 것이며, 기본적으로 코드의 설계에 문제가 있음을 보여준다.

이 부분은 Chapter 5 - 3절의 리팩토링 항목에서 자세히 살펴본다.


1.3 고품질의 코드를 작성하는 방법

우리 모든 개발자들은 고품질의 코드를 작성하고 싶어하고, 어떻게 해야 고품질의 코드를 작성할 수 있을지 고민한다.

앞에서 7가지의 중요한 코드 품질 평가 기준을 제시했다.

 

고품질의 코드는 유지하기 쉽고, 읽기 쉽고, 확장하기 쉽고, 유연하고, 간결하고, 재사용 가능하고, 테스트가 가능한 코드를 의미한다.

 

위 코드 품질 평가 표준을 충족하는 고품질의 코드를 작성하려면

  • 객체지향 설계 패러다임
  • 설계 원칙
  • 디자인 패턴
  • 코딩 규칙
  • 리팩토링 기술

등 세련되고 구현 가능한 프로그래밍 방법론을 마스터해야 한다.

 

이러한 프로그래밍 방법론을 마스터하는 궁극적인 목표는 '고품질의 코드를 작성하기 위해서' 이다.

1.3.1 객체지향

프로그래밍 패러다임을 주도하는 3가지 스타일인 절차적 프로그래밍, 함수형 프로그래밍, 객체지향 프로그래밍이 있다. 대중적인 프로그래밍 언어의 대부분은 객체지향 프로그래밍 언어이다. 대부분의 프로젝트도 객체지향 프로그래밍 스타일을 기반으로 개발된다.


캡슐화, 추상화, 상속, 다형성의 풍부한 특성으로 인해 객체지향 프로그래밍은

복잡한 설계 사상을 실현할 수 있으므로 다양한 설계 원칙과 디자인 패턴 코딩 구현의 기초가 된다.

 

이 책은 객체지향의 일곱 가지 이론을 마스터하는것을 목표로 한다.

이 부분은 Chapter 2에서 다루게 된다.

  1. 객체지향의 네 가지 주요 특성: 캡슐화, 추상화, 상속, 다형성
  2. 객체지향 프로그래밍과 절차적 프로그래밍의 차이점과 연계
  3. 객체지향 분석, 객체지향 설계, 객체지향 프로그래밍
  4. 인터페이스와 추상 클래스의 차이점과 각각의 응용 시나리오
  5. 구현이 아닌 인터페이스를 기반으로 한 설계 사상
  6. 더 많은 합성, 더 적은 상속의 설계 사상
  7. 절차적인 빈약한 도메인 모델과 객체지향의 풍부한 도메인 모델

1.3.2 설계 원칙

설계 원칙은 코드 설계에서 배운 몇 가지 교훈이다.

  • 설계 원칙은 추상적으로 보이며
  • 정의와 설명이 모호하며
  • 동일한 설계 원칙에 대해 사람마다 다르게 해석하는

특징이 있다.

 

따라서 단순히 정의만 암기하면 프로그래밍 설계 능력에 큰 도움이 되지 않는다.
각각의 설계 원칙이 어떤 문제와 응용 시나리오를 해결하는 데 사용되는 것인지 파악해야한다.

 

이러한 내용을 모두 마스터해야만 설계 원칙을 프로젝트에 유연하고 적절하게 적용할 수 있다.

  • 설계 원칙은 정신적인 방법이며 / 디자인 패턴은 움직임이다.
  • 설계 원칙은 디자인 패턴보다 더 보편적이고 중요하다.
  • 설계 원칙을 마스터해야만 특정 디자인 패턴을 사용하는 이유를 명확하게 이해하고 올바르게 적용하고 새로운 패턴을 만들 수 있다.

아래 설계 원칙들을 이애하고 숙달하자. 해당 내용들은 Chapter 3에서 다루게 된다.

  • 단일 책임 원칙
  • 개방 폐쇄 원칙
  • 리스코프 치환 원칙
  • 인터페이스 분리 원칙
  • 의존 역전 원칙
  • KISS 원칙
  • YAGNI 원칙
  • DRY 원칙
  • LoD

1.3.3 디자인 패턴

디자인 패턴은 소프트웨어 개발에서 자주 접하는 일부 설계 문제에 대해 요약된 솔루션 또는 설계 사상을 모아둔 것이다.
대부분의 디자인 패턴은 코드 디커플링과 확장성 문제를 해결한다.


설계 원칙에 비해 디자인 패턴은 덜 추상적이므로, 이해하기 쉽고 코드 구현도 복잡하지 않다.

이 책에서는 22개의 고전적인 디자인 패턴에 대해 다루며, 이 패턴들은 크게 생성, 구조, 행동으로 구분할 수 있다.
각각 Chpater 6, 7, 8에서 알아본다.

  • 생성 디자인 패턴
    • 싱글턴 패턴, 팩터리 패턴, 빌터 패턴, 프로토타입 패턴
  • 구조 다지인 패턴
    • 프록시 패턴, 데코레이터 패턴, 어댑터 패턴, 브리지 패턴, 퍼사드 패턴, 복합체 패턴, 플라이웨이트 패턴
  • 행동 디자인 패턴
    • 옵저버 패턴, 템플릿 메서드 패턴, 전략 패턴, 책임 연쇄 패턴, 상태 패턴, 반복자 패턴, 비지터 패턴, 메멘토 패턴, 커맨드 패턴, 인터프리터 패턴, 중재자 패턴

1.3.4 코딩 규칙

코딩 규칙은 주로 가독성 문제를 해결한다.

 

(추상적, 이론 레벨) 설계 원칙 > 디자인 패턴 > 코딩 규칙 (구체적, 코드 레벨)

개발자가 개발한 프로젝트가 복잡하다면 변수, 클래스, 함수, 코드 명명 규칙, 주석 범위 등 코딩 규칙에 능숙해야 한다.

  • 설계 원칙, 디자인 패턴보다 더 기본적이고 중요하다.

Chapter 4에서 코드 품질을 향상시킬 수 있는 코딩 규칙 17가지에 대해 알아본다.

1.3.5 리팩토링 기법

소프트웨어 개발에서 소프트웨어가 계속 반복되어 업데이트되는 한 언제까지나 적용되는 만능의 설계는 없다.

요구 사항의 변경과 지속적인 코드 업데이트로 인해 기본 설계에는 반드시 문제가 발생한다.


이러한 문제를 해결하기 위해 코드를 리팩토링 해야한다. 이를 통해 코드가 '희망이 없는 수준까지 손상'되는 것을 효과적으로 방지한다.

 

리팩토링을 위한 도구로

  • 객체지향 프로그래밍 패러다임, 설계 원칙, 디자인 패턴, 코딩 규칙이 있다.
  • 설계 원칙과 디자인 패턴에 대한 중요한 응용 시나리오가 리팩토링이다.

리팩토링과 관련해서 3가지 측면을 중점에 두고 있다.
이러한 내용을 통해

  • 일부 리팩토링 기술을 마스터할 수 있을 뿐만 아니라
  • 지속적인 리팩토링의 감각(Sense)을 확립하고
  • 리팩토링을 개발의 일부로 받아들여
  • 일상 개발에 통합할 수 있는 기회가 되길 바란다.
  1. 리팩토링의 목적(Why), 대상(What), 시기(When), 방법(How)
  2. 리팩토링에 오류가 없는지 확인하기 위한 기술적 수단: 단위 테스트, 코드 테스트 용이성
  3. 두 가지 다른 규모의 리팩토링: 대규모 고수준의 리팩토링과 소규모 저수준의 리팩토링

 

객체지향 프로그래밍, 설계 원칙, 디자인 패턴, 코딩 규칙, 리팩토링 기술 사이의 관계를 정리해본다.
  • 객체지향 프로그래밍
    • 캡슐화, 추상화, 상속, 다형성 등의 풍부한 기능으로 인해 복잡한 설계 사상을 구현할 수 있다.
    • 이를 통해, 다양한 설계 원칙과 디자인 패턴 코딩 구현의 기초가 된다.
  • 설계 원칙
    • 코드 설계를 이끌어내는 몇 가지 경험의 요약이다.
    • 코드 설계의 일반적인 방향을 나타내는 정신에 해당한다.
    • 디자인 패턴보다 일반적이다.
  • 디자인 패턴
    • 소프트웨어 개발에서 자주 접하는 일부 설계 문제에 대해 요약된 해결 방법 또는 사상들이다.
    • 다지인 패턴을 적용하는 주요 목적은 디커플링을 통해 코드의 확장성을 향상시키는 것.
    • 추상화 측면에서 설계 원칙은 디자인 패턴보다 더 추상적이고, 디자인 패턴은 더 구체적이며 구현하기 쉽다.
  • 코딩 규칙
    • 코드 가독성 문제를 해결한다.
    • 코딩 규칙은 디자인 패턴보다 더 구체적이고 코드 세부 사항에 더 중점을 두고 구현이 가능하다.
  • 리팩토링
    • 코드 품질 저하를 방지하는 효과적인 수단.
    • 객체지향 프로그래밍 패러다임, 설계 원칙, 디자인 패턴, 코딩 규칙에 대한 이론적 지식에 의존한다.

 

코드 품질에 대한 인식을 갖는게 매우 중요하다. 앞으로 코드를 작성하기 전에 항상 아래 문구를 생각해보자.
  1. 앞으로의 확장 요구 사항.
  2. 코드의 어느 부분이 변경될지.
  3. 코드의 어느 부분이 변경되지 않을 상태로 유지될지.
  4. 이렇게 코드를 작성하면 나중에 새로운 기능을 추가할 때 어렵게 되지 않을지.
  5. 코드의 가독성이 떨어지지 않을지.

위 항목들을 항상 생각하고 코드를 작성할 필요가 있다. 이렇게 항상 코드 품질을 의식한다면 고품질의 코드를 작성하는게 어렵지만은 않을 것이다.


1.4 과도한 설계를 피하는 방법

앞에서 우리는 코드 품질에 주의를 기울여야 하고, 코드를 작성하기 전에 코드 설계 단계를 무시하지 않아야 한다고 말했다.
물론 코드를 설계하지 않는 것은 좋지 않지만, 과도하게 설계하는 것도 좋지 않다.

 

코드를 과도하게 설계하고 디자인 패턴을 남용하는 것을 좋아하는 주니어 개발자들이 있다. 코드를 시작하기도 전에 코드 설계 작업에 오랜 시간을 할애한다. 간단한 요구 사항이나 단순한 코드의 경우 코드가 더 유연하고 향후 확장을 위한 견고한 기반이 되기를 바라면서 다양한 디자인 패턴을 적용하는 경우가 있다.

 

하지만 과도한 설계는 나중에 요구 사항이 변하지 않을 수도 있기 때문에, 이런 시도는 코드의 복잡성만 높일 뿐이다. 디자인 패턴의 남용을 피하는 방법에 대해 이야기 해본다.

1.4.1 코드 설계의 원래 의도는 코드 품질을 향상시키는 것이다

'원래 의도', 원래 의도는 결국 우리가 그 일을 하는 이유를 의미한다. 제품이 몇 번이고 업데이트되고, 몇 번이고 방향을 바꿔도 원래 의도는 바뀌지 않는게 일반적이다. 제품을 변형할지, 특정 기능을 구현해야 할지 말설여질 때 원래 의도를 생각해보면 결론을 내릴 수 있다.

 

디자인 패턴에서도 마찬가지로, 디자인 패턴은 방법일 뿐이며 이를 적용하는 궁극적인 목적인 원래 의도는 코드의 품질을 향상시키는 것,

즉 코드의 가독성, 확장성, 유지 보수성을 향상시키는 것이다.

 

코드를 설계할 때 우리는 우선적으로

  • 왜 이런 방식으로 설계하는지
  • 왜 이 디자인 패턴을 적용해야 하는지
  • 이것이 실제로 코드의 품질을 향상시킬 수 있는지
  • 코드 품질의 어떤 측명을 개선할 수 있는지
    생각해야 한다.

위 문제에 대해 명확하게 생각하기 어렵거나 이유가 터무니 없이 적다면 과도한 설계이며 설계를 위한 설계일 뿐이다.

1.4.2 코드 설계의 원칙은 앞에 있고, 뒤에 방안이 있다

코드를 제품으로 보면

  • 제품을 만들 때 제품의 페인 포인트가 어디인지, 사용자의 진짜 니즈가 무엇인지 먼저 생각하고 요구 사항에 맞는 기능을 개발해야지, 화려한 기능을 먼저 개발한 후 요구 사항을 끼워 맞추는 방식으로 개발하면 안된다.

코드 설계도 마찬가지이다. 먼저 가독성이 낮고 확장성이 떨어지는 등의 코드의 페인 포인트를 분석한 다음, 해당 포인트를 디자인 패턴과 설계 원칙을 사용해서 개선해야 한다.

 

특정 부분만을 바라보고 맹목적으로 이전에 봤던 어떤 디자인 패턴과 설계 원칙의 적용 시나리오와 유사하다고 적합성을 판단하지 않고 마음대로 적용해서는 안된다.

 

실제로 주니어 개발자들은 문제를 구체적으로 분석할 줄 모르며, 손에 망치를 들고 어떤 것이 못인지 확인한다.

어느 것이 올바른 것인지 알지 못한 채 각종 디자인 패턴을 적용해 보는것이다. 이렇게 코드를 작성하고 나서 자신이 작성한 매우 복잡한 코드를 보고 자기 만족에 빠지는데 가장 피해야할 자세이다.

1.4.3 코드 설계의 응용 시나리오는 복잡한 코드에 적용되어야 한다

일부 디자인 패턴 서적에서 제공하는 몇 가지 간단한 예제를 보고 이를 디자인 패턴의 일반적인 응용 시나리오라고 잘못 생각하는 경우가 있다.
이러한 오해와 함께 자신의 프로젝트에 맹목적으로 적용하고 복잡한 디자인 패턴을 사용하여 간단한 문제를 해결한다.
이것이 많은 주니어가 디자인 패턴을 배우고 나서 과도한 설계를 하는 주요 이유이다.

 

디자인 패턴을 하는 이유는

  • 디커플링
    • 더 나은 코드 구조를 사용하여 단일 책임을 위해 큰 코드 조각을 작은 클래스로 분할하여
      코드가 높은 응집도와 낮은 결합도의 특성을 충족하도록 하는 것이다.
  • 생성 디자인 패턴은 - 사용 코드에서 생성 코드를 분리하는 것이며
  • 구조 디자인 패턴은 - 다른 기능 코드를 분리하는 것이며
  • 행동 디자인 패턴은 - 다른 행동 코드를 분리하는 것이다.

디커플링의 주요한 목적은 코드의 복잡성을 처리하는 것이다.

따라서 복잡한 코드 문제를 해결하기 위해 디자인 패턴이 만들어진다. 그렇지 않다면 적용할 필요가 없다.

1.4.4 지속적인 리팩토링은 과도한 설계를 효과적으로 방지한다

디자인 패턴을 적용하면 코드 확장성을 향상시킬 수 있지만 동시에 코드 가독성이 나빠질 수 있다.

 

복잡한 설계를 한 번 도입하면 확장 요구사항이 없더라도 복잡한 설계를 삭제할 수 없고 이 복잡한 설계에 따른 작업을 항상 수행해야 한다.

실현 가능성이 낮은 미래의 요구 사항을 위해 처음부터 디자인 패턴을 도입하기 보다, 진짜 문제가 발생했을 때 이를 해결하기 위한 디자인 패턴을 사용하는 것을 고려해야 한다.

 

만약 디자인 패턴을 적용할지 애매모호 할때, 당분간 디지인 패턴을 사용하지 않으면 코드가 커지면서 변경해야할 코드가 많지 않을까 생각할 수 있다.
그렇지 않은 경우라면 도입하지 말고 KISS(Keep It Simple, Stupid) 원칙을 따르자.

1.4.5 특정 시나리오 외의 코드 설계에 대해 이야기하지 않는다

특정한 시나리오 없이 코드 설계가 합리적인지 아닌지 얘기하는 것은 의미가 없다.

 

모바일 게임 프로젝트의 경우 시장에서 받아들여질 것인지 여부가 매우 불확실하다. 따라서 일부 모바일 게임 프로젝트 개발의 경우 초기 단계에서 코드 설계와 품질에 많은 시간을 할애하지 않는다.


반면에, MMORPG 같은 대규모 게임을 개발한다면 프로젝트를 뒤집어 엎는 비용이 매우 높으므로 코드 품질이 매우 중요하게 된다. 그렇지 않으면 품질이 낮아서 버그가 너무 많으며 후기 단계에 유저 관리가 불가능해진다.

 

따라서 시나리오에 맞게 코드 설계에 대해 논의해야 한다.

 

코드 설계를 배울 때 우리는 문제 분석 능력과 문제 해결 능력의 훈련에 관심을 가져야 한다.
  • 코드를 읽을 때 코드의 장단점을 분석하고, 그 이유를 설명할 수 있어야 하고, 코드를 개선하는 방법도 알아야 한다.
  • 22가지 디자인 패턴 원리와 코드 구현을 외우더라도 이론적 지식만 있고
    실제 프로젝트 코드에서 구체적인 문제를 자세히 분석하는 능력이 없다면
    디자인 패턴을 남용하고 과도한 설계를 하게 된다.

 

End.

반응형
Comments
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday