Skip to content

Latest commit

 

History

History
414 lines (249 loc) · 17.3 KB

Chapter05.md

File metadata and controls

414 lines (249 loc) · 17.3 KB

5장 가독성 높은 코드를 작성하라

가독성은 본질적으로 주관적인 것이며 그것이 정확히 무엇을 의미하는지 확실하게 정의하기는 어렵다.

논의 사항

명명된 상수를 사용하는 것이 좋을까요,, 공급자 함수를 사용하는게 좋을까요?

개개인마다 스타일의 차이가 있을 수 있지만, 선호하시는 스타일이나 장점 단점이 있을까요?

++ 익명함수를 사용하신 적의 예를 알려주실 수 있나요?

책의 내용 및 정리

핵심주제

  • 코드가 그 자체로 설명이 되도록 하기 위한 방법
  • 다른 사람들에게 코드의 세부적 내용을 명확하게 함
  • 언어의 기능을 사용할 때 그에 합당한 이유를 가져야 함

서술형 멍칭 사용

이름을 붙이는 것은 그것이 스스로 설명되는 방식으로 언급함으로써 읽기 쉬운 코드 작성을 위한 기회이기도 하다.

책에서 주어지는 극단적인 예제처럼.. 코딩테스트용으로 짠 코드들은 다시 읽기 매우 힘들다는 단점이 있다.

해결방법중의 하나로 서술적인 이름을 짓는 것이다.

배워야하는 시선은 team.containsPlayer(playName)의 호출이 Team클래스를 확인하지 않더라도 동작을 이해할 수 있다는 점..

호출 자체로 클래스를 유추하거나 이해하게 세분화되어 있거나 이름 자체가 서술적이면 보는 사람도 편하게 이해가 된다는 점이다.

주석문의 적절한 활용

코드 내에서 주석문이나 문서화는 다음과 같은 목적을 수행할 수 있다.

  • 코드가 무엇을 하는지 설명
  • 코드가 왜 그 일을 하는지 설명
  • 사용 지침 등 기타 정보 제공

주석을 사용하기 이전에 주의해야하는 점은 클래스같은 단위가 큰 코드가 무엇을 하는지 요약하는 높은 수준에서의 주석문은 유용하지만, 메서드,변수등 한줄 씩 주석을 다는 행위는 조심해야 한다.

앞서 다룬 서술적인 명칭을 사용한다면 해당 코드는 그 자체로 설명이 된다.

코드의 기능을 설명하기 위해서 낮은 층위에 주석을 많이 추가하는 행위 자체가 가독성을 떨어진다.

그럼에도 낮은 층위에서 사용해야하는 문제들은 적절하게 상식에 맞게 사용하는 것이 중요

중복된 주석문의 유해성

코드자체로 해석이 기능이 설명되는 코드가 있다고 했을 때 해당 코드에 주석을 달아놨다면 해당 주석은 중복된 것이라고 본다.

중복된 주석의 문제는 다음과 같다.

  • 개발자는 주석문을 유지보수해야 한다. 코드를 변경하면 주석문 역시 수정해야 한다.
  • 코드를 지저분하게 만들어서 가독성을 떨어트린다.

따라서 코드자체가 기능을 설명할 수 있도록 작성하는 것이 좋다.

주석문으로 가독성 높은 코드를 대체할 수 없다.

쉽게 함정에 빠질 수 있는 문제로 코드자체의 가독성이 부족해서 주석문이 필요할 수 있다.

하지만 더 좋은 접근법은 코드자체의 품질을 높이는 것이다.

주석문은 코드의 이유를 설명하는 데 유용하다.

코드자체로 설명하기 어려운 것에는 이유(왜)가 있다.

다른 개발자가 배경 상황없이 코드를 봤을 때 코드의 존재이유를 설명하기엔 주석이 매우 적절하다.

  • 제품 또는 비지니스 의사 결정
  • 이상하고 명확하지 않은 버그에 대한 해결책
  • 의존하는 코드의 예상을 벗어나는 동작

주석문은 유용한 상위 수준의 요약 정보를 제공할 수 있다.

코드가 무슨 일을 하는지 설명하는 주석문과 문서는 마치 책을 읽을 때 줄거리 같다.

  • 책의 모든 페이지 단락 앞에 줄거리가 있다먼 성가시고 읽기 어려운 책
  • 책의 목차부분이나 장의 시작부분에 간략한 요약을 보여주면 매우 유용할 책

아래의 경우가 주석을 상위수준에서 다루는 것은 다음과 같은 이점이 있다.

  • 클래스가 수행하는 작업 및 다른 개발자가 알고 있어야 할 중요한 세부 사항을 개괄적을 관리하는 문서
  • 함수에 대한 입력 매개변수 또는 기능을 설명하는 문서
  • 함수의 반환값이 무엇을 나타내는지 설명하는 문서

그럼에도 개발자들 대부분은 문서를 확인하지 않기 때문에 주석문에 의지하지 않는 편이 바람직하다.

코드 줄 수를 고정하지 말라

일반적으로 코드베이스의 코드 줄 수는 적을수록 좋다.

코드줄이 많다는 것은 코드가 지나치게 복잡하거나, 기존 코드를 재사용하지 않고 있음을 나타내는 신호일 수 있다.

또한 코드 줄이 많으면 읽어야하는 양이 늘어나기 때문에 인지부화가 올 수 있다.

그러나 이러한 주장때문에 줄 수를 극단적으로 줄여버리는 사례가 종종 발생한다.

코드 줄 수는 우리가 실제로 신경 쓰는 것들을 간접적으로 측정해줄 뿐이다.

따라서 1장에서 다룬 내용을 명심해야 한다.

  1. 이해하기 쉽다.
  2. 오해하기 쉽다.
  3. 실수로 작동이 안 되게 만들기 어렵디.

간결하지만 이해하기 어려운 코드는 피하라

일명 닌자코드..

코드의 길이를 극단적으로 줄이고 로직을 몰아넣음으로 전체적인 줄 수를 줄일 수 있지만 읽기는 매우 어렵다.

  • 다른 개발자는 이 단 한 줄의 코드에서 많은 세부사항을 도출하기 위해 많은 노력을 기울여야 한다.
  • 따라서 수정이 매우 힘들다.

클래스를 세분화하는 것과 마찬가지로 코드자체도 모듈화, 가독성을 위해 더 많은 줄이 필요하더라도 가독성이 높은 코드를 작성하라

일관된 코딩 스타일을 고수하라

문법적으로 올바른 문장을 쓰려면 지켜야 할 규칙이 있디. 이에 더해 잘 읽히는 문장을 쓰기 위해 따라야 할 문체에 관한 지침들도 있다.

다양한 언어에서 제공하는 스타일은 제각각이며, 회사나 팀마다 스타일이 다르다.

매번 다른 스타일을 사용해가며 일을 하게 되면 과거 작성한 다른 코드와 대소문자의 레벨차이가 발생할 수 있다.

깊이 중첩된 코드를 피하라

일반적으로 코드는 다음과 같이 서로 중첩되는 블록으로 구성된다.

  • 함수가 호출되면 그 함수가 실행되는 코드가 하나의 블록이 된다.
  • if문의 조건이 참일 때 실행되는 코드는 하나의 블록이 된다.
  • for 루프의 각 반복 시 실행되는 코드는 하나의 블록이 된다.
if (...)
{
  for (...)
  {
    if(...)
    {
      ... 
    }
  }
}

중첩이 깊은 경우

깊이 중첩된 코드는 읽기 어려울 수 있다.

if(...)
{
  return ...;
} else {
  if (...)
  {
    return ...;
  } else {
    return ...;
  }

  retur ...;;
}

중첩을 최소화하기 위한 구조 변경

앞선 코드의 중첩을 피해하기 위해 논리를 재구성하는 것은 쉬울 때가 많다.

if (...)
{
  return ...;
}
if (...)
{
  return ...;
}
if (...)
{
  return ...;
}
return ...;

중첩은 너무 많은 일을 한 결과물이다.

중첩이 너무 많은 코드의 경우 많은 일을 한 함수가 너무 몰아서 하고 있음을 의심해야 한다.

따라서 더 작은 함수로 나누면 문제를 해결할 수 있다.

더 작은 함수로 분리

2장에서 다룬 하나의 함수가 너무 많은 일을 하면 추상화 계층이 나빠진다는 점을 기억하라.

중첩이 없더라도 많은 일을 하는 함수를 더 작은 함수로 나누는 것은 여전히 바람직 하다.

함수 호출도 가독성이 있어야 한다.

어떤 함수의 이름이 잘 명명되면 그 함수가 무슨 일을 하는지 분명하지만 이름을 잘 지어졌더라도 함수의 인수가 무엇을 위한 것이고, 무슨 역할을 하는지 명확하지 않다면 함수 호출 자체가 이해되지 않을 수 있다.

많은 함수 인수

기본적으로 함수 호출은 인수의 개수가 늘어나면 이해하기 힘들어진다.

함수의 인수의 개수가 많다는 것은 근본적인 문제를 나타낸다.

추상화 계층을 적절하게 정의하지 않았거나, 코드가 모듈화되지 않았음을 의미

매개변수는 이해하기 어려울 수 있다.

sendMessage("hello", 1, true)

void sendMessage(String message, int priority, Boolean allowRetry){

}

함수 호출 시 각 인수의 값이 무엇을 의미하는지 알려면 함수 정의를 확인해봐야 한다.

함수 정의가 완전히 다른 파일에 있거나, 수백 줄 떨어져 있다면 이것은 상당히 힘든 작업일 수 있다.

명명된 매개변수 사용

다양한 언어에서 지원하는 명명된 매개변수 또는 선택적 매개변수를 활용하여 가독성을 높일 수 있다.

서술적 유형 사용

위의 정수형이나 불리언값은 어떤 종류의 값이라도 의미할 수 있기 때문에 서술적이지 않다.

서술적 유형을 위해서 특정 유형을 만들어서 매개변수들로 나타내는 바를 설명하는 것이다.

  • 클래스: 메시지의 우선순위를 클래스로 표현한다.
  • 열거형: 재시도 정책은 불리언 대신 두 가지 옵션이 있는 열거형을 사용한다.
class MessagePriority{
  ...
  MessagePriority(int priority) { ... }
  ...
}

enum RetryPolicy {
  ALLOW_RETRY,
  DISALLOW_RETRY
}

void sendMessage(
  String message,
  MessagePriority priority,
  RetryPolicy retryPolicy)
  {
    ...
  }
)

---  

sendMessage("hello", new MessagePriority(1), RetryPolicy.ALLOW_RETRY);

때로는 훌룡한 해결책이 없다.

사용하는 언어가 명명된 매개변수를 지원하지 않는경우, 사각형의 모서리위치를 매개변수로 받아야 한다면 호출 순서를 헷갈릴 가능성이 있다.

주석을 사용하여 생성자 아래에 크기를 적을 수 있지만 값이 수정되는 경우 주석또한 반영해야 하기 때문에 만족스러운 해결책이 아니다.

IDE는 경우

IDE에서 지원하는 기능은 백그라운드 작업을 통해 함수 정의를 미리 찾고 매개변수의 이름을 표시해준다.

// 실제 코드
sendMessage("hello", 1, true);

// IDE가 보여주는 코드
sendMessage(message:"hello", priority:1, allowRetry:true);

매우 유용한 기능이지만 가독성을 높이기 위해 IDE기능에 의존하는 것은 좋지 않다.

설명되지 않은 값을 사용하지 말라

하드 코드로 작성된 값이 필요한 경우가 많이 있는데, 몇가지 일반적인 예가 다음과 같다.

  • 한 수량을 다른 수량으로 변환할 때 사용하는 계수
  • 작업이 실패할 경우 재시도의 최대 횟수와 같이 조정 가능한 파라미터 값
  • 어떤 값이 채워질 수 있는 템플릿을 나타내는 문자열

하드 코드로 작성된 모드 값에는 두 가지 중요한 정보가 있다.

  • 값이 무엇인지: 컴퓨터가 코드를 실행할 때 이 값을 알아야 한다.
  • 값이 무엇을 의미하는지: 개발자가 코드를 이해하려면 값의 의미를 알아야 한다. 이 정보가 없으면 코드르 이해할 수 없다.

값은 당연히 존재한다. 가장 중요한 점은 다른 개발자가 해당 값들을 명확하게 이해하도록 하는 것이 중요하다.

설명되지 않은 값은 혼란스러울 수 있다.

함수에 들어가는 다양한 리터럴 값이 있다.

코드에 설명되지 않는 값이 있으면 혼란을 초래하고 이로 인해 버그가 발생할 수 있다. 그 값이 무엇을 의미하는지 다른 개발자들에게 명확하게 알려주는 것이 중요하다.

잘 명명된 상수를 사용하라

함수 내부의 리터럴값들을 피하기 위해서, 수정에 간편함을 위해서 상수이름 자체를 통해 값을 설명하고 가독성을 높이는 것

잘 명명된 함수를 사용하라

코드의 가독성을 높이기 위해서 잘 명명된 함수를 사용하는 방법은 두 가지가 있다.

  • 상수를 반환하는 공급자 함수
  • 변환을 수행하는 헬퍼 함수

공급자 함수란, 개념적으로 상수를 사용하는 것과 동일하며, 단지 약간의 다른 방식으로 이루어진다.

private static Double kilogramsPerUsTon()
{
  return 907.1847;
}

헬퍼 함수란, 공급자 함수를 사용하는 것의 대안으로 수량의 변환을 하위 문제로 만들어 이 기능을 전문적으로 수행하는 함수를 작성하는 것이다.

일반적으로 정의한 값이나 헬퍼함수를 다른 개발자들이 재사용할 것인지 고려해볼 만한 가치가 있다.

즉, 유틸리티 클래스(헬퍼 클래스)를 따루 둬서 관리하는 것

이것 또한 한번에 몰아서 작성하는 것이 아닌 성격에 맞게 어느정도 분리하는 것이 좋다.

익명 함수를 적절하게 사용하라

익명 함수란, 이름이 없는 함수이며, 일반적으로 코드 내의 필요한 지점에서 인라인으로 정의된다.

익명 함수는 간단한 로직에 좋다.

단 하나의 문장이면 충분하고, 해결하려는 문제는 간단한 내용은 익명함수로 다루는 것이 좋다.

논리가 단순, 자명

명명된 함수로 작성하는 것보다 이득인 경우와 아닌 경우가 항상 다르기 때문에 충분히 고민 후 결정하는 것이 좋다.

나중에 가면 어느정도 틀이 보이기 시작하지 않을까..

익명 함수는 가독성이 떨어질 수 있다.

앞 장에서 다룬 내용과 같이 함수의 이름은 그 함수가 무엇을 하는지 간결하게 요약해주기 때문에 코드의 가독성을 높이는 데 매우 유용하다.

하지만 익명 함수는 정의상 이름이 없기 때문에 코드를 읽는 사람에게는 제한된 정보를 제공한다.

따라서 익명 함수가 얼마나 간단한 것이든 함수의 내용이 자명하지 않다면 코드의 가독성은 떨어지기 마련이다.

따라서 익명함수를 쓰는것이 반드시 좋은 것은 아니다.

대신 명명 함수를 사용하라

익명대신의 명명함수의 단점은 더 많은 코드를 작성해야 한다는 것이다.

대신 익명함수는 항상 작성해야하는 문장을 작성하지 않아도 되기 때문에 코드를 줄이는 데는 뛰어나지만.. 이름이 없다는 단점이 있다.

따라서 간단하고 자명한 논리는 익명 함수, 복잡한 논리는 명몀 함수

익명 함수가 길면 문제가 될 수 있다.

함수형 프로그래밍이라고 해서 반드시 익명 함수를 사용해야 하는 것은 아니다.

2장에서 말한 읽고, 이해하고, 재사용하기 쉽도록 작고 간결하게 작성하는 것이 중요하다고 했다.

위의 규칙을 잊어버리고 너무 많은 논리, 익명 함수 중첩등 거대한 익명함수를 생성하곤 한다.

만약 익명함수가 2~3줄 이상으로 길어진다면 명명 함수로 수정하는 것이 좋다.

프로그래밍 언어의 새로운 기능을 적절하게 사용하라

여전히 프로그래밍언어는 변화를 거듭하고 있다. 성장하는 개발자라면 변화하는 패러다임에 당연하게 관심을 갖기 마련이다.

다양한 기능들을 활용하기를 권한다.

but, 신기술을 억지로 적용하기 보다 적합한지 진지한 판단이 필요하다.

새 기능은 코드를 개선할 수 있다.

자바의 스트림기능, C#의 프로퍼티 등등 다양한 기능들은 가독성이 좋은 코드를 만들어준다.

예제의 내용이 Rx프로그래밍과 매우 닮은 듯한 느낌이다.

유니티에서 본 스트림을 사용한 프로그래밍과 비슷하다.

불분명한 기능은 혼동을 일의킬 수 있다.

프로그래밍 언어에서 제공하는 기능이 확실한 이점을 가지고 있다고 하더라도 다른 개발자들에게 얼마나 알려저 있는지 고려해볼 필요가 있다.

마찬가지로 도입에 대한 팀의견, 동료의 의견이 중요하다.

적용하기 애매하거나 반응이 냉소적이라면 사용하지 않는 것이 좋을 때도 있다.

작업에 가장 적합한 도구를 사용하라

예제와 비슷한 예로 프로그래밍 패턴을 공부할 때 필요가 아닌 학습을 위해 진행중인 프로젝트에 억지로 끼워넣은 적이 있다.

생각한 내용보다 가독성도 떨어지고 방대해지는 것을 보고 나중에 깨달았지만 처음부터 좀 더 고민하고 만들었다면 문제가 생길일이 없었다.

느낀점

이번 장은 당장 적용해볼 수 있는 내용과 머릿속에 있던 내용들을 한번에 잘 정리해준 장이라 매우 마음에 든 챕터다..

지금 읽고 있는 견습패턴과 관련이 있는 내용들이 많아서 나름대로의 해석이 가능한 점이 재밌다.

팀단위의 관리나 적용방법도 고민해볼 수 있는 것 같고 내용 자체도 어렵지 않아서 두고두고 읽으면 좋을 내용