3.4 닌자 코드

컬러·2021년 1월 27일
1

JavaScript

목록 보기
23/27

생각없이 배우기만 하면 얻는 것이 없고,
생각만 하고 배우지 않으면 오류나 독단에 빠질 위험이 있다.

공자

닌자라 불리던 전설 속 개발자들은 유지보수 담당 개발자를 혹독하게 훈련하고자 다양한 편법을 사용하곤 했다.

구루 급의 코드 리뷰 전문가들은 테스트 코드에서 이런 편법을 발견하면 박수갈채를 보내곤 했다.

어떨 때 초보 개발자가 닌자보다 더 적극적으로 나서서 이런 편법을 사용하곤 한다.

닌자가 사용하던 편법을 잘 살펴보고, 자신은 닌자인지, 코드 리뷰어인지, 초보 개발자인지 판단해보자.

  • 닌자 같은 무림 고수가 되는 게 꿈인 개발자는 많지만, 그 목표를 달성하는 건 아주 극소수이다.

코드 짧게 쓰기

가능한 한 코드를 짧게 써서, 당신이 얼마나 똑똑한 사람인지 보여주기.
지엽적인 문법 지식 등을 총동원하면 코드 양을 획기적으로 줄일 수 있다.
조건부 연산자 '?' 를 사용한 예시를 살펴보자.

i = i ? i < 0 ? Math.max(0, len + i) : 1 : 0;

이렇게 코드를 작성해놓으면 코드를 접하는 다른 개발자들은 아주 즐거운 시간을 보낼것이다. i가 나타내는 게 뭔지 파악하는데 꽤 많은 시간을 소모하고, 결국엔 답을 찾지 못해 찾아올 것이다.

그럼 어깨를 한번 으쓱이며 이렇게 말해주자. 코드를 길게 작성하는 것보단 짧게 작성하는 게 일을 잘하는 비결이라고.

글자 하나만 사용하기

도는 숨어서 이름도 없지만, 오직 도만이 스스로를 잘 빌려주고 만물이 성하도록 돕는다.
노자(도덕경)

글자 하나만 사용해 변수 이름을 짓자. a,b,c 처럼 말이다.
변수 이름이 잛아지면 무림 고수가 숲속 깊은 ㄱ소에 몸을 숨기는 것처럼 변수의 정의된 곳을 찾더라도 변수 ab가 무엇을 의미하는지 절대 "해석"할 수 없게 된다.

아, 예외 상황이 하나 있다. 진짜 닌자들은 "for"문에서 변수 i를 절대 사용하지 않는다. 다른 데선 i를 아무 거리낌 없이 사용하지만, 반복문에서만큼은 사용하지 않는다. 대신 for 문에선 생소한 xy 같은 변수를 사용한다.

남들이 잘 사용하지 않는 변수를 반복문에서 쓰면 아주 멋져 보인다. 특히나 반복문이 한, 두페이지를 차지할 정도로 길면 이런 편법이 더 빛을 발한다.(반복문을 가능한 길게 작성하는 것도 닌가자 되는 훈련 중 하나이다.) 당신의 코드를 읽을 누군가가 카운터 역할을 하는 변수 i 대신 x가 사용되었다는 걸 빨리 눈치채지 못하도록 하자.

약어 사용하기

팀에서 한 글자 짜리 변수나 모호한 변수명을 사용하지 못하게 하는 제약이 있다면 약어를 쓰는 기지를 발휘하자. 변수명은 짧을수록 좋다.
예시:

  • list - lis.
  • userName - ua.
  • browser - brsr.
  • 등등..

모든 걸 줄여서 당신의 코드를 읽을 가치가 있는 직감이 뛰어난 개발자만 유지보수를 담당 할 수 있게 해 놓자.

포괄적 명사 사용하기

네모가 아주 크면 마치 모서리가 없는 것처럼 보이며,
큰 그릇은 천천히 만들어지고,
소리가 너무 크면 들리지 않는 것 같고,
거대한 형상은 형태가 없다.

노자(도덕경)

무언가를 명명할 땐 obj, data, value, item, elem 같이 다양한 개념을 포괄할 수 있는 명사를 사용하자. 포괄하는 개념이 많은 명사일수록 더 좋다.

  • data는 가장 이상적인 변수명이다. 가능한 모든 곳에서 이 변수명을 사용하자. 변수가 실제로 담고있는건 데이터 니까
    그런데 변수명 data가 이미 선정된 상황이라면 어떻게 해야할까? 그럴 땐 value 를 사용하면 된다. 이 단어 역시 추상도가 높아 많은 개념을 포함할 수 있고, 변수에 값(value) 이 저장된다는 건 변함없는 사실이다.

  • str, num 같이 자료형과 연관된 변수명을 사용하자.
    닌자 훈련소에 갓 입회한 개발자는 이 방식을 보고 고개를 갸우뚱 할 것이다. 이게 훈련 비법이라고? 네, 맞습니다!

이렇게 변수명을 만들면 누군가 당신의 코드를 봤을 때, 어떤 것도 유추할 수 없게 된다! 작성자 당신만 자료형 정보를 보고 어떤 값이 저장될지 유추 할 수 있다. 그 누구도 해석할 수 없는 변수가 만들어졌다. 목표를 달성했다!

사실 자료형을 파악하는 건 식은 죽 먹기이다. 디버깅 툴을 사용하면 된다. 그런데 변수의 의미는 파악이 쉽지 않다. 변수 str이나 num이 실제 어떤 의미를 가진 변수인지 코드만 보고는 쉽게 알 수 없다.

독심술을 배우지 않는 이상 그 누구도 당신이 작성한 변수의 의미를 파악할 수 없게 된다.

새로운 변수명이 더는 떠오르지 않는다면 어떻게 해야 할까? data1, item2, elem5 처럼 옆에 숫자를 붙여주면 된다.

철자가 유사한 단어 사용하기

주의력이 깊은 개발자만 닌자가 작성한 코드를 읽을 자격이 있다. 이런 개발자는 어떻게 감별할 수 있을까?

여러 가지 방법이 있는데 그중 하나는 datedata같이 유사한 철자를 가진 단어를 조합해 사용하는 것이다.

가능한 모든 곳에서 이런 조합을 사용해 주의력이 떨어지는 개발자를 걸러내자.

이렇게 작성해 놓으면 닌자조차도 자신이 작성한 코드를 읽는 데 시간이 많이 소모될 것이다. 차 한잔 마시면서 코드를 읽으면 되니까 문제는 없을 거다.

동의어 사용하기

모든 일 중 가장 어려운 일은
어두운 방에서 검은 고양이를 찾는 일이다.
특히 그 방에 고양이가 없을 때에.

공자

유사한 뜻을 가진 단어 여러 개를 같은걸 명명하는 데 사용해서 당신의 풍부한 어휘력을 은연중에 드러내 보자.

스크린에 메시지를 보여주는(display) 동작을 수행하는 함수라면 접두어 display…를 사용해 displayMessage라고 이름을 짓고, 사용자 이름(user name)을 화면에 보여주는(show) 함수는 접두어 show…를 써 showName이라고 이름을 짓자.

코드를 읽는 사람이 메시지를 보여주는 것과 사용자 이름을 보여주는 것에 미묘한 차이가 있을 것 같다고 착각하게 해야 한다. 실제론 아무런 차이가 없지만 말이다.

다른 동료 닌자에게도 이 방법을 전파하면 효과는 더 좋아진다. 무언가를 “보여주는(showing)” 함수를 만든다고 가정했을 때, John이 접두어 display...를 사용했다면, Peter는 다른 접두어 render..를, Ann은 또 다른 접두어 paint...를 사용하게끔 말이다. 코드가 더 다채로워지고 흥미를 유발한다.

해트 트릭도 달성하였다!

아! 중대한 차이가 있는 두 함수에 같은 접두어를 사용하는 것도 추천한다.

프린터(printer)를 사용하는 함수는 printPage(page), 화면에 문자(text)를 출력(print)해 주는 함수는 printText(text)라고 명명하면 된다.

유지보수를 담당한 지 얼마 안 된 개발자가 코드를 곱씹을 수 있도록 여러 곳에 훈련 장치를 배치해 놓아야 한다. printMessage라는 함수를 보았을 때 "이 함수는 어디에 메시지를 출력하는 걸까? 프린터를 이용해 메시지를 출력하는 걸까? 아니면 그냥 화면에 메시지를 출력해주는 걸까?"라고 생각하게끔 말이다. 함수 printMessage(message)가 새 창에 메시지를 띄우는 동작을 담당한다면 훈련 강도는 더 높아질 것이다.

이름 재사용

무언가를 만들기 시작하면,
그에 걸맞은 이름이 생기니,
이미 이름이 있다면,
무릇 멈출 줄 알아야 하고,
멈출 줄 알면 위태롭지 않을 수 있다.

노자(도덕경)

변수 선언은 정말 필요한 경우에만 하자.
새로운 값을 저장할 때 기존 변수를 활용하면, 변수 선언을 최대한 피할 수 있다.

함수를 구현 중이라면 내부 변수를 선언하지 않고, 매개변수에 넘어온 값만 사용하자.

변수에 현재 어떤 값이 있는지, 값의 유래는 어디인지 쉽게 파악하지 못하게 술수를 부려 개발자의 직관력과 암기력을 높여주자. 이름을 재사용하면 코드를 한줄 한줄 읽어가면서 동작 과정을 분석해야 하고, 분기 모두를 다 따라가면서 상황에 따라 값이 어떻게 변하는 지도 관찰해야 하므로 직관력이 부족한 개발자에게 도움이 된다.

함수나 반복문 중간에 할당 값을 은밀히 바꾸면 훈련 강도를 좀 더 높일 수 있다.

function ninjaFunction(elem) {
  // 매개변수로 받아온 elem을 이용한 코드

  elem = clone(elem);

  // elem의 복제(clone)본을 이용한 코드
}

elem = clone(elem); 아래에 있는 코드를 수정하던 개발자는 아마 당신의 탁월함에 놀라움을 금치 못할 것이다. 디버깅으로 코드를 면밀히 검토한 후에서야 본인이 복제본을 이용해 작업하고 있다는 걸 깨달을 수 있으니까.

내공이 높은 닌자도 이렇게 작성된 코드 앞에선 속수무책이기 때문에, 극기 훈련이 필요할 때 이 방법을 아주 추천한다.

재미로 언더스코어 사용하기

_name이나 __value처럼 변수명 앞에 ___(언더스코어, 밑줄 표시)을 붙이자. 코드 작성자만 언더스코어가 무엇을 의미를 알게 해도 좋고, 장난으로 붙이거나 의미를 계속 바꿔가면서 붙이는 건 더 좋다.

이렇게 하면 일거양득이다. 코드 길이가 늘어나 가독성이 떨어지는 효과가 있고, 동료 개발자들은 언더스코어의 의미를 파악하는데 품이 많이 들게 된다.

똑똑한 닌자라면 코드 한 영역에 밑줄 표시를 몰아서 쓰고, 다른 곳에서는 쓰지 않는 트릭을 쓰기도 한다. 이렇게 하면 코드가 에러에 취약해지는데, 다른 개발자를 훈련하기엔 이만한 방법이 없다.

과장 형용사 사용하기

superElement, megaFrame, niceItem처럼 개체 앞에 적절한 형용사를 붙여 해당 개체가 얼마나 멋진지 알려주자.

사람들은 아무 의미도 없이 붙여놓은 형용사super.., mega.., nice.. 등을 보고, "분명 무슨 의미가 있을 거야"라고 생각하며 눈에 쌍심지를 켜고 코드를 분석하려고 할 것이다.

외부 변수 덮어쓰기

빛 속에선 어둠을 볼 수 없고,
어둠 속에선 빛나는 곳에 있는 모든 것을 볼 수 있습니다.

관윤자

변수 이름을 짓는데 골머리를 썩이지 말고, 함수 내부와 외부에 동일한 이름을 가진 변수를 선언해 사용하자.

let user = authenticateUser();

function render() {
  let user = anotherValue();
  ...
  ...함수 길이가 긺...
  ...
  ... // <-- 개발자는 user와 관련된 이 부분의 코드를 수정해야 함
  ...
}

함수 render가 긴 상황에서 user와 관련된 함수 하단부 로직만 수정해야 하는 상황이라고 해 보자. 개발자는 코드를 처음부터 읽지 않고 해당 로직이 있는 부분부터 읽을 확률이 높습니다. 그럼 첫 줄에서 user가 다시 정의되었다는 것을 놓치게 된다.

함수 내부에서 변수 user를 다시 정의했음에도 불구하고, 개발자는 user가 외부 변수(authenticateUser()의 호출 결과)인지 착각하며 작업을 진행할 겁니다. 함정이 참 깊다. 디버거가 나타날 타이밍이다.

부작용이 있는 코드 작성하기

isReady(), checkPermission(), findTags()같은 함수들은 단순 확인용으로 사용되고 외부의 무언가를 바꾸진 않는다. "부작용"이 없는 함수들이다.

이런 함수에 본래 기능을 넘어선 “유용한” 기능을 더해주자.

is.., check.., find...등의 접두사가 붙은 함수가 무언가를 바꿀 수 있도록 기능을 더해주면 동료들에게 놀라움을 선사해줄 수 있을 것이다. 회사에서 당신의 입지는 더 넓어진다.

예상치 않은 결과를 반환하는 것도 한 방법이다.

checkPermission이라는 함수를 호출했을 때 반환 값이 truefalse가 아니라면 좋다. 확인 여부와 다른 정보를 함께 엮어 만든 객체를 반환해 당신의 독창성을 뽐내시기 바란다.

if (checkPermission(..))가 왜 작동하지 않는지 물어보는 개발자에게 "문서를 읽어보세요!"라고 답하며 본 페이지를 보여주자.

함수에 다양한 기능 넣기

큰 도는 이쪽저쪽 할 것 없이 어디에나 넘쳐흐른다.
노자(도덕경)

함수 기능을 확장하자. 함수가 할 수 있는 동작을 함수 이름에 한정 짓지 말자.

함수 validateEmail(email)에 유효한 이메일 주소인지 확인해 주는 기능 이외에, 잘못된 이메일을 입력했을 때 에러 메시지를 보여준다거나 메일 주소를 다시 입력해달라는 기능을 추가하자.

함수 이름에서 유추할 수 없을 만한 기능을 추가할수록 더 좋다. 진정한 닌자라면 상상력을 발휘해 그 누구도 알 수 없을 법한 기능을 추가할 수 있을 것이다.

함수 하나에 여러 기능을 욱여넣으면 코드 재사용도 방지할 수 있다.

어떤 개발자가 메시지를 보여주는 기능 없이, 이메일 주소 유효성만 확인하는 기능을 구현해야 한다고 하자. 닌자가 작성한 함수 validateEmail(email)은 두 가지 기능을 모두 하고 있기 때문에, 재사용할 수 없다는 불상사가 발생한다. 재사용 자체가 불가능해지니, 질문하는 사람도 없다는 장점이 생긴다.

요약 ✔

지금까지 소개한 모든 방법은 허구가 아니다. 숙련된 개발자들도 이렇게 코드를 작성할 때가 있다. 당신보다 경력이 많은 개발자도 예외는 아니다.

  • 편법 중 몇 개만 사용해도, 놀라운 코드를 만들 수 있게 된다.
  • 편법을 많이 사용하면 유지 보수하기가 힘들어져서 당신을 해고할 수 없게 된다..
  • 편법을 모두 사용하면 후배 개발자들에게 엄청난 영감을 심어줄 수 있다.

(이 본문에 나오는 내용 전부 쓰지말라는 말이다...)

0개의 댓글