본문 바로가기
오늘은 뭘 배울까?/책

좋은 코드란 무엇일까?

by Kim Juhwan 2022. 7. 31.

1. 면접에서 질문을 받았다
2. 책에서 답을 찾아보자
3. 책에 있는 좋은 예시들
   3-1. 이름에 정보 담기
   3-2. 주석
   3-3. 코드 분량 줄이기

   3-4. 테스트와 가독성
4. 느낀 점 💡

 

 

 


 

 

1. 면접에서 질문을 받았다

아마 저 문장 때문에 질문을 받지 않았을까.

 

"본인이 생각하는 좋은 코드와 나쁜 코드는 무엇인가요?"

이번에 면접에서 받았던 질문이다.

평소 나름의 규칙대로 고민을 하며 코드를 짜 왔기에 대답을 잘할 수 있을 거라 생각했다.

하지만 지금 내 답변을 돌이켜보면 만족스럽지 못한 대답이였던 것 같다.

뭔가 딱 정의를 내려서 말하기 애매하다고 해야할까.

그래서 다시 한번 고민해보기로 했다.

 

나에게... 좋은 코드란...?

 

2. 책에서 답을 찾아보자

 

원래 같았으면 인터넷에서 정보를 찾아보려고 했을 텐데, 이번에는 책에서 찾아보기로 했다.

요새 면접을 보러 다니면서

"독학할 때 주로 어떤 방식을 통해서 하세요?", "책은 안 읽으시나요?"류와 같은 질문을 자주 받았다.

나 포함 모든 지원자들 입에서 책으로 공부한다는 답변은 들어볼 수 없었다.

 

나도 블로그를 운영하는 입장이지만 사실 인터넷에 돌아다니는 글에는 잘못된 정보들이 많다.

(내가 작성한 글들도.. 분명 그런 것들이 있을 것이다)

책은 인터넷에 쉽게 쓰이는 글보다 훨씬 더 많이 고민하고

훨씬 더 많은 검증절차를 거쳐 쓰이기 때문에 책으로 공부하는 습관을 길러보려고 한다.

 

3. 책에 있는 좋은 예시들

책에는 챕터별로 예시를 들며 어떤 코드가 좋고 나쁜지를 보여주고 있다.

쭉 읽으면서 공감 가거나 내 코드에도 적용해봐야겠다 싶은 것들을 정리해보았다.

 

3-1. 이름에 정보 담기

  • 특정한 단어 고르기 (30p ~ 32p)
fun getPage(url) {
    ...
}

 

책에서는 위의 함수명이 무의미한 단어를 포함하고 있다고 소개하고 있다.

여기서 위 함수는 로컬 캐시, 데이터베이스, 인터넷 중 어디서 값을 가져오고 있는 것일까?

만약 인터넷에서 값을 가져오고 있다면 'get'은 이 의미를 충분히 내포하고 있을까?

아니다. 오히려 FetchPage()나 DownloadPage()가 더 의미 있을 것이다.

 

책에서는 이와 비슷한 단어들을 예시로 들며 대안을 제시하는데

무의미하거나 혼동될 수 있는 예시를 보며 평소에 내가 사용했던 것들이라 정곡을 찔린듯한 기분이었다.

 

  •  루프 반복자 (35p)
for (int i = 0; i < clubs.size(); i++) {
    for (int j = 0; j < clubs[i].members.size(); j++) {
        for (int k = 0; k < users.size(); k++) {
            if (clubs[i].members[k] == users[j]) {
                ...
            }
        }
    }
}

평소에 알고리즘 문제를 풀 때 i, j, k를 활용하곤 했는데

그때마다 항상 저 if문을 사용할 때 너무 헷갈렸던 기억이 있다.

여기에 i가 들어가야 하나..? 저기에 k인가..?

 

책에서는 이를 하지 말아야 할 방법이라고 소개하고 있다.

차라리 club_i, members_i, users_i를 사용하든지

아니면 이를 줄여서 ci, mi, ui로 사용하라고 말한다.

 

// 버그를 찾기 어렵다
if (clubs[i].members[k] == users[j])

// 버그를 찾기가 쉽다
// ui와 mi 위치가 바뀌었군!
if (clubs[ci].members[ui] == users[mi])

확실히 이렇게 사용하니 어디에 어떤 인덱스가 들어가야 하는지 한눈에 보인다.

가급적 이름에 의미를 담는 노력을 해야겠다고 느꼈다.

 

3-2. 주석

  • 일관성과 간결성을 위해 줄 바꿈 재 정렬하기 (65p)
public class PerformanceTester {
    public static final TcpConnectionSimulator wifi = new TcpConnectionSimlulatr(
        500, /* kbps */
        80, /* milisecs 대기시간 */
        200, /* 흔들림 */
        1 /* 패킷 손실 % */);
        
    public static final TcpConnectionSimulator t3_fiber =
        new TcpConnectionSimlulatr(
            45000, /* kbps */
            10, /* milisecs 대기시간 */
            0, /* 흔들림 */
            0 /* 패킷 손실 % */);
        
    public static final TcpConnectionSimulator cell = new TcpConnectionSimlulatr(
        100, /* kbps */
        400, /* milisecs 대기시간 */
        250, /* 흔들림 */
        5 /* 패킷 손실 % */);
}

책에서는 위의 코드를 아래와 같이 수정할 수 있다고 소개하고 있다.

 

public class PerformanceTester {
    // TcpConnectionSimulator     (처리량,   지연속도,   흔들림,   패킷손실)
    //                             [kbps]     [ms]      [ms]    [percent]
    
    public static final TcpConnectionSimulator wifi =
        new TcpConnectionSimlulatr(500,        80,      200,       1);
        
    public static final TcpConnectionSimulator t3_fiber =
        new TcpConnectionSimlulatr(45000,      10,      0,         0);
        
    public static final TcpConnectionSimulator cell =
        new TcpConnectionSimlulatr(100,        400,     250,       5);
}

처음에 나는 이 코드를 보고 감탄했다.

파라미터 값들 사이가 띄어쓰기 되었지만 오히려 그렇게 함으로써 가독성을 챙기는 방법이 굉장히 신선했다.

정말 간단한데도 생각해보지 못한 방법이라 좋은 꿀팁인 것 같다.

 

  • 설명을 위한 설명을 달지 말라 (78p ~ 80p)

이 페이지에서는 무가치한 주석을 달지 말라고 이야기하고 있다.

함수의 선언과 주석 내용이 실질적으로 일치한다면 이는 가치가 없다는 것이다.

 

또, 나쁜 이름에 주석을 달지 말라고 나와있다.

함수 이름에서 그 의미를 파악할 수 없다면 이는 나쁜 이름이고

나쁜 가독성을 메우려고 노력하는 '애쓰는 주석'은 피해야 한다는 것이다.

 

그러면서 예시로 들어준 공식이 참 마음에 들었다.

좋은 코드 > 나쁜 코드 + 좋은 주석

 

3-3. 코드 분량 줄이기

  • 라이브러리에 친숙해져라 (196p)

평균적인 수준의 sw 개발자는 하루에 출시할 수 있는 수준이 코드를

평균 10줄 정도 작성한다고 한다.

출시할 수 있는! 코드 말이다.

라이브러리 안에 있는 코드는 한 줄 한 줄 모두 엄격한 설계, 디버깅, 재작성, 문서화, 최적화, 테스트를 거친 코드들이다.

라이브러리를 사용하면 시간도 절약하고, 코드 양도 줄어든다.

 

그래서 이 책에서는 다음과 같은 조언을 하고 있다.

매일 15분씩 표준 라이브러리에 있는 모든 함수/모듈/형들의 이름을 읽어라

 

모든 라이브러리를 달달 외울 수 없겠지만

적어도 "아 맞다! 전에 비슷한 API를 본 적이 있는데..."라며 찾아볼 수 있으니 말이다.

 

이 내용을 읽고 내가 코틀린에서 제공하는 표준 라이브러리의 몇 퍼센트를 활용하고 있을까 생각해봤다.

당장 String과 관련된 함수도 사용하는 게 항상 정해져 있고 모르는 함수가 수두룩 하다.

이 챕터를 보고 내가 라이브러리를 적극 활용하고 있지 못하고 있구나를 새삼 깨달았다.

 

3-4. 테스트와 가독성

  • 테스트 함수에 이름 붙이기 (216p)

이 챕터에서는 유닛 테스트와 관련된 이야기를 하고 있는데

그중에서도 이 부분이 가장 눈길이 갔다.

 

책의 앞부분에서는 함수 명의 길이가 어느 상황에 얼마나 길어야하고 짧아야 하는지를 설명하는 부분이 나온다. (42p)

하지만 테스트 코드를 작성할 때는 "함수명을 너무 길지 않게 해야 한다"는 원리가 적용되지 않는다고 설명하고 있다.

함수명의 길이보다는 상황을 잘 설명할 수 있느냐에 초점을 맞춰야 한다.

 

fun Test_SortAndFilterDocs_BasicSorting() {

}

fun Test_SortAndFilterDocs_NegativeValues() {

}

책에서는 잘 작성된 예시로 위와 같은 함수를 보여준다.

Test_<함수이름>_<상황>() 과 같은 형태를 이용하는 것을 추천하고 있다.

 

코틀린의 경우 한글로도 테스트 코드 함수명을 작성할 수 있어서

위 규칙과 같이 사용하면 가독성이 좋은 테스트 코드를 작성할 수 있을 것 같다.

 

4. 느낀 점

책에 있는 내용을 모두 인용하면 저작권에 문제가 있다고 해서

포스팅에는 일부 내용만을 담았지만 책을 전부 읽었다.

여러 팁들도 알게 되고 좋은 코드와 나쁜 코드의 예시를 다양하게 접할 수 있어서 도움이 많이 됐다.

 

  • 주석에 대한 나의 생각이 바뀌었다.

나는 평소에 주석이 필요 없는 코드가 좋은 코드라고 생각했었다. (면접에서도 그렇게 대답하기도 했고)

설명 없이도 함수 명에서 어떤 기능을 하는 함수인지 한눈에 알 수 있어야 하고

구현부를 보면 스르륵 읽히는 코드가 좋은 코드라고 생각했다.

 

좋은 코드 > 나쁜 코드 + 좋은 주석 인 것은 맞지만, 주석이 필요하지 않다는 것은 잘못된 생각이었다.

좋은 코드 + 좋은 주석 > 나쁜 코드 + 좋은 주석 나는 이렇게 해석할 수 있음을 간과했다.

좋은 주석을 다는 방법에 대해서 배웠으니 프로젝트에 적용을 해봐야겠다.

 

  • 좋은 코드란...

책에서는 좋은 코드를 작성하기 위한 여러 방법들을 제시하고 있다.

그리고 이 책을 관통하는 핵심 아이디어는

"코드는 이해하기 쉬워야 한다", "이해할 때 걸리는 시간을 최소로 만들어야 한다"이다.

결국 책 제목처럼 읽기 좋은 코드를 작성해야 한다는 것이다.

어떻게 보면 추상적인 답변일 수도 있는데

이에 대해 자세히 고찰을 작성한 블로거분이 계셔서 링크를 남긴다.

 

 

좋은 글을 작성하기 위해서는 좋은 책을 많이 읽어야 하는 것처럼

좋은 코드를 작성하기 위해서는 좋은 프로젝트를 많이 들여다봐야겠다.

오늘의 공부 끝~!

 

 


📘 참고한 자료

  • 읽기 좋은 코드가 좋은 코드다 - 더스틴 보즈웰, 트레버 파우커

 

 

반응형

댓글