Skip to main content

5장 형식 맞추기

1. 📌 핵심 개념 정리

✅ 요약하기

1. 코드 형식의 목적
  • 코드 형식은 의사소통의 일환이다. 오늘 구현한 기능이 다음 버전에서 바뀔 확률은 매우 높지만, 코드의 가독성은 앞으로 바뀔 코드의 품질에 지대한 영향을 미친다.
  • 원래 코드는 사라질지라도 개발자의 스타일과 규율은 지속되며, 유지보수와 확장성에 결정적인 역할을 한다.
2. 적절한 행 길이 및 파일 크기
  • 조사된 여러 프로젝트(JUnit, FitNesse, testNG, Time and Money, JDepend, Tomcat 등)에 따르면 평균 파일 길이는 약 65줄이며, 가장 긴 파일은 400줄, 가장 짧은 파일은 6줄이다.
  • 특히 JUnit, FitNesse, Time and Money 같은 프로젝트는 파일 크기가 약 200줄 정도로 작았으나, 충분히 큰 시스템을 구축할 수 있음을 보여준다.
  • 엄격한 규칙은 없지만, 이해하기 쉽도록 파일과 함수의 길이를 가능한 짧게 유지하는 것이 바람직하다.
3. 개념 분리와 빈 행 사용
  • 코드는 왼쪽에서 오른쪽, 위에서 아래로 읽힌다. 각 행은 하나의 수식이나 절을 나타내며, 행의 묶음은 하나의 개념을 표현한다.
  • 빈 행은 새로운 개념의 시작을 알리는 시각적 단서로, 서로 다른 개념이나 기능을 분리하여 가독성을 높인다.
4. 세로 밀집도와 수직 거리
  • 세로 밀집도는 서로 관련성이 높은 코드 행들을 가깝게 배치하는 것을 의미한다. 관련 있는 코드가 멀리 떨어져 있으면 코드를 이해하기 어렵다.
  • 수직 거리 또한 관련 개념이 서로 다른 파일에 분산되지 않도록 하여, 하나의 파일 내에 밀접하게 배치하는 것이 좋다.
  • 이러한 원칙은 불필요한 protected 변수 사용을 줄이는 데에도 도움을 준다.
5. 변수 및 함수 선언 위치
  • 지역 변수는 해당 변수가 사용되는 위치에 최대한 가까이 선언한다. 예를 들어, 루프 제어 변수는 루프 내부에 선언하는 것이 바람직하다.
  • 멤버(인스턴스) 변수는 클래스의 맨 처음에 선언하여, 클래스 내에서 일관된 위치에 모아두는 것이 좋다.
  • 종속 함수는 다른 함수를 호출하는 경우, 호출하는 함수(상위 개념)를 먼저 배치하고 호출되는 함수(하위 개념)를 그 아래에 배치하여 독자가 흐름을 예측할 수 있도록 한다.
  • 가장 중요한 개념과 함수는 상단에 배치하여 가독성을 높인다.
6. 개념적 유사성 (친화도)
  • 기능이나 역할이 비슷한 함수나 코드 블록은 서로 가까이 배치한다.
  • 예를 들어, 단위 테스트 관련 함수들(예: assertTrue, assertFalse)은 명명법과 기능이 유사하여 서로 인접하게 배치하는 것이 좋다.
    public class Assert {
        static public void assertTrue(String message, boolean condition) {
            if (!condition)
                fail(message);
        }
    
        static public void assertTrue(boolean condition) {
            assertTrue(null, condition);
        }
    
        static public void assertFalse(String message, boolean condition) {
            assertTrue(message, !condition);
        }
    
        static public void assertFalse(boolean condition) {
            assertFalse(null, condition);
        }
    }
    
7. 가로 형식 맞추기
  • 한 행은 가능한 짧게 유지하자. 조사에 따르면 한 프로젝트의 평균 행 길이는 약 45자 정도로 나타났으며, 일반적으로 100~120자 내외가 적당하다.
  • 가로 공백은 연산자와 피연산자 사이, 인수 목록에서 쉼표 뒤에 사용하여 각 요소의 구분을 명확하게 한다.
    public static double root2(int a, int b, int c) {
        double determinant = determinant(a, b, c);
        return (-b - Math.sqrt(determinant)) / (2 * a);
    }
    
  • 함수 이름과 괄호 사이에는 공백을 넣지 않아 함수와 인수의 밀접함을 나타낸다.
  • 과도한 가로 정렬은 오히려 의도를 흐릴 수 있으므로, 필요할 때만 사용하도록 한다.
8. 들여쓰기
  • 들여쓰기는 코드의 계층 구조와 범위를 명확히 하는 데 필수적이다.
  • 복잡한 if문, while문, 짧은 함수 등에서도 일관된 들여쓰기를 유지해야 한다.
  • 들여쓰기를 무시하는 유혹이 들더라도 항상 기본 규칙으로 돌아가 들여쓰기를 올바르게 적용하자.
    • 개선 전
      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String pathInfo = req.getPathInfo();
      
      if (pathInfo != null) {routeGetMethod(req, resp); return;}
      
      List<GameItem> gameItemList = youtubeService.readAllYoutubeItem();
      responseYoutubeListAsJson(resp, gameItemList);}
      
    • 개선 후
      @Override
      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
          String pathInfo = req.getPathInfo();
      
          if (pathInfo != null) {
              routeGetMethod(req, resp);
              return;
          }
      
          List<GameItem> gameItemList = youtubeService.readAllYoutubeItem();
          responseYoutubeListAsJson(resp, gameItemList);
      }
      
9. 팀 규칙
  • 개별 프로그래머마다 선호하는 스타일은 다를 수 있으나, 팀 내에서는 하나의 규칙에 합의하여 모두가 일관된 코딩 스타일을 유지해야 한다.
  • IDE의 서식 지정 도구(예: IntelliJ의 ⌥⌘L 또는 Ctrl+Alt+L)를 활용해 팀 표준에 맞게 코드를 자동 정렬하는 것도 좋은 방법이다.

2. 🤔 이해가 어려운 부분

🔍 질문하기

1. 종속 함수
  • 어려웠던 부분:
    단어 자체가 생소하여 처음에는 이해하기 어려웠다.
  • 이해한 점:
    종속 함수란 다른 함수에 직접적으로 의존하여 독립적으로 동작할 수 없는 함수를 의미한다.
    함수 간의 종속성이 강하면 코드의 유지보수성이 낮아지고, 재사용성과 테스트가 어려워진다.

2. 개념적 유사성 (친화도)
  • 궁금한 점:
    개념적 유사도가 높다는 것이 정확히 어떤 요인들을 의미하는가?
  • 해결:
    개념적 유사성은 아래와 같은 요인들에 의해 결정된다.
    • 기능이나 동작이 유사하다.
    • 같은 개념적 영역에 속한다.
    • 함께 변경될 가능성이 높다.
    • 서로 데이터를 공유하는 경향이 있다.

3. 📚 참고 사항

📢 논의하기

1. 관련 자료 공유
  • 추가 자료:
  • 참고 자료는 각 팀 혹은 프로젝트의 코드 표준을 정의하는 데 참고가 될 수 있다.

2. 논의 주제
  • 친화도가 높은 요인:
    개념적 유사성을 결정하는 추가 요인은 무엇일까?
    서로 다른 모듈 간 데이터 공유, 에러 처리 방식 등도 고려할 수 있다.
  • 빈 반복문 처리:
    때로는 빈 while문이나 for문을 사용해야 하는 경우가 있다.
    이러한 경우 어떻게 적절히 들여쓰고 표현할지에 대해 논의해볼 필요가 있다.