Skip to content

k-gn/readable-code

Repository files navigation

Readable Code

중요하게 생각해야 할 개념

  1. 추상
  2. 논리, 사고의 흐름
  3. 객체 지향 패러다임

도메인 : 해결하고자 하는 문제 영역

도메인 지식 : 도메인을 이해하고 해결하는 데 필요한 지식

클린코드를 추구하는 이유?

  • 좋은 가독성
  • 유지보수 수월
  • 시간과 자원의 절약 => 우리가 짠 코드가 잘 읽히도록 작성하는 것이 굉장히 중요!

프로그램 : 데이터 + 코드

추상과 구체

  • 추상 : 중요한 정보는 가려내어 남기고, 덜 중요한 정보는 생략하여 버린다.

  • 정보 함축, 제거 = 추상화

  • 적절한 추상화는 복잡한 데이터와 복잡한 로직을 단순화하여 이해하기 쉽도록 돕는다

    • 읽기가 좋다.
    • 적절한 추상화는 해당 도메인 문맥 안에서 가장 중요한 핵심 개념만 남겨 표현하는 것
  • 추상화 레벨이 높다 = 고수준

  • 추상화 레벨이 낮다 = 저수준

  • 추상으로부터 구체를 유추하지 못하는 경우

    • 추상화 과정에서 중요한 정보를 부각시키지 못함
      • 상대적으로 덜 중요한 정보를 남기고 중요한 정보는 제거한 경우
    • 해석자가 동일하게 공유하는 문백이 없는 경우
      • 중요한 정보의 기준이 다를 수 있다.
      • 도메인 영역 별 추상화 기준(도메인 지식)이 다를 수 있다.

이름 짓기

  1. 단수와 복수를 구분하기
  2. 이름 줄이지 않기
  3. 은어/방언 쓰지 않기 (도메인 용어 사용하기)
  4. 아무 의미없는 이름 쓰지 않기 (ex. a, c1)

추상화

  1. 메서드 추출
  • 전체 맥락을 포괄하는 의미를 가진 함수명을 주어야 한다.
  1. 한 메서드의 주제는 반드시 하나이다.
  2. 메서드 선언부
  • 추상화된 구체를 유추할 수 있는 적절한 의미가 담긴 이름
  • 파라미터와 연결지어 더 풍부한 이름 전달
    • 파라미터의 타입, 개수, 순서를 통해 의미 전달
    • 파라미터는 외부와 소통하는 창구
    • 파라미터를 줄일 수 있다면 줄이는게 복잡도를 낮춰줄 수 있다.
  1. 메서드 시그니처에 납득이 가는 적절한 반환값 돌려주기
  2. 항상 고민해야 할 것
  • 정확한 의미로 메서드 명이 추출되었나?
  • 메서드가 2가지 이상의 일을 하고있진 않을까?
  • 파라미터는 이게 맞을까?
  • 반환타입은 이게 맞을까?
  1. 추상화 레벨을 주변과 동등하게 한다.
  2. 매직넘버, 매직스트링
  • 의미를 갖고 있으나, 상수로 추출되지 않은 숫자나 문자열
  • 상수 추출로 이름을 짓고 의미를 부여함으로써 가독성 및 유지보수성 증가

논리, 사고의 흐름

  • 뇌 메모리에 적은 정보를 올리도록 코드를 작성할수록 읽기 쉬운 코드가 된다.
  1. Early Return
  • else if 또는 else 를 지양하자 (switch-case 문도 동일)
  • 일찍 return 할 수 있으면 빠르게 하자
  1. depth 줄이기
  • 중첩 반복문, 중첩 분기문 줄이기
  • 무조건 depth 를 줄이는데 급급한 것이 아니다.
    • 사고 과정의 depth 를 줄이는 것이 중요.
    • 때로는 메서드를 추출하는 것이 더 혼선을 줄 수 있다.
  • 사용할 변수는 가깝게 선언하자
  1. 공백 라인
  • 복잡한 로직의 의미 단위를 나눔

4.부정어를 대하는 자세

  • ! 는 가독성을 낮춘다.
    • 반대되는 표현이 있다면 ! 보단 반대 의미의 함수를 써주는 것이 복잡도를 낮춤
    • 아니면 문맥에 따라 제거해줄 수도 있음
  1. 해피케이스와 예외 처리
  • 견고한 소프트웨어를 위해 예외처리를 꼼꼼히 할 것
  • 예외가 발생할 가능성을 낮추기
  • 어떤 값의 검증이 필요한 경우는 주로 외부와의 접점
    • 사용자 입력, 객체 생성자, 외부 서버의 요청 등
  • 의도한 예외와 예상하지 못한 예외 구분하기
    • 사용자에게 보여줄 예외와, 개발자가 보고 처리할 예외 구분
  • Null
    • NPE 를 방지하는 경각심 가지기
    • return null 자제하기
      • optional 고려
  • Optional
    • 비싼 객체 = 꼭 필요한 상황에서 반환타입에 사용
    • 파라미터로 받지 말것
    • 반환 받으면 최대한 빠르게 사용할 것
  • orElse(something()) - 항상 something() 실행
  • orElseGET(() -> something()) - null 일때 something() 실행

객체지향 패러다임

  • 추상의 관점에서 바라보는 객체지향

    • 객체간의 협력과 객체의 책임
    • 관심사의 분리 (일련의 작업, 개념을 묶어 이름을 짓고 역할을 부여하는 것 = 추상화)
      • 높은 응집도, 낮은 결합도
  • 공개 메서드를 통해 객체의 책임을 드러낼 수 있다.

    • 외부 세계와 소통
  • 객체의 책임이 나뉨에 따라 객체 간 협력 발생

  • 관심사가 한곳에 모이기 때문에 유지보수성이 오름

  • 여러 객체를 사용하는 입장에서는 구체적인 구현에 신경쓰지 않고, 높은 추상화 레벨에서 도메인 로직을 다룰 수 있다.

  • 1개의 관심사로 명확하게 정의되어야 한다.

  • 생성자, 정적 팩토리 메서드에서 유효성 검증 가능


SOLID

SRP

  • 단일 책임 원칙
    • 하나의 클래스는 단 한가지의 변경 이유만을 가져야 한다.
    • 변경 이유 = 책임

OCP

  • 개방 폐쇄 원칙
    • 확장에는 열려있고, 수정에는 닫혀있어야 한다.
      • 기존 코드의 변경 없이 기능을 확장
    • 추상화와 다형성 활용

LSP

  • 리스코프 치환 원칙
    • 상속 구조에서 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환할 수 있어야 한다.
      • 자식 클래스는 부모 클래스의 책임을 준수
      • 부모 클래스의 행동을 변경하지 않음

ISP

  • 인터페이스 분리 원칙
    • 클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안된다.
      • 인터페이스를 잘게 쪼갤 것
      • 불 필요한 의존성으로 인한 결합도를 낮추기

DIP

  • 의존성 역전 원칙
    • 상위 수준의 모듈은 하위 수준의 모듈에 의존해서는 안된다.
      • 둘 모두 추상화에 의존해야 한다.
    • 의존성의 순방향 : 고수준 모듈이 저수준 모듈을 참조
    • 의존성의 역방향 : 고수준, 저수준 모듈이 모두 추상화에 의존
      • 저수준 모듈이 변경되어도 고수준 모듈에는 영향X
    • 저수준일수록 구체에 가깝다.

상속과 조합

  • 상속보다 조합을 사용하자.
  • 상속은 시멘트처럼 굳어지는 구조 = 수정이 어렵다.
    • 부모와 자식의 결합도가 높다. (ex. 부모의 필드를 직접 사용)
  • 조합과 인터페이스를 활용하는 것이 유연한 구조
  • 상속과 조합을 적절하게 같이 사용하는 것도 좋다.

Value Object (VO)

  • 도메인의 어떤 개념을 추상화하여 표현한 값 객체

    • 값 => 불변성, 동등성, 유효성 검증 등을 보장해야 한다.
      • 불변성 : final 필드 + setter 금지
      • 동등성 : 서로 다른 인스턴스여도 내부의 값이 같으면 같은 객체로 취급 (equals 및 hashCode 재정의)
      • 유효성 검증 : 객체가 생성되는 시점에 값에 대한 유효성 보장
      • 기본타입을 만들어 쓰는 느낌
    • 명확한 의미 부여 및 검증
  • Entity vs VO

    • Entity
      • 식별자가 존재
        • 식별자가 아닌 필드의 값이 달라도 식별자가 같으면 동등한 객체로 취급
    • VO
      • 내부의 모든 값이 같아야 동등한 객체로 취급

일급 컬렉션

  • 컬렉션을 포장하면서, 컬렉션만을 유일하게 필드로 가지는 객체
  • 컬렉션을 추상화하며 의미를 담을 수 있고, 가공 로직의 보금자리가 된다.
  • getter 로 컬렉션을 반환해야 한다면, 외부 조작을 피하기 위해 새로운 컬렉션으로 만들어 반환하자.

Enum

  • 상수의 집합
    • 상태와 행위를 한 곳에서 관리할 수 있는 추상화된 객체
    • 특정 도메인 개념에 대해 종류와 기능을 명시적으로 표현할 수 있다.

다형성

  • 변하는 것과 변하지 않는 것을 분리
    • 추상과 구체의 관점
  • OCP

주석의 양면성

  • 주석이 많다는 것은 그만큼 비즈니스 요구사항을 코드에 잘 못 녹였다는 뜻
  • 주석에 의존하는 코드가 되면 안된다.
  • 리팩토링할 때 제일 큰 난관 중 하나는 히스토리를 알 수 없는 코드
    • 후대에 전해야 할 의사결정 히스토리를 코드로 도저히 표현할 수 없을 때 주석으로 설명한다.

리팩토링은 한 단계마다 그 이유를 설명할 수 있어야 한다!


  • 능동적 읽기

    • 도메인 지식을 늘리기 위함 + 작성자의 의도를 파악
      • 공백으로 단락 구분
      • 메서드로 추상화
      • 주석으로 이해한 내용 표기
    • 이렇게 이해를 한 후 git reset --hard 하면 된다.
  • 오버 엔지니어링 (경험을 많이 쌓아야함)

    • 필요한 적정 수준보다 더 높은 수준으로 엔지니어링 ex. 구현체가 하나인 인터페이스
      • 인터페이스 형태가 아키텍처 이해에 도움을 주거나, 근시일 내에 구현체가 추가될 가능성이 높다면 OK
      • 구현체를 수정할 때 인터페이스도 수정해야함
      • 코드 탐색에 영향을 주고, 애플리케이션이 비대해 짐 ex. 너무 이른 추상화
      • 정보가 숨겨져서 복잡도가 높아짐
      • 의도를 파악하기 힘듬
  • 은탄환은 없다

    • 만능 해결사는 없다는 뜻

    • 클린코드도 은탄환이 아니다

    • 지속 가능한 소프트웨어의 품질 vs 기술 부채를 안고 가는 빠른 결과물

      • 결국 회사는 돈을 벌고 성장해야 하며, 시장에서 빠르게 살아남는 게 목표
    • 미래 시점에 잘 고치도록 코드 센스가 필요 (클린코드 사고법)

    • 모든 기술과 방법론은 적정 기술의 범위 내에서 사용되어야 한다. ex. 당장 급하게 배포나가야 하는데, 동료에세 style format 관련 리뷰를 주고 고치도록 강요하는 사람..

    • 도구를 쓸 때 사용하지 말아야 할 때도 알아야 한다.

      • 적정 수준을 알기 위해 때론 극단적으로 시도해보자. (경험)
    • 한번 쓰고 말 코드에 시간을 투자하지 말자. (적당히 알아볼 정도로만 작성)

      • 상황에 맞게 잘 판단하자.

코드 포맷 정렬 : Option + Cmd + L | Ctrl + Alt + L 코드 품질 향상 : Sonarlint 포맷 규칙 : .editorconfig

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages