20130903

easy used function

  함수가 없는 프로그래밍은 생각할 수 없다. 어떤 프로그래밍 언어를 선택하든지 함수가 기본 단위가 된다. 이렇게 기본이 되는 함수를 잘 만들어야 이해하기 쉬운 코드를 만들수 있다는데 다들 공감할 것이다. 이야기를 꺼내기에 앞서 학부시절 에피소드를 소개하고자 한다.

  학부시절 과제로 만든 C언어 프로그램 코드는 그 크기는 작고 수준도 낮았겟지만 다양한 코드를 읽어볼 수 있는 좋은 기회였던 것 같다. 어려운 과제가 나오면 삼삼오오 모여서 간밤에 서로 만들었던 코드를 비교해 보곤했다. 다른 사람은 200~300줄로 구현한것을 A4 1장에 구현한 친구를 보고 다들 신기해한 기억이 난다. 성적도 좋아 장학금을 받는 친구들이 대부분이다. 돌려보면 동작 속도도 빠르다. '나와는 다른 세계의 똑똑한 친구가 만든 코드라서 내가 이해할 수 없는 것이다'라고 다들 생각했던 것 같다. 우리는 컴퓨터가 잘 이해하는 코드에 무슨 문제가 있겠는가 생각했다. 우리가 그들보다 지적 수준이 낮아서 그들의 고귀한 코드를 이해하지 못할 뿐이라고 자책할뿐이였다.

  중복을 줄이다보면 코드가 줄어들고 좋은 코드가 된다고 한다. 그러나 앞에서 소개한 이야기는 그러한 것과는 다른 이야기 이다. 주석도 없이 변수는 3~4자의 알파벳으로 되어 있고 for, if등은 가로로 가지런히 줄을 서있다. 창의적인 압축코드였다. 얼마전 TV에서 초코렛에 밥을 비벼먹는 사람이 소개되었다. 당신의 애인이 창의력을 발휘해서 초코렛 비빔밥을 정성껏 만들어준다면 맛있게 먹겠는가? 한두번은 몰라도 매일은 힘들것 같다. 코드도 그렇다. 내가 만든다고 내 마음대로 해서는 안된다.
  자, 그렇다면 좋은 함수는 어떤 것일까? 물론 다들 생각이 조금씩 다를 것이다. 나는 로버트 마틴의 주장에 공감하고 그의 주장을 이야기 하고자 한다.


1. 한가지만 하는 짧은 코드가 좋은 코드이다.
  십수만 라인의 C 프로그램을 만들다가 어느 날 3000 라인이 넘는 함수를 몇시간만에 작석하고는 무척 괴로워했던 기억이 있다. 매우 복잡한 자료구조와 정교한 포인터들로 범벅이된 코드이기 때문에 함수들로 추상화하면 길을 잃을 것 같았다. 동료들은 내 C언어 실력이 뛰어나다는 말을 해주었지만 동료들이 이해할 수 없는 암호를 만들어 버린 것 같아 괴로웠다. 그 함수는 무척 빨랐고 메모리도 효율적으로 사용했고 수년간 버그가 발견되지 않았다. 그러나 난 그 함수를 읽기 쉽게 할 수 없었다. 아이스크림을 바닥에 떨어뜨리고 어쩔줄 몰라 울어버리는 아이와 바를바 없었다.
  어느날 3000줄 짜리 함수를 고쳐야 한다고 생각해보자. if와 for가 여기 저기 4중 5중 중접되어있다. 주석은 있는데 도대체 왜 달아놨는지 한심하기만 하다. 일주일을 봤지만 이해가 안간다. 고쳐보니 프로그램이 뻗어버린다. 원인을 알 수 없다. 이런 상황을 힘들게 이겨낸 것을 무용담처럼 후배들에게 떠들지 말자. 그냥 똥을 치웠을뿐이다.
  이쯤되면 긴 함수가 나쁘다는 생각이 들것이다. 그렇다면 길다는 기준이 있어야 하는데 그게 참 어렵다. 길다는 말자체가 모호하기 짝이 없다. 예전에 어떤 분은 자기 모니터가 해상도가 얼마라서 font크기를 몇으로 하니 한 화면에 몇줄이 들어가서 함수 길이를 그정도 이내로 한다고 한다. 쉽게 말해 자기 컴퓨터에서 한 화면에 나오면 좋아한다. 좋아보이는가? 그러나 난 이것도 길다고 생각한다. 함수 본문은 5줄을 넘으면 안된다. 애정남처럼 딱 정했으면 좋겠다. 사람은 한번에 6~8개 정도를 기억하고 처리할 수 있다고 한다(심리학에서 이 단위를 chunk라고 한다). 우리의 메모리 용량이 7인것이다. 메모리보다 큰 파일을 한번에 읽으면 어떻게 되는지 프로그래머라면 잘 알것이다. 함수의 signature 선언으로 함수 앞뒤에 2줄을 사용한다. 5+2=7 . That's it.
  경험이 많지 않거나 관성으로만 코딩을 해오신 분이라면 당황할지도 모르겠다. 필자를 사기꾼이라 하고 싶은가? 자신이 할 수 없으니 불가능하다고 생각하지 말자. 잘 짜여진 코드는 하나의 작업 책임만 가진다. if문 안에 주절주절 떠들지 않으면 된다. try catch 구문속에 수다를 널어놓지 말자. 한줄씩이면 충분하다. if else 구문은 1줄씩 해서 총 4줄로 구현하는 식으로 추상화 레벨을 높일 수 있다. 그런식으로 했가다는 함수가 너무 많아져서 관리 할 수 없다고? 1000줄짜리 함수 하나와 5줄자리 함수 50개중에 어느것이 더 이해하기 쉽겠는가? (200개가 아니라 50개라고 한것은 대부분 추상화 수준을 올리면 중복이 줄일 기회가 많아지기 때문이다.)


2. "switch"는 묻어버려라.
  "switch"는 작게 만들기 어렵다. "switch"가 있으면 한가지가 아니라 N가지 일을 하는 함수가 된다. 그렇다고 쓰기 않으려고 하니 너무 불편하다. 추상화 레벨의 저수준에서만 딱한번씩만 switch를 사용하여 factory를 구현하고, 분기에 따른 로직은 다형성으로 구현하자. 이러라고 OOP가 있는 것이다. 지저분한 switch는 땅속 깊이 묻어버리고 우리는 고상한 코드만 볼 수 있을 것이다. 물론 어쩔수 없이 상위 로직에서 switch를 써야 할 일도 있을 것이다. 하지만 정말 불가피한지 고민해봐야 한다.


3. 함수의 매개변수는 2개 이하
  매개변수가 없는 함수는 최고다. 1개가 있어도 좋다. 2개까지는 봐주자. 3개는 불쾌하다. 4개의 매개변수를 가진 함수를 만들려면 만들지 말고 차라리 옆사람에게 키보드를 넘겨라. 그런건 필요 없다.
  정수 8개를 매개변수로 받는 함수를 생각해보자. 인기가 좋아 여기 저기 호출된다고 생각해보자. 인기가 좋다니 나도 한번 호출해본다. 8개중에 하나만 순서가 틀려도 찾기 힘든 버그가 된다. 아주 긴장해야 한다. 살짝 녹은 겨울강의 얼음을 밟고 건너듯 조심해야 한다. 다른 사람도 이러게 호출했을 것이다. 그러나 누군가는 얼음이 깨져 겨울 강물에 빠지게 된다. 당신 잘못이 아니다. 호출하기 어렵고 실수하기 쉽게 만든 사람이 나쁘다. 그러나 곤경에 빠진건 당신이다.


4. 매개변수에 boolean은 절대 금지
  매개변수에 true나 false가 들어가서 호출된 함수를 보면 눈살을 찌푸리게 된다. 이런 양다리 바람둥이 같으니!! 이 함수는 분명 2가지 일을 한다. 함수는 칫솔과 같다. 친구와 같이 쓰지 말고 각자의 것을 마련해라. 왜냐고? 더러우니까. 사소한 이유로 당신의 코드를 더럽히지 말자.
  saveReportFile(true);
  이런 코드가 무슨일을 짐작도 하기 어렵다. 이런 코드는 양다리 바람둥이니까.


5. Side effect를 발생시키지 말자.
  함수 이름과 관련 없는 일은 하지 말자. "isValidUser(User user)" 이 함수는 무엇을 하는 함수일지 짐작이 될 것이다. 올바른 사용자가 아니라고 user 객체의 필드들을 모두 null로 초기화 해버린다면 어떨까? 사용자가 알수 없게 저렇게 캡슐화 하는게 진정한 추상화라 생각하는가? 저 함수를 쓸 때마다 뒤에 숨은 저런 기능을 기억해야 한다. 난 그런 것들을 모두 다 기억할 정도로 머리가 좋지 않다. 물론 기억이라는 것도 저런 side effect 때문에 몇시간을 디버깅으로 고생한 다음이겠지만 말이다.
  이 예제는 몇가지 의미가 더 있다. 매개변수로 넘어온 객체의 내부상태를 변경하려 하지 말라는 것이다. 출력은 매개변수로 하지 말고 return하라는 말이다. 그리고 함수는 질문에 답하거나 어떤 행위를 하거나  하나만 해야 한다.
  boolean setSession(Session session);
  이 함수는 뭘 하는 함수 일까? session 객체값을 이용해 세션을 변경하는 것일까? 아니면 세션이 설정이 되었는지 확인 하는 것일까? 반환값은 세션 설정에 성공하면 true일까? 아니면 세션이 잘 설정되어 있으면 true일까? 이런 저런 짐작으로 저 함수의 코드를 열어봐야 한다면 함수를 작게 나눈 의미가 없다. 그냥 긴 함수로 만들어라. 작은 함수들로 나누면 이해하기 어렵다고 불평하는 당신은 추상화에 대한 무지함을 드러낼 뿐이다.


6. 오류 코드보다는 예외 사용하자
  함수가 성공하면 1을 반환하고 실패하면 0을 반환하는 것과 비슷한 코드를 많이 보았다. 그런데 누구는 성공하면 0이고 실패하면 -1이란다. 실패하면 100~200 사이의 오류 코드를 반환하고 싶을 수도 있다. 오류 코드를 잘못 체크하거나 안해도 컴파일러는 알수 없다. 이런 것 때문에 발생하느 버그는 지옥이다. 이런것때문에 생일날 친구들과 약속을 잡았는데 새벽 2시까지 야근한다고 생각해보자. GG
  자바에서 예외는 checked exception과 runtime exception에 대해 구분해서 사용하는 것이 중요하다.  그러나 이 글의 주제에서 벗어나므로 나중에 기회가 다루면 예외의 사용법에 대해 포스팅하겠다. 예외를 사용하면 에러 체크를 가시화 할 수 있고 함수 호출부에서 에러코드를 체크하는 지저분한 if else의 반복을 제거하여 가독성이 올라간다는 것만 기억하면 좋겠다.

마치며..
  짧고 읽기 쉽게 잘 추상화 해놓은 코드를 보면 놀랍기만 하고 나는 저런 것을 절대 만들수 없을 것 같다는 생각을 할 수 있다. 원래 코드란게 그렇다. 그러나 대부분의 좋은 코드는 한번에 작성되는 경우는 거의 없다. 글짓기와 비슷하다. 우선 말이 되게 하고 고쳐보자.


출처 : http://dsmoon.tistory.com/entry/%EC%9D%BD%EA%B8%B0-%EC%89%AC%EC%9A%B4-%ED%95%A8%EC%88%98

댓글 없음:

Articles