[JS] Generator은 무엇일까? (1)

kysung95·2021년 5월 12일
2

개발 상식

목록 보기
6/13
post-thumbnail

안녕하세요. 김용성입니다.
오늘은 JavaScript의 Generator가 무엇인지, 그리고 왜 사용하는지에 대해 간략히 알아볼까합니다.

😯 배열과 Generator

사실 배열에 대해 모르시는 분은 없으실거라 생각합니다.
배열이라는 것이 존재함으로써 우리는 많은 편리함을 누릴 수가 있죠. 일련된 값들을 정리해놓고 특정 index의 값을 도출해내거나, 그렇게 정리된 값들을 filtering 해준다거나, 거꾸로 정렬을 한다던가 이 모든 것들이 배열과 그와 더불어 사용가능한 메소드들로 인해 매우 편하게 구현 가능하죠.

그렇지만 프로그래밍의 세계는 역시나 공평해요. 배열을 사용하여 위에서 언급한 기능들을 구현하는 것이 효율성 측면에서도 뛰어나다고 말할 수 있을까요?

🙅 그렇지 않습니다.

예를들어 다음과 같은 코드가 있다고 생각해볼게요.


const a=[1,2,3,4,5,6,7,8]

const b=a.reverse()

for (let i of b){
  console.log(i)  
}

상당히 간단한 코드입니다.
배열에 있는 요소를 거꾸로 출력하기 위한 코드인데요.
많은 분들이 한번 쯤은 이러한 reverse() 함수를 사용해보셨을거예요.
그렇지만 여러분, 위 코드는 동일 사이즈의 배열을 두번 메모리에 적재하게 되기 때문에 좋지 못한 효율성을 지니고 있다고 볼 수 있어요. 예시 코드에서는 배열의 크기가 8밖에 안되지만 만약 몇만개의 데이터가 reverse() 메소드를 사용하여 메모리에 중복 적재 되었을 때, 과연 좋은 퍼포먼스를 보일 수 있을까요?
그러한 이유로 다음과 같이 코드를 변경해보았습니다.


const a=[1,2,3,4,5,6,7,8]

for (let i=a.length-1;i>=0;i--){
  console.log(a[i])  
}

이렇게 해주면 위 코드와는 다르게 메모리에 배열이 두번 적재되지도 않고, 나름 괜찮다고 볼 수 있어요.
그렇지만 여전히 코드가 깔끔하지 않아보인다는 단점이 두드러지네요.🤦‍

이러한 문제는 generator를 통해서 해결해줄 수 있는데요.
그러면 이번에는 generator라는 녀석을 한 번 사용해볼까요?

generator 란

generator는 다음과 같은 성질을 지닌 함수입니다.

  • 반복 가능한 이터레이터(iterator)를 값으로 반환한다.
  • 작업의 일시 정지와 재시작이 가능하며 자신의 상태를 관리한다.

선언 방식

generator 선언 방법은 간단해요. '*'(asterrisk) 를 function 키워드과 함수 이름 사이에 기재해주면 되는데요.
이런식으로 선언할 수 있습니다.

function * counter(){
   //...
}

일반적인 함수에서는 보통 값을 돌려받기 위해 return 키워드를 사용하는데요. generator에서는 'yield'라는 키워드를 사용합니다.

yield 키워드 뒤에는 string 이나 array 등의 iterable한 객체들만 올 수 있습니다.

👉 generator 함수에서 return 값을 사용하면 다음과 같은 결과가 출력이 됩니다.

function * counter(){
   return 1
}

generatorObject=counter()
console.log(generatorObject) // {}
console.log(generatorObject.constructor) //GeneratorFunction {}

👉 그럼 이번에는 yield를 사용보겠습니다.

function * counter(){
   yield 1
   yield 2   
   yield 3   
}

generatorObject=counter()
console.log(generatorObject.next()) // {value: 1, done: false}
console.log(generatorObject.next()) // {value: 2, done: false}
console.log(generatorObject.next()) // {value: 3, done: false}
console.log(generatorObject.next()) // {value: undefined, done: true}

제가 주석으로 출력결과를 적어놓았는데요. 여기서 처음보는 것들이 존재할거예요.

next()가 뭔가요?

next()는 generator 함수를 실행시켜준다고 보시면 됩니다. 저걸 이용해서 yield로 선언된 iterable한
객체들을 불러오는 것이죠. 이건 뒤 실행 방식 파트에서 좀 더 자세히 설명하도록 하겠습니다.

✋ value와 done은 뭔가요??

generator 함수의 iterator result는 value 프로퍼티와 done 프로퍼티를 가진 객체입니다. value는 yield를 통해 선언된 값을 나타내며 done의 경우에는 해당 generator에서 더이상 나올 값이 없을 때 true로 변경됩니다.

실행 방식

generator 함수는 호출되어도 별다른 동작이 없는 진행이 없는 상태라고 보시면 됩니다.
그러한 상태에서 next()라는 메소드를 실행시켰을때 yield로 선언된 값들을 출력해주며 진행해 나가는 것이죠.

1, generator 함수는 호출해도 바로 실행되지 않습니다.
2, iterator object의 next 메소드가 호출되면 함수의 첫 번째 yield 연산자의 위치까지 실행하며 결괏값으로 result를 반환합니다. (위 코드를 기준으로) 첫번째 next 메소드가 호출되었을 때 제너레이터 함수의 내부 처리는 포인트 1에서 일시 정지 상태가 됩니다.
3,next 메소드가 한 번 더 호출되면 함수의 두 번째 yield 연산자의 위치까지 실행하며 iterator result를 반환하고 포인트 2에서 일시 정지 상태가 됩니다.
4, return을 만날 경우에는 done 값이 true로 변경되며 그 이후로는 iteratable한 객체가 반환되지 않습니다.

즉, yield는 해당 generator 내부의 iterator 값들이 출력되는 것에 대해 정지 역할을 해주고, next는 재생 역할을 해준다고 생각하시면 돼요!

활용

위에서 예시로 들었던 reverse 메소드를 한번 generator를 통해 만들어보겠습니다.


// generator를 생성해 놓음
function * reverse(array){
  for(let i=array.length-1;i>=0;i--){
     yield array[i]
  }
}

a=[1,2,3,4,5,6,7,8]

//보다 깔끔하진 코드
for(let value of reverse(a)){
   console.log(value)
}

어떤가요? generator를 하나 생성하고 나서부터는 훨씬 더 깔끔하게 reverse된 배열의 요소들을 출력할 수 있게 되죠? 이것이 바로 generator의 장점입니다.

어디에 사용할까?

예를 들어 방대한 양의 이미지들을 가공해서 다룬다고 생각을 해볼게요.
그렇게 할때 배열을 사용하면 모든 이미지에 대한 메모리를 할당해준 뒤에 작업을 하므로 효율적이지 못할 수가 있어요. 그럴 때는 generator를 이용해서 하나씩 데이터를 불러와서 처리하면 좋겠죠? 그런 유사한 상황일 때 generator를 사용하면 메모리 부담은 줄이고, 코드 가독성은 늘려서 작업을 할 수가 있어요.
또한 각 단계의 반복을 제어할 수 있기 때문에 while (true) { ... } 등을 사용해서 수열을 구현하기도 굉장히 좋습니다.

function* counter() {
  let i = 0;
  while (true) {
    yield i++;
  }
}

generatorObject=counter()
generatorObject.next().value // {value: 0, done: false}
generatorObject.next().value // {value: 1, done: false}
generatorObject.next().value // {value: 2, done: false}
generatorObject.next().value // {value: 3, done: false}
                   .
                   .
                   .
                   .

🙌 마무리

generator과 iterator 같은 경우에는 계속해서 공부를 이어나가야 할 것 같아요.
요즘 면접에서 많이 물어보는 추세이기도 하고, 어렵지만 분명히 도움이 되는 스킬이라고 생각합니다.
저도 아직 익숙치 못하지만 계속해서 다뤄보는 연습을 해야할 것 같아요.

generator 포스팅은 오늘이 1편이고 계속해서 2편도 올리도록 하겠습니다.
읽어주셔서 감사합니다.

profile
김용성입니다.

0개의 댓글