ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [ Flutter : 플러터 ] Linting 설정으로, 흔하게 실수할 수 있는 것을 build 전 방지하기.
    개발일지/flutter 2021. 4. 14. 22:16

    안녕하세요 개발하는 남자 개남입니다.

    오늘 정리해볼 포스팅의 주제는 Linting에 대해서 정리해보려고 합니다.


    Linting이란?

    린트 (lint) 또는 린터 (linter)는 소스 코드를 분석하여 프로그램 오류, 버그, 스타일 오류, 의심스러운 구조체에 표시(flag)를 달아놓기 위한 도구들을 가리킨다.
    (위키피디아 참조)

     

    사실 Linting 설정을 하지 않아도 앱을 만들고 사용자들에게 배포하는 데에 문제 될 요소는 없습니다. 그렇다면 왜 Lint를 설정해야 하는 것일까요? 그것은 앱을 배포하는 시점이 아닌 앱을 개발하는 시점에서 중요하게 작용되기 때문입니다. 또한, 개발 문화를 만들어가기 위한 하나의 방법?이라고 할까요? 

     

     

    1. 개발하는 시점에서 중요한 이유.

     

    개발을 단순히 빨리 하기 위해 날코딩으로 소스를 만들어 버리라는 말이 아닌것 다들 아시죠?

    개발이 속도전인 이유는 빠른 개발환경을 구축하여 보다 효율적인 개발 퍼포먼스를 위한 것입니다.

    그런 점에서 Lint를 설정을 통해 규칙적인 코딩 스타일을 만들 뿐 아니라 , 런타임 에러를 줄일 수 있습니다.

     

     

     

    2. 개발 문화를 만들어갈 수 있다.

     

    개발은 혼자 하는 싸움이 아닙니다. 물론 밴처기업에서 어쩔 수 없이 개발을 혼자 하기도 하지만, 개발은 팀 단위로 프로젝트를 진행하게 되죠. 각자가 맡은 부분을 개발하다 보면 개개인의 코드 스타일이 소스에 녹여 들어가게 되는데 그것은 결과물에 영향을 주진 않지만 협업하는 상황에서는 일정 부분 영향을 주게 됩니다. 자신의 코드 스타일에 대해서는 쉽게 소스가 한눈에 들어와 지지만 협업하는 개발자분의 소스는 코드 스타일이 달라 눈에 잘 들어와 지지 않고 집중해서 봐야 되는 경우가 생깁니다. 

    하여 Lint 의 코드 룰을 설정하여 개발 스타일 온도차를 최대한 줄여 퍼포먼스를 높일 수 있도록 도와줍니다. 

    이렇게 작은단위의 문화가 형성되어 이러한 규칙들이 전국 전 세계로 전파되어 동일한 코드 규칙을 모두가 사용한다면? 다른 사람의 코드들도 보는데 조금이나마 온도차를 낮출 수 있을 것입니다. 

     


    이제 dart lint 설정하는 방법을 알아보겠습니다.

     

    root 경로에서 yaml 파일을 하나 만들건데 파일 이름을 analysis_options.yaml로 만들겠습니다.

    해당 파일에 설정을 2가지를 추가 할 수 있습니다. 

    하나는 [analyzer] 다른 하나는 [linter] 입니다. 

     

    analyzer는 빌드 전 개발자가 실수할 수도 있는 소스코드를 분석해서 개발자에게 안내를 해주게 되는데 오류 소스를 다른 형태로 안내할 수 있도록 설정을 할 수 있습니다. 아무런 설정 하지 않고 linter에 룰만 추가한다면 default로 info로 설정이 되어 개발자에게 안내만 하게 됩니다. 

    오류 타입 종류는 다음과 같습니다.

     

    info

      - 소스 코드를 분석하여 위반되는 코드에 대해 안내만 하는 단계 

    warnings

      - 소스 코드 분석 후 위반되는 코드에 대해 경고를 알리는 단계 

    error

      - 소스 코드 분석 후 위반되는 코드에 대해 에러를 발생시켜 빌드를 하지 못하게 막는 단계 

    ignore 

      - 소스 코드 분석 후 위반되는 사항에 대해 무시하고 넘기는 단계 (이 단계에서는 로그 조차 보여주지 않는다.)

     

    analyzer 설정 사용 방법은 다음과 같습니다.

      analyzer:
        errors:
          missing_required_param: error
          prefer_const_constructors: warning

     

     

    (중요! 첫줄에 analyzer를 입력할 때 앞에 2칸을 띄워줘야 합니다.)

    위에서 설정한 것은 

    missing_required_param 에대한 룰을 위반한 경우 에러 처리 , prefer_const_constructor 룰에 위반할 경우 경고만 뜨도록 설정한 것입니다. 하지만 위의 설정만으로 소스코드를 검사하지 않습니다.  linter 룰을 설정해줘야 그제서야 검사를 실시합니다.

     

    linter rule 세팅 하는 방법

      linter:
        rules:
          missing_reauired_param: true
          prefer_const_declarations: true
          prefer_const_constructors: true

    위에 analyzer에 세팅한 2가지를 포함하여 추가적으로 declarations 규칙까지 설정했습니다.

    그럼 prefer_const_declarations 규칙 위반한 소스 코드는 어떤 단계로 알림을 줄까요? 그렇습니다, default는 info 단계이기 때문에 

    info로 설정이 됩니다.

     

    전체 full소스는 다음과 같습니다.

      analyzer:
        errors:
          missing_required_param: error
          prefer_const_constructors: warning
      linter:
        rules:
          missing_reauired_param: true
          prefer_const_declarations: true
          prefer_const_constructors: true

    설정한 것을 해석? 해보자면 

    코드 분석할 규칙은 총 3가지를 등록하였고  (missing_required_param , prefer_const_declarations , prefer_const_constructors) 

    missing_required_param 규칙 위반 시 에러 

    prefer_const_declarations 규칙 위반시 경고

    prefer_const_constructors 규칙 위반 시 안내


     

    자 그렇다면 Lint 규칙을 얼마나 설정할 수 있는 것일까요? 자세한 내용은 다음 링크를 통해서 확인 하시면 됩니다.

     

    Linter for Dart

    Linter for Dart Lint Rules Using the Linter Supported Lint Rules This list is auto-generated from our sources. Rules are organized into familiar rule groups. errors - Possible coding errors. style - Matters of style, largely derived from the official Dart

    dart-lang.github.io

    너무 많은 설정이 있어 어떤걸 설정해야 할지 모르겠다고요? 

    걱정하지 마세요. 다트언어를 만들 개발사 구글에서 사용하는 Lint 설정을 사용할 수 있습니다.

    pub.dev에서 pedantic이라고 검색하시면 해당 라이브러리를 사용하실 수 있습니다.

    사용 방법도 아주 쉽습니다. 아래 링크를 통해 설정하는 방법을 확인할 수 있습니다~~ 만, 

    오늘 포스팅에서 간단 설정법과 구글 Lint 설정중에 제 소스코드 스타일에 맞지 않는 몇 가지 설정만 언급하려고 합니다. 

     

    pedantic | Dart Package

    The Dart analyzer settings and best practices used internally at Google.

    pub.dev

    구글 pedantic 설정하는 방법은 간단합니다 

     

    pedantic 설정 방법

    우선 pubspec.yaml파일에 원하는 버전으로 install 설정을 세팅 합니다.

    dev_dependencies:
        flutter_test:
            sdk: flutter
    
        pedantic: ^1.11.0

    dependencies 나 dev_dependencies 둘 중 한 곳에만 추가하면 되지만 dev에 사용하기를 추천드립니다.

    pub get을 완료했다면 이제 analysis_options.yaml 설정 파일에 세팅 한 값을 지우고 

      include: package:pedantic/analysis_options.yaml

    한 줄만 추가해주시면 됩니다. (어때요 ~ 참 쉽죠?) 

    analysis_options 파일에 어떤 설정값들이 있는지 확인을 원하시는 분은 링크를 확인해보시면 됩니다.


    제 코드 스타일에 대해 pedantic으로 분석을 돌려본 결과 4가지의 오류를 범하고 있다고 나와서 어떤 오류 내용인지 함께 확인해보도록 하겠습니다.

     

    1. omit_local_variable_type 

     

    omit_local_variable_types

    omit_local_variable_types Group: style Maturity: stable View all Lint Rules Using the Linter CONSIDER omitting type annotations for local variables. Usually, the types of local variables can be easily inferred, so it isn't necessary to annotate them. BAD:

    dart-lang.github.io

    해당 검증에 대한 룰은 지역 변수를 선언할 때에 쉽게 추론이 가능한 변수 타입을 굳이 선언할 필요가 없다는 내용입니다. 

    간단 예제는 아래와 같습니다.

    //개남 코드 스타일
    void sampleFunction(){
    	int index = 0;
        print(index);
    }
    
    //omit_local_variable_types
    void sampleFunction(){
    	var index = 0;
        print(index);
    }

    int type으로 index 변수를 선언하려고 하는데 변수 타입을 굳이 지정할 필요 없다는 것입니다. 왜냐면 0이라는 값을 한눈에 알 수 있기 때문입니다. 사실 이 부분은 의아하다는 생각이 됩니다. javascript 때에 var로 모든 변수를 선언할 수 있어 편한 것은 맞지만 어떤 변수타입인지 파악되기 어려워 다른 타입의 값을 주입하는 오류를 범할 수 있다는 점에서 typescript 가 그것을 보안 하려 나올 정도로 javascript 에서 변수 타입 var 로 하는 것이 좋지 않았습니다. 

    그렇다면 구글에서는 왜? 쉽게 추론이 가능한 변수에 대해서 var로 선언을 하게 한 것일까? 

    플러터에서는 javascript의 장점을 가져오고 단점을 개선한 것으로 보입니다. 즉, 추론이 쉬운 변수 타입을 var 로 선정함으로 개발 속도를 높일 수 있을 뿐만 아니라 javascript 에 다른 타입의 값을 넣으려 한다면 오류를 뱉어 주기 때문에 단점을 개선하게 된 것입니다. 

    //javascript var 사용
    void sampleFunction(){
    	var index = 0;
        print(index); // 0 출력
        index = "string 주입"; //문제 발생 없음
        print(index); // string 주입 출력
    }
    
    //dart 
    void sampleFunction(){
    	var index = 0;
        print(index); // 0 출력
        index = "string 주입"; // 오류 발생 
    }

     

     

     

     

    2. unnecessary_this

     

    unnecessary_this

    unnecessary_this Group: style Maturity: stable View all Lint Rules Using the Linter From the style guide: DON'T use this when not needed to avoid shadowing. BAD: class Box { var value; void update(new_value) { this.value = new_value; } } GOOD: class Box {

    dart-lang.github.io

    해당 검증에 대한 룰은 클래스 내의 선언된 전역 변수를 사용할 때 this를 불필요한 부분까지 사용하지 마라입니다.

    간단 예제는 아래와 같습니다.

    // BAD
    class Box {
      var value;
      void update(new_value) {
        this.value = new_value;
      }
    }
    
    //GOOD
    lass Box {
      var value;
      void update(new_value) {
        value = new_value;
      }
    }

    update 함수 내에 value라는 변수명은 class 내의 한 개밖에 존재하지 않아 this를 사용하지 않아도 된다는 것입니다.

    제 코드 스타일에서는 종종 this를 사용하여 명확하게 표현하는 것을 좋아하는 편이었지만 고쳐야 할 것 같습니다. 

    그렇다면 this를 사용하는 때는 어떨 때일까요? 

    class Box {
      var value;
      void update(value) {
        this.value = value;
      }
    }

    class 전역에 선언된 value와 update 함수로 넘겨받은 파라미터 변수명이 같을 때 this.value를 사용하여 명확하게 구분 지어줄 때에 사용하라고 합니다. 

     

     

     

    3. unnecessary_new 

     

    unnecessary_new

    unnecessary_new Group: style Maturity: stable View all Lint Rules Using the Linter AVOID new keyword to create instances. BAD: class A { A(); } m(){ final a = new A(); } GOOD: class A { A(); } m(){ final a = A(); }

    dart-lang.github.io

    해당 검증에 대한 룰은 간단합니다 위젯이나 오브젝트(모델)를 생성할 때 new를 사용하여 생성하지 마라입니다.

    간단 예제는 아래와 같습니다

    //BAD
    class A { A(); }
    m(){
      final a = new A();
    }
    
    //GOOD
    class A { A(); }
    m(){
      final a = A();
    }

    이 부분은 다트/플러터를 최근 버전으로 시작하신 분들이라면 당연한 것이 아닌가? 하실 수 있습니다.

    하지만 예전 다트/플러터에서 위젯 혹은 오브젝트를 생성할 때에 new를 사용했던 때가 있었습니다.(저도 최근 버전 사용자이긴 합니다... )

    그러다 보니 구글링을 통해 예제 소스를 그대로 사용하다 보니 특정 파일에서 new를 사용한 곳이 있었더라고요 ㅎㅎㅎ

    이 부분도 다시 한번 명심 ~! 

     

     

     

    4. curly_braces_in_flow_control_structures

     

    curly_braces_in_flow_control_structures

    curly_braces_in_flow_control_structures Group: style Maturity: stable View all Lint Rules Using the Linter DO use curly braces for all flow control structures. Doing so avoids the dangling else problem. GOOD: if (isWeekDay) { print('Bike to work!'); } else

    dart-lang.github.io

    해당 검증에 대한 룰은 희비가 엇갈릴 것 같은데요 if/ for 문 등에 사용되는 {} < (culy bracket : 컬리 브래킷) 사용에 대한 부분입니다.

    간단 예제는 아래와 같습니다

    //BAD
    if (overflowChars != other.overflowChars)
      return overflowChars < other.overflowChars;
      
    //GOOD
    if (isWeekDay) {
      print('Bike to work!');
    } else {
      print('Go dancing or read a book!');
    }
    
    //GOOD
    if (arg == null) return defaultValue;

    소스코드를 최대한 줄일 수 있으면 줄이는 게 좋다고 하는 글을 읽었던 적이 있습니다 그곳에서는 위에서 BAD라고 했던 소스처럼 

    괄호(컬리 브래킷) 사용을 줄이면 코드 줄 수를 한 줄을 줄일 수 있어서 그것을 선호한다고 했던 것으로 기억하고 그때부터 if 나 for 문 내에 한 줄로 처리할 수 있는 부분이면 괄호(컬리 브래킷)를 제거하고 사용해 왔었는데, 이번 구글 pedantic의 lint 룰로 다시 한번 잘못 이해하고 있었구나 하고 정립하는 시간이 되었습니다.  

    구글에서도 if / for 문을 사용할 때 한줄로 처리 할 수 있는 부분은 예제 마지막 처럼 inline으로 길게 사용할때 괄호(컬리 브래킷)를 제거하여 사용하고 그 외에는 명확하게 괄호(컬리 브래킷)를 사용하여 구분을 주어라 라고 합니다. 

     

    하긴 제가 소스 코드를 읽고 분석할 때 복잡한 소스에서 if, for문에 괄호(컬리 브래킷)를 사용하지 않고 한 줄로 표현하지 않았던 소스코드를 볼 때 헷갈리고 눈에 확 들어오지 않았던 경험이 있었습니다. 이 부분도 이제 명확하게 알았으니 앞으로 실수하지 않도록 해야겠습니다.

     

     


    자 이렇게 다트/플러터의 lint 설정을 통해 개발자가 실수할 수 있는 부분은 미리 분석해줘서 코트 스타일을 통합해 주는 편리한 라이브러리를 살펴보았습니다. 물론 코드 스타일은 개취 이긴 하지만 구글의 개발자들이 위와 같은 룰을 적용하는데에는 분명 어떠한 이유에서 , 또는 어떤 경험을 통해서 얻어진 결과물이라 검증된 것이라고 생각하고 믿고 따라가도 되지 않을까? 하고 생각합니다. 

    이번 포스팅도 역시 정리하여 유튭 영상으로 올라갈 예정이니 시청 원하시는 분은 개발하는남자 채널 구독 부탁드립니다~! 

    댓글

Designed by Tistory.