home강의 홈으로
Section 3. 실전 RxJS!
Lesson 3. 온라인 타자속도 측정기 만들기
<script src="https://unpkg.com/@reactivex/rxjs/dist/global/rxjs.umd.js"></script> <div id='given'></div> <input id='input' type='text' /> <button id='start'>Start</button> <br> <div id='logs'></div> body { padding: 16px; } #given { height: 36px; } .loading { display: inline-block; padding: 3px 12px; color: tomato; font-size: 0.9em; font-weight: bold; border: 2px dashed tomato; border-radius: 4px; } .word { color: #333; font-size: 1.2em; font-weight: bold; } #input { width: 200px; height: 24px; line-height: 24px; margin-bottom: 8px; padding: 2px 8px; border: 2px solid #ccc; border-radius: 4px; } #start { color: white; background-color: dodgerblue; height: 30px; line-height: 30px; padding: 0 16px; border: 0; border-radius: 5px; outline: none; } .average { height: 48px; line-height: 48px; font-weight: bold; color: #333; border-bottom: 2px solid #ddd; } .average span { color: dodgerblue; } .score { height: 36px; line-height: 36px; padding: 0 8px; font-size: 0.92em; border-bottom: 1px solid #eee; } .score:nth-child(odd) { background-color: #f8f8f8; } .score span { font-weight: bold; }
  • 서버로부터 임의의 이름 받아오기
const { Subject, BehaviorSubject, fromEvent, combineLatest, from } = rxjs const { ajax } = rxjs.ajax const { tap, switchMap, pluck, startWith, filter, timeInterval, map, scan, reduce, skip } = rxjs.operators const given = document.getElementById('given') const input = document.getElementById('input') const start = document.getElementById('start') const logs = document.getElementById('logs') const wordSubject = new Subject().pipe( tap(word => given.innerHTML = `<span class="word">${word}</span>`) ) const ajaxSubject = new Subject().pipe( tap(_ => given.innerHTML = '<span class="loading">LOADING...<span>'), switchMap(_ => ajax('http://127.0.0.1:3000/people/name/random').pipe( pluck('response', Math.random() > 0.5 ? 'first_name': 'last_name'), tap(console.log) )) ) ajaxSubject.subscribe(word => { wordSubject.next(null) // 단어가 도착한 순간부터 초를 재기 위함 wordSubject.next(word) }) fromEvent(start, 'click').subscribe(_ => { input.focus() ajaxSubject.next() })
  • 받아온 이름과 타이핑된 글자 combine
combineLatest( wordSubject, fromEvent(input, 'keyup').pipe( pluck('target', 'value'), startWith(null) // 첫 단어 직전의 null과 combine되기 위한 초기값 ) ).subscribe(console.log)
  • 받아온 이름과 타이핑된 글자 combine → 둘이 같을 때 또는 이름을 갓 받아왔을 때(keyword === null) 발행
combineLatest( wordSubject, fromEvent(input, 'keyup').pipe( pluck('target', 'value'), startWith(null) // 첫 단어 직전의 null과 combine되기 위한 초기값 ) ).pipe( filter(([keyword, typed]) => { console.log(keyword, typed) return [typed, null].includes(keyword) }) ).subscribe(console.log)
  • 받아온 이름대로 타이핑시 입력칸 지우고 새 이름 받아오기
combineLatest( wordSubject, fromEvent(input, 'keyup').pipe( pluck('target', 'value'), startWith(null) // 첫 단어 직전의 null과 combine되기 위한 초기값 ) ).pipe( filter(([keyword, typed]) => { return [typed, null].includes(keyword) }) ).subscribe(([keyword, _]) => { if (keyword !== null) { // 받아온 이름과 타이핑이 일치할 때 input.value = '' ajaxSubject.next() } })
  • 이름이 입력될때마다의 시간 간격 얻기
combineLatest( wordSubject, fromEvent(input, 'keyup').pipe( pluck('target', 'value'), startWith(null) // 첫 단어 직전의 null과 combine되기 위한 초기값 ) ).pipe( filter(([keyword, typed]) => { return [typed, null].includes(keyword) }), timeInterval() // 단어가 갓 주어졌을 때 ~ 입력 성공했을 때 ).subscribe(result => { console.log(result.value) if (result.value[0] !== null) { // 받아온 이름과 타이핑이 일치할 때 input.value = '' ajaxSubject.next() } printRecords({ interval: result.interval, value: result.value[0] }) }) function printRecords (result) { console.log(result) }
  • 입력 완료시마다 내역 쌓고 평균 구하기
// function printRecords (result) { // console.log(result) // } // 대체 const recordSubject = new BehaviorSubject({ records: [], average: null }).pipe( filter(result => result.value !== null), scan((acc, cur) => { acc.records.push(cur) from(acc.records).pipe( reduce((acc2, cur2) => { return { lettersTotal: acc2.lettersTotal += cur2.value.length, intervalTotal: acc2.intervalTotal += cur2.interval } }, { lettersTotal: 0, intervalTotal: 0 }) ).subscribe(result => { acc.average = result.intervalTotal / result.lettersTotal }) return acc }) ) recordSubject.subscribe(console.log) function printRecords (result) { recordSubject.next(result) }
  • 내역과 평균 출력
// recordSubject.subscribe(console.log) 대체 recordSubject.pipe( skip(1) ).subscribe(result => { logs.innerHTML = `<div class="average">Average: <span>${result.average}</span></div>` + result.records.map(record => { return `<div class="score">${record.value}: <span>${record.interval}</span></div>` }).join('') })

🤔얄코에게 질문하기질문은 반.드.시 이리로 보내주세요! ( 강의사이트 질문기능 ✖ )

강의에서 이해가 안 되거나 실습상 문제가 있는 부분,
설명이 잘못되었거나 미흡한 부분을 메일로 알려주세요!

답변드린 뒤 필요할 경우 본 페이지에
관련 내용을 추가/수정하도록 하겠습니다.

이메일 주소
yalco@yalco.kr
메일 제목 (반드시 아래 제목을 붙여넣어주세요!)
[질문] RxJS 3-3

🛑질문 전 필독!!

  • 구글링을 먼저 해 주세요. 들어오는 질문의 절반 이상은 구글에 검색해 보면 1분 이내로 답을 찾을 수 있는 내용들입니다.
  • 오류 메시지가 있을 경우 이를 구글에 복붙해서 검색해보면 대부분 짧은 시간 내 해결방법을 찾을 수 있습니다.
  • 강의 페이지에 추가사항 등 놓친 부분이 없는지 확인해주세요. 자주 들어오는 질문은 페이지에 추가사항으로 업데이트됩니다.
  • "유료파트의 강의페이지는 어디 있나요?" - 각 영상의 시작부분 검은 화면마다 해당 챕터의 강의페이지 링크가 있습니다.
  • 질문을 보내주실 때는 문제가 어떻게 발생했고 어떤 상황인지 등을 구체적으로 적어주세요. 스크린샷을 첨부해주시면 더욱 좋습니다.
🌏 Why not change the world?