본문 바로가기
JavaScript

[자바스크립트 웹개발] 마성의 룰렛은 처음이지? Math.random() 함수를 이용한 리스트형 룰렛 구현하기

by 돈민찌 2021. 9. 28.
반응형

어느 정도 사이트의 주요기능이 자리를 잡고, 엔터테인먼트적인 요소를 추가하고 싶어서 룰렛을 만들었습니다. 이름은 룰렛이지만, 개발자스럽게 리스트 형태로 for 문을 돌게 만들었어요!!ㅎㅎㅎ 딱 내 스타일.. 자바스크립트로 리스트를 셔플하는 메소드는 구글링해보면 대부분 비슷한 답을 가지고 있어요. 알고리즘에 관해 잘 아는 편은 아니지만 어느 정도 이해를 할 수 있는 선을 넘는 코드는 사용을 주저하는 편이라 개중에 가장 마음에 드는 셔플 함수를 골랐어요.

// 리스트의 순서를 뒤섞는 함수입니다.
function shuffle(array) {
    for (let i = array.length - 1; i > 0; i--) {
        let j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }
    return array
}

일반적인 let i=0 초기화가 아닌 배열의 끝자리 인덱스부터 시작해 제일 첫번째 인덱스 바로 뒤(1번 인덱스)까지 1씩 줄어들면서, 0과 1 사이의 난수를 만드는 메소드인 random() 에서 나온 값에 n번째의 n을 곱해서 정수로 만들어, 해당 인덱스의 요소와 i번 인덱스의 요소를 스왑하는 형식입니다. random()이나 shuffle 만을 사용하는 것보다 예측하기 힘들고 결과값이 고른 편이라고 해 채택했습니다.

저희가 구현한 룰렛은 실제 룰렛처럼 처음에는 빠르게 빨간글씨가 한바퀴를 돌고 점점 느려지다가 마지막에 멈춰선 곳의 값이 얼럿으로 나타나는 형식입니다. 그렇다보니 for문을 특정한 시간 간격으로 수행하는 함수를, 그것도 고르지 않고 점차 빨라지며 여러바퀴를 순회하는 코드를 짜야합니다.

자바스크립트의 비동기적 작동방식에 익숙하지 않은 분들이라면, 파이썬의 time.sleep() 함수처럼 시간을 지연시켜주는 메소드가 자바스크립트에 없다는 사실을 받아들이기 힘드실 겁니다. 저도 그랬구요ㅎㅎ 처음 딱 코드 짜볼까 하고 팔 걷었을 땐 음 setInterval()은 무리겠지? 하고 인터벌부터 떠오르더라구요 네 정상적인것같습니다ㅎㅎ 하지만 우리는 무한대로 반복하는 루프를 만들고자 하는 것이 아니고, 또 속도도 자유롭게 선택하여 구현하여야 하기 때문에 적합하지 않습니다. 그래서 setTimeout 함수를 사용했습니다. 예시를 보여드리겠습니다.

// 비동기처리 방식 자바스크립트를 고려한 타이머 함수
const timer = ms => new Promise(r => setTimeout(r, ms))

Promise 객체를 여러번 만드는 것이 좋은 방법은 아니겠지만, 꽤나 보편적으로 쓰이는 지연방식 중에 하나인 것으로 판단해 사용했습니다. 화살표 함수로 구현했고, 밀리세컨드 값을 인수로 받아, 비동기 실행으로 시간을 밀리세컨드 만큼 지연시키기만 하는 함수입니다. 사용법은 파이썬의 time.sleep()과 거의 똑같습니다. 밀리세컨드 단위라는 점만 다르죠.

for (let i = 0; i < array.length; i++) {
    await timer(60)
    $(`span.word.word-${i}`).addClass('is-red')
    $("span.word").not(`.word-${i}`).removeClass('is-red')
}
for (let i = 0; i < array.length; i++) {
    await timer(100)
    $(`span.word.word-${i}`).addClass('is-red')
    $("span.word").not(`.word-${i}`).removeClass('is-red')
}
for (let i = 0; i < array.length; i++) {
    await timer(200)
    $(`span.word.word-${i}`).addClass('is-red')
    $("span.word").not(`.word-${i}`).removeClass('is-red')
}

이런 식입니다. 비동기적 방식 때문에 타이머함수가 실행되기 전에 다음 코드가 먼저 실행되겠지만, 비동기적 코드 간의 순서 관계는 명확하게 읽혀지니까 정확하게 원하는 대로 동작할 것 같습니다. await 키워드를 사용했으니 async function 안에 삽입해야겠죠ㅎㅎ 실제 사용한 코드 부분인데, 여러 숫자를 넣어가며 비교해본 결과 네번이 넘게 룰렛이 도는 것은 영 답답하게 느껴지고, 그렇다고 너무 빠르면 제대로 볼 시간도 없을 것 같아 처음엔 60ms, 그리고 100ms, 200ms, 600ms로 느려지다가 처음에 보여드린 랜덤한 인덱스의 값에 도달하면 멈추는 룰렛을 만들었습니다. 아이디어가 떠오르는데 시간이 걸리긴 헀지만 그 외에는 수월하게 작성할 수 밖에 없는 기본적인 문법이었고, 실제로 작동하는 것도 예쁜 배경화면의 모달이 떠오른 상태에서 숫자가 색이 변하는 모습이 꽤 재미를 줄 만했고 좋았습니다ㅎㅎ

물론 제이쿼리가 아닌 자바스크립트 방식으로도 구현이 가능하고, ms 값이 아닌 초를 인수로 받는 timer 함수를 만드는 것도 어렵지 않을 것 같네요ㅎㅎ 오늘은 설명이라기 보다는 경험 기록이라 이 정도만 하고 마치도록 하겠습니다 글 읽어주셔서 감사합니다ㅎㅎ

반응형

댓글