Linting은 잠재적인 문제를 찾기 위해 코드를 정적으로 분석하는 프로세스입니다.
이 경우 문제를 구성하는 요소는 프로그래밍 언어에 따라 다를 수 있으며 동일한 언어 내에서 프로젝트 간에도 다를 수 있습니다. 이러한 문제를 몇 가지 다른 범주로 분류합니다.
- 프로그래매틱
- 보안
- 문체
- 성능
각각의 몇 가지 예를 살펴보겠습니다.
문체 문제
그것은 모두 독자의 선호에 관한 것이기 때문에 코드를 스타일링하는 객관적으로 올바른 방법은 없습니다. 그러나 핵심은 일관성입니다. 일반적인 토론 포인트:
- 큰따옴표와 작은따옴표
- 탭 대 공백
- 최대 줄 길이
- 아래와 같이 여러 줄 호출의 들여쓰기:
# always single-line
foo(:a, :b, :c)
# aligned with first argument
foo(:a,
:b,
:c
)
# aligned with function name
foo(
:a,
:b
)
이는 전적으로 주관적이지만 전체 코드베이스를 일관되게 유지하려면 일반적으로 각 특정 프로젝트에 대한 표준에 동의하는 것이 좋습니다.
프로그래매틱 문제
여기에 다음과 같은 문제가 포함됩니다.
- 가독성 및 유지보수성 문제로 이어지는 매우 긴 메서드 본문
- 순환적 복잡성, 일반적으로 코드 복잡성을 측정하는 데 사용되는 메트릭
- 조건 내 할당. 대부분
if x = true
를 입력하면 , 실제로if x == true
를 의미했습니다. . 할당을 의미했더라도 여전히 덜 직관적인 접근 방식입니다.
보안 문제
일부 기능이나 관행에는 개발자가 인식하지 못하는 잠재적인 보안 문제가 있습니다.
예를 들어 Ruby에서 Kernel#open
파일이나 외부 URL을 열 수 있는 유연한 기능입니다. 그러나 open("| ls")
과 같은 이상한 호출로 임의의 파일 시스템 액세스도 허용합니다. .따라서 개발자에게 더 안전한 접근 방식(File#open
, IO.popen
, URI.parse#open
) 또는 자신의 책임하에 행동을 유지하기로 명시적으로 결정합니다.
성능 문제
컨텍스트에 따라 일부 옵션을 다른 옵션보다 더 효율적으로 만드는 Ruby의 내부 작동에 대한 많은 세부 정보가 있습니다.
그들에 대해 우리에게 경고하는 린터는 우리 프로그램의 일부 세부 사항을 최적화하면서 도중에 배우는 데 도움이 됩니다.
예를 들어, Ruby 2.5는 String#delete_suffix
를 도입했습니다. 문자열 끝에서 부분 문자열을 삭제합니다. 이 두 줄은 동일하지만 일반 문자열 regexmatch에 의존하지 않기 때문에 후자가 더 성능이 좋습니다.
str = 'string_with_suffix'
# bad
str.gsub(/suffix\z/, '')
# good
str.delete_suffix('suffix')
자동 수정
린터의 중요한 측면은 발견된 문제의 일부 또는 전부를 자동으로 수정하는 능력입니다. 라인 길이와 같은 스타일링 측면은 쉽게 자동화되므로 개발자의 부담을 제거하는 것이 합리적입니다. 다른 문제는 주관적이거나 사람의 개입이 필요할 수 있습니다. 큰 메서드를 리팩토링합니다. 이러한 경우에는 자동화가 불가능합니다.
대회 또는 구성
커뮤니티 또는 프로젝트 내에서 규칙이 타당한지에 대한 격렬한 토론이 종종 있습니다.
전통적인 해결책은 각 팀이 자신의 취향에 맞게 린트 규칙을 구성할 수 있도록 하여 구성원 내에서 토론을 해결할 수 있도록 하는 것입니다. 그러나 최근 몇 년 동안 여러 언어에서 단일 규칙으로 표준화하려는 노력이 있었습니다. 모든 곳에서 시행되는 일반적인 아이디어는 코드를 스타일링할 때 개발자가 갖는 정신적 오버헤드를 완전히 제거하는 것입니다. 어떤 줄 길이가 가장 효과적인지 논의하는 대신 모두가 커뮤니티에서 동의한 규칙을 사용합니다.
Ruby에서 이는 전체 구성을 허용하는 RuboCop과 반대 접근 방식을 취하고 공통 표준을 정의하는 StandardRB라는 두 개의 기존 린터로 변환됩니다.
루보캅
문서화된 규칙 집합을 제공하는 일반적인 접근 방식을 사용하며 각 규칙은 특정 문제를 찾습니다. 개발자는 자신의 프로젝트 내에서 특정 규칙을 비활성화하거나 조정할 수 있습니다.
# configure max allowed line length
Layout/LineLength:
Max: 80
# disable cyclomatic complexity
Metrics/CyclomaticComplexity:
Enabled: false
이미 정상적인 기본값이 포함되어 있으므로 변경하려는 특정 규칙에 대해서만 구성이 필요합니다.
bundle exec rubocop
실행 RuboCop은 전체 코드베이스를 분석하고 발견된 모든 문제를 나열합니다.
# test.rb
def badName
if something
return "inner result"
end
"outer result"
end
$ bundle exec rubocop
Inspecting 1 file
C
Offenses:
test.rb:1:1: C: [Correctable] Style/FrozenStringLiteralComment: Missing frozen string literal comment.
def badName
^
test.rb:1:5: C: Naming/MethodName: Use snake_case for method names.
def badName
^^^^^^^
test.rb:2:3: C: [Correctable] Style/IfUnlessModifier: Favor modifier if usage when having a single-line body. Another good alternative is the usage of control flow &&/||.
if something
^^
test.rb:3:12: C: [Correctable] Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols.
return "inner result"
^^^^^^^^^^^^^^
test.rb:6:3: C: [Correctable] Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols.
"outer result"
^^^^^^^^^^^^^^
1 file inspected, 5 offenses detected, 4 offenses auto-correctable
그런 다음 bundle exec rubocop --auto-correct
를 실행할 수 있습니다. , 그리고 대부분의 문제는 구성에 따라 수정됩니다.
bundle exec rubocop
설정 CI 파이프라인의 일부로 린트 규칙을 먼저 충족하지 않으면 코드젯이 통과하지 않도록 합니다.
표준RB
내부적으로 RuboCop을 실제로 사용하는 보다 최근의 프로젝트입니다. StandardRB의 주요 목표는 완전히 별도의 린터를 구축하는 것이 아니라 모든 사람이 논쟁하는 대신 사용할 수 있는 표준에 도달하는 것입니다.
처음 발표된 번개 이야기는 동기에 대해 매우 명확합니다. 사람들이 구문적 세부 사항에 대해 논쟁하는 데 시간을 덜 소비하고 커뮤니티 전체의 합의 결정을 따르면, 우리 모두는 정말 중요한 일, 즉 훌륭한 제품과 라이브러리를 구축하는 데 더 많은 시간을 할애할 수 있습니다.
RuboCop이 아래에서 사용되기 때문에 결과는 실제로 동일한 형식입니다. 유일한 차이점은 규칙을 사용자 정의할 수 없다는 것입니다.
StandardRB는 최근 1.0.0에 도달했습니다. 이는 사용할 규칙에 대한 대부분의 토론이 이미 해당 문제 페이지에서 발생했음을 의미합니다. 특정 규칙에 대해 관심이 있거나 동의하지 않는 경우 해당 페이지에서 관련 토론을 볼 수 있습니다.
그러나 궁극적으로 어느 정도 깊이 있게 논의되었다고 확신할 수 있습니다. 전체 커뮤니티가 모든 사항에 대해 100% 동의하는 것은 불가능합니다. 이 접근 방식의 철학은 사람들이 유연하고 동의하지 않고 결정을 내릴 수 있다는 것입니다.
최종 생각
과거 프로젝트에서 린트 규칙을 꼬집는 것을 자랑스럽게 생각하는 것보다 더 많은 시간을 보낸 후 StandardRB의 접근 방식에서 가치를 확실히 알게 되었고 가능한 한 이를 사용하는 것이 좋습니다.
대부분의 규칙에 대한 자동화된 수정과 함께 일관성의 모든 이점을 유지하면서 토론의 오버헤드를 제거하면 더 나은 소프트웨어를 더 효율적으로 제공하고 실제로 중요한 것에 집중할 수 있습니다.
다른 언어에서도 유사한 구성 가능성이 낮은 코드 포맷터를 채택하고 있습니다. mix formatter
Elixir 및 rustfmt
Rust에서는 둘 다 일부 구성 가능성을 허용하지만 커뮤니티는 놀랍게도 표준 내에서 유지하고 있습니다.
하지만 아이러니하게도 이 감정에 동의하지 않는다면 RuboCop은 여전히 완벽하게 유효한 옵션입니다.
추신 Ruby Magic 게시물이 언론에 공개되는 즉시 읽고 싶다면 Ruby Magic 뉴스레터를 구독하고 게시물을 놓치지 마세요!