요즘IT
위시켓
새로 나온
인기요즘 작가들컬렉션
물어봐
새로 나온
인기
요즘 작가들
컬렉션
물어봐
개발
AI
IT서비스
기획
디자인
비즈니스
프로덕트
커리어
트렌드
스타트업
서비스 전체보기
위시켓요즘IT
고객 문의
02-6925-4867
10:00-18:00주말·공휴일 제외
[email protected]
요즘IT
요즘IT 소개작가 지원
기타 문의
콘텐츠 제안하기광고 상품 보기
요즘IT 슬랙봇크롬 확장 프로그램
이용약관
개인정보 처리방침
청소년보호정책
㈜위시켓
대표이사 : 박우범
서울특별시 강남구 테헤란로 211 3층 ㈜위시켓
사업자등록번호 : 209-81-57303
통신판매업신고 : 제2018-서울강남-02337 호
직업정보제공사업 신고번호 : J1200020180019
제호 : 요즘IT
발행인 : 박우범
편집인 : 노희선
청소년보호책임자 : 박우범
인터넷신문등록번호 : 서울,아54129
등록일 : 2022년 01월 23일
발행일 : 2021년 01월 10일
© 2013 Wishket Corp.
로그인
요즘IT 소개
콘텐츠 제안하기
광고 상품 보기
개발

소프트웨어 개발자를 위한 코드를 작게 만드는 방법

길벗
8분
10시간 전
1.8K
에디터가 직접 고른 실무 인사이트 매주 목요일에 만나요.
newsletter_profile0명 뉴스레터 구독 중

복잡한 코드는 단순한 코드보다 읽기 어렵고, 이해하기 어렵습니다. 매우 복잡한 코드는 실수를 저지르기 더 쉽기 때문에 버그가 있을 가능성도 높기에 코드를 단순하고 작게 만들어야 합니다.

 

​긴 클래스와 메서드는 우리 생각보다 더 자주, 자연스럽게 발생합니다. 새로운 코드의 영향을 살펴보고 코드를 다시 디자인하기보다 시스템의 기존 클래스를 열어 새로운 코드를 추가하는 것이 더 쉬운 것이죠. 이번 글에서는 코드를 작게 만드는 데 도움이 되는 패턴에 대해 자세히 알아보겠습니다.

​

 

​

코드 단위를 작게 만들라

정보 시스템의 비즈니스 규칙은 지속적으로 진화하고 점점 더 복잡해지고 있습니다. 비즈니스 규칙을 빠르게 반영할 때 흔히 쓰이는 임시방편적인 방식은 기존 메서드에 코드를 덧붙이거나 클래스에 메서드를 추가하는 식으로 코드를 추가하는 것입니다.

 

그러다 보면 어느새 클래스가 길어지고, 이를 유지보수하는 데 엔지니어의 많은 에너지가 소모됩니다. 아무리 잘 디자인한 메서드나 클래스라도, 분량이 2,000줄이 되면 이해하기 어려운 건 마찬가지입니다.

 

따라서 코드 복잡도를 줄이는 주요 전략은 코드 단위의 크기를 줄이는 것이죠.

 

​복잡한 코드를 작은 단위로 나누는 것이 좋은 접근법이라는 사실을 설득하는 건 어렵지 않습니다. 작은 단위는 항상 큰 단위보다 낫고, 그 이유는 다음과 같습니다.

​

  • 클래스나 코드 단위가 작으면 개발자가 읽어야 할 코드의 양이 줄어든다.
    메서드가 다른 클래스를 호출하는 구조라면 정말 필요할 때만 다른 클래스의 코드를 열게 됩니다.
  • 작은 코드 단위는 처음부터 확장 가능성을 제공한다.
    모델링하고 나면 필요한 경우, 각 조각을 대체할 수 있습니다.
  • 테스트 가능성이 향상된다.
    클래스가 작으면 개발자가 비즈니스 로직의 특정 부분에 대해독립적인 단위 테스트를 작성할지 여부를 쉽게 결정할 수 있습니다.

 

작은 단위가 큰 단위보다 항상 더 좋다

 

실제로는 상단의 그림처럼 작은 메서드나 클래스를 사용해 복잡한 동작을 구축해야 합니다. 그림의 모든 클래스와 메서드는 작고, 한 가지 작업만 수행합니다. 따라서 누구든 쉽게 코드를 이해할 수 있고, 테스트도 쉬운 것이죠.

​

지금부터는 언제 코드를 메서드와 클래스로 나눌지 정하는 몇 가지 휴리스틱을 살펴보겠습니다. 또한 코드를 작은 단위로 나누고 싶지 않은 예외적인 경우에 대해서도 설명하겠습니다.

​

복잡한 메서드를 비공개 메서드로 나누기

큰 메서드를 작은 메서드 여럿으로 나누는 것은 복잡성을 줄이는 훌륭하고 쉬운 방법입니다. 큰 메서드 안에서 비공개 메서드로 이동할 수 있는 코드 조각만 찾으면 됩니다.

​

응집도란?

응집도가 높은 컴포넌트(클래스나 메서드)는 시스템 안에서 하나의 명확한 책임을 가집니다. 즉, 이런 컴포넌트는 한 가지 일만 하는데, 이러한 클래스는 의심할 여지 없이 여러 가지를 하는 클래스보다 작습니다. 응집도 높은 코드를 추구하면 자연스럽게 단순한 코드를 추구하게 됩니다.

 

비공개 메서드란?

비공개 메서드는 자신이 선언된 클래스 내부에서만 호출이 가능합니다.

코드 일부를 분리하고 싶지만, 클래스 밖에서 그 코드를 보거나 호출하기를 원하지 않을 때는 비공개 메서드가 완벽한 해답입니다.

 

새로운 비공개 메서드를 도입하는 게 적합한지, 코드 세그먼트가 독립된 단위가 될 수 있는지를 판단하는 가장 좋은 방법은 다음 5가지를 평가하는 것입니다. 평가 항목 5가지를 알아보고, 직접 적용해 보세요!

 

  • ​새 비공개 메서드의 목적을 설명하는 명확한 이름을 부여할 수 있는가?
  • 새 메서드가 응집력 있고 작으며, 공개 메서드가 쉽게 사용할 수 있는 동작을 수행하는가?
  • 새 메서드가 많은 파라미터나 클래스의 의존성에 의존하는가? 개발자가 새 메서드의 요구사항을 빠르게 이해할 수 있을 만큼 간결한가?
  • 메서드가 호출될 때 구현을 살펴보지 않아도 이름만으로 기능을 충분히 설명할 수 있는가?
  • 이 비공개 메서드를 정적 메서드로 만들 수 있는가?

​

복잡한 코드 단위를 다른 클래스로 옮기기

비공개 메서드가 추출한 코드를 두기에 이상적인 장소가 아닐 수도 있습니다. 특히 코드가 큰 단위의 주요 목표와 관련이 없다면 그렇습니다. 따라서 코드를 비공개 메서드 대신 다른 클래스로 이동할지 결정할 때는 다음을 고려해 보세요!

 

  • ​이 코드 조각이 클래스의 나머지 부분과는 다른 작업을 하는가?
  • 이 코드가 도메인 안에서 별도의 이름과 클래스가 필요할 정도로 중요한 일을 하는가?
  • 이 코드 조각을 독립적으로 테스트하고 싶은가?
  • 이 코드 조각이 다른 코드들이 의존하기를 원하지 않는 클래스에 의존하는가?

 

새로운 클래스로 동작을 옮길 때 가장 어려운 일은 새로운 클래스의 이름을 짓는 것입니다. 만약 좋은 이름을 빨리 떠올릴 수 있고, 새 클래스가 어떤 패키지에 위치해야 하는지 정확히 알 수 있다면 바로 새 클래스로 옮겨야 합니다.

 

코드를 작은 단위로 나누지 말아야 할 때

모든 규칙에는 예외가 있습니다. 언제 코드를 유지해야 할까요?

 

  • ​둘 이상의 퍼즐 조각이 독립적으로 존재할 수 없을 때, 강제로 분리하면 메서드 시그니처가 복잡해질 수 있습니다.
  • 퍼즐 조각이 교체될 가능성이 낮을 때
  • 해당 조각만 완전히 따로 떼어 테스트할 만한 가치가 없을 때
  • 퍼즐 조각의 개수가 몇 개 없을 때

 

이러한 때는 언제나 그렇듯 실용적인 접근이 중요하니 참고해두는 것이 좋습니다.

​

리팩터링하기 전에 전체적으로 살펴보기

더 복잡한 리팩터링 작업에서는 리팩터링이 끝난 최종 코드가 어떤 모습일지 상상해 봅시다. 3가지를 바탕으로 평가를 하면서 최종 결과를 머릿속에 그릴 수 없다면, 화이트보드나 종이에 그림을 그려보는 것이 좋습니다.

 

  • ​리팩터링 후 클래스는 어떤 모습이며, 서로 어떻게 연관되는가?
  • 미래의 모습이 마음에 드는가?
  • 디자인의 문제점이 보이는가?

 

예제 : 직원 데이터 임포트하기

피플그로우!에서는 직원 데이터를 일괄적으로 가져옵니다.

 

관리자는 직원의 이름, 이메일, 역할, 입사일이 포함된 CSV 파일을 업로드 하는데요. 직원이 데이터베이스에 이미 있으면 피플그로우!는 그 직원의 정보를 갱신합니다.

​

초기 구현은 코드 2-1의 코드와 비슷하며, 이 코드는 서드파티 라이브러리를 사용해 CSV를 파싱합니다. 그 후 시스템은 임포트한 데이터의 각 직원에 대해 데이터베이스에 새로운 직원을 생성하거나 데이터베이스에 있던 기존 직원을 갱신합니다.

​

코드는 복잡하지 않지만, 이는 단지 상황을 보여주기 위한 예시일뿐입니다. 실제 소프트웨어 시스템에서는 임포트 서비스가 수백 줄의 코드로 이뤄질 수도 있어요.

​

예시 코드 : 작게 나눠야 하는 큰 메서드

 

상단 예시의 import 메서드는 너무 많은 일을 하고 있어 코드에서 길을 잃기 쉽습니다. 이제 일부 코드를 이동시켜서 복잡성을 줄여야 합니다.

​

​먼저 CSV 파싱 로직을 별도의 클래스로 옮겨보겠습니다. 이 코드 몇 줄은 실제 작업을 CsvParserLibrary 클래스에 위임할 뿐이지만, 책임이 다르므로 독립된 클래스에도 잘 어울립니다.

​

또한, EmployeeimportCSVParser 클래스는 EmployeeParsedData의 리스트를 반환하는 parse 메서드를 제공합니다.

 

예시 : CSV 파서를 자체 클래스에 위치시키기

​

ImportEmployeeService 클래스를 개선할 수 있는 방법은 아직 더 있습니다.

 

import 메서드가 작업 흐름만 제어하도록 만들고, 실제 동작은 다른 클래스나 메서드에 구현하면 됩니다. 예를 들어, if 문에서 새로운 직원을 생성하거나 갱신하는 두 코드 블록을 비공개 메서드로 추출 가능합니다.

​

​이 두 메서드를 다른 클래스로 옮기게 될 것 같지는 않지만, 이 메서드들은 서로 관련이 있어 보이므로 현재로서는 ImportEmployeesService에 남겨두는 것이 적절해 보입니다.

​

​

 

상단의 코드를 보면 클래스는 훨씬 작아졌고, 메서드들의 응집도는 더 커진 것을 확인 가능합니다. import 메서드는 작업을 조정하는 역할만 하고 있습니다.

​

​EmployeeImportCSVParser를 호출해 파싱 결과를 얻고, EmployeeRepository를 호출해 직원이 데이터베이스에 존재하는지 확인한 후 이에 따라 적절한 동작을 결정해야 합니다. 이때 각각의 동작은 별도의 비공개 메서드가 처리합니다.

​

이제 개발자가 이 클래스를 읽고 역할을 파악하는 데 걸리는 시간이 훨씬 줄어들었습니다. 각각의 작은 코드 블록을 이해하는 데 걸리는 시간이 줄기도 하였습니다. 이처럼 '무엇'을 담당하는 메서드와 '어떻게'를 구현하는 다른 메서드로 나누는 것은 좋은 프랙티스입니다.

​

ImportEmployeesService는 이제 EmployeeRepository와 EmployeeImportCSVParser를 생성자를 통해 받아야 합니다. 즉, 코드의 다른 부분에서 이 클래스를 인스턴스화해야 한다는 의미입니다. 의존성은 생성자를 통해 전달받는 것이 좋으니 참고해 주세요!

​


 

  • 이 글은 길벗에서 출간된 책 <객체지향 시스템 디자인 원칙>에서 발췌·편집한 글입니다. 원문은 [여기]에서 볼 수 있습니다.

 

©️요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.