주요 내용으로 건너뛰기

2. SIMON-GAME WITH REDUX

Redux 사용해보기

2017.10.20 (금) 수정


이 글을 읽기 전에


사실 simon game 같은 간단한 게임에 Redux를 적용하는 건 과한 일일 수 있습니다. 


제가 저 당시에 setState만으로 만들어내지 못한건 단순히 리액트에 대한 제 이해가 부족했기 때문입니다.


즉, Redux 없이 setState 만으로 완전히 같은 기능을 하도록 만들 수도 있습니다. 


Dan Abramov의 "You might not redux"(https://medium.com/@dan_abramov/you-might-not-nee.    d-redux-be46360cf367)는 Redux 구조를 setState 만으로 구현해놨습니다. 


어쨌든 Redux 구조라는 건 결국 액션을 디스패치해서, 리듀서에서 결과를 처리하고 그 처리된 상태를 가져와 사용하는 거니까요.


결국 Redux를 모든 프로젝트에 적용하는건 overkill일 수 있습니다.


코드양이 늘어난다는 건 반가운 일은 아니죠 ㅋㅋ.


다시 원래 내용으로 돌아와서


simon game 에는 몇 초 뒤에 어떤 작업을 수행하는 함수가 필요합니다.


예를 들면 어떤 패드를 클릭하면 소리가 나면서 반짝이는 행동을 몇 초간 하다가 다른 패드가 또 그 동작을 반복해야 하죠.


만약 이런 함수가 필요하다면 일반 JS의 setTimeout 만 쓰셔도 충분히 가능합니다.



이렇게 하면 2초 뒤에 handleAsyncChange 함수가 실행되고 다른 메시지가 화면에 보이게 됩니다.


만약 2초 뒤에 message1을 보여주고 또 그 2초 뒤에 message2를 보여주고 싶다면



이런 식으로 한 단계를 더 추가하면 됩니다.


혹은 for 반복문을 통해서 간단하게



이렇게 작성하셔도 됩니다.







원글


저번 시간에는 simon game의 틀을 만들어보았습니다.


1. SIMON-GAME 시작하기: https://gompro.postype.com/post/730146/


 이제 각종 기능들을 구현하게 될 텐데요.


 이를 그냥 리액트의 state 조작을 통해서 구현하는 방법도 있지만 저는 Redux와 함께 구현해보도록 하겠습니다. 


저번 시간에도 말씀드렸듯이 Redux는 리액트의 상태관리를 위한 도구입니다. 


편리하고 유용하지만 가장 큰 단점으로는 배우기 어렵다는 점이 있습니다 ㅜㅜ


리액트의 상태 변화가 setState 함수 하나로 해결되는 것에 비하면 정말 난이도는 비교할 수가 없죠.


그래서 저 역시도 처음에는 Redux 없이 simon game 구현에 도전했었습니다. 


결과는 어찌저찌해서 구현은 했지만 상태변화의 '순서'를 정확하게 알아챌 수가 없었기 때문에


결국 특정 단계 (9~10 단계)를 넘어서면 오디오가 뒤섞이면서 렉이 걸리고 동작이 중단되었습니다. 


물론 상태변화와 뷰의 구분 또한 보장되지 않았구요.


Redux를 선택하는 데는 많은 이유가 있지만 가장 큰 것 중 하나는 


리액트가 기본적으로 우리에게 제공하는 setState 함수가 '비동기적'이라는 점이 큽니다. 


이는 안정적이고 예측가능한 상태관리를 어렵게 하기에 Redux의 가치를 더욱 빛나게 합니다.


setState의 비동기성 :  https://medium.com/@wereHamster/beware-react-setstate-is-asynchronous-ce87ef1a9cf3


다시 UserStory를 상기해봅시다. 


1. I am presented with a random series of button presses.

버튼을 누르는 순서는 무작위로 생성된다. (매번 정해진 순서로 플레이하는 것이 아님 )

(1번은 해결!)


2. Each time I input a series of button presses  correctly, I see the same series of button presses but with      an  additional step.

만약 버튼을 순서에 맞게 정확히 누르면, 그 단계의 순서와 함께 추가적인 순서가 제공된다.

(만약 이번 순서가 1-2-3-4 라면 정확히 눌렀을 때 1-2-3-4-7 이런 식으로 순서가 제공되어야 한다는 뜻입    니다.)  


3.  I hear a sound that corresponds to each button both when the series of   button presses plays, and          when I personally press a button.

효과음은 내가 버튼을 눌렀을 때뿐만 아니라 자동으로 순서가 재생될 때도 나와야한다.


4.  If I press the wrong button, I am notified that I have done so, and   that series of button presses starts      again to remind me of the  pattern  so I can try again.

만약 잘못된 버튼을 누르면, 그 사실을 유저에게 알려주고 처음부터 순서를 재생하여 유저가 다시 시도할    수 있게끔 한다. 


5. I can see how many steps are in the current series of button presses.

이번 단계를 마치려면 몇 번 버튼을 눌러야하는지 알 수 있다.


6. If I want to restart, I can hit a button to do so, and the game will return to a single step.

만약 다시 시작하고 싶다면, 어떤 버튼을 눌러 첫 단계로 돌아갈 수 있다.


7.  I can play in strict mode where if I get a button press wrong, it   notifies me that I have done so, and        the game restarts at a new  random  series of button presses.

스트릭트 모드 : 버튼을 잘못 누르면, 그 사실을 유저에게 알려주고 처음으로 돌아가 새로운 순서가 생성      됨.


8. I can win the game by getting a series of 20 steps correct. I am notified of my victory, then the game      starts over.

20 단계를 클리어하면 유저에게 게임을 클리어했다는 사실을 알려주고, 게임을 다시 시작한다.


Redux를 시작하기에 앞서 좋은 도구를 소개하겠습니다. 


1. React Developer Tools (각 컴포넌트의 구조와 상태변화를 관찰할 수 있음)

2. Redux DevTools (액션, 리듀서함수, 최종 상태 등을 시간 순서대로 관찰할 수 있음)


이 두 가지 도구는 모두 크롬 확장 도구나 파이어폭스 확장 도구에서 다운로드 받으실 수 있으니 이 두가지는 무조건 갖추고 시작하도록 합시다!


사용법은 다운로드 받은 뒤 개발자도구(F12)를 눌러서 리액트 아이콘을 클릭하시거나 주소창에 있는 리덕스 아이콘을 클릭하시면 됩니다.


이제 우리의 SIMON-GAME에 Reducer를 작성해줍시다.


src 폴더 아래에 reducers 폴더를 만들어주시고 두 개의 리듀서를 사용할 예정이기 때문에 


UserReducer.js로 이름붙여줍니다.


앞에서도 말했듯이 두 개의 리듀서를 사용할 예정이므로 reducers 폴더에 index.js 를 만들어주시고



src 폴더에 있는 index.js 로 가셔서 스토어를 생성하고 리듀서를 스토어와 연결해줍니다.


마지막으로 컴포넌트와 스토어를 연결해줄 container를 작성해봅시다.


이제 Pads.jsx 대신 PadsContainer를 Layout.jsx에 사용하면 됩니다.

  여기까지 따라오셨다면 

리덕스 첫 적용!
리덕스 첫 적용!


  아마 이런 화면을 볼 수 있으실 겁니다.


 이제 간단하게 동작원리를 파악해보도록 하죠.


전편에서도 말했듯이 Redux는 상태를 '통합적'으로 관리합니다. 


여러 지역 컴포넌트들이 서로 상태를 관리하는 것보다는 한 곳에서 상태를 관리하는 것을 선호하죠.


그리고 그 모든 상태를 가지고 있는 곳이 바로 스토어 입니다.


우리는 맨 먼저 리듀서를 작성했고 그 다음 스토어를 생성하는 코드를 작성했으며 마지막으로 container를 이용해서 상태를 props의 형태로 컴포넌트에 전달했습니다.


대략 이런 관계가 됩니다.


리듀서 (state를 가지고 있음) ----스토어 (리듀서들이 보내주는 state를 가지고 있음) -----컨테이너(컴포넌트에 state를 연결)


복잡해보이지만 앞으로 프로젝트를 진행하면서 굉장히 고마워하게 될 Redux입니다. 


여기까지 왔다면 


5. I can see how many steps are in the current series of button presses.

이번 단계를 마치려면 몇 번 버튼을 눌러야하는지 알 수 있다.


5번 UserStory를 클리어할 수 있겠군요.



이제 3이라는 숫자가 보이시나요?


벌써 두 개의 UserStory를 통과했군요.


이제부터 복잡한 단계가 시작됩니다.


그 전에 utils에서 가져오는 녀석들이 이름이 굉장히 길어 거슬리니 index.js 를 만들어줍시다.


   이제 요녀석들을 불러오는게 한결 편해졌군요.


  그 동안 Pads를 네 개씩이나 타이핑하느라 힘들었을 여러분들을 위해 더 간편한 방법을 제시합니다.

한결 보기 좋군요.


그 다음은 소리와 애니메이션 단계입니다. 


3.  I hear a sound that corresponds to each  button both when the series of   button presses plays, and          when  I personally press a button.

효과음은 내가 버튼을 눌렀을 때뿐만 아니라 자동으로 순서가 재생될 때도 나와야한다.


3번째 UserStory와 관련이 있겠네요.


이제 Pads에 오디오를 연결해줍시다.

 자 그러면 이제 어떻게 소리와 애니메이션이 나오도록 할 수 있을까요?


역시 가장 좋은 방법은 상태를 변화시키는 것입니다.


저번 시간에 작성했던 Pad 컴포넌트를 들여다봅시다.


각 Pad마다 playing 값을 줘서 playing이 참이면 애니메이션과 소리를 재생하고 아니라면 그냥 놔두는거죠.


그렇다면 얼른 PadReducer를 작성해야겠군요.



UserReducer 와 굉장히 흡사한 모습입니다.


똑같이 상태를 받아서 상태를 돌려주고 있죠.


이제 상태를 가져와봅시다.

제대로 연결되었다면 이 화면이 뜰 것이다.
제대로 연결되었다면 이 화면이 뜰 것이다.


pads도 있고, status 그리고 seq까지 모두 다 있군요.


다음 질문입니다. 어떻게 하면 playing 값이 바뀔 때마다 오디오가 재생될까요?


정답은 리액트의 라이프사이클을 이용하는 것입니다.



여기서 this[activePad[0].position] 부분이 궁금하실 수 있는데 이는 리액트의 ref를 사용한 것입니다.



이제 모든 것이 갖춰줬으니 playing값을 참으로 만들 액션을 작성해봅시다.


여태까지는 상태를 작성하고 상태를 받아서 가져오는 것에 그쳤습니다. 


하지만 액션을 작성하고 컴포넌트에서 dispatch 하면 상태를 변화시킬 수 있습니다.


직접 작성해보죠.


src 아래에 actions 디렉토리를 만들고


PadActions.js 를 작성해줍시다.

저 같은 경우는 액션의 타입(상수로 정의된 문자열)과 액션(타입 아래)을 같은 곳에 작성했지만 둘을 서로 분리해서 작성해도 상관없습니다. 


저는 다만 이렇게 하는게 좀더 편해서 둘을 한데 모았습니다.


index.js도 빼놓으면 안 되겠죠?


이제 PadReducer를 수정해봅시다.


마지막으로  눌렀을 때 애니메이션과 소리가 나오도록 Pads를 수정합시다.


이제 결과를 확인해보죠!!




4번째 패드의 playing 값이 참이 되었다.
4번째 패드의 playing 값이 참이 되었다.


   여기서 리덕스 devtool의 진가가 발휘됩니다.


   어떤 액션이 디스패치되었는지는 물론 상태가 어떻게 변하는지 또 밑의 슬라이더를 조정하면 각 액션이      디스패치된 후의 상태까지 세밀하게 파악가능합니다.


   정말 짱짱한 도구라고 할 수 있죠.


   이번 시간은 여기까지 하도록 하겠습니다. 다음에는 본격적으로 UserStory들을 클리어해보도록 하겠습니    다.


다음편들:


3편 https://gompro.postype.com/post/732228

4편 https://gompro.postype.com/post/732275

5편 https://gompro.postype.com/post/732388

이준형 님의 창작활동을 응원하고 싶으세요?

댓글

SNS 계정으로 간편하게 로그인하고 댓글을 남겨주세요.