자바/자바 코딩의 기술

자바 코딩의 기술 - 9장 ( 실전 준비 )

끄적끄적 2022. 4. 10. 12:54

코드의 첫 90%에 개발 시간의 첫 90%가 쓰인다. 코드의 나머지 10%에 개발 시간의 마지막 90%가 쓰인다. - 톰 카길

9-1 정적 코드 분석 도구

- SpotBugs( https://spotbugs.github.io/) 자바에서 가장 오래된 정적 코드 분석도구이다.

- Checkstyle(https://checkstyle.sourceforge.io/), PMD(https://pmd.github.io/) 도 유명하나, 다소 장황하고 특정 코드 방식을 고집하는 등 선호도를 탄다.

- Error Prone(https://errorprone.info/) 은 ConcurrentHashMap 내 버그를 감지하면서 유명해 졌는데, 구글이 자체 개발했다.

- Code Inspection( https://www.jetbrains.com/help/idea/code-inspection.html ) 은 IntelliJ 에서 사용할 수 있게 해준다.

9-2. 팀 내 자바 포맷 통일

팀 내에서 자바 코드의 포맷을 맞추는게 좋다.(하다못해 탭의 표준이나, 한 행에 최대 문자 수 등)

팀 내에 논쟁의 소지가 있다면 업계 표준을 쓰는 것도 좋다.
( 구글 스타일 가이드 :   https://google.github.io/styleguide/javaguide.html)

서식화를 검증하는 자바 포맷도 있다. (https://github.com/google/google-java-format)

9-3. 빌드 자동화

어떤 개발 툴(IntelliJ, eclipse, Net Beans)이나 OS를 사용하더라도 빌드를 독립적으로 수행할 수 있도록 빌드도구를 사용하라. Gradle, maven, Ant 등이 있다.

9-4. 지속적 통합

빌드를 자동화하였다면, 테스트를 수행하고 배포를 할 수 있는 통합도구가 필요하다.

Jenkins를 사용하거나 전용 품질분석 서버(SonarQube, Travis-CI, Codacy) 를 검토하라.

9-5. 생산준비와 납품

사용자가 사용하게 되면 로그를 한 곳에 모으고, 로그를 검색/분석, 대시보드로 시각화하여야 한다. ELK와 Graylog를 검토하라.

9-6. 콘솔 출력 대신 로깅

System.out.println(), System.err.println()은 에러를 분석하는데도 한계가 있으며, 매번 String을 병합하는 비용도 든다.

log4j를 활용해서 debug, info, warn, error, fatal등 로그 수준에 맞게 로그를 남겨야 한다.

9-7. 다중 스레드 코드 최소화 및 독립

병렬 실행은 여러 동시성 이슈를 야기하며, 테스트를 제대로 하기도 어렵다. 성능 이슈가 없다면 다중 스레드 코딩은 사용하지 않고, 성능이슈가 발생할 때만 고려하자. 동시 실행을 구현한다면 최소한의 패키지에서만 사용하도록 독립적으로 구현하라.

9-8. 고급 동시 실행 추상화 사용하기

동시 실행에 대해 주의하고, AtomicInteger, LongAdder, ConcurrentHashMap, CopyOnWriteArrayList, BlockingQueue와 같은 자료구조를 살펴보라.

9-9. 프로그램 속도 향상

List<Supply> supplies;

long countDifferentKinds() {
    return supplies.stream()
                   .sequential() // this can be omitted
                   .filter(Supply::isUncontaminated)
                   .map(Supply::getName)
                   .distinct()
                   .count();
}

위와 같이 sequential 을 사용하면 한번에 하나씩 연산하는 것을 스트림의 parallel을 이용해면, CPU의 여러 코어가 동시 작업이 가능하다. 단, Sort()나 forEachOrdered() 를 써야 한다면 sequential이 더 빠를 수 있다.

List<Supply> supplies;

long countDifferentKinds() {
    return supplies.stream()
                   .parallel()
                   .filter(Supply::isUncontaminated)
                   .map(Supply::getName)
                   .distinct()
                   .count();
}

9-10. 틀린 가정 알기

class NameTag {

    final String name;

    NameTag(String fullName) {
        this.name = parse(fullName).toUpperCase();
    }

    String parse(String fullName) {
        String[] components = fullName.split("[,| ]");
        if (components == null || components.length < 2) {
            return fullName;
        }
        if (fullName.contains(",")) {
            return components[0];
        } else {
            return components[components.length - 1];
        }
    }
}

class Usage {

     static void main(String[] args) {
        System.out.println(new NameTag("Neil Armstrong").name);
        System.out.println(new NameTag("Neil Alden Armstrong").name);
        System.out.println(new NameTag("Armstrong, Neil").name);
        System.out.println(new NameTag("Edwin Eugene Aldrin, Jr.").name);
        System.out.println(new NameTag("William \"Bill\" H. Gates III").name);
        System.out.println(new NameTag("刘伯明").name);
    }
}

이름의 성과 같은 부분은 나라마다 틀리고, 사람마다 틀리는 예외가 항상 발생할 수 있다. 호출하는 사용자가 직접 설정하게 하는게 나을 수 있다.

class NameTag {

    final String name;

    NameTag(String name) {
        Objects.requireNonNull(name);
        this.name = name;
    }
}

class Usage {

     static void main(String[] args) {
        System.out.println(new NameTag("ARMSTRONG").name);
        System.out.println(new NameTag("ARMSTRONG").name);
        System.out.println(new NameTag("ALDRIN").name);
        System.out.println(new NameTag("ARMSTRONG").name);
        System.out.println(new NameTag("GATES").name);
        System.out.println(new NameTag("刘").name);
    }
}

 

 

반응형