0w0

초보 프로그래머가 범하는 실수들(The Mistakes I Made As a Beginner Programmer)

초보 프로그래머 범하는 실수들을 특정해 그것을 피하는 습관을 배우는 방법.

우선 처음으로 말해둘 것이 있습니다.

실수를 하는게 나쁘다는 전제로 쓴 글이 아닙니다.

오히려 실수를 스스로 깨닫고, 징후를 간파해, 피하기 위한 글입니다.

나는 과거에 이런 실수를 범해 여러가지를 배웠습니다.

지금은 이것을 피하기 위한 코딩을 습관으로 가지고 있습니다.

여러분도 그렇게 할 수 있습니다.

순서는 무작위입니다.

1. 설계하지 않고 구현한다

품질 높은 컨텐츠는 용의주도하게 작성하지 않으면 안됩니다.

그러기 위해 신중한 사고와 연구가 필요합니다.

품질 높은 프로그램도 예외가 아닙니다.

좋은 프로그램은 적절한 흐름에 의해 만들어집니다.

생각, 연구, 계획, 구현, 검증, 수정.

아쉽게도 전부를 한 번에 표현할 적당한 말은 없습니다.

각각의 프로세스에 어느 정도의 비중을 둘까, 적절한 가중치를 검토하는 습관을 만드는 것이 중요합니다.

내가 초보일 때 범한 큰 실수는 생각, 연구없이, 갑자기 코드부터를 적는 것이었습니다.

이것은 작은 애플리케이션에서는 유효한 수단일지 모르나, 대규모 애플리케이션에는 엄청난 악영향을 끼칩니다.

생각하기 전에 말해 후회한 적이 있을지도 모릅니다.

이와 같이 생각하기 전에 코딩하는 것을 후회할지 모릅니다.

생각할 필요가 있습니다.

코딩도 생각을 전달하는 수단의 하나입니다.

화가 났을때, 입을 열기 전에 10을 세라, 개빡쳤다면 100을 세라. - Thomas Jefferson.

제가 바꿔 말하면 이렇게 됩니다.

코드를 볼 때, 리팩토링 전에 10을 세라, 테스트를 적지 않았다면 100을 세라. - Samer Buna

프로그래밍이란 주로, 기존의 코드를 읽는 것입니다.

필요한 기능과 현재 구현의 괴리를 조사해, 어떻게 최소한의 테스트로 차이를 메울 수 있는지 설계하는게 중요합니다.

실제 코드 작성은 이 프로세스 전체의 10% 정도 밖에 되지 않습니다.

프로그래밍은 코드를 적는 것이라 생각하지 맙시다.

프로그래밍은 논리와 창조입니다.

2. 구현하기 전에 설계를 너무한다

코드를 적기전에 계획을 세우는 것은 좋습니다. 맞습니다.

그러나 너무 과하면 오히려 나빠집니다.

물을 너무 많이 마시면 독이 되는 것처럼.

완전한 설계를 하려는 것은 안 됩니다.

프로그래밍 세계에 그것은 존재하지 않습니다.

구현을 하기위해 필요한 적당한 수준의 설계를 찾아봅시다.

실제 설계는 자주 바뀌는 것 입니다.

적절한 설계는 코드 구조를 클린하게 하기 위해서 필요합니다.

하지만 과한 설계는 그저 시간을 허비하는 것입니다.

설계는 조금씩 하는 것이 좋습니다.

모든 기능을 한 번에 설계하는 것은 단호하게 금지해야합니다.

워터폴이라 부르며, 시스템 순서에 맞춰 완성시켜 설계하는 것입니다.

이 방법에는 도대체 어느 정도의 설계가 필요해질까요.

대부분의 소프트웨어 프로젝트에는 워터폴 설계는 제대로 돌아가지 않습니다.

복잡하면 할수록 애자일 외에는 방법이 없습니다.

프로그램 개발은 변화에 민감할 필요가 있습니다.

워터폴 설계시점에는 없던 기능을 구현한 적이 있을 것입니다.

워터폴 설계시점에 고려하지 않았다는 이유로 기능을 삭제한 적도 있을 것입니다.

버그는 수정하고, 변화에 적응할 필요가 있습니다.

즉 애자일이어야 할 필요가 있습니다.

단, 다음 구현할 기능에 대해서 미리 설계해야합니다.

너무 적은 설계도 너무 많은 설계도 당신의 코드 품질을 깍아먹습니다.

그리고 품질 낮은 코드는, 당신 자체의 품질과 연결됩니다.

3. 품질관리의 과소평가

코드를 적을 때 우선해야할 한 가지는 읽기 편함입니다.

읽기 불편한 코드는 잡동사니입니다.

코드는 재활용 가능해야합니다.

코드 품질관리의 중요성을 과소평가하지 맙시다.

코딩 구현할 것을 전달하는 수단이라 생각합시다.

코더의 주 임무는, 작업한 솔루션의 구현을 전달하는 것입니다.

프로그래밍에 관해 마음에 드는 문구를 소개합니다.

코드를 적을 때는, 유지관리자가 당신의 주소를 아는 사이코패스라는 것을 잊지말고 코딩해라. - Jonh Woods

훌륭합니다. John!

작은 것조차 문제입니다.

만약에 인텐트와 대문자, 소문자가 바르게 사용되지 않았다면 코딩할 자격이 없어 보입니다.

tHIS is

WAY MORE important

than

         you think

또 다른 찝찝한 점은 긴 행입니다.

80자 넘은 행만큼 읽기 괴로운 것은 없습니다.

if 블록을 보기 좋게 하기 위해서 복수의 조건식을 1행에 넣어버리고 싶을지도 모릅니다.

그러나 그딴 짓을 하면 안 됩니다.

80자 제한을 넘겨서는 절대 안 됩니다.

이렇게 단순한 문제의 대부분은 Linting, formatting 도구로 간단히 해결합니다.

JavaScript라면 ESLintPrettier 같이 우수한 도구가 존재합니다.

반드시 이들을 사용하도록 합시다.

코드 품질에는 여러 지뢰가 존재합니다.

함수의 행수가 너무 많다.

긴 코드는 항상 단위 테스트 가능한 작은 단위로 분할할 필요가 있습니다.

개인적으로 10행 이상 함수는 너무 길다 생각한다. (필수가 아닌 가볍게 지향점이라 여기면 됩니다.)

이중부정을 사용한다.

사용하면 안 됩니다.

이중부정을 사용하는 것은 무지하게 나쁜 것이라 생각하지 않을 수가 없습니다.

(이중부정을 사용하는 것은 무지하게 나쁜 것입니다.)

너무 짧거나, 범용적인 데이터 타입을 사용한 명명

변수명에는 설명이 들어가야하며, 애매하지 않은 이름을 붙여야합니다.

컴퓨터 과학에서 어려운 점은 캐시삭제랑 이름 짓기, 단 2개입니다. - Phil Karlton

매직넘버 하드코딩

문자열이나 수치에 고정된 값을 설정하고 싶은 경우에는 값을 변수에 넣어, 적절한 이름을 붙입시다.

const answerToLifeTheUniverseAndEverything = 42;

문제 회피

쇼트 커트나 회피책을 그릇한 문제에서 피하면 안 됩니다.

현실을 직면합시다.

긴 코드가 좋다 생각한다

대부분의 경우 짧은 코드가 좋습니다.

읽기 좋게하려면 코드를 길게 씁시다.

코드를 짧게하기 위해 기교를 부려 one-liners나 삼항연산자를 쓰지 맙시다.

필요하지도 않는데 의도적으로 코드를 길게 쓸 필요는 없습니다.

필요하지 않는 부분까지 의도적으로 코드를 길게할 필요도 없습니다.

불필요한 코드를 삭제하는 것이 프로그램에 최적의 가장 좋은 개선방법입니다.

프로그램 진척을 행수로 계산하는 것은 비행기 구조의 진취를 무게로 정하는 것과 같습니다. - Bill Gates

조건 로직의 과용

조건 로직이 필요한 경우의 태반은 조건 로직이 불필요합니다.

대체할 수 있는 방법을 확인해, 가장 읽기 편한 걸 고릅시다.

명백하게 속도에서 차이가 나는 것이 아니라면, 퍼포먼스을 최적화할 필요가 없습니다.

또 요다기법이나 조건식 중에 대입도 피합시다.

4. 최초 해결책에 몰두한다.

프로그래밍을 할 때, 첫 문제 해결책을 발견하면 바로 그것에만 몰두한 적이 있습니다.

첫 해결책은 복잡한 함을 생각하기 전에 구현함으로 필연적으로 실패로 귀결됩니다.

처음 떠올린 해결책은 매력적일지도 모르겠지만, 잘 생각해보면 대체로 더 좋은 방법을 발견할 수 있습니다.

문제에 대해 복수의 해결책을 생각한다는 것은 문제를 완전히 이해하고 있다는 소리입니다.

프로그래머의 일은 단순하게 문제의 해결책을 찾는 것이 아닙니다.

더욱 간단하게 문제를 해결책을 찾는 것입니다.

간단하게라는 뜻은 해결책이 제대로 적절하게 기능하면서, 읽기도, 이해하기도, 보수하기도 쉬운 것이라는 뜻입니다.

소프트웨어 설계에는 두가지 방법이 있다.

하나는 가능한 간단하면서, 명백하게 결함이 없는 것

다른 하나는 가능한 복잡하면서, 명백하게 결함이 없는 것이다. - C.A.R Hoare

5. 포기하지 않는다.

가장 높은 빈도 실수는 처음 해결책이 가장 간단한 해결책이 아니라는 것을 눈치채고도 그 방법을 고집한다입니다.

포기하지 않은 정신의 나쁜 버전입니다.

포기하지 않는 정신은 좋은 방법이지만, 프로그래밍에 적용하면 안 됩니다.

프로그래밍에서 옳은 자세는 빨리 사라지며, 빈번히 실패하는 것입니다.

해결책의 의문을 생겼다면, 던져버리고 생각을 다시합시다.

해결책에 얼마나 많은 투자를 했어도 말입니다.

git과 같은 소스관리 도구는 다양한 해결책을 나눠 테스트하는 것에 최적화 되어 있습니다.

코드에 얼마나 많은 시간과 노력을 들였어도, 코드 품질과 전혀 관계없는 소리입니다. 나쁜 코드를 배제할 필요가 있습니다.

6. 검색하지 않는다.

문제를 해결할 때, 먼저 할 것을 하지 않아 많은 시간을 허비한 적이 많습니다.

정말 최첨단 기술에서 사용하는 것이 아니라면, 당신이 만난 문제는, 대체로 누군가가 이미 같은 문제를 만나 해결한 것이기도합니다.

검색부터 하자는 소리입니다.

경우에 따라 당신이 문제라 생각하는 것이 문제가 아니라 대답해주기도 합니다.

당신에게 필요한 것은, 문제를 수정하는 것이 아니라, 오히려 받아들이는 것입니다.

문제를 해결에 필요한 것을 모두 알 필요가 없습니다.

인터넷이 놀라울 정도로 해결책을 가지고 있습니다.

물론, 인터넷을 사용할 때 염두해주세요.

초보에게 있을 법한 행동 중에 하나가, 읽지 않고 그대로 사용하는 것입니다.

만약 문제를 정확히 해결하는 코드라도 이해하지 않은 코드를 결코 사용하지 말아주세요.

크리에이티브한 사람하는 가장 위험한 생각은, 자신이 무엇을 하는지 잘 알고 있다 생각하는 것입니다. - Bret Victor

7. 캡슐화하지 않는다.

요점은 OOP를 이용하라는 소리가 아닙니다.

기술에 상관없이 캡슐화를 생각하는 것이 좋습니다.

캡슐화를 하지 않은 시스템은 점점 보수하기 어려워집니다.

애플리케이션에서 어떤 기능을 처리하는 곳은 한 곳뿐입니다.

통상적으로 한 객체의 책임입니다.

객체가 공개할 것은 밖에서 객체를 사용할 때 필요한 최소한 정보입니다.

기밀을 갖기 위함이 아니라, 애플리케이션의 각 부분의 의존관계를 적게하기 위한 컨셉을 만들기 위함입니다.

규칙에 따라, 어딘가 떨어진 장소에 무엇인가 안 움직이지일까 걱정하지말며, 클래스, 객체, 메서드 내부를 안전하게 변환하는 것이 가능해집니다.

클래스는 관련할 로직과 스테이트를 모은 개념적 집합의 단위로 만들어집니다.

클래스는 청사진이며, 클래스 객체, 함수 객체를 뜻합니다.

모듈, 혹은 패키지로 구성할 때도 있습니다.

로직 클래스에서는 하나의 독립된 태스크에 하나의 메서드가 필요합니다.

메서드는 하나의 행동만 하며, 정확히 처리되어야 합니다.

같은 클래스는 같은 메서드 가져야합니다.

초보 프로그래머일 때, 저는 어떻게 클래스를 나눠야 하는가 개념적 집합을 몰라서, 무엇인 독립한 태스크인가 잘라 나누는 것이 불가능했습니다.

각 관계가 특별히 없도록, 잡다한 코드를 욱여넣은 Util 클래스가 있다면, 이는 초보라는 증거입니다.

한 곳에 간단히 변경을 가했는데 다른 곳에서 문제가 전파되어 몇 곳이나 수정하지 않으면 안 되는 경우, 그것도 초보자 코드의 특징입니다.

클래스 메서드를 추가하거나 메서드에 기능을 추가하기 전에 생각할 시간을 가지기 바랍니다.

생각을 나중으로 미루거나, 나중에 리팩토링하면 되지, 여기지 마세요.

이것이 올바른 길로 가는 첫 걸음입니다.

좋은 생각은 코드를 응집도를 높히고, 결합도를 낮추는 것입니다.

고응집, 저결합이란, 관련하는 코드는 하나의 클래스로 정리해서 적으며, 각 클래스 사이의 의존관계를 줄인다는 의미의 표현하는 귀여운 용어입니다.

8. 불필요한 확장성을 담는다.

현재 해결책을 뛰어 넘는 방법을 생각하는 것은 꽤 매력적입니다.

당신이 쓴 이런 저런 행에 만약 여기에 ㅇㅇ가 있으면 이런 생각을 떠올릴 수도 있습니다.

이는 엣지 케이스 테스트에 대해 생각할 때는 좋은 방법이지만, 아직 구현되지 않은 대상에 하려는 것은 실수입니다.

머리에서 만약 여기에 ㅇㅇ가 있으면이 무엇인지 반드시 분류할 필요가 있습니다.

오늘 필요하지 않은 코드를 오늘 작성할 필요가 없습니다.

결정되어 있지 않은 일을 집어넣지 마십시오.

성장하기 위한 성장은 암세포이다. - Edward Abbey

9. 올바른 자료구조를 사용하지 않는다.

코드리뷰를 할 때, 초보 프로그래머가 알고리즘에만 중점을 두고는 합니다.

적절한 알고리즘을 고르고, 필요에 따라 사용하는 것은 좋습니다만, 그것으로 천재 프로그래머가 될 수 없습니다.

사용하고 있는 언어에 준비된 다양한 자료구조의 장점과 단점을 기억하시면 더 좋은 개발자가 될 것 입니다.

부적절한 자료구조를 사용하는 것은, 여기에 초보가 있습니다. 말하는 것과 같은 행동입니다.

이 글에서는 자료구조의 상세한 것까지 다루지 않지만, 간단한 예시를 몇 올리겠습니다.

맵(객체)가 아니라 리스트(배열)로 관리한다.

가장 자주 일어나는 실수가 자료구조 선택에서 복수의 레코드를 map이 아니라 list로 관리하는 것입니다.

맞습니다, 레코드 관리는 map를 사용해야 합니다.

각 레코드에 그 레코드를 특정하기 위한 하나의 키가 존재하는 경우에 대해 이야기해봅시다.

스칼라값에 list를 사용하면 문제없이, 특히 값을 push해서 사용하는 경우 더 많은 선택이 됩니다.

JavaScript에는 우선 일반적으로 list는 array, map은 object입니다. (최근에는 Map도 있습니다.)

레코드를 관리하기 위해 list를 사용하는 것은 그른 일입니다.

이것이 중요한 이유는 식별자를 사용해서 레코드를 검색하는 경우, map은 list보다 현저히 빠르기 때문입니다.

실제로 차이가 느껴질 때는 콜렉션이 거대했을 경우입니다. 이런 이유로도 충분한 이유입니다.

Not Using Stacks

반복을 필요할 때, 재귀를 이용하면 됩니다.

하지만 재귀코드를 최적화하는 것은 꽤 어렵습니다.

특히 싱글 스레드 환경이라면 더욱 그러합니다.

재귀함수의 최적화는 대상에 따라 난이도가 급격히 달라집니다.

만약 2개 이상 값을 반환하는 코드의 최적화라면, 반환이 하나인 함수에 보다 무척 어려워집니다.

초보자가 그냥 넘기는 경우도 있습니다만, 재귀 대신 사용할 수 있는 자료구조가 있다는 것입니다.

바로 스택입니다.

함수를 호출하는 대신에 스택을 쌓아, 완료되면 pop합시다.

10. 코드를 악화시킨다.

messy your room

지저분한 방을 상상해보세요.

이 방에 뭔가 둬야된다 요구를 들었습니다.

방은 이미 충분히 난잡하기에, 적당히 둬도 괜찮지 않을까 생각할 수 있습니다.

이 방법 몇 초면 되지요.

난잡한 코드에는 그러면 안됩니다.

절대 악화시키면 안됩니다.

조금이라도 좋으니, 작업을 개시하기 전에 코드를 정리합시다.

지저분한 방에 해야할 일은, 새로운 것을 두기 위해 정돈하는 겁니다.

예를 들어 둬야할 것이 옷이라면, 우선은 옷장을 정리해야겠지요.

그것만으로, 당신는 올바른 일을 하기 위한 첫 발을 뗀 것입니다.

코드를 점점 악화시키는 방법을 몇 가지 들어보겠습니다.

function isOdd(number) {
  if (number % 2 === 1) {
    return true;
  } else {
    return false;
  }
}

isOdd는 몇 가지 문제가 있습니다만, 가장 큰 문제는 어디일까요? 바로 불필요한 if문입니다.

function isOdd(number) {
  return number % 2 === 1;
}

11. 의미없이 주석을 쓴다.

되도록 주석은 쓰지 않는 것이 좋은데 어려운 일입니다.

대부분의 주석은 코드 내의 요소명을 적절히 써서 대체할 수 있습니다.

// This function sums only odd numbers in an array

const sum = (val) => {
  return val.reduce((a, b) => {
    if (b % 2 === 1) {
      // If the current number is even
      a += b; // Add current number to accumulator
    }
    return a; // The accumulator
  }, 0);
};

이 코드는 이렇게 쓰면 주석을 쓸 필요가 없습니다.

const sumOddValues = (array) => {
  return array.reduce((accumulator, currentNumber) => {
    if (isOdd(currentNumber)) {
      return accumulator + currentNumber;
    }
    return accumulator;
  }, 0);
};

함수명이나 인수명을 적절히 붙이면 대부분의 주석은 필요하지 않습니다.

주석을 쓰기 전에 생각해보세요.

물론 어떻게든 주석을 쓰지 않으면 안되는 경우가 생기기도 합니다.

그럴 때는 이 코드는 무엇인지(WHAT)이 아니라, 이 코드는 왜 그렇게 되는가(WHY)를 설명해야합니다.

코드에 WHAT 주석을 쓸 때 경우에도, 너무 뻔한 건 적지마세요.

// create a variable and initialize it to 0

let sum = 0;

// Loop over array
array.forEach(
  // For each number in the array
  (number) => {
    // Add the current number to the sum variable
    sum += number;
  }
);

코드에 잡음을 넣을 뿐, 전혀 도움되지 않습니다.

이런 프로그래머가 되면 안됩니다.

또 저렇게 처리하면 안됩니다.

대처가 가능한 주석을 삭제하세요.

부하가 이렇게 쓰면 바로 한 마디 해줍시다.

13. 테스트를 적지 않는다.

요점을 말하면, 당신이 스스로가 테스트를 적지 않아도 생각을 그대로 코드로 옮겨 적을 수 있는 숙련된 프로그래머라 생각한다면 당신은 초보입니다.

테스트 코드를 적지 않았다면, 코드 외의 방법으로 프로그램을 수동테스트했을 것입니다.

Web 애플리케이션을 만든다면, 코드 조금 쓸 때마다 화면을 새로고침해서 확인합니다.

저도 그러합니다.

코드를 수동테스트 하는 것은 이상한 것이 아닙니다.

그러나 동시에 코드를 자동테스트하는 방법을 생각하지 않으면 안됩니다.

수동테스트를 정상적으로 마친 후에, 편집기로 돌아가, 새 코드를 적고, 다시 수동테스트... 이런 식으로 몇 번씩 같은 짓을 한다면, 이걸 자동적으로 수행하는 코드를 쓰는게 좋을 것입니다.

당신은 사람입니다.

당신은 몇 번 테스트한 내용을 잊어버립니다.

이러한 반복은 컴퓨터에게 시킵시다.

가능하다면 코드 본체를 쓰기 전에 코드가 가져야 할 조건을 설계, 추측하는 것부터 시작해봅시다.

테스트 주도 개발(TDD)을 말만이 아니라, 기능이나 디자인에 대해서도 긍정적인 영향을 줍니다.

TDD가 모든 경우에 적절한 것은 아니기에, 잘 적용되지 않은 프로젝트도 있습니다.

그렇지만 일부라도 적용할 수 있다면 바로 적용할 기법입니다.

13. 코드가 움직인다면, 코드는 옳다.

홀수만 더하는 sumOddValues 함수를 봅시다.

무엇이 문제일까요?

const sumOddValues = (array) => {
  return array.reduce((accumulator, currentNumber) => {
    if (currentNumber % 2 === 1) {
      return accumulator + currentNumber;
    }

    return accumulator;
  });
};

console.assert(sumOddValues([1, 2, 3, 4, 5]) === 9);

assert은 문제없이 동작합니다. 메테타시 메테타시.

이 코드는 미완성입니다. 샘플 코드를 포함해 몇 몇 테스트를 통과했지만 그 이상 문제가 있습니다.

Problem #1

인수가 비어있을 때 처리가 없습니다. 함수를 인수없이 호출하면 에러가 발생합니다.

TypeError: Cannot read property 'reduce' of undefined.

TypeError: Cannot read property 'reduce' of undefined.

좋지 못한 코드는 크게 두 가지 징조가 있습니다.

TypeError: Cannot execute function for empty list.

아마 에러를 던지는 대신 0를 반환하는 설계가 더 적절할 수도 있습니다.

어찌되었든, 개선을 할 필요가 있겠네요.

Problem #2

잘못된 입력 값 대책이 없습니다.

배열이 아니라, 문자열, 수치, Object 등이 들어가면 어떻게 될까요?

sumOddValues(42);

// TypeError: array.reduce is not a function

array.reduce은 정의된 함수이므로, 이런 메세지는 좀 그럽니다.

인수가 array이므로 함수 내 배열을 의미합니다.

그러나 에러 메세지가 의미하는 것은, 42.reduce는 함수가 아니라는 것입니다.

이런 식의 에러 메세지는 혼란을 조장합니다.

더 친절한 메세지를 띄우는 것이 현명합니다.

TypeError: 42 is not an array, dude.

문제점 1, 2은 엣지케이스입니다.

엣지케이스는 대응방법이 대부분 패턴화되어있어서, 크게 고민할 경우가 없습니다.

그럼 이런 경우는 어떻습니까?

sumOddValues([1, 2, 3, 4, 5, -13]) // => still 9

-13가 홀수임에도 불구하고, 이 함수는 9를 반환합니다.

이 함수에는 이 기능이 필요합니까?

이 입력에는 예외를 낼 필요가 있습니까?

음수를 받아들일 필요가 있습니까?

지금처럼 음수를 무시해도 됩니까?

그렇다면 함수명은 sumPositiveOddNumbers가 적절할 것입니다.

상기한 것처럼 어떤 사양으로 결정할까는 간단합니다.

요점은 사양을 명문화 하는 테스트 케이스를 적지 않은 경우, 나중에 보수담당자가 음수를 무시하는 것이 의도적인 것인가 버그인 것인가 애매하다는 점입니다.

이건 버그가 아니라 사양입니다. - 이름 모를 사람

Problem #3

유효한 모든 케이스가 테스트되어 있지 않습니다.

이 함수에는 처리가 제대로 일어나지 않는 단순한 엣지케이스가 존재합니다.

sumOddValues([2, 1, 3, 4, 5]) // => 11

2는 홀수가 아닌데도, 결과에 포함되어있습니다.

이유는 간단한데, reduce에 두 번째 인수를 전달하면 accumulator 초기값으로 취급됩니다.

두 번째 인수를 넘기지 않은 경우, reduce 컬렉션의 처음 값을 accumulator의 초기값으로 합니다.

그래서 sumOddValues 함수 결과에 컬렉션의 첫번째 값이 이미 포함되어버립니다.

코드를 적을 때, 값을 체크하는 테스트를 쓰지 않았다면, 문제가 바로 발견할 수 있었을 겁니다.

테스트를 최소한밖에 쓰지 않거나, 엣지테스트를 쓰지않는다. 등의 징조는 초심자라는 증거입니다.

14. 기존의 코드를 의심하지 않는다

당신이 혼자서 북치고 장구치는 슈퍼스타가 아닌 이상, 품질이 좋지 않은 코드를 만날 것입니다.

초보자는 코드의 품질을 신경쓰지 않고, 잘 움직이니까 좋은 코드라 인식하고 그걸 배웁니다.

더 나쁜 것은, 그게 좋은 코드라 생각하고 있으니 온갖 장소에 나쁜 코드를 양산합니다.

딱 봐도 나쁜 코드를 어떤 특별한 이유도 없이 계속 쓰던 시기는 있기 마련입니다.

그럴 경우에는 그 코드가 왜 그렇게 쓰여지있는가 주석을 다는 것이 좋은 코드를 구분하는 방법입니다.

초보자라면 이해할 수 없는데다가 설명도 없는 나쁜 코드는 나쁜코드이다.

이렇게 생각합시다.

이에 이해 질문하며, git blame 해봅시다.

코드 작성자가 이미 없으며, 어떤 경우에는 내용을 기억조차 못하는 경우 코드를 파고들어, 속을 이해합시다.

코드를 완전히 파악한 후에, 그 코드에 대해 좋고 나쁨을 판단합시다.

이해하기 전에 가정을 붙여 추측하면 안됩니다.

15. 베스트 프락티스에 갇힌다

베스트 프락티스는 유해한 단어라 생각합니다.

베스트 프락티스, 즉 이건 이 이상 연구할 여지가 없음, 의문의 가질 틈이 없음 이라는 뜻입니다.

베스트 프락티스라는 건 없습니다.

현 시점에, 그 프로그래밍 언어에 대한 굿 프락티스만 존재할 뿐입니다.

일찍이 베스트 프락티스된 문법이었던 것이 지금은 배드 프락티스라 인식되고 있습니다.

충분한 시간을 가하면, 더 좋은 방침을 찾는 것이 될 것입니다.

베스트 프락티스에 몰두하는 걸 그만두고, 베스트를 만드는데 집중합시다.

어디선가 베스트 프락티스를 해라 해서, 누가 그렇게 하고 있다면, 누가 그렇게 말한다면, 그렇기에 한다.

이런 짓은 하지 말아주세요.

저기에는 제가 이 글에서 밝힌 모든 조언도 포함되어 있습니다.

모든 것을 의심하고, 정석으로 도전하며, 모든 선택지를 조사해, 의식적으로 의도를 결정합시다.

16. 최적화에 몰두한다

시기상조한 최적화는, 프로그래밍에서 만악의 근원이다. - Donald Knuth(1974)

도널드 커누스가 발언을 한 이후, 프로그래밍은 크게 변했지만 발언의 중요성은 변하지 않았습니다.

하나를 배울 때에는, 병목되는 곳이 어딘가에 있을거라 추측하는 것을 최적화할 필요가 없습니다.

코드를 실행하기 전에, 최적화 하는 것은 시기상조이며, 애초에 최적화가 완전하게 필요없을 가능성도 있습니다.

물론 새 코드를 도입하는 경우 이미 검토해야 할 최적화 항목은 존재합니다.

만약 Node.js라면, 이벤트 루프가 넘치거나, 콜스택을 블록하지 않도록 하는 것이 무척 중요합니다.

이를 염두하면서, 제일 먼저 최적화해야 합니다.

구현중 코드가 콜스택을 블록하는가에 늘 자문해주십시오.

기존 코드를 측정을 하지않고 하는 최적화는 유해하며, 피해야 할 일입니다.

퍼포먼스를 올리기 위해 바꿔야하는 것에, 예상 외 버그가 발생할 가능성도 있습니다.

발생하지 않은 퍼포먼스 문제를 최적화 하기 위해서 시간을 소비하는 것은 쓸모없는 행동입니다.

17. 엔드유저 시점으로 보지 않는다

애플리케이션에 기능을 추가하는 간단한 방법은 무엇일까요.

기존의 인터페이스에 적합한가 고르는 것이 적절합니다.

그 기능이 유저에서 정보입력을 필요로 하는 경우, 이미 존재하는 폼에 추가합니다.

그 기능이 다른 곳으로의 링크를 추가하는 것이라면, 이미 존재하는 링크 메뉴에 추가합니다.

개발자 시점으로 보지마세요.

자신이 엔드유저라는 것은 시선으로 만사를 봅시다.

기능이 사용자의 무엇을 필요로하는가, 사용자가 어떻게 행동할까 생각합시다.

중요한 것은 유저가 기능을 간단히 사용하는 방법이지, 간단히 구현하는 것이 아닙니다.

18. 적절한 도구를 사용하지 않는다

누구나 프로그래밍을 할 때 좋아하는 도구를 가지고 있습니다.

어떤 도구는 멋지며, 어떤 도구는 좋지 않습니다만, 대부분의 도구는 어떤 분야에 강력하지 다른 분야에서는 그렇지 않습니다.

망치는 못을 벽에 박을 때는 좋은 도구지만, 나사를 돌릴 때는 최악의 도구입니다.

망치가 너무 좋아도, 나사를 돌리면 안됩니다.

Amazon 유저리뷰가 5.0여도 그러합니다.

사용할 도구를 인기로 고르는 것이 초보자인 증거입니다.

이러는 이유 중 하나가 일에 더 적합한 도구인가 모르기 때문이지요.

당신은 지금 사용하고 있는 도구는 당신이 지금 알고 있는 것 중에서 제일 좋은 도구일 수도 있지만, 결코 모든 도구 중 가장 좋은 것을 아닙니다.

사용하는 도구에 손을 떼서, 새 도구를 시도해 볼 필요가 있습니다.

일부 코더는 새 도구 사용을 거부합니다.

그들은 기존 도구에 익숙해져서, 새 도구를 습득하고 싶지 않을 것입니다.

마음은 이해합니다만, 하지만 그런 태도는 잘못 된 태도입니다.

당신은 원시적 도구에 시간을 낭비하면서 집을 세울 수도, 좋은 도구에 돈과 시간을 투자한 후에 단기적으로 더 좋은 집을 세울수도 있습니다.

도구는 끊임없이 개선되기에, 끊임없이 학습하면서, 사용할 필요가 있습니다.

19. 코드 문제가 데이터 문제로 여긴다

프로그램의 중요한 일 중 하나는 데이터 관리입니다.

프로그램은 새 코드를 추가하거나, 낡은 레코드를 삭제하거나, 레코드를 변경하는 인터페이스입니다.

프로그램이 버그는 정말 사소해도, 치명적으로 예측 불가능한 데미지를 데이터에 줄 수 있습니다.

모든 데이터가 버그가 있는 구간을 지나갔다면, 사태는 더 심각합니다.

초보는 코드와 데이터의 관계성을 묶는 것이 어려울지도 모르겠습니다.

기능은 현재 동작하지도 않으면서, 중요하지 않으므로, 무의식적으로 버그가 들어간 코드를 계속 적을 수도 있습니다.

문제는 버그가 들어가 코드가 데이터의 안정성을 조용히 파괴할 수 있다는 가능성입니다.

더 나쁜 경우는 데이터에 일어난 버그를 고치지 않고, 코드 버그만 수정하는 것입니다.

코드는 작은 데이터의 문제를 증폭시켜, 결국 회복 불가능한 수준으로 데이터를 파손시킵니다.

이런 문제에서 데이터를 지키기 위해서는?

하나의 예시로 데이터의 정합성을 검토할 수 있는 복수의 레이어를 사용할 수 있습니다.

하나의 레이어로는 정합성을 보장할 수 없습니다.

프론트엔드, 백엔드, 네트워크, 여기에 데이터베이스 레이어에 검증을 넣어봅시다.

이게 어렵다면, 최소한 데이터베이스 레벨의 제약은 사용하면 안됩니다.

데이터베이스 제약에 대해 이해하며, 테이블이나 열을 추가하는 것에 사용할 제약을 반드시 사용합시다.

초보자가 범하기 쉬운 데이터 정합성에 관한 하나의 문제는 트랜잭션이라는 생각의 결여입니다.

복수의 조작이 서로 의존해서 데이터를 변경한다면, 그 조작의 하나가 실패할 경우 모든 것을 처음으로 돌리기 위해 트랜잭션을 사용할 필요가 있습니다.

20. 바퀴의 재발명

이건 어려운 부분입니다.

프로그래밍은 지금도 여전히 모든 것이 알려져 분야가 아니며, 어떤 바퀴에는 재발명할 가치가 있기도 합니다.

모든 것이 상당히 빨리 변화하며, 새 요소가 점점 유입됩니다.

현재 시간대보다 잘 돌아가는 바퀴가 필요한 경우는 이미 존재하고 있는 바퀴를 커스터마이즈하는 것이 아니라, 그냥 바퀴를 재발명할 필요가 있습니다.

하지만 기존설계를 벗어난 바퀴는 죽어도 그걸 써야한다 하는게 아니라면 재발명하지 말아주세요.

많은 선택지에서 적절한 브랜드의 바퀴를 선택하는 것은 무척 어렵습니다.

구입하기 전에 여러 선택지를 테스트해보세요.

소프트웨어의 좋은 점은 많은 것이 무료이며, 바퀴 내부를 직접볼 수 있다는 것이다.

바퀴의 품질은 내부 코드 설계를 보는 것으로 쉽게 판단할 수 있습니다.

가능하다면, 오픈소스 바퀴를 사용해주십시오.

오픈소스는 간단히 패키지를 수정하는 것이 가능하며, 간단히 교환하는 것도 되는데다가, 여기에 커뮤니티의 서포트도 있습니다.

또한 바퀴가 필요한 경우는, 차를 1대 구입하는 것이 아니라, 이미 가지고 있는 차에 바퀴만 바꾸시길 바랍니다.

함수 하나, 둘을 사용하기 위해서, 라이브러리 전체를 도입하지 마세요.

적절한 예시가 JavaScript의 Lodash입니다.

배열을 셔플하기 위해서는 shuffle 메서드만 쓰면 되지, lodash 라이브러리 전체를 임포트하면 안됩니다.

// 예시

import lodash from 'lodash';

import { shffule } from 'lodash';

import shuffle from 'lodash/shffule';

import shuffle from 'lodash.shuffle';

// 1번은 너무 크고, 4번은 패치에 따라 어떻게 변할줄 모른다.

// 되도록 3번을 고르며, 이렇게 하면 적은 번들사이즈로 만들 수 있다.

// https://www.labnol.org/code/import-lodash-211117

21. 코드리뷰를 대하는 방법

초보자 티가 나는 징조 중 하나가 코드리뷰를 비판/비난으로 여기는 것입니다.

심지어 좋아하지도, 감사히 여기지도 않습니다, 오히려 무서워하죠.

이건 잘못된 것입니다.

이렇게 느끼고 있다면 빨리 태도를 바꿀 필요가 있습니다.

모든 코드리뷰는 학습의 계기라 생각해주세요.

환영하고, 인정해 배웁시다.

가장 중요한 점은 리뷰어에게 감사하자는 것입니다.

당신은 영원히 코드를 배워야합니다.

그것을 의식해주세요.

대부분의 코드리뷰는 당신이 모르는 것을 알려줍니다.

학습기회라 여깁시다.

가끔 리뷰어가 실수해서 당신도 그들에게 무엇인가 알려줄지도 모릅니다.

단, 당신의 코드만으로 어디가 나쁜지 명백히 알 수 없어서, 코드를 수정할 필요가 없을수도 있습니다.

리뷰어에게 무엇인가 배울 기회가 있다면, 그건 프로그래머에게 무척 유익한 활동중 하나입니다.

22. 버전 관리하지 않는다

초보자는 Git 같은 버전관리 시스템의 힘을 모릅니다.

버전관리는 단순히 코드에 변경을 더해 push하는 도구가 아닙니다.

버전 관리는, 즉 역사입니다.

코드에 의심이 생겼을 때, 코드의 역사는 의문을 해결해줄 때 도움이 됩니다.

그러므로 커밋 메세지도 중요합니다.

그것은 당신의 구현이 무엇을 위해 있는지 표현하는 하나의 수단입니다.

커밋은 되도록 소규모로 하면, 미래의 관리자가 코드가 왜 그런 상태가 되었는지 조사할 때 도움이 됩니다.

빈번하고 짧게 커밋하며, 커밋 메세지는 현재형을 사용합시다.

커밋 메세지에 요약을 자세히 기술합시다.

메세지가 여러 행 필요하다면, 커밋이 너무 크다는 증거이므로 rebase합시다.

커밋 메세지에는 불필요한 것은 넣지맙시다.

예를 들어 추가, 변경, 삭제된 파일명 리스트는 필요없겠죠.

그것은 커밋자체에 들어가서, 커맨드로 간단히 볼 수 있으므로, 소음에 불과합니다.

또 파일 마다 다른 커밋 메세지를 붙이려는 분도 있겠습니다만, 커밋이 크다는 징후 중 하나입니다.

버전관리는 도달 가능성에 관한 것입니다.

어떤 함수의 설계, 필요성에 의문을 느낄 때, 그 함수가 도입된 커밋을 발견해서 당시의 상황을 알 필요가 있습니다.

커밋은 프로그램에 버그가 난입된 타이밍을 특정하는데 도움이 됩니다.

bisect 커맨드를 사용해 버그가 들어간 커밋을 이진탐색으로 특정 것도 가능해집니다.

버전 관리는 변경이 아닌 곳에서도 대활약합니다.

스테이징 환경 구축, 패치 선택, 수정의 리셋, 보류, 커밋의 수정, 차이 확인, 되돌리기 등등 코드를 다루는데 풍부한 기능이 있는 도구가 생기는 것입니다.

이것을 이해해, 학습하며, 사용합시다.

Git의 기능을 적게 아는 사람은 초보자에 가깝다 할 수 있습니다.

23. 글로벌의 과용

여기서는 함수형 프로그래밍과 그 이외의 패러다임의 차이를 말하는 것이 아닙니다.

그건 다른 글이 있습니다.

여기서는 글로벌은 만약의 원흉, 가능하다면 무조건 피해야한다는 것이라는 사실을 말할 것입니다.

만의 하나 그것이 어렵더만, 글로벌 사용은 최저로 눌러주시길 바랍니다.

제가 초보자일 때 의식하지 못한 것 중 하나가, 정의한 모든 변수가 공유 상태에 놓여있었다는 것입니다.

다시 말하면, 변수가 변수와 같은 스코프에 있는 모든 요소에서 조작가능하다는 뜻입니다.

스코프가 글로벌에 가까울수록, 공유상태의 치안이 악화됩니다.

가능하다면 작게 스코프를 가지며, 밖으로 유출시키지 마세요.

복수의 리소스가 동시에 하나의 변수를 조작하면, 글로벌은 큰 문제가 생깁니다.

즉, 경합상태입니다(Race Condition).

초보자는 경합상태, 특히 데이터 잠금 문제 회피책으로 타이머를 사용하는 경향이 있습니다.

그건 위험신호입니다.

하지마세요.

코드리뷰에서 지적할 일이고, 막을 일입니다.

24. 에러를 싫어한다

에러는 좋은 것입니다.

코드가 전진한다는 의미죠.

개선점 추적을 간단히 할 수 있습니다.

프로 개발자는 에러를 사랑하지만, 초보는 그렇지 않습니다.

에러가 마음에 들지 않는다면, 에러를 향한 태도를 바꿀 필요가 있습니다.

에러는 힌트이며, 대처이며, 활용한다 생각합시다.

어떠한 에러는 예외적으로 개선할 필요가 있습니다.

예외는 유저가 정의하는 에러의 하나입니다.

다른 몇 종류의 에러도 그대로 둘 필요가 있습니다.

애플리케이션을 크래시해서 강제종료 시켜주니까요.

25. 휴식을 하지 않는다

당신은 인간입니다, 뇌에는 휴식이 필요합니다.

몸도 휴식이 필요합니다.

당신은 점점 자신만의 영역에 들어가, 먹고 자는 것조차 잊으며 집중하기도 할 것입니다.

저는 이것이 초보라는 증거라 생각합니다.

이것은 누가 대신해줄 수 있는 것이 아닙니다.

일의 흐름에 휴식을 넣어야합니다.

짧은 휴식을 많이 취해, 책상에서 떨어져 주변을 걷고, 다음에 무엇을 할지 생각합시다.

심기일전해서 코드로 돌아옵시다.

그렇 의미로 제 글은 무척 글이었습니다.

당신에게 휴식이 필요하겠네요.

읽어주셔서 감사합니다.