본문 바로가기
Programming/Java, Spring

한 메서드에 오직 한 단계의 들여쓰기만 - 우아한테크코스 우테코 클린코드 #2

by Renechoi 2022. 10. 30.

 

이전글에 이어 우아한테크코스에서 요구하는 클린코드 원칙들을 살펴본다. 

 

검색을 하면서 이 규칙들이 <소트웍스 앤솔러지 : 소프트웨어 기술과 혁신에 관한 에세이, 마틴 파울러 수트웍스 지음, 옮긴이 다수, 위키북스> 의 책에서 주장된 내용이라는 것을 확인했다. 

 

2009년에 나온 책으로 아쉽게도 동네 도서관들에서는 발견할 수가 없었다. 

 

 

 
소트웍스 앤솔러지(위키북스 IT LEADERS 7)
내일을 준비하는 IT 리더들을 위한 'IT LEADERS' 시리즈, 제7권 『소트웍스 앤솔러지』. 미국에서 가장 주목받고 있는 시스템 통합ㆍ컨설팅 회사인 소트웍스의 최고 책임 과학자 마틴 파울러 등이 함께 모여 저술한 것이다. 이 책은 애자일 분야의 최고수로 가득한 소트웍스가 가진 탁월한 문화를 에세이 형식으로 기술한다. 소트웍스 사람들이 경험하고 적용하여 얻은 지혜와 통찰력을 바탕으로 소프트웨어 기술과 혁신에 대해 이야기하고 있다. 소프트웨어 개발 실무에 대해서도 중점적으로 다룬다.
저자
마틴 파울러 외
출판
위키북스
출판일
2009.02.26

 


 

한 메서드에 오직 한 단계의 들여쓰기(indent)만 허용했는가? 

 

이와 같은 규칙의 근거는 책에 따르면 다음과 같다. 

 

... 거대한 메서드는 응집력이 떨어진다. ... 코드가 500줄짜리 괴물들로 어질러져 있으면 의욕이 꺾이기 쉽다. ... 한 메서드 안에 중첩된 제어구조가 있다면 다단계의 추상화를 코드로 짠 것이며, 고로 한 가지 일을 하고 있다는 뜻이다. 

그러니까 말하자면 

1) 긴 코드는 응집력이 떨어짐

2) 중첩된 제어구조는 긴 코드일 확률이 높음 

3) 한 메서드는 한 가지 일을 하는 것이 바람직함 

4) 짧을 수록 그 목표 성취가 가능함 

 

이를 위해서 라는 것이다. 

 

정확히 한 가지 일을 하는 메서드들로 작업을 하면 코드가 달라지기 시작한다. 애플리케이션의 각 단위가 더 작아짐에 따라 재사용의 수준은 기하급수적으로 상승하기 시작한다. ... 주어진 컨텍스트에서 단일 객체의 상태를 관리하는 3줄짜리 메서드라면 많은 다양한 컨텍스트에서 쓸 수 있다. 

 

메서드가 한 가지 일을 맡아서 하게 되면 짧아지는 효과로 인해 가독성이 좋아지는 것은 물론 유지 보수에도 수월해진다. 

 

오직 한 단계의 들여쓰기란 어떤 것인가 하면 다음과 같다. 

 

책에서 제시된 예시이다. 

 

 

 

< 원칙을 벗어난 상황> 

class Board {
   ...
   String board() {
      StringBuffer buf = new StringBuffer();
      for (int i = 0; i < 10; i++) {
         for (int j = 0; j < 10; j++)
            buf.append(data[i][j]);
         buf.append("\n");
      }
      return buf.toString();
   }
}

 

 

<리팩토링하여 원칙을 지킨 상황> 

class Board {
   ...
   String board() {
      StringBuffer buf = new StringBuffer();
      collectRows(buf);
      return buf.toString();
   }
 
   void collectRows(StringBuffer buf) {
      for (int i = 0; i < 10; i++)
         collectRow(buf, i);
   }
 
   void collectRow(StringBuffer buf, int row) {
      for (int i = 0; i < 10; i++)
         buf.append(data[row][i]);
      buf.append("\n");
   }
}

 

 

이와 같은 리팩토링으로 인덴트를 1단계만 남기면 가독성이 좋아지고 기능이 분리되는 것을 볼 수 있다. 

 

 

작성한 코드 중에 아래와 같이 indent depth가 중첩되어 있는 코드가 있다. 

 

 

public static String solution(String cryptogram) {

   String answer;
   String regex = "(\\w)(\\1+)";
   Pattern patternRegex = Pattern.compile(regex);

   while (true) {
      Matcher patternMatcher = patternRegex.matcher(cryptogram);
      StringBuilder stringBuilder = new StringBuilder();

      while (patternMatcher.find()) {
         patternMatcher.appendReplacement(stringBuilder, "");
      }
      patternMatcher.appendTail(stringBuilder);
      cryptogram = String.valueOf(stringBuilder);

      System.out.println(cryptogram);
      patternMatcher = patternRegex.matcher(cryptogram);
      if (!patternMatcher.find()) {
         answer = cryptogram;
         break;
      }
   }
   return answer;
}

 

이 코드의 로직은 

 

1. regex 패턴을 만들고 

2. 패턴과 검증할 문자열을 비교하여 찾고 

3. addReplacement() 함수를 통해 제거한 후 stringBuilder를 통해 재구축을 하고 

4. while문이 끝난 이후 마지막 tail 처리를 위해 한 번 더 stringBuilder로 구축을 해주고 

5. 마지막으로 string 변환을 해주고 

6. 여전히 중복이 있으면 계속 반복이 돌아가면서 없을 때까지 반복하는

 

로직이다. 

 

여기서 중복 제거의 로직은 별도의 메소드를 만들어 다음과 같이 리팩토링 할 수 있을 것이다. 

 

private static String removeDuplication(Matcher patternMatcher) {
   String cryptogram;
   StringBuilder stringBuilder = new StringBuilder();
   
   while (patternMatcher.find()) {
      patternMatcher.appendReplacement(stringBuilder, "");
   }
   
   patternMatcher.appendTail(stringBuilder);
   cryptogram = String.valueOf(stringBuilder);
   
   return cryptogram;
}

 

그러면 다음으로 while문을 끝내줄 조건을 설정하는 if문이 2단계의 depth로 존재한다. 

 

이 부분은 메소드로 빼줄 수가 없는데 break을 end point를 while문 안에서 써주어야 반복을 종료시킬 수 있기 때문이다. 

 

. . . 

 

if 문을 없애고 break 시킬 수 있는 다른 방법을 고민중인데 

 

본질적으로 loop 안에서 끊어주는 조건을 설정하고 break를 해주어야 탈출이 가능한 상황이다. 

 

. . .

 

다른 방법을 찾아보면서 메서드를 일단 나누어 보았고 

 

if로 break을 처리하는 다른 방법은 찾지 못해 1줄로 처리해보았다. 

 

 

public static String solution(String cryptogram) {
   String answer;
   Matcher patternMatcher;

   while (true) {
      boolean doesDuplicationExists = checkDuplication(cryptogram);
      patternMatcher = setMatcher(cryptogram);
      cryptogram = removeDuplication(patternMatcher);

      if (!doesDuplicationExists) { break; }

   }

   answer = cryptogram;

   return answer;
}

 

 

반응형