DRY, KISS, YAGNI

쏘쏘임·2021년 10월 26일
1

글을 쓴 목적 : 지난 일주일간의 페어 프로그래밍, 3.5일간의 토이 프로젝트를 통해 지금까지 배운 이론을 구현해보고 리팩토링 해보는 시간을 가졌다. 구현도 미숙한 점이 많지만 특히 리팩토링을 하며 어려움을 많이 겪었다. 아직 좋은 코드에 대한 판단 능력이 부족하기 때문이다.
좋은 코드란 무엇을 말하는지, 대표적인 코드 원칙이라고도 불리우는 DRY, KISS, YAGNI에 대해 살펴 보았다.

DRY 하게, WET 하지 않도록

Don't Reapeat Yourself
Write Everything Twice

“Every knowledge piece must have a single unambiguous, authoritative representation within a system.”
The Pragmatic Programmer by Andrew Hunt &
David Thomas

모든 논리 조각 들은 시스템 내에서 모호하지 않고 명확하게 단일적으로 표현되어야 한다. 즉, 하나의 로직을 이곳 저곳 쓰지 말라는 뜻이다. 중복된 로직을 이곳 저곳에서 쓸 수록 응집도가 떨어져 오류를 잡아내기 힘들 뿐더러, 유지 보수 과정에서 변동이 있을 경우 하나하나 찾아서 수정해 줘야하는 번거로움, 오류 발생의 위험 증가 등의 문제가 있다.
이 때, 중복될 수 있는 코드를 합쳐 코드의 라인 수를 줄이는 것이 목적이 아니다. DRY 원칙에서 말하는 것은 '로직'의 중복을 방지다.

DRY & WET 예시

게임이 하나 있다고 가정하자. 이 게임은 필드 내에 여러 타입의 캐릭터들이 존재한다. 현재 필드 내에 존자해는 캐릭터들을 모아 각각 다른 처리를 해주어야 한다.

아래는 현재 필드에 있는 캐릭터들 중 그라운드 캐릭터,드래곤 캐릭터, 플라잉 캐릭터를 반환하는 함수들을 만들어 보는 함수들이다.

Before

function getGroundCharacters() {
  const characters = [];
  const allCharacters = getCharactersFormField();

  for (let i = 0; i<allCharacters.length; i++) {
    if (characterList[i].type === 'ground') {
      characters.push(characterList[i].content);
    }
  }

  return characters;
}
 
function getDragonCharacters() {
  const characters = [];
  const allCharacters = getCharactersFormField();

  for (let i = 0; i<allCharacters.length; i++) {
    if (characterList[i].type === 'dragon') {
      characters.push(characterList[i].content);
    }
  }

  return characters;
}

function getFlyingCharacters() {
  const characters = [];
  const allCharacters = getCharactersFormField();

  for (let i = 0; i<allCharacters.length; i++) {
    if (characterList[i].type === 'flying') {
      characters.push(characterList[i].content);
    }
  }

  return characters;
}

위의 함수는 타입에 따라 필요한 캐릭터들을 뽑아내는 로직이 반복해서 사용되고 있었다. 이 로직을 분리해서 작성해보았다.

After

function getIceCharacters() {
  const allCharacters = getCharactersFormField();
  return getCharacters(allCharacters, 'ice');
}

function getDragonCharacters() {
  const allCharacters = getCharactersFormField();
  return getCharacters(allCharacters, 'dragon');
}

function getFlyingCharacters() {
  const allCharacters = getCharactersFormField();
  return getCharacters(allCharacters, 'flying');
}

// 반복되는 로직 빼기
function getCharacters(characters, type) {
  const charactersByType = [];
  for (let i = 0; i<characters.length; i++) {
    if (characters[i].type === 'flying') {
      charactersByType.push(characters[i]);
    }
  }
  return charactersByType;
}

After 2

추가로 반복문을 사용하지 않고 배열 메서드를 사용하면 더 간결하고 가독성 좋은 코드를 작성할 수 있다.

function getCharacters(characters, type){
	return characters.filter(character => character.type === type);
}

KISS

Keep It Simple, Stupid

좋은 코드를 작성하는데 가독성을 빼놓을 수 없다. 유지 보수를 하는 것도, 협업을 하는 것도 모두 사람이 하는 일이기 때문이다.

KISS 예시

지난주 타이핑 게임을 구현하는 토이 프로젝트에서 사용자의 이름을 중복체크하기 위한 함수를 만들었다.

Before

처음 지정한 함수는 isNameExist, 이름이 존재하는가에 대한 불리언 값을 반환하는 함수다. findIndex를 통해 인덱스를 찾고 이 값이 -1이 아니면, 즉, 존재하면 true값을 반환하도록 했다.
이는 findIndex가 찾는 요소가 있다면 인덱스를, 없다면 -1을 반환한다는 특성을 이해하고 응용한 것이며 함수 사용 자체도 직관적이지 않다.

  const newUsername = $input.value;

  const isNameExist =
    getFromLocalStorage('records', []).findIndex(
      record => record.username === newUsername
    ) >= 0;

After

이를 변경한 것이 isNameUnique다. 현재 입력하려는 아이디가 유일한 값이 될 수 있을지를 체크하려는 것이기 때문에 이름과 불리언 결과 값을 바꿔주었다. 메서드 사용도 every 함수와 콜백 함수 record.username !== username 를 이용해 모든 리코드의 username이 새로 넣으려는 username과 같지 않은지 확인하는 것을 명확히 하였다.

  const newUsername = $input.value;

  const isNameUnique = getFromLocalStorage('records', []).every(
    record => record.username !== newUsername
  );

YAGNI

You Aren't Gonna Need It

개발을 할 때 당장 요구되지도 않은 기능을 앞서 만들어내지 말라는 원칙이다. 불필요한 기능들을 미리 작성한다면 구현, 기획 단계에서 불필요한 시간을 소모할 뿐 아니라 테스트 업무도 늘어난다. 또한, 아직은 무의미한 이 코드로 인해 오히려 오류가 발생할 수도 있고 불필요하게 소스 코드 길이를 늘리게 된다.
(단, 확장성, 유지 보수성을 고려하며 코드를 작성하되 불필요하게 앞서 나가지 말자는 의미다. 이 코드가 정말 필요한 것인가에 대한 고민이 필요하다.)


참고링크

https://github.com/qkraudghgh/clean-code-javascript-ko
https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it

profile
무럭무럭 자라는 주니어 프론트엔드 개발자입니다.

0개의 댓글