관리 메뉴

HAMA 블로그

[번역] 자바스크립트에서 for 루프를 다시 생각해보자 본문

Javascript

[번역] 자바스크립트에서 for 루프를 다시 생각해보자

[하마] 이승현 (wowlsh93@gmail.com) 2017. 9. 26. 15:40

이 글은 Joel Thoms의 "자바스크립트에 대한 애정을 언어의 90%를 쓰레기통에 버리면서 다시 발견했던 과정"
 번역한 글을  보고, 번역이 안된 중요 부분을 추가 번역 한 것입니다.




[번역] 자바스크립트에서 for 루프를 다시 생각해보자 

JavaScript 의 for  루프는 나름 그 역할을 잘 해주었지만, 이제는 쓸모가 없어졌으며 새로운 함수형 프로그래밍 기술에 의해 은퇴 해야 할 때로 보입니다.걱정은 하지 마세요 굳이 함수형 프로그래밍 마스터가 될 필요는 없습니다. 그저 현재의 프로젝트에서 바로 시작 해 볼 수 있는 간단한 (하지만 쿨한) 녀석입니다.

JavaScript for loop의 문제점은 무엇일까요?

for 루프는 상태 변이와 부수효과을 조장합니다. 그 결과  각종 버그와 예측할 수없는 코드의 잠재적인 원인이 되곤 합니다. 우리는 모두 전역 상태가 나쁘다는 말을 들었습니다. (지역상태도 전역상태와 같이 악을 공유하고 있지만, 규모가 작기 때문에 큰 문제가 되진 않았습니다.) 하지만 우리는 실제로 문제를 해결하지 못했으며 단지 그것을 최소화 하는데 주력 하였지요.

가변적인 상태에서 - 알 수 없는 특정 시점에 - 변수가 알려지지 않은 이유로 - "변경" 되어, 그  값이 변경된 이유를 디버깅하고 검색하는 데 몇 시간을 소비하게됩니다. 뭐 머리 좀 쥐어 뜯으면 되겠지요. 하지만 머지않아..머리카락이 그리워질 거에요.

이제 그 부수효과에 대해 재빠르게  이야기해 보죠. 이 단어가 별로 와닿지 않는다면 그냥 끔찍한 부작용으로 생각하는게 좋을거 같습니다. 혹시 당신의 몸에 부작용이 생기길 바랍니까? 아니죠?  마찬가지로 프로그램에 부작용이 있기를 원하십니까? 역시 아니겠죠?  네 저는 제 프로그램에 부작용이 생기는 것을 원하지 않습니다! 따라서 이 문제에 대해서 좀 적극적으로 행동 했습니다.

그럼 부수효과가 무엇일까요?

함수가 함수 범위 밖의 어떤 것을 수정할 때 부수효과가 있는 것으로 간주합니다. 멤버 변수의 값을 변경하거나, 키보드 입력을 읽거나, API 호출을하거나, 디스크에 데이터를 쓰고, 콘솔에 로깅하는 등의 작업등이 그것 들이죠.

부수효과은 강력하고 효율적인것 처럼 보이기도 하지만  그 만큼 큰 책임이 따릅니다.  

자기도 모르게 떠 안을 수 있는 큰 책임을 줄이기 위한 방법은 가능한 경우 부수효과를 제거하거나 캡슐화하여 관리해야 합니다. 부수효과가 있는 기능은 테스트하기가 어렵고 추론하기도 더 어렵습니다. 가능한 한 제거 하십시요. 그렇다면 우리는 부작용에 대해 걱정하지 않게 될  것입니다.

좋습니다.  
썰은 그만 풀고 개발자 답게 코드로 봅시다. 아마 수천 번 이상 본 전형적인 for 루프를 살펴 보겠습니다.

const cats = [
  { name: 'Mojo',    months: 84 },
  { name: 'Mao-Mao', months: 34 },
  { name: 'Waffles', months: 4 },
  { name: 'Pickles', months: 6 }
]
var kittens = []

for (var i = 0; i < cats.length; i++) {
  if (cats[i].months < 7) {
    kittens.push(cats[i].name)
  }
}
console.log(kittens)

앞으로 이 코드를 단계별로 리팩터링하여 좀 더 아름답게 변형하는 것이 얼마나 쉬운지 확인 시켜 드릴 것입니다.

1. 첫 번째로 변경하고자 하는 것은 if 문을 자체 함수로 추출하는 것입니다.

const isKitten = cat => cat.months < 7
var kittens = []
for (var i = 0; i < cats.length; i++) {
  if (isKitten(cats[i])) {
    kittens.push(cats[i].name)
  }
}

일반적으로 if 문을 추출하는 것은 가독성에 매우 좋습니다. 즉 마법수 같은 느낌의 "7 개월 미만" 이라는  표현보다는 "새끼 고양이" 로 필터링을 변경하는 것은 생각보다 매우 중요한 일이죠. 이제 코드를 읽을 때 의도가 명확 해집니다. 왜 태어난지 7개월 미만의 고양이를 구하는거야? 우리의 의도는 새끼 고양이를 찾는 것입니다, 그래서 코드가 그렇게 말하게 만드십시오! 읽는 사람도 매우 편해 할 것입니다.

또 다른 이점은 isKitten이 이제 재사용이 가능하다는 것입니다.

코드를 재사용 가능하게 만드는 것이 항상 우리의 목표 중 하나가되어야합니다.

2. 다음 변경은 cat 형식의 개체에서 이름으로 변환하는 부분을 추출하는 것입니다. 이 변환의 목적은 나중에 자세히 이해할 수있게 될 것이므로 지금 당장은  그냥 믿어 봅시다.

const isKitten = cat => cat.months < 7
const getName = cat => cat.name
var kittens = []
for (var i = 0; i < cats.length; i++) {
  if (isKitten(cats[i])) {
    kittens.push(getName(cats[i]))
  }
}

필자는 filter 와 map의 메커니즘을 설명하기 위해 몇 단락을 작성하는 것을 고려했지만 그것들을 설명하지 않고서도 그냥 이 코드를 읽고 이해할 수 있다는 것을 보여 주기 위해 건너 뛸 것입니다. filter 문은 조건문과 매칭되고, map 은 변환과 매칭된다는것을 알 수 있을 것 입니다. 반복해서 보세요. 
많은 경우 이해 안되는것도 반복해서 보면 이해가 되기 마련입니다. 

const isKitten = cat => cat.months < 7
const getName = cat => cat.name
const kittens = cats.filter(isKitten).map(getName)

우리는 for 문과 kittens.push (...)를 제거했으며 더 이상 var이 사용되지 않게 됩니다. 
즉 상태의 변경이 없어 졌습니다. const 세상이 되었습니다.

const (var와 let보다)를 사용하는 코드는 섹시합니다.

3. 필자가 제안 할 마지막 리팩토링은 필터링과  매핑을 자체 함수로 추출하는 것입니다. 모두 함께 묶어 보시죠. (역주: for 문에서 i 변수를 쫒아가며 남이 짠 혹은 자신이 예전에 짠 코드를 이해할 필요가 없이 자연스럽게 소설 읽듯이 변화 되었습니다.)

const isKitten = cat => cat.months < 7
const getName = cat => cat.name
const getKittenNames = cats =>
  cats.filter(isKitten)
      .map(getName)
const cats = [
  { name: 'Mojo',    months: 84 },
  { name: 'Mao-Mao', months: 34 },
  { name: 'Waffles', months: 4 },
  { name: 'Pickles', months: 6 }
]
const kittens = getKittenNames(cats)
console.log(kittens)




추가 번역 예정 글 ) 

- if 문을 다시 생각해보자.  
switch 문에게 마지막 인사를  
- 악의 축이던 this 없는 자바스크립트 프로그래밍  



Comments