2015년 12월 10일 목요일

모던 자바의 역습(3) 자바 코딩 스타일 변천사

이번 포스팅은 김대우 님(http://lekdw.blogspot.kr/)과 함께 진행한 동명의 웨비너의 발표 내용에 바탕을 두고 작성되었습니다. 세상에 나온지 어느덧 20년. 오랜동안 프로그래밍 언어의 절대 강자로서 세상을 호령하던 자바를 둘러싼 진실 혹은 거짓말 그리고 과거와 미래에 대하여 알아봅니다.
스크롤의 압박을 피하기 위해 이번 포스팅은 다섯 파트로 나누어 연재합니다.


모던 자바의 역습 

3. 자바 코딩 스타일 변천사


자바는 20년이나 되는 역사속에서 꾸준히 발전을 거듭해온 만큼 언어 사양 자체의 변화에 따른 코딩 스타일의 변화를 겪어왔습니다. 구식 코딩 스타일은 당장 프로그래을 작동시키는 데에는 큰 문제가 없어보이나 가독성을 떨어트리거나 어떤 경우에는 버그의 원인이 되기도 하는 등 품질 저하의 요인으로 작용하기 때문에 코딩 규약 등에 포함시켜 프로젝트나 프로덕트 전체의 코딩 스타일을 최신 스타일로 유지할 필요가 있습니다.

이번 장에서는 아직까지도 쉽게 접할 수 있는 구식 코딩 스타일의 잔재를 확인하고 최신 코딩 스타일을 적용시키는 방법에 대해서 살펴보도록 하겠습니다.

파일을 행 단위로 읽어 리스트에 저장하는 간단한 프로그램을 예로 들어보겠습니다.

C 스타일 자바private List m_list=null; // null 초기화private int read_file(String str_le_name){ // 스네이크 케이스로 메소드명을 작성    String str_line; // 지역 변수를 함수 맨 윗부분에 몰아서 정의하고 있음    List list_lines=new ArrayList();    int i_result // 함수의 처리 결과를 숫자로 반환        =read_le(str_le_name,list_lines); // 결과값을 참조에 의한 호출을 이용해 넘겨줌      if(i_result==0){ //        List list_record=new ArrayList();        for(int i=0;i<list_lines.size();i++){            str_line=(String)list_lines.get(i);            Record record=new Record();            i_result=parse_line(str_line,record); // 결과값을 참조에 의한 호출을 이용해 넘겨줌            if(i_result!=0){                return i_result; //하위함수의 결과값이 0(정상종료)이 아닐경우 이를 그대로 반환            }            list_recordord.add(record);        }        m_list=list_record;        return 0; //정상 종료 시 0 반환    }else{        return i_result;    }}

C 스타일의 자바 코드는 자바 초창기에 흔히 보였지만 오늘날에도 원래 C 언어에서 넘어온 개발자들이 흔히 고수하는 스타일이기도 합니다. 구조적 프로그래밍 패러다임이 녹아 있는 C 스타일의 자바 코드는 가독성을 크게 해칠 뿐만 아니라 객체지향적 디자인 품질도 저하 시키기 때문에 자바 코딩 스타일로서는 매우 좋지 않은 형태라 할 수 있습니다.

자바1.4 스타일
private List resultList;private List readFile(String leName) throws SystemException { //예외를 이용한 에러 처리    List lines = readFile(fileName);    List recordList = new ArrayList();    for (int i = 0; i < lines.size(); i++) {        String line = (String) lines.get(i);        Record record = parseLine(line);        recordList.add(record);    }    return recordList; //값을 반환}

자바 1.4 스타일은 지금까지도 흔히 쓰일 정도로 가장 오랫동안 자바 코딩의 표준 스타일로 자리 잡았던 코드 스타일입니다. 방금 전에 살펴보았던 C 스타일 코드와 비교해보았을 때 가장 큰 차이점은 에러 처리에 있어서 결과값이 아닌 예외를 사용한다는 점입니다. 결과값 또한 처리 결과 얻어진 데이터를 반환하고 있으며, 참조에 의한 호출은 사용하지 않게 된 점을 볼 수 있습니다. 이 밖에 메소드명이나 변수명에 스네이크 케이스대신 단어의 첫 글자에 대문자를 사용하여 가독성을 높인 낙타표기법을 이용하는 것도 중요한 포인트 입니다.

자바 7 스타일
private List<Record> readFile(String leName)throws SystemException {    List<String> lines = readFile(fileName); //제네릭을 이용한 형 지정.    List<Record> recordList = new ArrayList();    for (String line : lines) {        Record record = parseLine(line);        recordList.add(record);    }    return recordList;}


자바 7의 경우 제네릭과 foreach가 적용되어 코드가 좀 더 심플해진 것을 알 수 있습니다.

try-with-resources

자바 7에 도입된 문법 중 아직도 많은 자바 프로그래머에게 생소한 try-with-resources에 대해서 간단히 살펴보겠습니다.

방금 전 살펴 보았던 파일 읽기 예제에 예외 처리를 추가하여 보겠습니다.

private List<String> readFile(String leName) {    List<String> lines = new ArrayList<String>();    BufferedReader reader = null;    try {        reader = new BufferedReader(new FileReader(fileName));        String line;        while ((line = reader.readLine()) != null) {            lines.add(line);        }    } catch (FileNotFoundException ex) {        throw new SystemException(FILE_NOT_FOUND, ex);    } catch (IOException ex) {        throw new SystemException(FILE_READ_ERROR, ex);    }    nally {        try {            if (reader != null) {                reader.close();            }        } catch (IOException ex) {            // 이건 무시?        }    }    return lines;}

자바 개발자라면 io 예외에 대해 어디까지 처리해줘야 하는지 한 번쯤은 고민해봤을 것 입니다. 자바 7의 경우 try-with-resources 구문을 통해 이러한 딜레마를 간단히 해결할 수 있습니다.

private List<String> readFile(String leName) {    List<String> lines = new ArrayList<>();    try (FileReader in = new FileReader(fileName);            BufferedReader reader = new BufferedReader(in)) {        String line;        while ((line = reader.readLine()) != null) {            lines.add(line);        }    } catch (FileNotFoundException ex) {        throw new SystemException(FILE_NOT_FOUND, ex);    } catch (IOException ex) {        throw new SystemException(FILE_READ_ERROR, ex);    }    return lines;}

위의 예제처럼 파일을 읽어들여 리스트 형태로 반환하는 것뿐이라면 자바 7의 NIO2에 새로 추가된 API인  File.readAllLines를 이용해 다음과 같이 간단히 처리할 수도 있습니다.

private List<String> readFile(String leName) {    try {        return Files.readAllLines(Paths.get(fileName), Charset.defaultCharset());    } catch (IOException ex) {        throw new SystemException(FILE_READ_ERROR, ex);    }}


새로운 코딩 스타일에 적응하기

사용하는 언어의 새 버전이 등장할 때마다 기대감에 설레이기도 하지만 한편으로는 새 스타일에 적응하는 것에 대한 부담감이 있는 것 또한 사실입니다. 필자의 경우 새 코딩 스타일에 적응하는 방법으로 다음과 같은 원칙을 세워놓고 있습니다.
  • 프로젝트 도중에는 언어의 버전이 바뀐다 하더라도 특별한 경우가 아닌 이상 코딩 표준은 변경하지 않는다.
  • 새로운 코딩 스타일의 적용은 개개인이 실시하는 것이 아닌 코딩 표준(coding conventions)에 먼저 적용한 후에 팀이나 프로젝트 전체에 일괄적으로 적용시킨다.
  • 코딩 스타일의 준수 여부는 가급적 Checkstyle이나 SonarQube와 같은 정적 해석툴을 이용해 기계적으로 실시할 수 있도록 하며, 정적 해석툴이 지원하지 못하는 범위는 코드 리뷰를 통해 준수 여부를 확인한다.