이제 정답을 제출하여 문제를 맞췄는지 틀렸는지를 체크해보도록 하겠습니다.

 

Submit 버튼에 @click='submitAnswers' 이벤트를 만들어두고, methods에서 submitAnswers를 정의해주면 됩니다.

 

   
<button class="submit-button" @click="submitAnswers">Submit</button>
 
 
submitAnswers() {
      let correctAnswerCount = 0
      this.selectedAnswerList.forEach((selectedAnswer, idx)=>{
        if(selectedAnswer == this.questions[idx].correctAnswer){
          correctAnswerCount++
        }
          console.log(`${this.questions.length}개의 문제 중 ${correctAnswerCount}개를 맞추셨습니다!!`)
      })
    }
 

간단하게 함수를 만들어보았습니다. 이제 정답을 체크하고 잘 작동하는지 테스트해보도록 합니다.

 

b를 선택하니 0개 , c를 선택하니 1개를 맞췄다고 잘 나오는걸 확인할 수 있습니다.

 

이제 문제를 좀 더 다뤄보도록 하겠습니다.

 

문제가 텍스트로 되어있을 수도 있지만, 이미지를 보여주고 문제를 풀어야 할 수도 있게끔 만들고 싶습니다.

 

 

mypjt의 src의 asssets에 이미지를 넣어보도록 합시다.

 

이미지의 이름은 problem + 숫자 로 만들어서 자동으로 해당 문제에 이미지가 들어갈 수 있도록 하면 좋을 것 같습니다.

 

 
<div>
      <h3 class="question-number">문제 {{index+1}}</h3>
      <p class="question-text">{{question}}</p>
      <img :src="image" alt="">
    </div>
 

문제를 보여주는 html코드를 수정합니다. img 태그를 넣어주고 src를 동적으로 받습니다.

 

src는 data에서 정해주도록 합니다.

 

mypjt 의 src폴더는 @으로 바로 접근할 수가 있지만, 단순 문자열에 @을 넣어서는 사이 불가능합니다. 

 

실제로 문자열@이 들어갈 뿐이니까요. 그렇기에 우리는 require를 사용해서 넣어주어야 합니다.

 

 

 

 
data(){
    return {
      selectedAnswer:null,
      image:require(`@/assets/problem${this.index}.png`)
    }
 

 

 

이미지가 잘 들어가는 걸 볼 수 있습니다.

 

자 이제, Submit버튼을 누른 이후의 결과를 작업해주도록 합시다.

 

일단 Submit버튼을 누르면 몇 문제를 맞췄는지, 그리고 어떤 문제를 틀렸는지와,  고른답, 실제정답이 나와야합니다.

 

그리고 기존 문제들과 Submit버튼은 사라지게 만들어야합니다.

 

일단 문제들이 사라지게 만드는 것 부터 해보도록 하겠습니다.

 

 
submitAnswers() {
      let correctAnswerCount = 0
      this.selectedAnswerList.forEach((selectedAnswer, idx)=>{
        console.log(selectedAnswer, this.questions[idx].correctAnswer)
        if(selectedAnswer === this.questions[idx].correctAnswer){
          correctAnswerCount++
        }
        this.showIndex = -1
      }
      )
          console.log(`${this.questions.length}개의 문제 중 ${correctAnswerCount}개를 맞추셨습니다!!`)
    }
 

버튼을 눌렀을 때, 작동하는 버튼입니다. 버튼을 누르면 showIndex를 -1로 설정해주면 v-show에서 걸러낼 수 있을 것 같습니다.

 

그리고 동시에 버튼을 누르면 Submit버튼도 사라지게 만들어야 하니 button에도 v-show를 넣어주도록 합니다.

 

(Router를 만들어서 아예 ResultComponent를 만들고 그곳으로 이동시키는 방법이 더욱 나을 것 같긴 한데, 

 

아직 Router를 배우지 않아 일단 이렇게 하도록 하겠습니다...

 

 
<QuestionComponent
      v-for="(question, index) in questions"
      :key="index"
      :question="question.question"
      :answer="question.answer"
      :index="index"
      v-show="index == showIndex"
      @next-move="nextMove"
      @prev-move="prevMove"
      @save-answer="saveAnswer"
    />
    <button class="submit-button" @click="submitAnswers" v-show="showIndex!=-1">Submit</button>
 

버튼에는 showIndex가 -1이 되면 사라지게 만들어주었습니다. 이제 showIndex가 -1일 때 보여줄 컴포넌트를 하나 만들도록 하겠습니다.

 

그것이 바로 ResultComponent입니다.

 

Result컴포넌트를 하나 만들고 v-show를 걸어서  showIndex가 -1일때 나오도록 해줍니다.

 

제출하기 전에는 없어도 되는 컴포넌트이니 v-if를 사용해도 될 것 같습니다. 

 

   
<ResultComponent v-if="showIndex===-1" />
 

자 이제 확인해보도록 합니다.

 

ResultComponent.vue

<template>
    <div>
        정답을 몇개 맞췄는지 보여주고

        틀린 문제들을 출력할 컴포넌트입니다.

        기존 화면이 사라지고 이 컴포넌트가 잘 나오고 있는지 테스트중입니다.
    </div>
</template>

<script>
export default {
    name: 'MypjtResultComponent',

    data() {
        return {
            
        };
    },

    mounted() {
        
    },

    methods: {
        
    },
};
</script>

<style lang="scss" scoped>

</style>

아주 완벽합니다!!! 이제 여기에 몇개의 문제를 맞췄는지 보여주기 위해서 props를 넘겨주어야 할텐데요.

 

이렇게 작업을 하고나니 submit버튼은 그저 showIndex값만 -1로 바꿔주고 그 외에 값들을 계산은 computed함수를 이용하면 더욱 깔끔해질 것 같습니다.

 

 
submitAnswers() {
     
        this.showIndex = -1
      }
  },
  computed :{
    correctAnswerCount(){
      let correctAnswerCount = 0
      this.selectedAnswerList.forEach((selectedAnswer, idx)=>{
        console.log(selectedAnswer, this.questions[idx].correctAnswer)
        if(selectedAnswer === this.questions[idx].correctAnswer){
          correctAnswerCount++
        }
      }
      )
      return correctAnswerCount
    }
  },
 

이렇게 computed를 설정해주고 이 값을 props로 넘겨주도록 합시다. 총 몇 문제인지도 체크하기 위해 그 값도 넣어줍니다.

 

 
    <ResultComponent v-if="showIndex===-1" :correct-answer-count="correctAnswerCount"/>
 

ResultComponent에서도 props로 받아줍니다.

 

 
총 {{totalCount}}의 문제 중 {{correctAnswerCount}}개의 문제를 맞췄습니다
 

이렇게 값을 추가해주면?

 

잘 나오긴 하는데... 이미지를 넣어준 이후로 이미지가 존재하지 않으면 컴포넌트가 맛이 가버리는 현상이 생겼습니다...

 

이미지가 반드시 있어야하는건 아닌데, 404에러는 생길 수 밖에 없고... 

 

img src에 바로 인덱스를 넣는 것이 아니라 부모 컴포넌트의 questions에서 이미지 주소를 미리 생성해놓고, 이미지 주소값이 넘어 왔을 때만 img태그를 만드는 작업이 필요할 것 같습니다...

 

그건 이따가 고쳐주고 일단은 이미지를 모두 채워놓도록 하겠습니다.

이렇게 제출을 하면서 느끼게 된건데, 다음문제가 없는데 다음문제 버튼이 있는것도 굉장히 불편하게 느껴집니다.

 

마지막 인덱스와 처음 인덱스에는 각각 다음 문제와 이전 문제의 버튼을 히든으로 해두어야겠습니다.

 

이 때는 v-show를 사용하면 안됩니다. 왜냐면 v-show는 태그를 사라지게 하지는 않지만 display:none으로 만들어버리거든요

 

우리는 다음문제 버튼의 위치가 바뀌는 걸 원하지 않습니다. visible을 hideen으로 바꿔주도록 클래스 바인딩을 해두록 합시다.

 
computed : {
    isFirst(){
      return this.index==0 ? true : false
    }
  },

문제는 Last일 때 다음 문제를 사라지게 하는건데요, 문제 전체의 개수를 알 수 없으니 알 수가 없습니다. 부모 컴포넌트에서 문제 개수를 넘겨주도록 합니다.

 

 
<QuestionComponent
      v-for="(question, index) in questions"
      :key="index"
      :question="question.question"
      :answer="question.answer"
      :index="index"
      :totalCount="questions.length"
      v-show="index == showIndex"
      @next-move="nextMove"
      @prev-move="prevMove"
      @save-answer="saveAnswer"
    />
 

totalCount 추가 questions.length

 

 

 
 <button @click="prevMove" class="btn next-btn" :class="{'invisible' : isFirst}"> 이전 문제</button>
 <button @click="nextMove" class="btn prev-btn" :class="{'invisible' : isLast}">다음 문제</button>
 

버튼을 추가하고 각각 computed에 정의한 함수들로 클래스를 on/off 시켜줍니다.

 

<template>
  <div>
    <div>
      <h3 class="question-number">문제 {{index+1}}</h3> 
      <p class="question-text">{{question}}</p>
      <img :src="image" alt="">
    </div>
    <div class="container d-flex justify-content-center">
      <div class="options">
        <div class="answerBox d-flex align-items-center justify-content-center ">
          <input type="radio" v-model="selectedAnswer"  :name="'answer'+index" value='a' :id="'a'+index"> <label class="ps-4 me-5" :for="'a'+index">{{ answer.a }}</label> 
          <input type="radio" v-model="selectedAnswer" :name="'answer'+index" value='b' :id="'b'+index"> <label class="ps-4 me-5" :for="'b'+index">{{ answer.b }} </label> 
          <input type="radio" v-model="selectedAnswer" :name="'answer'+index" value='c' :id="'c'+index"> <label class="ps-4 me-5" :for="'c'+index">{{ answer.c }} </label> 
          <input type="radio" v-model="selectedAnswer" :name="'answer'+index" value='d' :id="'d'+index"> <label class="ps-4" :for="'d'+index">{{ answer.d }} </label> 
        </div>
        <div class="d-flex justify-content-between" style="width:500px;">
          <button @click="prevMove" class="btn next-btn" :class="{'invisible' : isFirst}"> 이전 문제</button>
          <button @click="nextMove" class="btn prev-btn" :class="{'invisible' : isLast}">다음 문제</button>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data(){
    return {
      selectedAnswer:null,
      image:require(`@/assets/problem${this.index+1}.png`),
    }
  },
  computed : {
    isFirst(){
      return this.index==0 ? true : false
    },
    isLast(){
        return this.index==this.totalCount-1 ? true : false
    }
  },
  props:{
    question:String,
    answer:Object,
    index:Number,
    totalCount:Number,
  },
  methods:{
    nextMove(){
      this.$emit('next-move')
    },
    prevMove(){
      this.$emit('prev-move')
    },
    saveAnswer(){
      this.$emit('save-answer', {index:this.index, selectedAnswer:this.selectedAnswer})
    }
  
  },
  watch :{
    selectedAnswer :{
      handler(){
        this.saveAnswer()
      }
    }
  }
}
</script>

<style>
.question-number {
  color: #FF69B4;
  font-size: 24px;
  margin-bottom: 10px;
}

.question-text {
  color: #4B0082;
  font-size: 20px;
  margin-bottom: 20px;
  text-shadow: 2px 2px 3px #FFB6C1;
}

.options {
  background-color: #FFFACD;
  border-radius: 10px;
  box-shadow: 2px 2px 10px #D2691E;
  padding: 20px;
}

.answerBox {
  border: 1px solid black;
  width: 500px;
  height: 100px;
}

.btn {
  background-color: #FF69B4;
  border: none;
  border-radius: 10px;
  color: #FFF;
  font-size: 16px;
  font-weight: bold;
  margin-top: 20px;
  padding: 10px 20px;
  text-shadow: 2px 2px 3px #FFB6C1;
  transition: all 0.3s ease-in-out;
}

.btn:hover {
  box-shadow: 2px 2px 5px #D2691E;
  cursor: pointer;
  transform: scale(1.1);
}

.invisible{
  display:hidden,
}
</style>

 

라디오 버튼과 보기 문항 사이를 누르면 클릭이 안돼서, padding값들도 조정해주고 자잘한 버그들을 다시 수정했습니다.

 

 

자 이제 Result 컴포넌트에서 내가 틀린 문제가 무엇인지를 알려주었으면 좋겠습니다.

 

Result안에서 다시 for문을 돌려 틀린 문제들을 출력해주면 좋을 것 같습니다.

 

Result는 props로 틀린 문제 리스트를 받아야겠군요

 

틀린 문제 리스트는 어떻게 받을 수 있을까요

 

correctAnswerCount와는 반대로 맞췄을 때가 아니라 틀렸을 때를 찾으면 되고, 틀렸을 때 틀린 값을 증가시키는게 아니라 그 값들을 리스트로 반환하면 됩니다.

 

filter를 사용하면 간단하게 해결이 되겠네요

 

만들어봅시다.

 

 
computed :{
    correctAnswerCount(){
      let correctAnswerCount = 0
      this.selectedAnswerList.forEach((selectedAnswer, idx)=>{
        console.log(selectedAnswer, this.questions[idx].correctAnswer)
        if(selectedAnswer === this.questions[idx].correctAnswer){
          correctAnswerCount++
        }
      }
      )
      return correctAnswerCount
    },
      getWrongtAnswerList(){
     
      const wrongAnswerList = this.selectedAnswerList.filter((selectedAnswer, idx)=>{
        return selectedAnswer != this.questions[idx].correctAnswer
    })
    return wrongAnswerList
  },
  },
 

computed를 다음과 같이 생성해줍니다. wrongAnswerList를 잘 가져오는지 확인해보도록 합니다.

 

   
    <ResultComponent
    v-if="showIndex===-1"
    :correct-answer-count="correctAnswerCount"
    :totalCount="questions.length"
    :wrongAnswerList="getWrongtAnswerList"
    />
 

ResultComponent에 wrongAnswerList를 내려주고, 

 

 

 props :{
        correctAnswerCount:Number,
        totalCount:Number,
        wrongAnswerList:Object,
    },

ResultComponent.vue에서 받아주도록 합니다. 이제 반복문을 돌려 출력해보도록 할까요?

이런.. 틀린 답의 문제들을 내려주는게 아니라 무엇을 틀린 답으로 골랐는지를 내려주고 있습니다...

 

하지만 틀린 답도 내려줘야하는건 맞으니 일단 이렇게 남겨두고, 틀린 문제들을 어떻게 내려줄지 고민해봐야겠습니다.

+ Recent posts