이전 포스트에서는 심리테스트에 들어갈 자료구조를 구성하고,
시작페이지랑 테스트 데이터가 연동되게 작업을 했었으니, 이번에는 "시작하기" 버튼을 눌러서 실제 테스트가 시작되게 만들고, 문제와 답변을 보여주고, 답변이 선택되면 답변마다의 점수가 카운팅 되면서 넘어가고, 다음 문제가 없을 경우 누적된 점수에 따라 결과페이지를 보여주는걸 한번 만들어 보자.
🤗 이번 포스트로 이런게 만들어집니다. 🤗
- 총 문항 :
- 소요시간 :
정리
- 테스트를 시작 할 수 있는 start() 매서드 만들기
- 답변을 골랐을때 다음 문제로 넘어가는 next() 매서드 만들기
- 다음 문제가 없을 경우 getResult() 매서드 호출하기
- 누적된 점수를 가지고 결과페이지를 보여주는 getResult() 매서드 만들기
아. json 내의 data와 result의 형태를 구성해봤다
{
"title": "티스토리 방문자 레벨 테스트",
"description": "당신은 어떤 방문자인가요?",
"type": "point",
"startButtonText": "방문자 레벨 테스트하기",
"data": [
{
"question": "이번 포스트에 공감을 누르셨나요?",
"imageUrl": "",
"answerList": [
{
"answer": "👍🏻 그렇다",
"point": 1
},{
"answer": "👎🏻 그렇지 않다",
"point": 0
}
]
},{
"question": "제 블로그를 구독중이신가요?",
"imageUrl": "",
"answerList": [
{
"answer": "🤩 그렇다",
"point": 1
},{
"answer": "😥 그렇지 않다",
"point": 0
}
]
}
],
"result": [
{
"title": "🚶♂️🚶♀️지나가던 친절한 행인",
"description": "방문해주셔서 감사합니다~ 😉",
"imageUrl": "",
"point": "0-1"
},{
"title": "💏티스토리 이웃",
"description": "항상 고마워요~ 🥰",
"imageUrl": "",
"point": "2"
}
]
}
1. start() 매서드 만들기
우선 원래대로라면 DOM을 조작할 render() 매서드를 따로 만드는 편이긴 한데.. 이번엔 그냥 진행해 보자.
나중에 리팩터링 할 때 하면 된다.
start() {
document.querySelector('.information').style.display = 'none';
document.querySelector('.title').innerHTML = `Q${this.current + 1}.`;
document.querySelector('.description').innerHTML = this.question[this.current].question;
document.querySelector('.button_wrap').innerHTML = '';
for(let i = 0; i < this.question[this.current].answerList.length; i++){
const button = document.createElement('button');
button.innerHTML = this.question[this.current].answerList[i].answer;
button.addEventListener('click', () => {
if(this.type === "point") this.point += this.question[this.current].answerList[i].point;
this.next();
});
document.querySelector('.button_wrap').appendChild(button);
}
}
start() 매서드의 경우 별다른 조건이 없어서 빠르게 만들 수 있다.
위에서 받아온 테스트의 제목, 설명, 그리고 문제들 등을 받아와서 해당 DOM을 채워준다.
문제의 답변이 여러 개일 수 있으므로 for문으로 버튼을 하나씩 생성해서 button_warp에 추가해 주었다.
이때 클릭될 경우의 이벤트 리스너도 같이 달아줘야 하고, 기존 테스트의 버튼이 남아있을 수 있으니 추가해 주기 전에 button_warp을 한번 비워주자.
하단의 for문 (button_warp을 채우는 부분, 문제의 답변을 그려주는 부분)은 next() 매서드에서도 활용할 수 있을 것 같다.
2. next() 매서드 만들기
next() {
if(this.current === this.testLength - 1){
this.getResult();
return;
}
this.current++;
document.querySelector('.title').innerHTML = `Q${this.current + 1}.`;
document.querySelector('.description').innerHTML = this.question[this.current].question;
document.querySelector('.button_wrap').innerHTML = '';
for(let i = 0; i < this.question[this.current].answerList.length; i++){
const button = document.createElement('button');
button.innerHTML = this.question[this.current].answerList[i].answer;
button.addEventListener('click', () => {
if(this.type === "point") this.point += this.question[this.current].answerList[i].point;
this.next()
});
document.querySelector('.button_wrap').appendChild(button);
}
}
우선 current, 문제의 현재 인덱스가 테스트 길이와 같으면 result를 호출하고 함수를 종료시킨다.
이때 this.testLength - 1을 해주는 이유는 current는 0부터 시작해서 올라가고 있기 때문에, 개수를 맞춰주기 위해 -1을 빼준 것이고, 마지막 문제가 아닐 경우 current를 1 올려준다.
그리고 title에는 Q1. 의 형태가 되도록 this.current + 1을 해준 값을 넣어준다. 여기도 똑같이 current가 0부터 시작하므로 +1을 해줘야 한다. 같은 방식으로 description 부분도 문제의 question으로 채워주고,
start()와 동일하게 button_warp을 비워주고, 문제 번호와 맞는 버튼들을 채워준다.
이때 여기서 타이틀, 문제와 버튼을 채워주는 부분은 중복되고 있으니
renderQuestion()이라는 매서드를 하나 만들고 start()와 next() 매서드에서 호출해 보자.
start() {
this.renderQuestion();
}
next() {
if(this.current === this.testLength - 1){
this.getResult(); return
};
this.current++;
this.renderQuestion();
}
renderQuestion() {
document.querySelector('.information').style.display = 'none';
document.querySelector('.title').innerHTML = `Q${this.current + 1}.`;
document.querySelector('.description').innerHTML = this.question[this.current].question;
document.querySelector('.button_wrap').innerHTML = '';
for(let i = 0; i < this.question[this.current].answerList.length; i++){
const button = document.createElement('button');
button.innerHTML = this.question[this.current].answerList[i].answer;
button.addEventListener('click', () => {
if(this.type === "point") this.point += this.question[this.current].answerList[i].point;
if(this.type === "indicator") this.indicator += this.question[this.current].answerList[i].indicator;
this.next()
});
document.querySelector('.button_wrap').appendChild(button);
}
}
그나마 조금은 깔끔해졌다. 흠..
생각해 보니까 그냥 next() 매서드 내에서 current가 0이면 start를 해줘도 좋을 거 같은데..
우선 나중에 어떤 식으로 확장될지 모르니 더 이상 코드를 줄이진 말자.
3. getResult() 매서드 만들기
우선 this.type 이 point (점수 누적형) 일 때니 조건을 걸어주자.
점수의 범위는 1점일 때.. 2점일 때.. 3점일 때.. 이런 식으로 가면 너무 빡빡하니
문자열 "0-1" 이런식으로 넣어주자. "3" 이런 형태일 때도 작동하게 해 보자.
getResult() {
if(this.type === "point"){
const result = this.result.filter(({point}) => {
if(point.split("-").length === 1) this.point === +point ? true : false;
const [min, max] = point.split("-");
return this.point >= +min && this.point <= +max ? true : false;
})[0];
document.querySelector('.title').innerHTML = result.title;
document.querySelector('.description').innerHTML = result.description;
document.querySelector('.button_wrap').innerHTML = '';
const replayBtn = document.createElement('button');
replayBtn.innerHTML = '다시하기';
replayBtn.addEventListener('click', () => location.reload());
document.querySelector('.button_wrap').appendChild(replayBtn);
return;
}
}
이렇게 작성해 주면, 테스트 타입이 point일 때, result에서 point 범위를 가져와 해당 범위에 맞는 결과를 보여주고,
버튼이 다시 하기로 바뀌게 된다.
끝!
다음 포스트에서는 mbti처럼 성향을 분석?.. 까진 아니고 분류해서 테스트 결과를 보여주는 타입을 만들어보자.