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()
})
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('')
})
🤔얄코에게 질문하기질문은 반.드.시 이리로 보내주세요! ( 강의사이트 질문기능 ✖ )
🛑질문 전 필독!!
- 구글링을 먼저 해 주세요. 들어오는 질문의 절반 이상은 구글에 검색해 보면 1분 이내로 답을 찾을 수 있는 내용들입니다.
- 오류 메시지가 있을 경우 이를 구글에 복붙해서 검색해보면 대부분 짧은 시간 내 해결방법을 찾을 수 있습니다.
- 강의 페이지에 추가사항 등 놓친 부분이 없는지 확인해주세요. 자주 들어오는 질문은 페이지에 추가사항으로 업데이트됩니다.
- "유료파트의 강의페이지는 어디 있나요?" - 각 영상의 시작부분 검은 화면마다 해당 챕터의 강의페이지 링크가 있습니다.
- 질문을 보내주실 때는 문제가 어떻게 발생했고 어떤 상황인지 등을 구체적으로 적어주세요. 스크린샷을 첨부해주시면 더욱 좋습니다.