jichoi / lms_front star
권지수 2024-08-12
240812 권지수 문제 목록 및 조회
@1f754d57b4ed6af71dd7c333c9ebf60c1c7324da
client/resources/css/reset.css
--- client/resources/css/reset.css
+++ client/resources/css/reset.css
@@ -80,20 +80,20 @@
     
 }
 html,
-body{height: 100%;  background-color: #eaedf4;}
+body{ background-color: #eaedf4;}
 body{position:relative;text-align: left; overflow-x: hidden; width: 1920px;}
 ::-webkit-scrollbar {width: 10px; height: 10px;}
 ::-webkit-scrollbar-track {border-radius: 5px;background-color: #EAEDF4;}
 ::-webkit-scrollbar-thumb {	border-radius: 5px; background-color: #FFBA08;}
 #root, #app{
-    height: 100%; 
+    /* height: 100%;  */
    }
 input, select, span,p, label {
     font-size: 16px;
 }
 input::placeholder{color: #8C8E92;}
 input[type="text"]{ padding:10px 20px; width: 100%; }
-
+input#file-upload-button{padding: 10px ;} 
   
 button{
     border: none;
@@ -112,4 +112,24 @@
 border: 0;}
 thead{background-color: #eaedf4;}
 tbody tr{border-bottom: #eaedf4 1px solid; cursor: pointer;}
-td{padding: 10px ; text-align: center;} 
(No newline at end of file)
+td{padding: 10px ; text-align: center;} 
+
+/* .btn-upload {
+    width: 100px;
+    height: 40px;
+    background: #f0f0f0;
+    border: 1px solid rgb(77,77,77);
+    font-weight: 500;
+    cursor: pointer;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    &:hover {
+      background: rgb(77,77,77);
+      color: #fff;
+    }
+  }
+  
+  #file {
+    display: none;
+  } */
(No newline at end of file)
client/resources/css/style.css
--- client/resources/css/style.css
+++ client/resources/css/style.css
@@ -277,7 +277,7 @@
   background-image: url('../img/img199_12p.png');
   width: 367px;
   height: 182px;
-  padding: 20px 60px 20px 50px;
+  padding: 20px 50px 20px 40px;
   color: #fff;
 }
 .mypage .textbook{font-family: 'ONEMobilePOPOTF';}
@@ -336,7 +336,7 @@
   width: max-content; 
     position: absolute;
     right: 45px;
-    z-index: -1;
+    z-index: 10000;
 }
 .myphoto .class{border-radius: 0px; padding: 10px;}
 .myphoto .class .box {
@@ -441,10 +441,15 @@
   width: 1063px;
 }
 
-.rabbit {
+.rabbit-start {
   position: absolute;
   top: -45px;
   left: 393px;
+}
+.rabbit-end {
+  position: absolute;
+  bottom: 24px;
+    right: 40px;
 }
 
 .rabbit img {
@@ -470,6 +475,13 @@
 .race-btn p.long {
   width: fit-content;
 }
+/* .popup-yellow{
+  background-image: url('../img/img139_72s.png');
+  min-width: 439px !important;
+  min-height: 244px !important;
+  background-color: transparent !important;
+  border: 0 !important;
+} */
 /* 사진촬영 */
 .camera{background-color: #000;}
 .camera .header{height: 110px; background-color: #eaedf4; padding: 0 60px;}
@@ -500,6 +512,20 @@
 .complete-wrap img{width: inherit;}
 .complete-wrap .photo{cursor: pointer; width: 200px; height: 130px; }
 /* 챕터 */
+.time-hint{
+  position: absolute;
+  top: 50px;
+  right: 50px;
+  text-align: center;
+}
+.hint-btn{
+  background-image: url('../img/btn01.png');
+  width: 110px;
+  height: 45px;
+  padding: 5px 20px;
+    font-size: 28px;
+    font-family: 'ONEMobilePOP';
+}
 .content-wrap {
   margin: 90px 60px 0 60px;
 
@@ -741,9 +767,6 @@
   width: 560px;
 }
 .time-bg{
-  position: absolute;
-  top: 50px;
-  right: 50px;
   text-align: center;
   font-family: 'neodgm'; background-image: url('../img/img55_s.png'); width: 110px; height: 128px;}
   .time-bg > div{position: relative;}
@@ -830,7 +853,7 @@
 .side_t{
   background-color: #fff;
   width: 360px;
-  height: 100%;
+  /* height: 100%; */
   padding: 25px 40px;
 }
 
@@ -849,7 +872,7 @@
     to { opacity:1; transform:translate3d(0, 0, 0); }
 }
 
-select{min-width: 128px; border-radius: 5px; padding: 10px 20px; font-size: 18px;}
+select{min-width: 128px; border-radius: 5px; padding: 10px 20px; font-size: 18px; }
 select option{font-size: 20px; font-weight: bold;}
 
 
@@ -917,7 +940,7 @@
     font-family: 'Pretendard-Bold';
 }
 .new-btn{
-  padding: 5px 20px;
+  padding: 10px 30px;
   font-size: 20px;
     font-family: 'Pretendard-Bold';
     border-radius: 5px;
@@ -1046,4 +1069,22 @@
     min-height: 20rem;
   }
   .dropbox p{line-height: 20rem;}
+
+
+  /* 단원 버튼 */
+ .unit-pagination button {
+    font-size: 2rem;
+    font-family: 'Pretendard-Regular';
+    padding: 5px 30px;
+    border: #c6c6c6 3px solid;
+    border-radius: 5px;
+    color: #331600;
+    background-color: #EAEDF4 
+}
+
+ .unit-pagination .selected-btn {
+    background-color:#c6c6c6;
+    /* color: #fff; */
+    font-family: 'Pretendard-ExtraBold';
+}
 /* ------------------------선생님 --------------------- */
(No newline at end of file)
 
client/resources/img/img222_57s.png (Binary) (added)
+++ client/resources/img/img222_57s.png
Binary file is not shown
client/views/layout/Header.vue
--- client/views/layout/Header.vue
+++ client/views/layout/Header.vue
@@ -102,7 +102,7 @@
 
       // 페이징 정보
       page: 1,
-      pageSize: 3,
+      pageSize: 2,
       totalpages: null,
 
       // 사용자 정보
client/views/layout/Side.vue
--- client/views/layout/Side.vue
+++ client/views/layout/Side.vue
@@ -4,7 +4,7 @@
         <div class="profile mb30">
             <div class="flex align-start">
                 <img src="../../resources/img/img16_s.png" alt="">
-                <div class="ml25" style="width: 100%;">
+                <div class="ml25" >
                     <p class="name mb10">학생이름</p>
                     <p class="mb5">xx중학교 3학년 x반</p>
                     <progress-bar :progress="progress"></progress-bar>
client/views/layout/Side_t.vue
--- client/views/layout/Side_t.vue
+++ client/views/layout/Side_t.vue
@@ -33,9 +33,10 @@
                 <details>
                     <summary>교재 관리</summary>
                     <router-link to="/textbook.page" class="tpt">홈</router-link>
-                    <router-link to="/Board.page" class="tpt">게시판</router-link>
-                    <router-link to="/Board.page" class="tpt">학생목록</router-link>
-                    <router-link to="/Board.page" class="tpt">교재</router-link>
+                    <router-link to="/TextList.page" class="tpt">지문</router-link>
+                    <router-link to="/QuestionList.page" class="tpt">문제</router-link>
+                    <router-link to="/VocaList.page" class="tpt">단어장</router-link>
+                    <router-link to="/ExamList.page" class="tpt">평가</router-link>
                 </details>
             </div>
         </div>
client/views/pages/AppRouter.js
--- client/views/pages/AppRouter.js
+++ client/views/pages/AppRouter.js
@@ -35,6 +35,7 @@
 import Chapter3_1 from "./main/Chapter/Chapter3_1.vue";
 import Chapter3_2 from "./main/Chapter/Chapter3_2.vue";
 import Chapter3_3 from "./main/Chapter/Chapter3_3.vue";
+import Chapter3_3_1 from "./main/Chapter/Chapter3_3_1.vue";
 import Chapter3_4 from "./main/Chapter/Chapter3_4.vue";
 import Chapter3_5 from "./main/Chapter/Chapter3_5.vue";
 import Chapter3_6 from "./main/Chapter/Chapter3_6.vue";
@@ -81,12 +82,20 @@
 import ClassDetail from "./teacher/ClassDetail.vue";
 import TextList from "./teacher/TextList.vue";
 import TextInsert from "./teacher/TextInsert.vue";
+import TextDetail from "./teacher/TextDetail.vue";
 import QuestionList from "./teacher/QuestionList.vue";
 import QuestionInsert from "./teacher/QuestionInsert.vue";
+<<<<<<< HEAD
 import QuestionDetail from "./teacher/QuestionDetail.vue"; 
+=======
+import QuestionDetail from "./teacher/QuestionDetail.vue";
+>>>>>>> e47769b90c7ad4f0b34f38bb2a56a8a69a894941
 import VocaList from "./teacher/VocaList.vue";
+import VocaInsert from "./teacher/VocaInsert.vue";
+import VocaDetail from "./teacher/VocaDetail.vue";
 import ExamList from "./teacher/ExamList.vue";
 import ExamDetail from "./teacher/ExamDetail.vue";
+import ExamInsert from "./teacher/ExamInsert.vue";
 import RoadMap from "./teacher/RoadMap.vue";
 
 const routes = [
@@ -142,6 +151,7 @@
     { path: '/Chapter3_1.page', name: 'Chapter3_1', component: Chapter3_1 },
     { path: '/Chapter3_2.page', name: 'Chapter3_2', component: Chapter3_2 },
     { path: '/Chapter3_3.page', name: 'Chapter3_3', component: Chapter3_3 },
+    { path: '/Chapter3_3_1.page', name: 'Chapter3_3_1', component: Chapter3_3_1 },
     { path: '/Chapter3_4.page', name: 'Chapter3_4', component: Chapter3_4 },
     { path: '/Chapter3_5.page', name: 'Chapter3_5', component: Chapter3_5 },
     { path: '/Chapter3_6.page', name: 'Chapter3_6', component: Chapter3_6 },
@@ -185,12 +195,20 @@
             { path: '/C_TextBookDetail.page', name: 'C_TextBookDetail', component: C_TextBookDetail },
             { path: '/TextList.page', name: 'TextList', component: TextList },
             { path: '/TextInsert.page', name: 'TextInsert', component: TextInsert },
+            { path: '/TextDetail.page', name: 'TextDetail', component: TextDetail },
             { path: '/QuestionList.page', name: 'QuestionList', component: QuestionList },
             { path: '/QuestionInsert.page', name: 'QuestionInsert', component: QuestionInsert },
+<<<<<<< HEAD
             { path: '/QuestionDetail.page', name: 'QuestionDetail', component: QuestionDetail }, 
+=======
+            { path: '/QuestionDetail.page', name: 'QuestionDetail', component: QuestionDetail },
+>>>>>>> e47769b90c7ad4f0b34f38bb2a56a8a69a894941
             { path: '/VocaList.page', name: 'VocaList', component: VocaList },
+            { path: '/VocaInsert.page', name: 'VocaInsert', component: VocaInsert },
+            { path: '/VocaDetail.page', name: 'VocaDetail', component: VocaDetail },
             { path: '/ExamList.page', name: 'ExamList', component: ExamList },
             { path: '/ExamDetail.page', name: 'ExamDetail', component: ExamDetail },
+            { path: '/ExamInsert.page', name: 'ExamInsert', component: ExamInsert },
             { path: '/C_TextList.page', name: 'C_TextList', component: C_TextList },
             { path: '/C_TextInsert.page', name: 'C_TextInsert', component: C_TextInsert },
             { path: '/C_QuestionList.page', name: 'C_QuestionList', component: C_QuestionList },
@@ -201,6 +219,8 @@
             { path: '/C_ExamInsert.page', name: 'C_ExamInsert', component: C_ExamInsert },
             
             { path: '/RoadMap.page', name: 'RoadMap', component: RoadMap },
+            
+            { path: '/TextDetail.page', name: 'TextDetail', component: TextDetail },
         ],
     },
 ];
client/views/pages/main/Camera.vue
--- client/views/pages/main/Camera.vue
+++ client/views/pages/main/Camera.vue
@@ -5,20 +5,24 @@
             <svg-icon type="mdi" :path="mdilChevronLeft" style="width: 50px; height: 50px;"></svg-icon>
             <p class="title"> 이전글</p>
          </button>
-         <button @click="goToPage('PhotoDesign')" ><img src="../../../resources/img/btn19_74s_normal.png" alt="">
+         <button @click="captureAndGoToPhotoDesign">
+            <img src="../../../resources/img/btn19_74s_normal.png" alt="">
          </button>
       </div>
-      <div class="body ">
-<img src="../../../resources/img/img140_747s.png" alt="">
+      <div class="body" ref="body">
+         <div id="container" ref="container">
+            <video v-if="!photoTaken" autoplay="true" ref="modalVideoElement" class="mirrored"
+               @canplay="onVideoLoaded"></video>
+         </div>
       </div>
    </div>
 </template>
+
 
 <script>
 import SvgIcon from '@jamescoyle/vue-icon';
 import { mdiMagnify, mdiWindowClose } from '@mdi/js';
 import { mdilChevronRight, mdilChevronLeft } from '@mdi/light-js';
-
 import axios from "axios";
 
 export default {
@@ -31,26 +35,105 @@
          mdilChevronLeft: mdilChevronLeft,
          showModal: false,
          searchOpen: false,
+         photoTaken: false,
+         stream: null,
+         videoReady: false,
       }
    },
    methods: {
-      
       goToPage(page) {
          this.$router.push({ name: page });
       },
       closeModal() {
          this.showModal = false;
+         if (this.stream) {
+            let tracks = this.stream.getTracks();
+            tracks.forEach(track => track.stop());
+            this.stream = null;
+         }
       },
       buttonSearch() {
          this.searchOpen = true;
       },
       closeBtn() {
          this.searchOpen = false;
-
       },
+      onVideoLoaded() {
+         this.videoReady = true;
+         this.adjustContainerSize();
+      },
+      adjustContainerSize() {
+         const video = this.$refs.modalVideoElement;
+         const container = this.$refs.container;
+         const body = this.$refs.body;
+         if (video && container) {
+            container.style.width = `${video.videoWidth}px`;
+            container.style.height = `${video.videoHeight}px`;
+            body.style.height = `${video.videoHeight}px`;
+         }
+      },
+      captureAndGoToPhotoDesign() {
+         const video = this.$refs.modalVideoElement;
+         const canvas = document.createElement('canvas');
+         canvas.width = video.videoWidth;
+         canvas.height = video.videoHeight;
+         const ctx = canvas.getContext('2d');
+
+         // 좌우 반전 적용
+         ctx.translate(canvas.width, 0);
+         ctx.scale(-1, 1);
+
+         ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
+         const imageDataUrl = canvas.toDataURL('image/png');
+         this.$router.push({ name: 'PhotoDesign', query: { image: imageDataUrl } });
+      }
+   },
+   mounted() {
+      // Clear localStorage when the component is mounted
+      localStorage.removeItem('capturedImage');
+      
+      this.videoReady = false;
+      navigator.mediaDevices.getUserMedia({ video: true })
+         .then(stream => {
+            const modalVideo = this.$refs.modalVideoElement;
+            modalVideo.srcObject = stream;
+            this.stream = stream;
+            modalVideo.addEventListener('loadedmetadata', this.adjustContainerSize);
+         })
+         .catch(error => {
+            console.log("error>>>>>>>>", error);
+         });
    },
    components: {
       SvgIcon
    },
 }
 </script>
+
+
+<style>
+.body {
+   width: 1435px;
+   height: auto;
+   margin: 0 auto;
+}
+
+#container {
+   position: relative;
+   margin: auto;
+   border: 10px #333 solid;
+   display: flex;
+   justify-content: center;
+   align-items: center;
+}
+
+video {
+   width: 100%;
+   height: auto;
+   background-color: #666;
+}
+
+.mirrored {
+   transform: scaleX(-1);
+}
+</style>
client/views/pages/main/Chapter/Chapter2_1.vue
--- client/views/pages/main/Chapter/Chapter2_1.vue
+++ client/views/pages/main/Chapter/Chapter2_1.vue
@@ -1,63 +1,169 @@
 <template>
-    <div id="Chapter1_1" class="content-wrap">
+  <div id="Chapter1_1" class="content-wrap">
       <div class="title-box mb25 flex align-center mt40">
-        <span class="title mr40">1. Hello WORLD</span>
-        <span class="subtitle">my name is dd</span>
+          <span class="title mr40">1. Hello WORLD</span>
+          <span class="subtitle">my name is dd</span>
       </div>
       <div class="flex justify-between align-center">
-        <div class="pre-btn" @click="goToPage('Chapter2')"><img src="../../../../resources/img/left.png" alt=""></div>
-        <div class="content title-box">
-         <p class="title mt25 title-bg">step2</p>
-          <div class="flex align-center mb30">
-          <p class="subtitle2 mr20">오늘 배웠던 단어를 말하고 생성된 예문을 따라 읽어보세요</p>
-          <!-- <button><img src="../../../../resources/img/btn10_s.png" alt="">
-          </button> -->
-        </div>
-          <div class="flex justify-center ">
-          <div class="readGroup">
-             <section >
-                 <div class="imgGroup flex justify-center">
-                    <div class="mic-btn">
-                       <img src="../../../../resources/img/btn11_s.png" alt="">
-                    </div>
-    
-                 </div>
-                <article >
-                  <input type="text" class="speak mb25" placeholder="오늘 배운 단어를 말해보세요!">
-                  <p class="read-ai"><img style="margin-top: -5px;" src="../../../../resources/img/img43_s.png" alt=""></p>
-                </article>
-                
-              </section>
-  
+          <div class="pre-btn" @click="goToPage('Chapter2')">
+              <img src="../../../../resources/img/left.png" alt="" />
           </div>
-        </div>
-        </div>
-        <div class="next-btn" @click="goToPage('Chapter2_2')"><img src="../../../../resources/img/right.png" alt=""></div>
+          <div class="content title-box">
+              <p class="title mt25 title-bg">step2</p>
+              <div class="flex align-center mb30">
+                  <p class="subtitle2 mr20">오늘 배웠던 단어를 말하고 생성된 예문을 따라 읽어보세요</p>
+                  <!-- <button><img src="../../../../resources/img/btn10_s.png" alt="">
+        </button> -->
+              </div>
+              <div>
+                  <img src="http://localhost:8081/client/build/dad67b1726cb2d2b215965b433149ca3.png" data-num="1" />
+                  <p> {{ word }} </p>
+                  <p> {{ mean }} </p>
+                </div>
+              <div class="flex justify-center">
+                  <div class="readGroup">
+                      <section>
+                          <div class="imgGroup flex justify-center">
+                              <!-- 녹음 버튼 -->
+                              <div :class="['mic-btn', { notRecording: !isRecording }]" @click="toggleRecording">
+                                  <img src="../../../../resources/img/btn11_s.png" alt="" />
+                              </div>
+                          </div>
+                          <article>
+                              <p class="speakText mb25">
+                                  <span v-if="transcription === null"
+                                      >위의 버튼을 누른 후 오늘 배운 단어를 말해보세요!</span
+                                  >
+                                  <span v-else-if="!transcription || transcription.trim() === ''"
+                                      >다시 말해보세요!</span
+                                  >
+                                  <span v-else>{{ transcription }}</span>
+                              </p>
+                              <p class="read-ai">
+                                  <img style="margin-top: -5px" src="../../../../resources/img/img43_s.png" alt="" />
+                              </p>
+                          </article>
+                      </section>
+                  </div>
+              </div>
+          </div>
+          <div class="next-btn" @click="goToPage('Chapter2_2')">
+              <img src="../../../../resources/img/right.png" alt="" />
+          </div>
       </div>
-    </div>
-  </template>
-  
-  <script>
-  export default {
-    data() {
+  </div>
+</template>
+
+<script>
+import axios from 'axios';
+import SvgIcon from '@jamescoyle/vue-icon';
+import { mdiStop } from '@mdi/js';
+export default {
+  data() {
       return {
-      }
-    },
-    methods: {
+          isRecording: false, // 녹음 중 여부
+          mediaRecorder: null,
+          audioChunks: [], // 녹음된 오디오 데이터
+          audioBlob: null, // 오디오 Blob 객체
+          transcription: null, // 서버에서 받아온 텍스트 결과
+          stream: null, // MediaStream 객체
+
+          path: mdiStop,
+
+          /* audioURL : null // 오디오 URL 객체 */
+
+          word : "apple",
+          mean : "사과",
+          imageSrc : "http://localhost:8081/client/build/dad67b1726cb2d2b215965b433149ca3.png"
+      };
+  },
+  methods: {
       goToPage(page) {
-        this.$router.push({ name: page });
-      }
-    },
-    watch: {
-  
-    },
-    computed: {
-  
-    },
-    components: {
-    },
-    mounted() {
-      
-    }
-  }
-  </script>
(No newline at end of file)
+          this.$router.push({ name: page });
+      },
+      // 녹음 시작/중지 토글
+      async toggleRecording() {
+          if (this.isRecording) {
+              console.log('녹음 그만!');
+              this.stopRecording(); // 녹음 중이면 중지
+          } else {
+              console.log('녹음 시작!');
+              await this.startRecording(); // 녹음 중이 아니면 녹음 시작
+          }
+      },
+
+      // 녹음 시작
+      async startRecording() {
+          this.audioChunks = []; // 오디오 초기화
+          this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+          this.mediaRecorder = new MediaRecorder(this.stream);
+          this.mediaRecorder.ondataavailable = (event) => {
+              this.audioChunks.push(event.data); // 녹음 데이터 저장
+          };
+          this.mediaRecorder.onstop = () => {
+              this.audioBlob = new Blob(this.audioChunks, { type: 'audio/wav' });
+
+              /******************************/
+              // this.audioURL = URL.createObjectURL(this.audioBlob); // 오디오 URL 생성
+              // console.log('Audio URL:', this.audioURL);
+              /******************************/
+
+              console.log('Recorded Audio Blob:', this.audioBlob); // 콘솔에 Blob 확인
+              this.sendAudioToServer(); // 서버로 오디오 전송
+          };
+          this.mediaRecorder.start(); // 녹음 시작
+          this.isRecording = true; // 녹음 상태 변경
+      },
+
+      // 녹음 중지
+      stopRecording() {
+          this.mediaRecorder.stop(); // 녹음 중단
+
+          if (this.stream) {
+              this.stream.getTracks().forEach((track) => track.stop()); // 스트림의 모든 트랙 중지
+          }
+
+          this.isRecording = false; // 녹음 상태 변경
+      },
+
+      // 오디오 전송
+      async sendAudioToServer() {
+          const formData = new FormData();
+          formData.append('file', this.audioBlob, 'recording.wav');
+          try {
+              const resposne = await axios.post('/api/speechToText.json', formData, {
+                  headers: {
+                      'Content-Type': 'multipart/form-data',
+                  },
+              });
+              this.transcription = resposne.data;
+              console.log(`받은 데이터 : ${this.transcription}`);
+          } catch (error) {
+              console.log('파일 전송 실패 : ', error);
+          }
+      },
+  },
+  watch: {},
+  computed: {},
+  components: {
+      SvgIcon,
+  },
+  mounted() {},
+};
+</script>
+<style scoped>
+.mic-btn {
+  cursor: pointer;
+}
+.mic-btn.notRecording {
+  background-image: none;
+  cursor: pointer;
+}
+.speakText {
+  background-color: #fff8e9;
+  border: 0;
+}
+.speakText span {
+  font-size: 28px;
+}
+</style>
(No newline at end of file)
client/views/pages/main/Chapter/Chapter3_1.vue
--- client/views/pages/main/Chapter/Chapter3_1.vue
+++ client/views/pages/main/Chapter/Chapter3_1.vue
@@ -15,14 +15,17 @@
         </div>
 
         <div class="flex align-center justify-center" style="gap: 113px;">
-          <div class="time-bg">
-              <div>
-                <div class="time">
-                  <p class="second">{{ timer }}</p>
-                  <p class="text">sec</p>
+         <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
                 </div>
               </div>
-            </div>
+         </div>
           <div class="pickGroup ">
             <div class="flex" style="gap: 250px;">
               <article  class="text-ct">
@@ -108,7 +111,6 @@
 
 .pickGroup button {
   position: relative;
-  margin-right: 30px;
 }
 
 .pickGroup button p {
client/views/pages/main/Chapter/Chapter3_10.vue
--- client/views/pages/main/Chapter/Chapter3_10.vue
+++ client/views/pages/main/Chapter/Chapter3_10.vue
@@ -15,14 +15,17 @@
             </div>
 
         <div class="text-ct">
-          <div class="time-bg">
-            <div>
-              <div class="time">
-                <p class="second">{{ timer }}</p>
-                <p class="text">sec</p>
+          <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
+                </div>
               </div>
-            </div>
-          </div>
+         </div>
           <div class="imgGroup  mt20">
             <img src="../../../../resources/img/img116_59s.png" alt="">
             <p class="title1 mt10" style="width: auto;"><strong>진수가 거절한 제안</strong>은 무엇인지 우리말로 쓰세요.</p>
client/views/pages/main/Chapter/Chapter3_11.vue
--- client/views/pages/main/Chapter/Chapter3_11.vue
+++ client/views/pages/main/Chapter/Chapter3_11.vue
@@ -15,14 +15,17 @@
             </div>
 
         <div class="text-ct">
-          <div class="time-bg">
-            <div>
-              <div class="time">
-                <p class="second">{{ timer }}</p>
-                <p class="text">sec</p>
+          <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
+                </div>
               </div>
-            </div>
-          </div>
+         </div>
           <div class="imgGroup  mt20">
             <img src="../../../../resources/img/img115_58s.png" alt="">
             <!-- <p class="title1 mt10"><strong>진수가 거절한 제안</strong>은 무엇인지 우리말로 쓰세요.</p> -->
client/views/pages/main/Chapter/Chapter3_12.vue
--- client/views/pages/main/Chapter/Chapter3_12.vue
+++ client/views/pages/main/Chapter/Chapter3_12.vue
@@ -15,14 +15,17 @@
             </div>
 
         <div class=" text-ct">
-          <div class="time-bg">
-            <div>
-              <div class="time">
-                <p class="second">{{ timer }}</p>
-                <p class="text">sec</p>
+          <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
+                </div>
               </div>
-            </div>
-          </div>
+         </div>
           <div class="dragGroup mt40">
             <div class="flex justify-center" style="gap: 20px;">
               <button><img src="../../../../resources/img/img63_37s.png" alt="">
client/views/pages/main/Chapter/Chapter3_13.vue
--- client/views/pages/main/Chapter/Chapter3_13.vue
+++ client/views/pages/main/Chapter/Chapter3_13.vue
@@ -15,14 +15,17 @@
             </div>
 
         <div class="text-ct">
-          <div class="time-bg">
-            <div>
-              <div class="time">
-                <p class="second">{{ timer }}</p>
-                <p class="text">sec</p>
+          <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
+                </div>
               </div>
-            </div>
-          </div>
+         </div>
          <div class="flex justify-center ">
            <div class=" flex justify-between align-center" style="width: 50%;">
               <div class="pickGroup left">
client/views/pages/main/Chapter/Chapter3_14.vue
--- client/views/pages/main/Chapter/Chapter3_14.vue
+++ client/views/pages/main/Chapter/Chapter3_14.vue
@@ -15,14 +15,17 @@
             </div>
 
         <div class="text-ct">
-          <div class="time-bg">
-            <div>
-              <div class="time">
-                <p class="second">{{ timer }}</p>
-                <p class="text">sec</p>
+          <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
+                </div>
               </div>
-            </div>
-          </div>
+         </div>
           <div class="imgGroup"><img src="../../../../resources/img/img124_63s.png" alt=""></div>
           
           <div class="dropGroup flex align-center justify-center mt30">
client/views/pages/main/Chapter/Chapter3_15.vue
--- client/views/pages/main/Chapter/Chapter3_15.vue
+++ client/views/pages/main/Chapter/Chapter3_15.vue
@@ -9,39 +9,33 @@
       <div class="content title-box">
         <p class="title mt25 title-bg">step3</p>
         <div class="flex align-center mb30">
-               <p class="subtitle2 mr20">그림에 알맞는 낱말을 쓰세요.</p>
-               <!-- <button><img src="../../../../resources/img/btn10_s.png" alt="">
-               </button> -->
+               <p class="subtitle2 mr20">듣고 올바른 대답을 말해보세요!</p>
+               <button><img src="../../../../resources/img/btn10_s.png" alt="">
+               </button>
             </div>
 
         <div class="text-ct">
-          <div class="time-bg">
-            <div>
-              <div class="time">
-                <p class="second">{{ timer }}</p>
-                <p class="text">sec</p>
+          <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
+                </div>
               </div>
-            </div>
-          </div>
+         </div>
           <div class="imgGroup"><img src="../../../../resources/img/img125_64s.png" alt=""></div>
           
           <div class="dropGroup flex align-center justify-center mt30">
-            <div class="flex" style="gap: 20px;">
-              <div class="bd-bt textbox" @click="showButton(1)">
-                <p v-if="showButton1">A</p></div>
-              <div class="bd-bt textbox" @click="showButton(2)">
-                <p v-if="showButton2">A</p></div>
-              <div class="bd-bt textbox" @click="showButton(3)">
-                <p v-if="showButton3">A</p></div>
-              <div class="bd-bt textbox" @click="showButton(4)">
-                <p v-if="showButton4">A</p></div>
-            </div>
+            <img src="../../../../resources/img/btn18_64s_normal.png" alt="">
 
           </div>
 
         </div>
       </div>
-      <div class="next-btn" @click="goToPage('Chapter3_16')"><img src="../../../../resources/img/right.png" alt=""></div>
+      <div class="next-btn" @click="goToPage('Chapter4')"><img src="../../../../resources/img/right.png" alt=""></div>
     </div>
   </div>
 </template>
client/views/pages/main/Chapter/Chapter3_16.vue
--- client/views/pages/main/Chapter/Chapter3_16.vue
+++ client/views/pages/main/Chapter/Chapter3_16.vue
@@ -5,7 +5,7 @@
       <span class="subtitle">my name is dd</span>
     </div>
     <div class="flex justify-between align-center">
-      <div class="pre-btn" @click="goToPage('Chapter3_15')"><img src="../../../../resources/img/left.png" alt=""></div>
+      <div class="pre-btn" @click="goToPage('Chapter4')"><img src="../../../../resources/img/left.png" alt=""></div>
       <div class="content title-box">
         <p class="title mt25 title-bg">중간 평가 설문 조사</p>
         <div class="flex align-center mb30">
client/views/pages/main/Chapter/Chapter3_2.vue
--- client/views/pages/main/Chapter/Chapter3_2.vue
+++ client/views/pages/main/Chapter/Chapter3_2.vue
@@ -15,14 +15,17 @@
         </div>
 
         <div class="text-ct">
-          <div class="time-bg">
-            <div>
-              <div class="time">
-                <p class="second">{{ timer }}</p>
-                <p class="text">sec</p>
+          <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
+                </div>
               </div>
-            </div>
-          </div>
+         </div>
           <div class="imgGroup ">
             <img src="../../../../resources/img/img109_51s.png" alt="">  
           </div>
client/views/pages/main/Chapter/Chapter3_3.vue
--- client/views/pages/main/Chapter/Chapter3_3.vue
+++ client/views/pages/main/Chapter/Chapter3_3.vue
@@ -13,14 +13,17 @@
         </div>
 
         <div class="text-ct">
-          <div class="time-bg">
-            <div>
-              <div class="time">
-                <p class="second">{{ timer }}</p>
-                <p class="text">sec</p>
+          <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
+                </div>
               </div>
-            </div>
-          </div>
+         </div>
           <div class="pickGroup mt60 flex align-center justify-center" style="gap: 100px; margin-top: 7%;">
             <article style="gap: 60px; bottom: 159px;
     left: 242px;">
@@ -43,7 +46,7 @@
           </div>
         </div>
       </div>
-      <div class="next-btn" @click="goToPage('Chapter3_4')"><img src="../../../../resources/img/right.png" alt=""></div>
+      <div class="next-btn" @click="goToPage('Chapter3_3_1')"><img src="../../../../resources/img/right.png" alt=""></div>
     </div>
   </div>
 </template>
 
client/views/pages/main/Chapter/Chapter3_3_1.vue (added)
+++ client/views/pages/main/Chapter/Chapter3_3_1.vue
@@ -0,0 +1,150 @@
+<template>
+  <div id="Chapter1_1" class="content-wrap">
+    <div class="title-box mb25 flex align-center mt40">
+      <span class="title mr40">1. Hello WORLD</span>
+      <span class="subtitle">my name is dd</span>
+    </div>
+    <div class="flex justify-between align-center">
+      <div class="pre-btn" @click="goToPage('Chapter3_3')"><img src="../../../../resources/img/left.png" alt=""></div>
+      <div class="content title-box">
+        <p class="title mt25 title-bg">step3.</p>
+        <div class="flex align-center mb30">
+          <p class="subtitle2 mr20">1. see the picture</p>
+          <button><img src="../../../../resources/img/btn10_s.png" alt="">
+          </button>
+        </div>
+
+        <div class="text-ct">
+          <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
+                </div>
+              </div>
+         </div>
+          <div class="imgGroup ">
+            <img src="../../../../resources/img/img109_51s.png" alt="">  
+          </div>
+          
+          <div class="pickGroup mt60 flex align-center justify-center" style="gap: 100px; margin-top: 7%;">
+            <article style="gap: 60px; bottom: 159px;
+    left: 242px;">
+              <div class="flex align-center">
+                <button><img src="../../../../resources/img/img136_71s.png" alt="">
+                  <p>1</p>
+                </button>
+                <img src="../../../../resources/img/img122_62s.png" alt="">
+              </div>
+            </article>
+            <article style="gap: 60px; bottom: 159px;
+    right: 559px;">
+             <div class="flex align-center">
+                <button><img src="../../../../resources/img/img136_71s.png" alt="">
+                  <p>2</p>
+                </button>
+                <img src="../../../../resources/img/img123_62s.png" alt="">
+             </div>
+            </article>
+            <article style="gap: 60px; bottom: 159px;
+    right: 559px;">
+             <div class="flex align-center">
+                <button><img src="../../../../resources/img/img136_71s.png" alt="">
+                  <p>3</p>
+                </button>
+                <img src="../../../../resources/img/img121_62s.png" alt="">
+             </div>
+            </article>
+          </div>
+        </div>
+      </div>
+      <div class="next-btn" @click="goToPage('Chapter3_4')"><img src="../../../../resources/img/right.png" alt=""></div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      timer: '00'
+    }
+  },
+  methods: {
+    goToPage(page) {
+      this.$router.push({ name: page });
+    },
+    startTimer() {
+      if (this.intervalId) {
+        clearInterval(this.intervalId);
+      }
+      this.timer = 5;
+      this.intervalId = setInterval(() => {
+        if (this.timer > 0) {
+          this.timer--;
+        } else {
+          clearInterval(this.intervalId);
+        }
+      }, 1000);
+    }
+  },
+  watch: {
+
+  },
+  computed: {
+
+  },
+  components: {
+  },
+  mounted() {
+
+  }
+}
+</script>
+<style scoped>
+.inputbox {
+  font-size: 38px;
+  font-family: 'Pretendard-ExtraBold';
+}
+
+.imgGroup button {
+  position: relative;
+}
+
+.imgGroup button p,
+.textbox p {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  width: fit-content;
+  height: fit-content;
+  background: #ffffffb8;
+  border-radius: 5px;
+  padding: 10px;
+  font-size: 48px;
+  font-family: 'ONEMobilePOP';
+}
+
+.pickGroup p {
+  font-size: 34px;
+  font-weight: bold;
+}
+
+.pickGroup button {
+  position: relative;
+  margin-right: 30px;
+}
+
+.pickGroup button p {
+  font-size: 34px;
+  color: #c6c6c6;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+}
+</style>(No newline at end of file)
client/views/pages/main/Chapter/Chapter3_4.vue
--- client/views/pages/main/Chapter/Chapter3_4.vue
+++ client/views/pages/main/Chapter/Chapter3_4.vue
@@ -5,7 +5,7 @@
       <span class="subtitle">my name is dd</span>
     </div>
     <div class="flex justify-between align-center">
-      <div class="pre-btn" @click="goToPage('Chapter3_3')"><img src="../../../../resources/img/left.png" alt=""></div>
+      <div class="pre-btn" @click="goToPage('Chapter3_3_1')"><img src="../../../../resources/img/left.png" alt=""></div>
       <div class="content title-box">
         <p class="title mt25 title-bg">step3.</p>
         <div class="flex align-center mb30">
@@ -15,14 +15,17 @@
             </div>
 
         <div class="text-ct">
-          <div class="time-bg">
-            <div>
-              <div class="time">
-                <p class="second">{{ timer }}</p>
-                <p class="text">sec</p>
+          <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
+                </div>
               </div>
-            </div>
-          </div>
+         </div>
           <div class="pickGroup flex align-center justify-center" style="gap: 100px; margin-top: 7%;">
             <article style="gap: 60px; bottom: 159px;
     left: 242px;">
client/views/pages/main/Chapter/Chapter3_5.vue
--- client/views/pages/main/Chapter/Chapter3_5.vue
+++ client/views/pages/main/Chapter/Chapter3_5.vue
@@ -15,14 +15,17 @@
         </div>
 
         <div class=" text-ct">
-          <div class="time-bg">
-            <div>
-              <div class="time">
-                <p class="second">{{ timer }}</p>
-                <p class="text">sec</p>
+          <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
+                </div>
               </div>
-            </div>
-          </div>
+         </div>
           <div class="imgGroup ">
             <img src="../../../../resources/img/img125_64s.png" alt="">  
           </div>
client/views/pages/main/Chapter/Chapter3_6.vue
--- client/views/pages/main/Chapter/Chapter3_6.vue
+++ client/views/pages/main/Chapter/Chapter3_6.vue
@@ -14,14 +14,17 @@
                <button><img src="../../../../resources/img/btn10_s.png" alt="">
                </button>
             </div>
-            <div class="time-bg">
-               <div>
+            <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
                   <div class="time">
-                     <p class="second">{{ timer }}</p>
-                     <p class="text">sec</p>
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
                   </div>
-               </div>
-            </div>
+                </div>
+              </div>
+         </div>
             
             <div class="imgGroup">
                <img src="../../../../resources/img/img114_57s.png" alt="">
client/views/pages/main/Chapter/Chapter3_7.vue
--- client/views/pages/main/Chapter/Chapter3_7.vue
+++ client/views/pages/main/Chapter/Chapter3_7.vue
@@ -15,16 +15,19 @@
             </div>
 
         <div class="text-ct">
-          <div class="time-bg">
-            <div>
-              <div class="time">
-                <p class="second">{{ timer }}</p>
-                <p class="text">sec</p>
+          <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
+                </div>
               </div>
-            </div>
-          </div>
+         </div>
           
-          <div class="pickGroup flex align-center justify-between" style="gap: 100px;">
+          <div class="pickGroup flex align-center justify-between mt80" style="gap: 100px;">
             <p>1. 문제</p>
             <div class="flex justify-center" style="gap: 60px;">
               <article >
client/views/pages/main/Chapter/Chapter3_8.vue
--- client/views/pages/main/Chapter/Chapter3_8.vue
+++ client/views/pages/main/Chapter/Chapter3_8.vue
@@ -15,14 +15,17 @@
             </div>
   
           <div class="text-ct">
-            <div class="time-bg">
-            <div>
-              <div class="time">
-                <p class="second">{{ timer }}</p>
-                <p class="text">sec</p>
+            <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
+                </div>
               </div>
-            </div>
-          </div>
+         </div>
           <div class="imgGroup flex align-center justify-center mt50">
             <img src="../../../../resources/img/img114_57s.png" alt="">
                <input class="yellow-bd ml50" style="width: 30%;" type="text" name="" id="" placeholder="답을 입력하세요.">
client/views/pages/main/Chapter/Chapter3_9.vue
--- client/views/pages/main/Chapter/Chapter3_9.vue
+++ client/views/pages/main/Chapter/Chapter3_9.vue
@@ -15,14 +15,17 @@
             </div>
 
         <div class="mt50 text-ct">
-          <div class="time-bg">
-            <div>
-              <div class="time">
-                <p class="second">{{ timer }}</p>
-                <p class="text">sec</p>
+          <div class="time-hint">
+          <button class="hint-btn">HINT</button>
+            <div class="time-bg mt20">
+                <div>
+                  <div class="time">
+                    <p class="second">{{ timer }}</p>
+                    <p class="text">sec</p>
+                  </div>
+                </div>
               </div>
-            </div>
-          </div>
+         </div>
           <div class="imgGroup  mt50">
             <img src="../../../../resources/img/img115_58s.png" alt="">
             <div class=" mt50">
client/views/pages/main/Chapter/Chapter4.vue
--- client/views/pages/main/Chapter/Chapter4.vue
+++ client/views/pages/main/Chapter/Chapter4.vue
@@ -102,7 +102,7 @@
 
                 </div>
             </div>
-            <div class="next-btn" @click="goToPage('Dashboard')"><img src="../../../../resources/img/right.png" alt="">
+            <div class="next-btn" @click="goToPage('Chapter3_16')"><img src="../../../../resources/img/right.png" alt="">
             </div>
         </div>
     </div>
client/views/pages/main/Dashboard.vue
--- client/views/pages/main/Dashboard.vue
+++ client/views/pages/main/Dashboard.vue
@@ -6,131 +6,288 @@
                 <p class="subtitle">hi my name is dd!</p>
             </div>
             <div class="race-box">
-                <div class="rabbit"><img src="../../../resources/img/img09_s.png" alt=""></div>
+                <div class="rabbit-start"><img src="../../../resources/img/img09_s.png" alt=""></div>
                 <div class="rcon flex justify-end mb5">
                     <div class="race-btn" @click="goToPage('Chapter1')">
-                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)" data-num="1">
-                    <img :src="item.imgSrc1" >
-                    <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
-                 </button>
+                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)"
+                            data-num="1">
+                            <img :src="item.imgSrc1">
+                            <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
+                        </button>
                         <p>지문1</p>
                     </div>
                     <div class="race-btn" @click="goToPage('Chapter2')">
-                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)" data-num="2">
-                    <img :src="item.imgSrc1" >
-                    <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
-                 </button>
+                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)"
+                            data-num="2">
+                            <img :src="item.imgSrc1">
+                            <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
+                        </button>
                         <p>단어장</p>
                     </div>
                 </div>
                 <div class="lcon flex justify-between mb5">
                     <div class="race-btn" @click="goToPage('Chapter7')">
-                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)" data-num="7">
-                    <img :src="item.imgSrc1" >
-                    <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
-                 </button>
+                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)"
+                            data-num="7">
+                            <img :src="item.imgSrc1">
+                            <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
+                        </button>
                         <p>문제1</p>
                     </div>
                     <div class="race-btn" @click="goToPage('Chapter6')">
-                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)" data-num="6">
-                    <img :src="item.imgSrc1" >
-                    <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
-                 </button>
+                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)"
+                            data-num="6">
+                            <img :src="item.imgSrc1">
+                            <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
+                        </button>
                         <p>단어장</p>
                     </div>
                     <div class="race-btn" @click="goToPage('Chapter5')">
-                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)" data-num="5">
-                    <img :src="item.imgSrc1" >
-                    <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
-                 </button>
+                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)"
+                            data-num="5">
+                            <img :src="item.imgSrc1">
+                            <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
+                        </button>
                         <p>지문2</p>
                     </div>
                     <div class="race-btn" @click="goToPage('Chapter4')">
-                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)" data-num="4">
-                    <img :src="item.imgSrc1" >
-                    <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
-                 </button>
+                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)"
+                            data-num="4">
+                            <img :src="item.imgSrc1">
+                            <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
+                        </button>
                         <p>문제2</p>
                     </div>
                     <div class="race-btn" @click="goToPage('Chapter3')">
-                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)" data-num="3">
-                    <img :src="item.imgSrc1" >
-                    <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
-                 </button>
+                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)"
+                            data-num="3">
+                            <img :src="item.imgSrc1">
+                            <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
+                        </button>
                         <p>문제1</p>
                     </div>
                 </div>
                 <div class="rcon flex">
                     <div class="race-btn" @click="goToPage('Chapter8')">
-                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)" data-num="8">
-                    <img :src="item.imgSrc3" >
-                    <img :src="item.imgSrc4" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
-                 </button>
+                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)"
+                            data-num="8">
+                            <img :src="item.imgSrc3">
+                            <img :src="item.imgSrc4" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
+                        </button>
                         <p class="long">중간 평가</p>
                     </div>
                     <div class="race-btn" @click="goToPage('Chapter9')">
-                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)" data-num="9">
-                    <img :src="item.imgSrc1" >
-                    <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
-                 </button>
+                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)"
+                            data-num="9">
+                            <img :src="item.imgSrc1">
+                            <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
+                        </button>
                         <p>지문3</p>
                     </div>
                     <div class="race-btn" @click="goToPage('Chapter10')">
-                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)" data-num="10">
-                    <img :src="item.imgSrc1" >
-                    <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
-                 </button>
+                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)"
+                            data-num="10">
+                            <img :src="item.imgSrc1">
+                            <img :src="item.imgSrc2" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
+                        </button>
                         <p>단어장</p>
                     </div>
-                    <div class="race-btn" >
-                        <button class="popTxt" v-for="(item, index) in items" :key="index" @click="toggleImage(index)" data-num="11">
-                    <img :src="item.imgSrc3" >
-                    <img :src="item.imgSrc4" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
-                 </button>
-                        <p class="long">최종 평가</p>
+                    <div class="rcon flex">
+                        <div class="race-btn">
+                            <button class="popTxt" v-for="(item, index) in items" :key="index"
+                                @click="toggleImageAndShowPopup(index, '11')">
+                                <img :src="item.imgSrc3">
+                                <img :src="item.imgSrc4" :style="{ display: item.isSecondImageVisible ? 'block' : 'none' }">
+                            </button>
+                            <p class="long">최종 평가</p>
+                        </div>
                     </div>
                 </div>
             </div>
             <!-- 팝업 -->
-            <div v-show="searchOpen" class="popup-wrap">
-                <div class="popup-box ">
-                    <div class=" mb30 text-ct">
-                        <p class="title1 mb20">1단원이 끝났습니다! </p>
-                        <p class="title1"><em class="yellow">기념사진</em>을 촬영하러 가요 </p>
+            <div v-show="searchOpen2" class="popup-wrap">
+                <div class="popup-box">
+                    <button type="button" class="popup-close-btn" style="position:absolute; top:10px; right: 10px;"
+                        @click="closeModal">
+                        <svg-icon type="mdi" :path="mdiWindowClose" class="close-btn"></svg-icon>
+                    </button>
+
+                    <div class="mb30 text-ct">
+                        <p class="title1 mb20">1단원이 끝났습니다!</p>
+                        <p class="title1"><em class="yellow">기념사진</em>을 촬영하러 가요</p>
                     </div>
-                    <div class="flex justify-center ">
-                        <button type="button" title="사진촬영" class="new-btn" @click="goToPage('Camera')">
-                            사진촬영
+                    <div class="flex justify-center">
+                        <button type="button" title="사진촬영" class="new-btn" @click="openCameraModal">
+                            사진 촬영
                         </button>
                     </div>
                 </div>
             </div>
+
+            <!-- 카메라 모달 -->
+            <article v-show="showCameraModal" class="popup-wrap">
+                <div class="popup-box" style="top: 500px; left:500px">
+                    <div class="flex mb10 justify-between">
+                        <p class="popup-title">사진 촬영</p>
+                        <button type="button" class="popup-close-btn" @click="closeModal">
+                            <svg-icon type="mdi" :path="mdiWindowClose" class="close-btn"></svg-icon>
+                        </button>
+                    </div>
+                    <div class="box">
+                        <div style="width: 100%;">
+                            <!-- 여기에 카메라 기능을 구현 -->
+                            <!-- <p>카메라 모듈이 여기에 위치합니다.</p> -->
+
+                            <div id="container" ref="container">
+                                <video v-if="!photoTaken" autoplay="true" ref="modalVideoElement" class="mirrored"
+                                    @canplay="onVideoLoaded"></video>
+                            </div>
+
+                        </div>
+                    </div>
+                    <div class="flex justify-center mt20">
+                        <button type="button" class="new-btn" v-if="!photoTaken" @click="capturePhoto"
+                            :disabled="!videoReady">
+                            사진 촬영
+                        </button>
+
+                    </div>
+                </div>
+            </article>
+
+            <!-- 사진 모달 -->
+            <article v-show="showPhotoModal" class="popup-wrap">
+                <div class="popup-box" style="top: 500px; left: auto">
+                    <div class="flex mb10 justify-between">
+                        <p class="popup-title">사진 꾸미기</p>
+                        <button type="button" class="popup-close-btn" @click="closePhotoModal">
+                            <svg-icon type="mdi" :path="mdiWindowClose" class="close-btn"></svg-icon>
+                        </button>
+                    </div>
+                    <div class="flex justify-between align-center" style="gap: 40px;">
+                        <div class="content" style="padding: 30px; min-width: 401px; min-height: 710px;">
+                            <div class="tool">
+                                <div class="flex justify-center mb20" style="gap: 20px;">
+                                    <button class="popTxt" style="width: 101px;" v-for="(item, index) in items_photo"
+                                        :key="index" @click="updateContent(index)"
+                                        :class="{ active: selectedIndex === index }">
+                                        <img :src="item.imgSrc1" style="display: block;">
+                                        <img :src="item.imgSrc2" v-if="selectedIndex === index" style="display: block;">
+                                    </button>
+                                </div>
+                            </div>
+
+                            <div class="stickers" v-show="!stickersVisible">
+                                <div class="toolbar">
+                                    <label for="brushSize" style="font-size: 9px;">펜 굵기</label>
+                                    <input type="color" v-model="color" />
+                                    <input type="range" id="brushSize" min="1" max="10" v-model="brushSize"
+                                        @input="updateBrushSize" style="width: 100px; margin-left: 5px;" />
+                                    <button class="new-btn" style="font-size: 9px;" @click="setTool('draw')">펜</button>
+                                    <button class="new-btn" style="font-size: 9px;" @click="setTool('eraser')">지우개</button>
+                                    <button class="new-btn" style="font-size: 9px;" @click="clearAll">전체 지우개</button>
+                                </div>
+                            </div>
+
+                            <div class="stickers" v-show="stickersVisible">
+                                <button><img src="../../../resources/img/img146_75s.png" alt=""></button>
+                                <button><img src="../../../resources/img/img147_75s.png" alt=""></button>
+                                <button><img src="../../../resources/img/img148_75s.png" alt=""></button>
+                                <button><img src="../../../resources/img/img149_75s.png" alt=""></button>
+                                <button><img src="../../../resources/img/img150_75s.png" alt=""></button>
+                                <button><img src="../../../resources/img/img151_75s.png" alt=""></button>
+                                <button><img src="../../../resources/img/img152_75s.png" alt=""></button>
+                                <button><img src="../../../resources/img/img153_75s.png" alt=""></button>
+                                <button><img src="../../../resources/img/img154_75s.png" alt=""></button>
+                                <button><img src="../../../resources/img/img155_75s.png" alt=""></button>
+                                <button><img src="../../../resources/img/img156_75s.png" alt=""></button>
+                                <button><img src="../../../resources/img/img157_75s.png" alt=""></button>
+                                <button><img src="../../../resources/img/img158_75s.png" alt=""></button>
+                            </div>
+                        </div>
+                        <div>
+                            <div class="content" style="height: 549px; 
+                                position: relative;
+                                width: 973px; 
+                                display: flex; 
+                                justify-content: center; 
+                                align-items: center;">
+                                <canvas ref="canvas" style="position: absolute;"></canvas>
+                            </div>
+                            <div class="btn-wrap flex justify-center mt40" style="gap: 40px;">
+                                <button class="login-btn" @click="openCameraModal">
+                                    <img src="../../../resources/img/btn07_s.png" alt="">
+                                    <p>재촬영</p>
+                                </button>
+
+                                <button class="login-btn" type="submit" @click="goToPage('PhotoEdit')">
+                                    <img src="../../../resources/img/btn07_s.png" alt="">
+                                    <p>완성</p>
+                                </button>
+                            </div>
+                        </div>
+                        <div class="content" style="padding: 30px; min-width: 401px; min-height: 710px;">
+                            <div class="mb20">
+                                <p class="popup-title" style="font-size: 32px">랜덤 단어</p>
+                            </div>
+                            <div class="flex-column" style="gap: 10px;">
+                                <button class="login-btn"><img src="../../../resources/img/img141_75s.png" alt="">
+                                    <p class="title">a</p>
+                                </button>
+                                <button class="login-btn"><img src="../../../resources/img/img152_75s_01.png" alt="">
+                                    <p class="title">a</p>
+                                </button>
+                                <button class="login-btn"><img src="../../../resources/img/img144_75s.png" alt="">
+                                    <p class="title" style="color: #fff;">a</p>
+                                </button>
+                                <button class="login-btn"><img src="../../../resources/img/img145_75s.png" alt="">
+                                    <p class="title mt20" style="color: #fff;">a</p>
+                                </button>
+                            </div>
+                        </div>
+                    </div>
+                    <!-- <div class="box">
+                        <div style="width: 100%;">
+                            <div id="container">
+                                <canvas ref="canvas"></canvas>
+                            </div>
+                        </div>
+                    </div> -->
+
+                </div>
+            </article>
+
         </div>
         <div class="complete-wrap mt90 myphoto">
             <h2 class="mb40">이 단원을 끝낸 친구들</h2>
             <article class=" flex-column" style="gap: 5px;">
                 <div class="flex" style="gap: 5px;">
-                    <div @click="buttonSearch" class="photo" ><img   src="../../../resources/img/img143_75s.png" alt=""></div>
+                    <div @click="buttonSearch2" class="photo" ><img   src="../../../resources/img/img143_75s.png" alt=""></div>
+                    <div @click="buttonSearch" class="photo"><img src="../../../resources/img/img143_75s.png" alt=""></div>
                 </div>
             </article>
+            <!-- 팝업 -->
             <article class="popup-wrap" v-show="searchOpen">
                 <div class="popup-box ">
                     <div class="flex mb10  justify-between">
                         <p class="popup-title">알림</p>
-                        <button type="button" class="popup-close-btn" @click="closeBtn">
+                        <button type="button" class="popup-close-btn" @click="closeBtn2">
                             <svg-icon type="mdi" :path="mdiWindowClose" class="close-btn"></svg-icon>
                         </button>
                     </div>
                     <div class="box">
                         <div style="width: 910px;"><img src="../../../resources/img/img140_747s.png" alt=""></div>
                     </div>
-                   <div class="flex justify-between mt20">
+                    <div class="flex justify-between mt20">
                         <div class="text  flex">
                             <p class="title2 date ml30">2024-08-06</p>
                             <span class=" title1 ml30">1단원을 마친 <em class="yellow">가나다</em>친구</span>
                         </div>
-                        <div class="title2 flex align-center" style="gap: 10px;"><svg-icon type="mdi" :path="mdiHeart" style="color: #FFBA08;"></svg-icon><p><em class="yellow">1</em></p></div>
-                   </div>
+                        <div class="title2 flex align-center" style="gap: 10px;"><svg-icon type="mdi" :path="mdiHeart"
+                                style="color: #FFBA08;"></svg-icon>
+                            <p><em class="yellow">1</em></p>
+                        </div>
+                    </div>
                 </div>
             </article>
         </div>
@@ -141,41 +298,442 @@
 <script>
 import SvgIcon from '@jamescoyle/vue-icon';
 import { mdiMagnify, mdiHeart, mdiWindowClose } from '@mdi/js';
+
 export default {
     data() {
         return {
             items: [
-            { imgSrc1: 'client/resources/img/img11_1_s.png', imgSrc2: 'client/resources/img/img12_1_s.png', imgSrc3: 'client/resources/img/img11_2_s.png', imgSrc4: 'client/resources/img/img12_2_s.png', isSecondImageVisible: false },
-         ],
+                {
+                    imgSrc1: 'client/resources/img/img11_1_s.png',
+                    imgSrc2: 'client/resources/img/img12_1_s.png',
+                    imgSrc3: 'client/resources/img/img11_2_s.png',
+                    imgSrc4: 'client/resources/img/img12_2_s.png',
+                    isSecondImageVisible: false
+                },
+            ],
+            items_photo: [
+                {
+                    imgSrc1: 'client/resources/img/btn20_75s_normal.png',   //펜 선택되지 않음
+                    imgSrc2: 'client/resources/img/btn20_75s_click.png' //펜 선택됨
+                },
+                {
+                    imgSrc1: 'client/resources/img/btn21_75s_normal.png',   //스티커 선택되지 않음
+                    imgSrc2: 'client/resources/img/btn21_75s_click.png'     //스티커 선택됨
+                },
+            ],
             mdiWindowClose: mdiWindowClose,
             mdiHeart: mdiHeart,
             showModal: false,
-            searchOpen: false,
-            searchOpen2: false,
+            searchOpen: false,  // 사진 상세보기 모달창
+            searchOpen2: false, // 단원 마친 후, 사진 촬영 여부 선택 모달창
+            showCameraModal: false, // 카메라 모달창
+            showPhotoModal: false, // 사진꾸미기 모달창
+            photoTaken: false,
+            photo: null,    //캡쳐 사진
+            videoReady: false, // 비디오 준비 상태를 나타내는 플래그
+            stream: null,
+            canvasWidth: 0,
+            canvasHeight: 0,
+            selectedIndex: 0,   //툴 선택 여부 인덱스
+            stickersVisible: false, // 스티커 표시 여부 
+
+            //사진 꾸미기 관련 변수
+            drawHistory: [],    //도형 기록
+            tempLines: [],  //펜 기록
+            stickers: [],   //스티커 파일 기록
+            draggingStickerIndex: null, //스티커 드래그
+            activeStickerIndex: null,  // 현재 활성화된 스티커의 인덱스
+            nextLineId: 0,  //획 아이디
+            tool: 'draw',   //툴 결정
+            color: '#000000',   //펜 기본 색상
+            isDrawing: false,   //그리는 중인지 판단하는 변수
+            brushSize: 5,  // 초기 펜 굵기
+            startX: 0,
+            startY: 0,
+            canvasRect: {
+                topLeft: { x: 0, y: 0 },
+                bottomRight: { x: 0, y: 0 }
+            }
+
         }
     },
     methods: {
         toggleImage(index) {
-         this.items[index].isSecondImageVisible = !this.items[index].isSecondImageVisible;
-      },
+            this.items[index].isSecondImageVisible = !this.items[index].isSecondImageVisible;
+        },
+        toggleImageAndShowPopup(index, dataNum) {
+            this.toggleImage(index);
+            if (dataNum === '11') {  // 최종 평가 버튼 클릭 시
+                this.searchOpen2 = true;  // 모달창 열기
+            }
+        },
+        updateContent(index) {
+            this.selectedIndex = index;
+
+            // 선택된 버튼이 스티커 버튼(인덱스 1)인지 확인
+            if (index === 1) {
+                this.stickersVisible = true;  // 스티커 툴 보이기
+            } else {
+                this.stickersVisible = false; // 스티커 툴 숨기기
+            }
+        },
         goToPage(page) {
             this.$router.push({ name: page });
         },
-        closeModal() {
-            this.showModal = false;
+        openCameraModal() {
+            this.closeModal();
+            this.closePhotoModal();
+
+            this.drawHistory = [];
+            this.stickers = [];
+            this.tempLines = [];
+            this.videoReady = false; // 비디오 준비 상태 초기화
+
+            this.showCameraModal = true;
+            navigator.mediaDevices.getUserMedia({ video: true })
+                .then(stream => {
+                    const modalVideo = this.$refs.modalVideoElement;
+                    modalVideo.srcObject = stream;
+                    this.stream = stream;
+                    modalVideo.addEventListener('loadedmetadata', this.adjustContainerSize);
+                })
+                .catch(error => {
+                    console.log("error>>>>>>>>", error);
+                });
         },
+        closeModal() {  //웹캠 팝업 닫기
+            // this.showModal = false;
+            this.searchOpen2 = false;
+            this.showCameraModal = false;
+            this.photoTaken = false;
+            this.photo = null;
+
+            //스트림 종료
+            if (this.stream) {
+                let tracks = this.stream.getTracks();
+                tracks.forEach(track => track.stop());
+                this.stream = null;
+            }
+        },
+        closePhotoModal() { //사진꾸미기 팝업 닫기
+            this.showPhotoModal = false;
+            this.closeModal();
+        },
+        onVideoLoaded() {
+            this.videoReady = true;
+            this.adjustContainerSize();
+        },
+        adjustContainerSize() {
+            const video = this.$refs.modalVideoElement;
+            const container = this.$refs.container;
+            const body = this.$refs.body;
+            if (video && container) {
+                container.style.width = `${video.videoWidth}px`;
+                container.style.height = `${video.videoHeight}px`;
+                body.style.height = `${video.videoHeight}px`;
+            }
+        },
+
         buttonSearch() {
             this.searchOpen = true;
         },
+        buttonSearch2() {
+            this.searchOpen2 = true;
+        },
         closeBtn() {
             this.searchOpen = false;
+        },
+        capturePhoto() {
+            // 사진 촬영 기능 구현
+            console.log("cam on");
+
+            if (!this.videoReady) return; // 비디오가 준비되지 않았으면 사진을 찍지 않음
+
+            const video = this.$refs.modalVideoElement;
+            const canvas = document.createElement('canvas');
+
+            canvas.width = video.videoWidth;
+            canvas.height = video.videoHeight;
+            this.canvasWidth = video.videoWidth;
+            this.canvasHeight = video.videoHeight;
+            const context = canvas.getContext('2d');
+            context.translate(canvas.width, 0);
+            context.scale(-1, 1);
+            context.drawImage(video, 0, 0, canvas.width, canvas.height);
+            this.photo = canvas.toDataURL('image/png');
+            this.photoTaken = true;
+            this.showPhotoModal = true;
+            console.log("PhotoModal open");
+            this.$nextTick(() => {
+                console.log("canvas setup");
+                // console.log("Photo data>>>>", this.photo);
+                this.setupCanvas();
+            });
 
         },
-    },
-    watch: {
+        setupCanvas() {
+            const canvas = this.$refs.canvas;
+            // const container = this.$refs.container;
+            if (!canvas) {
+                console.error("Canvas reference not found");
+                resolve();
+                return;
+            }
+            const context = canvas.getContext('2d');
+            if (!context) {
+                console.error("Canvas context not found");
+                return;
+            }
+            const image = new Image();
+            image.src = this.photo;
+            // console.log("Photo data>>>>", image.src);
+            image.onload = () => {
+                console.log("Image loaded successfully");
+                // this.canvasWidth = image.width;
+                // this.canvasHeight = image.height;
+                //이미지 크기가 캔버스와 안맞으면 이미지 불러오는데에 에러 남
+                // container.style.width = this.canvasWidth;
+                // container.style.height = this.canvasHeight;
 
-    },
-    computed: {
+                canvas.width = this.canvasWidth;
+                canvas.height = this.canvasHeight;
+
+                const rect = canvas.getBoundingClientRect();
+                // 좌측 상단 좌표
+                const topLeft = {
+                    x: rect.left,
+                    y: rect.top
+                };
+
+                // 우측 하단 좌표
+                const bottomRight = {
+                    x: rect.right,
+                    y: rect.bottom
+                };
+
+                // 캔버스 크기 초기화
+                this.updateCanvasRect();
+
+                // 윈도우 리사이즈 이벤트 리스너 추가
+                window.addEventListener('resize', this.updateCanvasRect);
+
+                // 클릭 이벤트 핸들러 추가
+                this.$refs.canvas.addEventListener('click', this.handleCanvasClick);
+
+
+                // canvas.width = canvas.clientWidth;
+                // canvas.height = canvas.clientHeight;
+                // this.canvasWidth = canvas.clientWidth;
+                // this.canvasHeight = canvas.clientHeight;
+
+                context.clearRect(0, 0, canvas.width, canvas.height);  // 이전 이미지 있으면 초기화
+                context.drawImage(image, 0, 0, this.canvasWidth, this.canvasHeight);
+                this.addCanvasEventListeners(); //추가해야함
+            };
+            image.onerror = (error) => {
+                console.error("Error loading image: ", error);
+            };
+        },
+
+        addCanvasEventListeners() {
+            const canvas = this.$refs.canvas;
+            canvas.addEventListener('mousedown', this.onMouseDown);
+            canvas.addEventListener('mouseup', this.onMouseUp);
+            canvas.addEventListener('mousemove', this.onMouseMove);
+            canvas.addEventListener('click', this.onCanvasClick);
+        },
+        setTool(tool) {
+            this.tool = tool;
+        },
+        updateBrushSize() {
+            // 펜 굵기 변경 로직
+            if (this.tool === 'draw') {
+                this.setBrushSize(this.brushSize);
+            }
+        },
+        setBrushSize(size) {
+            this.brushSize = size;
+            const context = this.$refs.canvas.getContext('2d');
+            context.lineWidth = size;
+        },
+        // 캔버스 크기 갱신 함수
+        updateCanvasRect() {
+            const rect = this.$refs.canvas.getBoundingClientRect();
+            this.canvasRect = {
+                topLeft: { x: rect.left, y: rect.top },
+                bottomRight: { x: rect.right, y: rect.bottom }
+            };
+            console.log(">>>>>>>>>2222", rect.left);
+        },
+        getCanvasPosition(event) {
+
+            const rect = this.canvasRect;
+            console.log(">>>>>>>>>", this.canvasRect);
+            this.updateCanvasRect();
+
+            // 윈도우 리사이즈 이벤트 리스너 추가
+            window.addEventListener('resize', this.updateCanvasRect);
+
+            // 클릭 이벤트 핸들러 추가
+            this.$refs.canvas.addEventListener('click', this.handleCanvasClick);
+
+            // // 좌측 상단 좌표
+            // const topLeft = {
+            //     x: rect.left,
+            //     y: rect.top
+            // };
+
+            // // 우측 하단 좌표
+            // const bottomRight = {
+            //     x: rect.right,
+            //     y: rect.bottom
+            // };
+
+            // console.log(this.scrollLeft)
+
+            const x = event.clientX - rect.topLeft.x
+            const y = event.clientY - rect.topLeft.y
+
+
+            console.log(`클릭한 좌표: x=${event.clientX}, y=${event.clientY}`);
+            console.log(`계산베이스 좌표: x=${rect.topLeft.x}, y=${rect.topLeft.y}`);
+            console.log(`계산베이스 좌표: x=${rect.topLeft.x}, y=${rect.topLeft.y}`);
+            console.log(`계산된 좌표: x=${x}, y=${y}`);
+            return {
+                x, y
+            };
+        },
+        onMouseDown(event) {
+
+            // 캔버스 크기 초기화
+            this.updateCanvasRect();
+
+            // 윈도우 리사이즈 이벤트 리스너 추가
+            window.addEventListener('resize', this.updateCanvasRect);
+
+            // 클릭 이벤트 핸들러 추가
+            this.$refs.canvas.addEventListener('click', this.handleCanvasClick);
+
+            const { x, y } = this.getCanvasPosition(event);
+            this.startX = x;
+            this.startY = y;
+            const context = this.$refs.canvas.getContext('2d');
+            context.strokeStyle = this.color;
+            context.lineWidth = this.brushSize;  // 브러시 크기 설정
+            if (this.tool === 'draw') {
+                context.beginPath();
+                context.moveTo(this.startX, this.startY);
+                this.nextLineId++;
+            }
+            this.isDrawing = true;
+        },
+        onMouseUp(event) {
+            if (!this.isDrawing) return;
+            const { x, y } = this.getCanvasPosition(event);
+            const context = this.$refs.canvas.getContext('2d');
+            context.strokeStyle = this.color;
+            context.lineWidth = this.brushSize;  // 브러시 크기 설정
+            if (this.tool === 'rectangle') {
+                context.strokeRect(this.startX, this.startY, x - this.startX, y - this.startY);
+                this.drawHistory.push({ type: 'rectangle', startX: this.startX, startY: this.startY, endX: x, endY: y, color: this.color });
+            } else if (this.tool === 'circle') {
+                context.beginPath();
+                const radius = Math.sqrt(Math.pow((x - this.startX), 2) + Math.pow((y - this.startY), 2));
+                context.arc(this.startX, this.startY, radius, 0, 2 * Math.PI);
+                context.stroke();
+                this.drawHistory.push({ type: 'circle', startX: this.startX, startY: this.startY, radius, color: this.color });
+            } else if (this.tool === 'draw') {
+                context.lineTo(x, y);
+                context.stroke();
+                this.tempLines.push({ id: this.nextLineId, startX: this.startX, startY: this.startY, endX: x, endY: y, color: this.color });
+            }
+            this.isDrawing = false;
+        },
+        onMouseMove(event) {
+            if (!this.isDrawing || this.tool !== 'draw') return;
+            const { x, y } = this.getCanvasPosition(event);
+            const context = this.$refs.canvas.getContext('2d');
+            context.strokeStyle = this.color;
+            context.lineWidth = this.brushSize;  // 브러시 크기 설정
+            context.lineTo(x, y);
+            context.stroke();
+            this.tempLines.push({ id: this.nextLineId, startX: this.startX, startY: this.startY, endX: x, endY: y, color: this.color });
+            this.startX = x;
+            this.startY = y;
+        },
+        onCanvasClick(event) {
+            if (this.tool === 'eraser') {
+                const { x, y } = this.getCanvasPosition(event);
+                this.eraseDrawing(x, y);
+            }
+        },
+        eraseDrawing(x, y) {
+            const eraserRadius = 10;
+            this.drawHistory = this.drawHistory.filter(item => {
+                if (item.type === 'rectangle') {
+                    return !(x >= item.startX && x <= item.endX && y >= item.startY && y <= item.endY);
+                } else if (item.type === 'circle') {
+                    const distance = Math.sqrt(Math.pow((x - item.startX), 2) + Math.pow((y - item.startY), 2));
+                    return !(distance <= item.radius);
+                }
+            });
+            const linesToDelete = this.tempLines.filter(line => {
+                const distanceToLine = this.distanceToLineSegment(line.startX, line.startY, line.endX, line.endY, x, y);
+                return distanceToLine <= 10;
+            }).map(line => line.id);
+            this.tempLines = this.tempLines.filter(line => !linesToDelete.includes(line.id));
+            this.redraw();
+        },
+        distanceToLineSegment(x1, y1, x2, y2, px, py) {
+            const lengthSquared = Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2);
+            if (lengthSquared === 0) return Math.sqrt(Math.pow(px - x1, 2) + Math.pow(py - y1, 2));
+            const t = Math.max(0, Math.min(1, ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / lengthSquared));
+            const projX = x1 + t * (x2 - x1);
+            const projY = y1 + t * (y2 - y1);
+            return Math.sqrt(Math.pow(px - projX, 2) + Math.pow(py - projY, 2));
+        },
+        clearAll() {
+            this.drawHistory = [];
+            this.stickers = [];
+            this.tempLines = [];
+            this.redraw();
+        },
+        redraw() {
+            const canvas = this.$refs.canvas;
+            const context = canvas.getContext('2d');
+            const image = new Image();
+            image.src = this.photo;
+            image.onload = () => {
+                context.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
+                context.drawImage(image, 0, 0, this.canvasWidth, this.canvasHeight);
+                this.drawHistory.forEach(item => {
+                    context.strokeStyle = item.color;
+                    if (item.type === 'draw') {
+                        context.beginPath();
+                        context.moveTo(item.startX, item.startY);
+                        context.lineTo(item.endX, item.endY);
+                        context.stroke();
+                    } else if (item.type === 'rectangle') {
+                        context.strokeRect(item.startX, item.startY, item.endX - item.startX, item.endY - item.startY);
+                    } else if (item.type === 'circle') {
+                        context.beginPath();
+                        context.arc(item.startX, item.startY, item.radius, 0, 2 * Math.PI);
+                        context.stroke();
+                    }
+                });
+                this.tempLines.forEach(line => {
+                    context.strokeStyle = line.color;
+                    context.beginPath();
+                    context.moveTo(line.startX, line.startY);
+                    context.lineTo(line.endX, line.endY);
+                    context.stroke();
+                });
+                this.stickers.forEach((sticker, index) => {
+                    context.drawImage(sticker.img, sticker.x, sticker.y, sticker.width, sticker.height);
+                });
+            };
+        },
+
 
     },
     components: {
@@ -183,6 +741,104 @@
     },
     mounted() {
         console.log('main mounted');
+
+
+    },
+    computed() {
+
+    },
+    beforeDestroy() {
+        // 컴포넌트가 파괴되기 전에 리스너 제거
+        window.removeEventListener('resize', this.updateCanvasRect);
+        this.$refs.canvas.removeEventListener('click', this.handleCanvasClick);
     }
 }
-</script>
(No newline at end of file)
+</script>
+
+<style>
+.body {
+    width: 1435px;
+    height: auto;
+    margin: 0 auto;
+}
+
+#container {
+    position: relative;
+    margin: auto;
+    border: 10px #333 solid;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    z-index: 100;
+}
+
+
+video {
+    width: 100%;
+    height: auto;
+    background-color: #666;
+}
+
+.mirrored {
+    transform: scaleX(-1);
+}
+
+.new-btn:disabled {
+    background-color: #FFF3D7;
+    cursor: not-allowed;
+}
+
+/* button {
+    margin: auto;
+    padding: 5px 10px;
+    font-size: 13px;
+    cursor: pointer;
+    display: flex;
+    justify-content: center;
+    text-align: center;
+} */
+
+.sticker {
+    position: absolute;
+    cursor: move;
+}
+
+.sticker-handle {
+    width: 15px;
+    height: 15px;
+    background: rgba(255, 255, 255, 0.521);
+    position: absolute;
+    bottom: 0;
+    right: 0;
+    cursor: nwse-resize;
+    font-size: 13px;
+    font-weight: bolder;
+    color: rgb(63, 63, 63);
+}
+
+.sticker-delete {
+    position: absolute;
+    top: 0;
+    right: 0;
+    background: rgba(255, 0, 0, 0.425);
+    color: white;
+    padding: 5px;
+    cursor: pointer;
+}
+
+.toolbar {
+    display: flex;
+    justify-content: center;
+    margin-top: 10px;
+}
+
+.toolbar button {
+    margin: 5px;
+    padding: 5px 10px;
+    cursor: pointer;
+}
+
+.toolbar input {
+    margin: 5px;
+}
+</style>
(No newline at end of file)
client/views/pages/main/MyPage.vue
--- client/views/pages/main/MyPage.vue
+++ client/views/pages/main/MyPage.vue
@@ -172,4 +172,7 @@
 }
 </script>
 <style scoped>
+.mypage .textbook {
+    width: 22%;
+}
 </style>
(No newline at end of file)
client/views/pages/main/PhotoBook.vue
--- client/views/pages/main/PhotoBook.vue
+++ client/views/pages/main/PhotoBook.vue
@@ -6,96 +6,49 @@
        <div>
             <div class="title-box flex justify-end mb40">
                 <select name="" id="">
-                    <option value="">A반</option>
+                    <option v-for="classItem in classList" :key="classItem.sclsId" :value="classItem.sclsId" @click="currentPage = 1; stdPhotoSelectList(classItem.sclsId)">
+                        {{ classItem.sclsNm }}
+                    </option>
                 </select>
             </div>
-            <div class="btnGroup">
-                <button @click="selectedTab = 'tab1'" type="button" title="글쓰기" class="tab-btn">
-                    <img v-if="selectedTab !== 'tab1'" src="../../../resources/img/btn49_15s_normal.png" alt="">
+            <div class="btnGroup" style="display: flex; flex-direction: column;">
+                <button v-for="n in totalPages" :key="n" @click="changePage(n)" type="button" title="페이지 버튼" class="tab-btn">
+                    <img v-if="currentPage !== n" src="../../../resources/img/btn49_15s_normal.png" alt="">
                     <img v-else src="../../../resources/img/btn49_15s_click.png" alt="">
-                    <p :class="{ 'custom-style': selectedTab === 'tab1' }">1</p>
+                    <p :class="{ 'custom-style': currentPage === n }">{{ n }}</p>
                 </button>
             </div>
             <div v-if="selectedTab === 'tab1'" class="tab-box">
                 <div class="flex justify-between">
-                    <div class="photo" style="transform: rotate(2deg);" @click="buttonSearch">
-                        <div class="class ">
+                    <div v-for="(photo, index) in (photoList?.result || []).slice(0, 3)" :key="index" class="photo" :style="{ transform: getRotation(index) }" @click="buttonSearch(photo)">
+                        <div class="class">
                             <div class="box">
                                 <div><img src="../../../resources/img/img213_15s.png" alt=""></div>
                             </div>
                             <div class="text flex justify-between mt20">
-                                <span class="member ml30">20</span>
-                                <p class="title2">1단원</p>
+                                <span class="member ml30">{{ photo.likeData }}</span>
+                                <p class="title2">{{ photo.unitName }}</p>
                             </div>
-                        </div>
-                    </div>
-                    <div class="photo" style="transform: rotate(-1deg);" @click="buttonSearch">
-                        <div class="class photo">
-                            <div class="box">
-                                <div><img src="../../../resources/img/img213_15s.png" alt=""></div>
-                            </div>
-                            <div class="text flex justify-between mt20">
-                                <span class="member ml30">20</span>
-                                <p class="title2">1단원</p>
-                            </div>
-    
-                        </div>
-                    </div>
-                    <div class="photo" style="transform: rotate(1deg);" @click="buttonSearch">
-                        <div class="class ">
-                            <div class="box">
-                                <div><img src="../../../resources/img/img213_15s.png" alt=""></div>
-                            </div>
-                            <div class="text flex justify-between mt20">
-                                <span class="member ml30">20</span>
-                                <p class="title2">1단원</p>
-                            </div>
-    
                         </div>
                     </div>
                 </div>
                 <div class="flex justify-between mt50">
-                    <div class="photo" style="transform: rotate(-2deg);" @click="buttonSearch">
-                        <div class="class ">
+                    <div v-for="(photo, index) in (photoList?.result || []).slice(3, 6)" :key="index + 3" class="photo" :style="{ transform: getRotation(index + 3) }" @click="buttonSearch(photo)">
+                        <div class="class">
                             <div class="box">
                                 <div><img src="../../../resources/img/img213_15s.png" alt=""></div>
                             </div>
                             <div class="text flex justify-between mt20">
-                                <span class="member ml30">20</span>
-                                <p class="title2">1단원</p>
-                            </div>
-    
-                        </div>
-                    </div>
-                    <div class="photo" style="transform: rotate(1deg);" @click="buttonSearch">
-                        <div class="class ">
-                            <div class="box">
-                                <div><img src="../../../resources/img/img213_15s.png" alt=""></div>
-                            </div>
-                            <div class="text flex justify-between mt20">
-                                <span class="member ml30">20</span>
-                                <p class="title2">1단원</p>
-                            </div>
-    
-                        </div>
-                    </div>
-                    <div class="photo" style="transform: rotate(-1deg);" @click="buttonSearch">
-                        <div class="class ">
-                            <div class="box">
-                                <div><img src="../../../resources/img/img213_15s.png" alt=""></div>
-                            </div>
-                            <div class="text flex justify-between mt20">
-                                <span class="member ml30">20</span>
-                                <p class="title2">1단원</p>
+                                <span class="member ml30">{{ photo.likeData }}</span>
+                                <p class="title2">{{ photo.unitName }}</p>
                             </div>
                         </div>
                     </div>
                 </div>
-    
             </div>
             <div class="popup-wrap" v-show="searchOpen">
-                <div class="popup-box ">
-                    <div class="flex mb10  justify-between">
+                <div class="popup-box">
+                    <div class="flex mb10 justify-between">
                         <p class="popup-title">알림</p>
                         <button type="button" class="popup-close-btn" @click="closeBtn">
                             <svg-icon type="mdi" :path="mdiWindowClose" class="close-btn"></svg-icon>
@@ -104,12 +57,16 @@
                     <div class="box">
                         <div><img src="../../../resources/img/img184_91t.png" alt=""></div>
                     </div>
-                    <div class="text flex justify-between mt20">
-                        <span class=" title1">1단원을 마친 <em class="yellow">가나다</em>친구</span>
-                        <p class="title2 date">2024-08-06</p>
+                    <div class="text flex justify-between mt20" v-if="photoData.length > 0">
+                        <span class="title1">{{ photoData[0].unitName }}을 마친 <em class="yellow">{{ photoData[0].stdName }}</em>친구</span>
+                        <p class="title2 date">{{ photoData[0].photoDate }}</p>
+                    </div>
+                    <div class="text flex justify-between mt20" v-else>
+                        <span class="title1">데이터를 불러올 수 없습니다.</span>
                     </div>
                 </div>
             </div>
+
        </div>
     </div>
 </template>
@@ -119,11 +76,21 @@
 import { mdiMagnify, mdiHeart, mdiWindowClose } from '@mdi/js';
 import { mdilArrowRight } from '@mdi/light-js';
 import ProgressBar from '../../component/ProgressBar.vue';
+import axios from "axios";
 
 
 export default {
     data() {
         return {
+            classList: [],
+            photoList: [],
+
+            photoData: [],
+
+            currentPage: 1,
+            pageSize: 6,
+            totalPages: 1,
+
             mdiWindowClose: mdiWindowClose,
             selectedTab: 'tab1',
             mdiMagnify: mdiMagnify,
@@ -136,15 +103,95 @@
         }
     },
     methods: {
+        stdClassesSelectList: function () {
+            const vm = this;
+            axios({
+                url: "/classes/selectClass.json",
+                method: "post",
+                headers:{
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    userId:"1"
+                }
+            })
+            .then(function (response) {
+                console.log("classList - response : ", response.data);
+                vm.classList = response.data.data;
+                vm.currentPage = 1;
+            })
+            .catch(function (error) {
+                console.log("classList - error : ", error);
+                alert("학생 반 조회에 오류가 발생했습니다.");
+            });
+        },
+
+        stdPhotoSelectList: function (sclsId) {
+            const vm = this;
+            axios({
+                url: "/photo/stdPhotoList.json",
+                method: "post",
+                headers:{
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                        "stdId":"1",
+                        "sclsId":"1", // 여기에 sclsId들어가야함
+                        page: vm.currentPage,
+                        pageSize: vm.pageSize
+                }
+            })
+            .then(function (response) {
+                console.log("photoList - response : ", response.data);
+                vm.photoList = response.data;
+                vm.totalPages = Math.ceil(response.data.photoCount / vm.pageSize);
+            })
+            .catch(function (error) {
+                console.log("photoList - error : ", error);
+                alert("반별 내 사진 조회에 오류가 발생했습니다.");
+            });
+        },
+
+        getRotation(index) {
+            const rotations = [2, -1, 1, -2, 1, -1];
+            return `rotate(${rotations[index]}deg)`;
+        },
+
+        changePage(pageNumber) {
+            this.currentPage = pageNumber;
+            this.stdPhotoSelectList(this.selectedClassId);
+        },
+
         closeModal() {
             this.showModal = false;
         },
-        buttonSearch() {
+        buttonSearch(photo) {
+            if(!photo) return;
+
+            const vm = this;
             this.searchOpen = true;
+            axios({
+                url: "/photo/photoDetail.json",
+                method: "post",
+                headers:{
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                        "photoId":photo.photoId
+                }
+            })
+            .then(function (response) {
+                console.log("photoData - response : ", response.data);
+                vm.photoData = response.data;
+            })
+            .catch(function (error) {
+                console.log("photoData - error : ", error);
+                alert("사진 조회에 오류가 발생했습니다.");
+            });
         },
+
         closeBtn() {
             this.searchOpen = false;
-
         },
         goToPage(page) {
             this.$router.push({ name: page });
@@ -173,7 +220,12 @@
 
     },
     computed: {
-
+        currentPhotos() {
+            // 현재 페이지에 해당하는 사진들만 반환
+            const start = (this.currentPage - 1) * this.pageSize;
+            const end = start + this.pageSize;
+            return this.photoList.result.slice(start, end);
+        }
     },
     components: {
         SvgIcon,
@@ -181,6 +233,15 @@
     },
     mounted() {
         console.log('Main2 mounted');
+        this.stdClassesSelectList();
+        this.stdPhotoSelectList();
     }
 }
-</script>
(No newline at end of file)
+</script>
+
+<style>
+.btnGroup button {
+    cursor: pointer;
+    z-index: 100000;
+}
+</style>
(No newline at end of file)
client/views/pages/main/PhotoDesign.vue
--- client/views/pages/main/PhotoDesign.vue
+++ client/views/pages/main/PhotoDesign.vue
@@ -5,13 +5,14 @@
       <span class="title mr40">기념 사진을 꾸며봅시다.</span>
     </div>
     <div class="flex justify-between align-center" style="gap: 40px;">
-      <div class="content " style="padding: 30px;">
+      <div class="content" style="padding: 30px;">
         <div class="tool">
           <div class="flex justify-center mb20" style="gap: 20px;">
-            <button class="popTxt" v-for="(item, index) in items" :key="index" @click="updateContent(index)" :class="{ active: selectedIndex === index }">
-                       <img :src="item.imgSrc1" style="display: block;">
-                       <img :src="item.imgSrc2" v-if="selectedIndex === index" style="display: block;">
-                    </button>
+            <button class="popTxt" v-for="(item, index) in items" :key="index" @click="updateContent(index)"
+              :class="{ active: selectedIndex === index }">
+              <img :src="item.imgSrc1" style="display: block;">
+              <img :src="item.imgSrc2" v-if="selectedIndex === index" style="display: block;">
+            </button>
           </div>
         </div>
         <div class="stickers">
@@ -31,8 +32,8 @@
         </div>
       </div>
       <div>
-        <div class="content " style="height: 549px; width: 973px;">
-          <button><img src="../../../resources/img/img143_75s.png" alt=""></button>
+        <div class="content" style="height: 549px; width: 973px;">
+          <button><img class="captured-img" :src="capturedImage" alt="Captured Image"></button>
         </div>
         <div class="btn-wrap flex justify-center mt40" style="gap: 40px;">
           <button class="login-btn" @click="goToPage('Camera')">
@@ -40,8 +41,8 @@
             <p>재촬영</p>
           </button>
 
-          <button class="login-btn" type="submit" @click="goToPage('PhotoEdit')"><img
-              src="../../../resources/img/btn07_s.png" alt="">
+          <button class="login-btn" type="submit" @click="goToPage('PhotoEdit')">
+            <img src="../../../resources/img/btn07_s.png" alt="">
             <p>완성</p>
           </button>
         </div>
@@ -65,7 +66,6 @@
           </button>
         </div>
       </div>
-
     </div>
   </div>
 </template>
@@ -75,49 +75,49 @@
   data() {
     return {
       items: [
-        { imgSrc1: 'client/resources/img/btn20_75s_normal.png', imgSrc2: 'client/resources/img/btn20_75s_click.png', },
-        { imgSrc1: 'client/resources/img/btn21_75s_normal.png', imgSrc2: 'client/resources/img/btn21_75s_click.png', },
+        { imgSrc1: 'client/resources/img/btn20_75s_normal.png', imgSrc2: 'client/resources/img/btn20_75s_click.png' },
+        { imgSrc1: 'client/resources/img/btn21_75s_normal.png', imgSrc2: 'client/resources/img/btn21_75s_click.png' },
       ],
-      timer: '00',
       selectedIndex: 0,
+    };
+  },
+  computed: {
+    capturedImage() {
+      // Retrieve the captured image from route query parameters
+      return this.$route.query.image || ''; // Return an empty string if no image is provided
     }
   },
   methods: {
     updateContent(index) {
-         this.selectedIndex = index;
-        //  this.currentCon = this.items[index].con;
-      },
+      this.selectedIndex = index;
+    },
     goToPage(page) {
       this.$router.push({ name: page });
     },
-    startTimer() {
-      if (this.intervalId) {
-        clearInterval(this.intervalId);
-      }
-      this.timer = 5;
-      this.intervalId = setInterval(() => {
-        if (this.timer > 0) {
-          this.timer--;
-        } else {
-          clearInterval(this.intervalId);
-        }
-      }, 1000);
-    }
-  },
-  watch: {
+    // captureAndGoToPhotoDesign() {
+    //     const video = this.$refs.modalVideoElement;
+    //     const canvas = document.createElement('canvas');
+    //     canvas.width = video.videoWidth;
+    //     canvas.height = video.videoHeight;
+    //     const ctx = canvas.getContext('2d');
 
-  },
-  computed: {
+    //     // 좌우 반전 적용
+    //     ctx.translate(canvas.width, 0);
+    //     ctx.scale(-1, 1);
 
+    //     ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
+    //     const imageDataUrl = canvas.toDataURL('image/png');
+    //     this.$router.push({ name: 'PhotoDesign', query: { image: imageDataUrl } });
+    // }
   },
-  components: {
-  },
-  mounted() {
-
-  }
 }
 </script>
+
 <style scoped>
+.captured-img {
+  margin: auto auto;
+}
+
 .imgGroup {
   width: fit-content;
 }
@@ -163,5 +163,8 @@
 .pickGroup article>div>p {
   font-size: 64px;
 }
-.popTxt{width: 101px;}
-</style>
(No newline at end of file)
+
+.popTxt {
+  width: 101px;
+}
+</style>
client/views/pages/teacher/Board.vue
--- client/views/pages/teacher/Board.vue
+++ client/views/pages/teacher/Board.vue
@@ -1,8 +1,14 @@
 <template>
   <div class="title-box flex justify-between mb40">
     <p class="title">게시판</p>
-    <select name="" id="">
-      <option value="">A반</option>
+    <select v-model="sclsId" @change="handleClassId()">
+      <option
+        v-for="(item, index) in classList"
+        :key="index"
+        :value="item.sclsId"
+      >
+        {{ item.sclsNm }}
+      </option>
     </select>
   </div>
   <div class="search-wrap flex justify-end mb20">
@@ -38,11 +44,11 @@
           :class="{ 'selected-row': selectedRow == item.dataList }"
           @click="[goToPage('noticeDetail'), selectBoardList(item)]"
         >
-          <td>{{ totalBoard - index }}</td>
+          <td>{{ createNo(index) }}</td>
           <td>{{ item.bbsTtl }}</td>
           <td>{{ item.bbsCls }}</td>
           <td>{{ userNm }}</td>
-          <td>{{ item.bbsTm }}</td>
+          <td>{{ item.bbsTm.substr(0, 16) }}</td>
         </tr>
       </tbody>
     </table>
@@ -50,22 +56,19 @@
       class="table-pagination flex justify-center align-center mb20 mt30"
       style="gap: 10px"
     >
-      <button>
-        <img
-          src="../../../resources/img/btn27_90t_normal.png"
-          alt=""
-          @click="previousPage"
-          :disabled="page === 1"
-        />
+      <button @click="goToPagination(currentPage - 1)">
+        <img src="../../../resources/img/btn27_90t_normal.png" alt="" />
       </button>
-      <button class="selected-btn">{{ page }}</button>
-      <button>
-        <img
-          src="../../../resources/img/btn28_90t_normal.png"
-          alt=""
-          @click="nextPage"
-          :disabled="page === totalPages"
-        />
+      <button
+        v-for="page in paginationButtons"
+        :key="page"
+        @click="goToPagination(page - 1)"
+        :class="{ 'selected-btn': currentPage === page - 1 }"
+      >
+        {{ page }}
+      </button>
+      <button @click="goToPagination(currentPage + 1)">
+        <img src="../../../resources/img/btn28_90t_normal.png" alt="" />
       </button>
     </article>
     <div class="flex justify-end">
@@ -93,20 +96,24 @@
 
       // 게시글 정보
       dataList: [],
-      totalBoard: null,
+      totalPosts: 0,
       selectedRow: "",
+      bbsTm: "",
 
       // 페이징
-      page: 1,
-      pageSize: 8,
-      totalPages: null,
+      currentPage: 0,
+      itemsPerPage: 8,
 
       // 반 아이디 (추후 세션에서 받는걸로 수정)
-      sclsId: "1",
+      sclsId: "",
+      classList: [],
 
+      userId: "",
       // 검색어
       searchKeyword: "",
       selectedSearchOption: "bbsTtl",
+
+      selected: null,
     };
   },
   methods: {
@@ -131,39 +138,44 @@
     // 게시글 전체 조회
     boardList() {
       const vm = this;
+      vm.sclsId = JSON.parse(sessionStorage.getItem("sclsId"));
       axios({
         url: "/board/findAll.json",
         method: "post",
         headers: {
           "Content-Type": "application/json; charset=UTF-8",
         },
-        data: { page: vm.page, pageSize: vm.pageSize, sclsId: vm.sclsId },
+        data: {
+          page: vm.currentPage + 1,
+          pageSize: vm.itemsPerPage,
+          sclsId: vm.sclsId,
+        },
       })
         .then(function (res) {
           console.log("dataList - response : ", res.data);
-          console.log(res.data.result[0].boardClass[0].board);
-
-          vm.dataList = res.data.result[0].boardClass[0].board;
-          vm.userNm = res.data.result[0].userNm;
-          vm.userId = res.data.result[0].userId;
-          vm.totalBoard = res.data.totalBoard;
-          vm.totalPages = Math.ceil(vm.totalBoard / vm.pageSize);
+          if (res.data.result.length !== 0) {
+            vm.dataList = res.data.result[0].boardClass[0].board;
+            vm.userNm = res.data.result[0].userNm;
+            vm.userId = res.data.result[0].userId;
+            vm.totalPosts = res.data.totalBoard;
+            vm.selectClass();
+            sessionStorage.removeItem("bbsId");
+            sessionStorage.removeItem("file");
+          } else {
+            vm.selectClass();
+          }
         })
         .catch(function (error) {
-          console.log("result - error : ", error);
-          alert("비상 비상!");
+          console.log("boardListError - error : ", error);
         });
     },
 
     // 게시글 정보 세션에 저장
     selectBoardList(item) {
-      sessionStorage.setItem("selectedBoardList", JSON.stringify(item));
+      sessionStorage.setItem("bbsId", JSON.stringify(item.bbsId));
     },
 
     // 반 아이디 세션에 저장
-    setClassId() {
-      sessionStorage.setItem("sclsId", JSON.stringify(this.sclsId));
-    },
 
     // 게시글 검색
     boardDataSearch() {
@@ -171,8 +183,8 @@
       let searchPayload = {
         keyword: vm.searchKeyword,
         option: vm.selectedSearchOption,
-        page: vm.page,
-        pageSize: vm.pageSize,
+        page: vm.currentPage,
+        pageSize: vm.itemsPerPage,
         sclsId: vm.sclsId,
       };
       axios({
@@ -188,7 +200,7 @@
           vm.dataList = res.data.result[0].boardClass[0].board;
           vm.userNm = res.data.result[0].userNm;
           vm.userId = res.data.result[0].userId;
-          vm.totalBoard = res.data.totalBoard;
+          vm.totalPosts = res.data.totalBoard;
         })
         .catch(function (error) {
           console.log("dataSearch - error : ", error);
@@ -196,29 +208,70 @@
         });
     },
 
-    previousPage() {
-      if (this.page > 1) {
-        this.page -= 1;
-        this.boardList();
-      }
+    // 반 조회
+    selectClass() {
+      const vm = this;
+      axios({
+        url: "/classes/selectClass.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: {
+          userId: vm.userId,
+        },
+      })
+        .then(function (res) {
+          console.log("classData - response : ", res.data);
+          vm.classList = res.data.data;
+          vm.selected = res.data.data.sclsId;
+        })
+        .catch(function (error) {
+          console.log("classData - error : ", error);
+        });
+    },
+    handleClassId() {
+      sessionStorage.setItem("sclsId", JSON.stringify(this.sclsId));
+      this.boardList();
     },
 
-    nextPage() {
-      if (this.page < this.totalPages) {
-        this.page += 1;
-        this.boardList();
+    createNo(index) {
+      return this.totalPosts - (this.currentPage * this.itemsPerPage + index);
+    },
+
+    goToPagination(page) {
+      if (page < 0 || page >= this.totalPages) {
+        return;
       }
+      this.currentPage = page;
+      this.boardList();
     },
   },
   watch: {},
-  computed: {},
+  computed: {
+    totalPages() {
+      return Math.ceil(this.totalPosts / this.itemsPerPage);
+    },
+    paginationButtons() {
+      let start = Math.max(0, this.currentPage - 2);
+      let end = Math.min(start + 5, this.totalPages);
+
+      if (end - start < 5) {
+        start = Math.max(0, end - 5);
+      }
+
+      return Array.from({ length: end - start }, (_, i) => start + i + 1);
+    },
+    startIndex() {
+      return this.currentPage * this.itemsPerPage;
+    },
+  },
   components: {
     SvgIcon,
   },
   mounted() {
     console.log("Main2 mounted");
     this.boardList();
-    this.setClassId();
   },
 };
 </script>
client/views/pages/teacher/C_TextBookDetail.vue
--- client/views/pages/teacher/C_TextBookDetail.vue
+++ client/views/pages/teacher/C_TextBookDetail.vue
@@ -1,13 +1,13 @@
 <template>
     <div class="title-box flex justify-between mb40">
-        <p class="title">A반</p>
+        <p class="title">A교재</p>
         <select name="" id="">
-            <option value="">A 교재</option>
+            <option value="">A 반</option>
         </select>
     </div>
     <div class="board-wrap">
-        <label for="" class="title2">단원</label>
-        <div class="table-pagination flex mt10">
+        <label for="" class="title1">단원</label>
+        <div class="unit-pagination flex mt10" style="gap: 10px;">
             <button class="selected-btn">1</button>
             <button>2</button>
             <button>3</button>
client/views/pages/teacher/ClassDetail.vue
--- client/views/pages/teacher/ClassDetail.vue
+++ client/views/pages/teacher/ClassDetail.vue
@@ -1,153 +1,218 @@
 <template>
-    <div class="title-box flex justify-between mb40">
-        <p class="title">반 관리</p>
+  <div class="title-box flex justify-between mb40">
+    <p class="title">반 관리</p>
+  </div>
+  <div class="wrap mb30">
+    <div class="flex justify-between mb30 align-center">
+      <label for="" class="title1">게시판</label>
+      <div class="look-btn flex align-center" @click="goToPage('Board')">
+        <p>자세히 보기</p>
+        <svg-icon type="mdi" :path="mdilArrowRight" class="ml10"></svg-icon>
+      </div>
+    </div>
+    <div class="table-wrap">
+      <table>
+        <thead>
+          <td>No.</td>
+          <td>제목</td>
+          <td>내용</td>
+          <td>작성자</td>
+          <td>등록일</td>
+        </thead>
+        <tbody>
+          <tr
+            v-for="(item, index) in dataList"
+            :key="item.id"
+            :class="{ 'selected-row': selectedRow == item.dataList }"
+            @click="[goToPage('noticeDetail'), selectBoardList(item)]"
+          >
+            <td>{{ totalPosts - index }}</td>
+            <td>{{ item.bbsTtl }}</td>
+            <td>{{ item.bbsCls }}</td>
+            <td>{{ userNm }}</td>
+            <td>{{ item.bbsTm.substr(0, 16) }}</td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+  </div>
+  <div class="flex justify-between" style="gap: 30px">
+    <div class="wrap mb30">
+      <div class="flex justify-between mb30 align-center">
+        <label for="" class="title1">학생 목록</label>
+        <div class="look-btn align-center flex">
+          <p>자세히 보기</p>
+          <svg-icon type="mdi" :path="mdilArrowRight" class="ml10"></svg-icon>
+        </div>
+      </div>
+      <div class="table-wrap">
+        <table>
+          <thead>
+            <td>No.</td>
+            <td>이름</td>
+            <td>학년</td>
+            <td>반</td>
+          </thead>
+          <tbody>
+            <tr>
+              <td></td>
+              <td></td>
+              <td></td>
+              <td></td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
     </div>
     <div class="wrap mb30">
-        <div class="flex justify-between mb30 align-center">
-            <label for="" class="title1">학습 현황</label>
-            <div class="look-btn flex align-center">
-                <p>자세히 보기 </p>
-                <svg-icon type="mdi" :path="mdilArrowRight" class=" ml10"></svg-icon>
+      <div class="flex justify-between mb30 align-center">
+        <label for="" class="title1">책 </label>
+        <div class="align-center flex look-btn">
+          <p>자세히 보기</p>
+          <svg-icon type="mdi" :path="mdilArrowRight" class="ml10"></svg-icon>
+        </div>
+      </div>
+      <div class="flex" style="gap: 50px">
+        <div class="textbook">
+          <div class="box" style="gap: 10px"></div>
+          <div class="text">
+            <p class="title1" style="color: #fff">A 교재</p>
+            <div
+              class="btnGroup mt15 flex align-center justify-end"
+              style="gap: 10px"
+            >
+              <button>수정</button>
+              <p>&#124;</p>
+              <button @click="showConfirm('delete')">삭제</button>
             </div>
+          </div>
         </div>
-        <div class="table-wrap">
-            <table>
-                <thead>
-                    <td>No.</td>
-                    <td>제목</td>
-                    <td>내용</td>
-                    <td>작성자</td>
-                    <td>등록일</td>
-                </thead>
-                <tbody>
-                    <tr>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                    </tr>
-                </tbody>
-            </table>
+        <div class="textbook">
+          <div class="box" style="gap: 10px"></div>
+          <div class="text">
+            <p class="title1" style="color: #fff">A 교재</p>
+            <div
+              class="btnGroup mt15 flex align-center justify-end"
+              style="gap: 10px"
+            >
+              <button>수정</button>
+              <p>&#124;</p>
+              <button @click="showConfirm('delete')">삭제</button>
+            </div>
+          </div>
         </div>
+      </div>
     </div>
-    <div class="flex justify-between" style="gap: 30px;">
-        <div class="wrap mb30">
-            <div class="flex justify-between mb30 align-center">
-                <label for="" class="title1">학생 목록</label>
-                <div class="look-btn align-center flex">
-                    <p>자세히 보기 </p>
-                    <svg-icon type="mdi" :path="mdilArrowRight" class=" ml10"></svg-icon>
-                </div>
-            </div>
-            <div class="table-wrap">
-                <table>
-                    <thead>
-                        <td>No.</td>
-                        <td>이름</td>
-                        <td>학년</td>
-                        <td>반</td>
-                    </thead>
-                    <tbody>
-                        <tr>
-                            <td></td>
-                            <td></td>
-                            <td></td>
-                            <td></td>
-                        </tr>
-                    </tbody>
-                </table>
-            </div>
-        </div>
-        <div class="wrap mb30">
-            <div class="flex justify-between mb30 align-center">
-                <label for="" class="title1">책 </label>
-                <div class="align-center flex look-btn"><p>자세히 보기 </p><svg-icon type="mdi" :path="mdilArrowRight" class=" ml10"></svg-icon></div>
-            </div>
-            <div  class=" flex " style="gap: 50px;">
-                <div class="textbook">
-                    <div class="box " style="gap: 10px;">
-                    </div>
-                    <div class="text ">
-                        <p class="title1" style="color: #fff;">A 교재</p>
-                        <div class="btnGroup mt15 flex align-center justify-end" style="gap: 10px;">
-                            <button>수정</button><p>&#124;</p>
-                            <button @click="showConfirm('delete')">삭제</button>
-                        </div>
-                    </div>
-                </div>
-                <div class="textbook">
-                    <div class="box " style="gap: 10px;">
-                    </div>
-                    <div class="text ">
-                        <p class="title1" style="color: #fff;">A 교재</p>
-                        <div class="btnGroup mt15 flex align-center justify-end" style="gap: 10px;">
-                            <button>수정</button><p>&#124;</p>
-                            <button @click="showConfirm('delete')">삭제</button>
-                        </div>
-                    </div>
-                </div>
-            </div>
-        </div>
-    </div>
+  </div>
 </template>
 
 <script>
-import SvgIcon from '@jamescoyle/vue-icon';
-import { mdiMagnify,  } from '@mdi/js';
-import { mdilArrowRight } from '@mdi/light-js';
-import ProgressBar from '../../component/ProgressBar.vue';
-
+import SvgIcon from "@jamescoyle/vue-icon";
+import { mdiMagnify } from "@mdi/js";
+import { mdilArrowRight } from "@mdi/light-js";
+import ProgressBar from "../../component/ProgressBar.vue";
+import axios from "axios";
 
 export default {
-    data() {
-        return {
-            mdiMagnify: mdiMagnify,
-            mdilArrowRight: mdilArrowRight,
-            timer: "00:00",
-            progress: 20,
+  data() {
+    return {
+      mdiMagnify: mdiMagnify,
+      mdilArrowRight: mdilArrowRight,
+      timer: "00:00",
+      progress: 20,
 
-            // 교사 홈페이지에서 쿼리 파라미터로부터 전달받은 선택된 반의 아이디
-            selectedClassId : this.$route.query.sclsId 
-        }
+      // 교사 홈페이지에서 쿼리 파라미터로부터 전달받은 선택된 반의 아이디
+      selectedClassId: this.$route.query.sclsId,
+
+      // 게시글 정보
+      dataList: [],
+      totalPosts: 0,
+      selectedRow: "",
+      bbsTm: "",
+
+      // 페이징
+      currentPage: 0,
+      itemsPerPage: 5,
+
+      // 반 아이디
+      sclsId: "",
+    };
+  },
+  methods: {
+    goToPage(page) {
+      this.$router.push({ name: page });
     },
-    methods: {
-        goToPage(page) {
-            this.$router.push({ name: page });
+    increaseProgress() {
+      if (this.progress < 100) {
+        this.progress += 10;
+      }
+    },
+    showConfirm(type) {
+      let message = "";
+      if (type === "cancel") {
+        message = "삭제하시겠습니까?";
+      } else if (type === "reset") {
+        message = "초기화하시겠습니까?";
+      } else if (type === "save") {
+        message = "등록하시겠습니까?";
+      }
+
+      if (confirm(message)) {
+        this.goBack();
+      }
+    },
+
+    // 게시글 조회
+    boardList() {
+      const vm = this;
+      axios({
+        url: "/board/findAll.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
         },
-        increaseProgress() {
-            if (this.progress < 100) {
-                this.progress += 10;
-            }
+        data: {
+          page: vm.currentPage + 1,
+          pageSize: vm.itemsPerPage,
+          sclsId: vm.selectedClassId,
         },
-        showConfirm(type) {
-            let message = '';
-            if (type === 'cancel') {
-                message = '삭제하시겠습니까?';
-            } else if (type === 'reset') {
-                message = '초기화하시겠습니까?';
-            } else if (type === 'save') {
-                message = '등록하시겠습니까?';
-            }
+      })
+        .then(function (res) {
+          console.log("dataList - response : ", res.data);
 
-            if (confirm(message)) {
-                this.goBack();
-            }
-        },
-    },
-    watch: {
+          vm.dataList = res.data.result[0].boardClass[0].board;
+          vm.userNm = res.data.result[0].userNm;
+          vm.userId = res.data.result[0].userId;
+          vm.totalPosts = res.data.totalBoard;
 
+          console.log(vm.userId);
+        })
+        .catch(function (error) {
+          console.log("result - error : ", error);
+        });
     },
-    computed: {
+    setClassId() {
+      sessionStorage.setItem("sclsId", JSON.stringify(this.selectedClassId));
+      sessionStorage.removeItem("selectedBoardList");
+      sessionStorage.removeItem("file");
+      this.boardList();
+    },
 
+    // 게시글 정보 세션에 저장
+    selectBoardList(item) {
+      sessionStorage.setItem("selectedBoardList", JSON.stringify(item));
     },
-    components: {
-        SvgIcon,
-        ProgressBar
-    },
-    mounted() {
-        console.log('Main2 mounted');
-        //console.log(`반 페이지 sclsId(반 아이디) 확인 : ${this.selectedClassId}`);
-    }
-}
-</script>
(No newline at end of file)
+  },
+  watch: {},
+  computed: {},
+  components: {
+    SvgIcon,
+    ProgressBar,
+  },
+  mounted() {
+    console.log("Main2 mounted");
+    //console.log(`반 페이지 sclsId(반 아이디) 확인 : ${this.selectedClassId}`);
+    this.setClassId();
+  },
+};
+</script>
 
client/views/pages/teacher/ExamInsert.vue (added)
+++ client/views/pages/teacher/ExamInsert.vue
@@ -0,0 +1,131 @@
+<template>
+    <div class="title-box flex justify-between mb40">
+        <p class="title">평가 등록</p>
+    </div>
+    <!-- <label for="" class="title1">문제 리스트</label>
+    <table class="mt20 mb100">
+        <colgroup>
+            <col style="width: 10%;">
+            <col style="width: 70%;">
+            <col style="width: 20%;">
+        </colgroup>
+        <thead>
+            <td>No.</td>
+            <td>문제</td>
+            <td>보기</td>
+        </thead>
+        <tbody>
+            <tr>
+                <td>1</td>
+                <td>1</td>
+                <td><button type="button" title="수정" class="new-btn">
+                        수정
+                    </button></td>
+            </tr>
+        </tbody>
+    </table> -->
+    <label for="" class="title1">상세 내용</label>
+    <div class="board-wrap mt20">
+        <div class="flex align-center mb20">
+            <label for="" class="title2">단원</label>
+            <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                <option value="bbsTtl">제목</option>
+                <option value="bbsCnt">내용</option>
+                <option value="userNm">작성자</option>
+                <option value="bbsCls">카테고리</option>
+            </select>
+        </div>
+        <div class="flex align-center mb20">
+            <label for="" class="title2">평가 유형</label>
+            <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                <option value="bbsTtl">제목</option>
+                <option value="bbsCnt">내용</option>
+                <option value="userNm">작성자</option>
+                <option value="bbsCls">카테고리</option>
+            </select>
+        </div>
+
+        <hr>
+        <div class="flex align-center mb20">
+            <label for="" class="title2">문제 1</label>
+            <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                <option value="bbsTtl">제목</option>
+                <option value="bbsCnt">내용</option>
+                <option value="userNm">작성자</option>
+                <option value="bbsCls">카테고리</option>
+            </select>
+        </div>
+        <div class="flex align-center mb20">
+            <label for="" class="title2">문제 2</label>
+            <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                <option value="bbsTtl">제목</option>
+                <option value="bbsCnt">내용</option>
+                <option value="userNm">작성자</option>
+                <option value="bbsCls">카테고리</option>
+            </select>
+        </div>
+        <div class="flex align-center mb20">
+            <label for="" class="title2">문제 3</label>
+            <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                <option value="bbsTtl">제목</option>
+                <option value="bbsCnt">내용</option>
+                <option value="userNm">작성자</option>
+                <option value="bbsCls">카테고리</option>
+            </select>
+        </div>
+        <div class="flex align-center mb20">
+            <label for="" class="title2">문제 4</label>
+            <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                <option value="bbsTtl">제목</option>
+                <option value="bbsCnt">내용</option>
+                <option value="userNm">작성자</option>
+                <option value="bbsCls">카테고리</option>
+            </select>
+        </div>
+        
+    </div>
+    <div class="flex justify-between mt50">
+        <button type="button" title="글쓰기" class="new-btn" @click="goToPage('C_ExamList')">
+            목록
+        </button>
+        <div class="flex">
+            <button type="button" title="글쓰기" class="new-btn mr10">
+                취소
+            </button>
+            <button type="button" title="글쓰기" class="new-btn">
+                등록
+            </button>
+        </div>
+    </div>
+</template>
+
+<script>
+import SvgIcon from '@jamescoyle/vue-icon';
+import { mdiMagnify } from '@mdi/js';
+
+
+export default {
+    data() {
+        return {
+            mdiMagnify: mdiMagnify,
+        }
+    },
+    methods: {
+        goToPage(page) {
+            this.$router.push({ name: page });
+        },
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+        SvgIcon
+    },
+    mounted() {
+        console.log('Main2 mounted');
+    }
+}
+</script>(No newline at end of file)
client/views/pages/teacher/ExamList.vue
--- client/views/pages/teacher/ExamList.vue
+++ client/views/pages/teacher/ExamList.vue
@@ -5,6 +5,12 @@
             <option value="">1단원</option>
         </select>
     </div>
+    <label for="" class="title2">단원</label>
+        <div class="unit-pagination flex mt10 mb20" style="gap: 10px;">
+            <button class="selected-btn">1</button>
+            <button>2</button>
+            <button>3</button>
+        </div>
     <div class="search-wrap flex justify-end mb20">
             <select name="" id="" class="mr10 data-wrap">
                 <option value="">중간</option>
@@ -78,7 +84,7 @@
                     <button><img src="../../../resources/img/btn28_90t_normal.png" alt=""></button>
                 </article>
                 <div class="flex justify-end ">
-                <button type="button" title="등록" class="new-btn" @click="goToPage('ExamDetail')">
+                <button type="button" title="등록" class="new-btn" @click="goToPage('ExamInsert')">
                     등록
                 </button>
         </div>
client/views/pages/teacher/Home.vue
--- client/views/pages/teacher/Home.vue
+++ client/views/pages/teacher/Home.vue
@@ -1,267 +1,289 @@
 <template>
-    <div class="title-box flex justify-between mb40">
-        <p class="title">홈</p>
-    </div>
-    <div class="content-t">
-        <div class=" flex " style="gap: 50px;" :style="{flexWrap: 'wrap'}">
-            <div class="class" v-for="classItem in classesList" :key="classItem.sclsId">
-                <div class="box gd-col2" style="gap: 10px;" @click="goToPage('ClassDetail', classItem.sclsId)">
-                    <div><img src="../../../resources/img/img176_82t.png" alt=""></div>
-                    <div><img src="../../../resources/img/img176_82t.png" alt=""></div>
-                    <div><img src="../../../resources/img/img176_82t.png" alt=""></div>
-                    <div><img src="../../../resources/img/img176_82t.png" alt=""></div>
-                </div>
-                <div class="text flex justify-between mt20">
-                    <p class="title1">{{ classItem.sclsNm }}</p>
-                    <span class="member">{{ classItem. studentCount}}</span>
-                </div>
-                <div class="btnGroup mt15 flex align-center justify-end" style="gap: 10px;">
-                    <button @click="editModeModal(classItem.sclsId)">수정</button>
-                    <p>&#124;</p>
-                    <button @click="deleteClass(classItem.sclsId)">삭제</button>
-                </div>
-            </div>
-            <div class="textbook-add">
-                <button @click="addModeModal"><img src="../../../resources/img/btn32_98t_normal.png" alt=""></button>
-
-            </div>
-            <!-- 팝업창 -->
-            <div v-show="searchOpen" class="popup-wrap">
-                <div class="popup-box ">
-                    <div class="flex justify-between mb30">
-                        <p class="popup-title">반 이름</p>
-                        <button type="button" class="popup-close-btn" @click="closeBtn">
-                            <svg-icon type="mdi" :path="mdiWindowClose" class="close-btn"></svg-icon>
-
-                        </button>
-                    </div>
-                    <div class="search-wrap mb30">
-                        <input type="text" v-model="createClassName"  class="data-wrap" placeholder="">
-                        <!-- <button type="button" >
+  <div class="title-box flex justify-between mb40">
+    <p class="title">홈</p>
+  </div>
+  <div class="content-t">
+    <div class="flex" style="gap: 50px" :style="{ flexWrap: 'wrap' }">
+      <div
+        class="class"
+        v-for="classItem in classesList"
+        :key="classItem.sclsId"
+      >
+        <div
+          class="box gd-col2"
+          style="gap: 10px"
+          @click="goToPage('ClassDetail', classItem.sclsId)"
+        >
+          <div><img src="../../../resources/img/img176_82t.png" alt="" /></div>
+          <div><img src="../../../resources/img/img176_82t.png" alt="" /></div>
+          <div><img src="../../../resources/img/img176_82t.png" alt="" /></div>
+          <div><img src="../../../resources/img/img176_82t.png" alt="" /></div>
+        </div>
+        <div class="text flex justify-between mt20">
+          <p class="title1">{{ classItem.sclsNm }}</p>
+          <span class="member">{{ classItem.studentCount }}</span>
+        </div>
+        <div
+          class="btnGroup mt15 flex align-center justify-end"
+          style="gap: 10px"
+        >
+          <button @click="editModeModal(classItem.sclsId)">수정</button>
+          <p>&#124;</p>
+          <button @click="deleteClass(classItem.sclsId)">삭제</button>
+        </div>
+      </div>
+      <div class="textbook-add">
+        <button @click="addModeModal">
+          <img src="../../../resources/img/btn32_98t_normal.png" alt="" />
+        </button>
+      </div>
+      <!-- 팝업창 -->
+      <div v-show="searchOpen" class="popup-wrap">
+        <div class="popup-box">
+          <div class="flex justify-between mb30">
+            <p class="popup-title">반 이름</p>
+            <button type="button" class="popup-close-btn" @click="closeBtn">
+              <svg-icon
+                type="mdi"
+                :path="mdiWindowClose"
+                class="close-btn"
+              ></svg-icon>
+            </button>
+          </div>
+          <div class="search-wrap mb30">
+            <input
+              type="text"
+              v-model="createClassName"
+              class="data-wrap"
+              placeholder=""
+            />
+            <!-- <button type="button" >
                             <img src="../../../resources/img/look_t.png" alt="">
                         </button> -->
-                    </div>
-                    <div class="flex justify-center ">
-                        <button type="button" title="글쓰기" class="new-btn mr10" @click="closeBtn">
-                            취소
-                        </button>
-                        <button type="button" title="등록" class="new-btn" @click="isEditMode ? updateClass() : insertClass()">
-                            {{ isEditMode ? '수정' : '등록' }}
-                        </button>
-                    </div>
-                </div>
-            </div>
+          </div>
+          <div class="flex justify-center">
+            <button
+              type="button"
+              title="글쓰기"
+              class="new-btn mr10"
+              @click="closeBtn"
+            >
+              취소
+            </button>
+            <button
+              type="button"
+              title="등록"
+              class="new-btn"
+              @click="isEditMode ? updateClass() : insertClass()"
+            >
+              {{ isEditMode ? "수정" : "등록" }}
+            </button>
+          </div>
         </div>
+      </div>
     </div>
+  </div>
 </template>
 
 <script>
-import axios from 'axios';
-import SvgIcon from '@jamescoyle/vue-icon';
-import { mdiMagnify, mdiWindowClose } from '@mdi/js';
+import axios from "axios";
+import SvgIcon from "@jamescoyle/vue-icon";
+import { mdiMagnify, mdiWindowClose } from "@mdi/js";
 export default {
-    data() {
-        return {
-            mdiWindowClose: mdiWindowClose,
-            showModal: false,
-            searchOpen: false,
+  data() {
+    return {
+      mdiWindowClose: mdiWindowClose,
+      showModal: false,
+      searchOpen: false,
 
-            classesList : [], // 불러온 반 정보
-            user_id : '2', //유저 아이디 : 현재는 고정
-            createClassName : "", // 생성 또는 수정할 반 이름
+      classesList: [], // 불러온 반 정보
+      user_id: "2", //유저 아이디 : 현재는 고정
+      createClassName: "", // 생성 또는 수정할 반 이름
 
-            isEditMode: false, // 추가 모드인지 수정 모드인지 구분하는 변수
-            current_editId : '', // 현재 수정할 반 id
-        }
+      isEditMode: false, // 추가 모드인지 수정 모드인지 구분하는 변수
+      current_editId: "", // 현재 수정할 반 id
+    };
+  },
+  methods: {
+    goToPage(page, sclsId) {
+      //console.log(`sclsId : ${sclsId}`); // 쿼리 확인
+      this.$router.push({ name: page, query: { sclsId: sclsId } });
     },
-    methods: {
-        goToPage(page, sclsId) {
-            //console.log(`sclsId : ${sclsId}`); // 쿼리 확인
-            this.$router.push({ name: page , query : {sclsId : sclsId}});
-        },
-        closeModal() {
-            this.showModal = false;
-        },
-        editModeModal(sclsId) {
-            this.searchOpen = true;
-            this.isEditMode = true; // 수정 모드로 설정
-            this.current_editId = sclsId
-        },
-        addModeModal() {
-            this.searchOpen = true;
-            this.isEditMode = false; // 추가 모드로 설정
-        },
-        closeBtn() {
-            this.searchOpen = false;
-            this.createClassName = ""; // 팝업 닫을 때 반 이름 초기화
+    closeModal() {
+      this.showModal = false;
+    },
+    editModeModal(sclsId) {
+      this.searchOpen = true;
+      this.isEditMode = true; // 수정 모드로 설정
+      this.current_editId = sclsId;
+    },
+    addModeModal() {
+      this.searchOpen = true;
+      this.isEditMode = false; // 추가 모드로 설정
+    },
+    closeBtn() {
+      this.searchOpen = false;
+      this.createClassName = ""; // 팝업 닫을 때 반 이름 초기화
+    },
+    showConfirm(type, callback) {
+      let message = "";
+      if (type === "delete") {
+        message = "삭제하시겠습니까?";
+      } else if (type === "reset") {
+        message = "초기화하시겠습니까?";
+      } else if (type === "save") {
+        message = "등록하시겠습니까?";
+      } else if (type === "edit") {
+        message = "수정하시겠습니까?";
+      }
 
+      if (confirm(message)) {
+        if (callback) callback(); // 콜백 함수 호출
+      }
+    },
+    // 조회
+    selectClass() {
+      sessionStorage.removeItem("sclsId");
+      axios({
+        url: "/classes/selectClass.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
         },
-        showConfirm(type, callback) {
-            let message = '';
-            if (type === 'delete') {
-                message = '삭제하시겠습니까?';
-            } else if (type === 'reset') {
-                message = '초기화하시겠습니까?';
-            } else if (type === 'save') {
-                message = '등록하시겠습니까?';
-            } else if (type === 'edit'){
-                message = '수정하시겠습니까?';
-            }
-
-
-            if (confirm(message)) {
-                if (callback) callback(); // 콜백 함수 호출
-            }
+        data: {
+          userId: this.user_id,
         },
-        // 조회
-        selectClass() {
-            axios({
-                url: "/classes/selectClass.json",
-                method: "post",
-                headers: {
-                    "Content-Type": "application/json; charset=UTF-8",
-                },
-                data: {
-                    userId: this.user_id
-                },
-            })
+      })
+        .then((res) => {
+          if (res.data.status === "success") {
+            console.log("classesList - response(조회) : ", res.data.data);
+            this.classesList = res.data.data;
+          } else {
+            console.log("조회에 실패했습니다: ", res.data);
+            alert("조회에 실패했습니다.");
+          }
+        })
+        .catch((err) => {
+          console.log("classesList - error(조회) : ", err);
+          alert("조회에 오류가 발생했습니다.");
+        });
+    },
+    // 추가
+    insertClass() {
+      if (this.createClassName.trim() === "") {
+        alert("반 이름을 입력해주세요");
+      } else {
+        this.showConfirm("save", () => {
+          axios({
+            url: "/classes/insertClass.json",
+            method: "post",
+            headers: {
+              "Content-Type": "application/json; charset=UTF-8",
+            },
+            data: {
+              userId: this.user_id,
+              sclsNm: this.createClassName,
+            },
+          })
             .then((res) => {
-                if (res.data.status === "success") {
-                    console.log("classesList - response(조회) : ", res.data.data);
-                    this.classesList = res.data.data;
-                } else {
-                    console.log("조회에 실패했습니다: ", res.data);
-                    alert("조회에 실패했습니다.");
-                }
+              if (res.data.status === "success") {
+                console.log("classesList - response(추가) : ", res.data.data);
+                this.selectClass();
+                this.createClassName = ""; // 반 이름 초기화
+                this.closeBtn(); // 생성 모달 닫기
+              } else {
+                console.log("추가에 실패했습니다: ", res.data);
+                alert("추가에 실패했습니다.");
+              }
             })
             .catch((err) => {
-                console.log("classesList - error(조회) : ", err);
-                alert("조회에 오류가 발생했습니다.");
+              console.log("classesList - error(추가) : ", err);
+              alert("추가에 오류가 발생했습니다.");
             });
-        },
-        // 추가
-        insertClass() {
-            if(this.createClassName.trim() === ""){
-                alert("반 이름을 입력해주세요");
+        });
+      }
+    },
+    // 삭제
+    deleteClass(sclsId) {
+      this.showConfirm("delete", () => {
+        axios({
+          url: "/classes/deleteClass.json",
+          method: "post",
+          headers: {
+            "Content-Type": "application/json; charset=UTF-8",
+          },
+          data: {
+            sclsId: sclsId,
+          },
+        })
+          .then((res) => {
+            if (res.data.status === "success") {
+              console.log("classesList - response(삭제) : ", res.data.data);
+              this.selectClass();
+            } else {
+              console.log("삭제에 실패했습니다: ", res.data);
+              alert("삭제에 실패했습니다.");
             }
-            else{
-                this.showConfirm('save', () => {
-                axios({
-                    url: "/classes/insertClass.json",
-                    method: "post",
-                    headers: {
-                        "Content-Type": "application/json; charset=UTF-8",
-                    },
-                    data: {
-                        userId: this.user_id,
-                        sclsNm: this.createClassName
-                    },
-                })
-                    .then((res) => {
-                        if (res.data.status === "success") {
-                            console.log("classesList - response(추가) : ", res.data.data);
-                            this.selectClass();
-                            this.createClassName = ""; // 반 이름 초기화
-                            this.closeBtn(); // 생성 모달 닫기
-                        } else {
-                            console.log("추가에 실패했습니다: ", res.data);
-                            alert("추가에 실패했습니다.");
-                        }
-                    })
-                    .catch((err) => {
-                        console.log("classesList - error(추가) : ", err);
-                        alert("추가에 오류가 발생했습니다.");
-                    });
-                });
-            }
-            
-        },
-        // 삭제
-        deleteClass(sclsId) {
-            this.showConfirm('delete', () => {
-                axios({
-                    url: "/classes/deleteClass.json",
-                    method: "post",
-                    headers: {
-                        "Content-Type": "application/json; charset=UTF-8",
-                    },
-                    data: {
-                        sclsId: sclsId
-                    },
-                })
-                .then((res) => {
-                    if (res.data.status === "success") {
-                        console.log("classesList - response(삭제) : ", res.data.data);
-                        this.selectClass();
-                    } else {
-                        console.log("삭제에 실패했습니다: ", res.data);
-                        alert("삭제에 실패했습니다.");
-                    }
-                })
-                .catch((err) => {
-                    console.log("classesList - error(삭제) : ", err);
-                    alert("삭제에 오류가 발생했습니다.");
-                });
+          })
+          .catch((err) => {
+            console.log("classesList - error(삭제) : ", err);
+            alert("삭제에 오류가 발생했습니다.");
+          });
+      });
+    },
+    // 수정
+    updateClass() {
+      if (this.createClassName.trim() === "") {
+        alert("반 이름을 입력해주세요");
+      } else {
+        this.showConfirm("edit", () => {
+          axios({
+            url: "/classes/updateClass.json",
+            method: "post",
+            headers: {
+              "Content-Type": "application/json; charset=UTF-8",
+            },
+            data: {
+              sclsId: this.current_editId,
+              sclsNm: this.createClassName,
+            },
+          })
+            .then((res) => {
+              if (res.data.status === "success") {
+                console.log("classesList - response(수정) : ", res.data.data);
+                this.selectClass();
+                this.createClassName = ""; // 반 이름 초기화
+                this.current_editId = ""; // 반 Id 초기화
+                this.closeBtn(); // 팝업 닫기
+              } else {
+                console.log("수정에 실패했습니다: ", res.data);
+                alert("수정에 실패했습니다.");
+              }
+            })
+            .catch((err) => {
+              console.log("classesList - error(수정) : ", err);
+              alert("수정에 오류가 발생했습니다.");
             });
-        },
-        // 수정
-        updateClass() {
-            if(this.createClassName.trim() === ""){
-                alert("반 이름을 입력해주세요");
-            }
-            else{
-                this.showConfirm('edit', () => {
-                    axios({
-                        url: "/classes/updateClass.json",
-                        method: "post",
-                        headers: {
-                            "Content-Type": "application/json; charset=UTF-8",
-                        },
-                        data: {
-                            sclsId: this.current_editId,
-                            sclsNm: this.createClassName
-                        },
-                    })
-                    .then((res) => {
-                        if (res.data.status === "success") {
-                            console.log("classesList - response(수정) : ", res.data.data);
-                            this.selectClass();
-                            this.createClassName = ""; // 반 이름 초기화
-                            this.current_editId = ""; // 반 Id 초기화
-                            this.closeBtn(); // 팝업 닫기
-                        } else {
-                            console.log("수정에 실패했습니다: ", res.data);
-                            alert("수정에 실패했습니다.");
-                        }
-                    })
-                    .catch((err) => {
-                        console.log("classesList - error(수정) : ", err);
-                        alert("수정에 오류가 발생했습니다.");
-                    });
-                });
-            }
-        }
+        });
+      }
     },
-    watch: {
-
-    },
-    computed: {
-
-    },
-    components: {
-        SvgIcon
-    },
-    mounted() {
-        console.log('Main2 mounted');
-        this.selectClass();
-    }
-}
+  },
+  watch: {},
+  computed: {},
+  components: {
+    SvgIcon,
+  },
+  mounted() {
+    console.log("Main2 mounted");
+    this.selectClass();
+  },
+};
 </script>
 
 <style>
 .content-t {
-    flex-wrap: wrap; 
-    height: 90%;
-    overflow-y: scroll;
+  flex-wrap: wrap;
+  height: 90%;
+  overflow-y: scroll;
 }
-</style>
(No newline at end of file)
+</style>
client/views/pages/teacher/Main_t.vue
--- client/views/pages/teacher/Main_t.vue
+++ client/views/pages/teacher/Main_t.vue
@@ -1,7 +1,7 @@
 <template>
     <div class="flex justify-between" style="height: 100%;">
        <Side_t></Side_t>
-       <div style="padding: 15px 60px 90px 60px; ">
+       <div style="padding: 15px 60px 120px 0px ">
         <Header></Header>
           <div class="main-wrap">
               <router-view />
client/views/pages/teacher/QuestionDetail.vue
--- client/views/pages/teacher/QuestionDetail.vue
+++ client/views/pages/teacher/QuestionDetail.vue
@@ -1,5 +1,6 @@
 <template>
     <div class="title-box flex justify-between mb40">
+<<<<<<< HEAD
         <p class="title">문제 조회</p>
     </div>
     <div class="board-wrap">
@@ -75,12 +76,195 @@
                 </table>
             </div>
         </div>
+=======
+        <p class="title">문제 등록</p>
+    </div>
+    <div class="board-wrap">
+        <div class="tab-box" >
+            <label class="mr20 title1">
+               <input type="radio" v-model="selectedTab" value="tab1" />
+               문제 유형 (일반형)
+            </label>
+            <label class="mr20 title1">
+               <input type="radio" v-model="selectedTab" value="tab2" />
+               문제 유형 (O,X형)
+            </label>
+            <label class="mr20 title1">
+               <input type="radio" v-model="selectedTab" value="tab3" />
+               문제 유형 (연결형)
+            </label>
+            <label class="mr20 title1">
+               <input type="radio" v-model="selectedTab" value="tab4" />
+               문제 유형 (다중 정답형)
+            </label>
+        </div>
+        <hr>
+       <div class="gd-col2 " >
+            <div class="flex align-center mb20">
+                <label for="" class="title2">카테고리</label>
+                <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                    <option value="bbsTtl">제목</option>
+                    <option value="bbsCnt">내용</option>
+                    <option value="userNm">작성자</option>
+                    <option value="bbsCls">카테고리</option>
+                </select>
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">문제 유형</label>
+                <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                    <option value="bbsTtl">제목</option>
+                    <option value="bbsCnt">내용</option>
+                    <option value="userNm">작성자</option>
+                    <option value="bbsCls">카테고리</option>
+                </select>
+            </div>
+            <div class="flex align-center">
+                <label for="" class="title2">지문</label>
+                <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                    <option value="bbsTtl">제목</option>
+                    <option value="bbsCnt">내용</option>
+                    <option value="userNm">작성자</option>
+                    <option value="bbsCls">카테고리</option>
+                </select>
+            </div>
+            <div class="flex align-center">
+                <label for="" class="title2">문제 지표</label>
+                <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                    <option value="bbsTtl">제목</option>
+                    <option value="bbsCnt">내용</option>
+                    <option value="userNm">작성자</option>
+                    <option value="bbsCls">카테고리</option>
+                </select>
+            </div>
+       </div>
+        <div class="flex align-center mb20 mt40">
+            <label for="" class="title2">문제 배점</label>
+            <input type="text" class="data-wrap">
+        </div>
+
+        <div class="flex align-center mb20">
+            <label for="" class="title2">내용</label>
+            <textarea name="" id="" class="data-wrap"></textarea>
+        </div>
+        <div class="flex align-center mb20">
+            <label for="" class="title2">힌트</label>
+            <input type="text" class="data-wrap">
+        </div>
+        <div class="flex align-center mb20">
+            <label for="" class="title2">첨부파일</label>
+            <input type="file" ref="fileInput" @change="handleFileUpload" />
+        </div>
+        <hr>
+        <div v-if="selectedTab === 'tab1'">
+            <div class="flex align-center mb20">
+                <label for="" class="title2">답1</label>
+                <input type="text" class="data-wrap">
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">답2</label>
+                <input type="text" class="data-wrap">
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">답3</label>
+                <input type="text" class="data-wrap">
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">답4</label>
+                <input type="text" class="data-wrap">
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">해설</label>
+                <textarea name="" id="" class="data-wrap"></textarea>
+            </div>
+        </div>
+       <div v-else-if="selectedTab === 'tab2'">
+            <div class="flex align-center mb20">
+                <label for="" class="title2">답</label>
+                    <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                            <option value="bbsTtl">O</option>
+                            <option value="bbsCnt">X</option>
+                        </select>
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">해설</label>
+                <textarea name="" id="" class="data-wrap"></textarea>
+            </div>
+       </div>
+       <div v-else-if="selectedTab === 'tab3'">
+            <div class="gd-col2 " >
+                <div class="flex align-center mb20 mr40" >
+                    <label for="" class="title2">문제1</label>
+                    <input type="text" class="data-wrap">
+                </div>
+                <div class="flex align-center mb20">
+                    <label for="" class="title2">답1</label>
+                    <input type="text" class="data-wrap">
+                </div>
+                <div class="flex align-center mb20 mr40">
+                    <label for="" class="title2">문제2</label>
+                    <input type="text" class="data-wrap">
+                </div>
+                <div class="flex align-center mb20">
+                    <label for="" class="title2">답2</label>
+                    <input type="text" class="data-wrap">
+                </div>
+                <div class="flex align-center mb20 mr40">
+                    <label for="" class="title2">문제3</label>
+                    <input type="text" class="data-wrap">
+                </div>
+                <div class="flex align-center mb20 ">
+                    <label for="" class="title2">답3</label>
+                    <input type="text" class="data-wrap">
+                </div>
+                <div class="flex align-center mb20 mr40">
+                    <label for="" class="title2">문제4</label>
+                    <input type="text" class="data-wrap">
+                </div>
+                <div class="flex align-center mb20">
+                    <label for="" class="title2">답4</label>
+                    <input type="text" class="data-wrap">
+                </div>
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">해설</label>
+                <textarea name="" id="" class="data-wrap"></textarea>
+            </div>
+       </div>
+       <div v-else-if="selectedTab === 'tab4'">
+                <div class="flex align-center mb20 mr40" >
+                    <label for="" class="title2">문제1</label>
+                    <input type="text" class="data-wrap">
+                    <input type="checkbox" class="ui-checkbox ml30">
+                </div>
+                <div class="flex align-center mb20 mr40">
+                    <label for="" class="title2">문제2</label>
+                    <input type="text" class="data-wrap">
+                    <input type="checkbox" class="ui-checkbox ml30">
+                </div>
+                <div class="flex align-center mb20 mr40">
+                    <label for="" class="title2">문제3</label>
+                    <input type="text" class="data-wrap">
+                    <input type="checkbox" class="ui-checkbox ml30">
+                </div>
+                <div class="flex align-center mb20 mr40">
+                    <label for="" class="title2">문제4</label>
+                    <input type="text" class="data-wrap">
+                    <input type="checkbox" class="ui-checkbox ml30">
+                </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">해설</label>
+                <textarea name="" id="" class="data-wrap"></textarea>
+            </div>
+       </div>
+       
+>>>>>>> e47769b90c7ad4f0b34f38bb2a56a8a69a894941
     </div>
     <div class="flex justify-between mt50">
         <button type="button" title="글쓰기" class="new-btn" @click="goToPage('QuestionList')">
             목록
         </button>
         <div class="flex">
+<<<<<<< HEAD
             <button type="button" title="글쓰기" class="new-btn mr10" @click="editQuestion">
                 수정
             </button>
@@ -96,6 +280,14 @@
             <p>삭제하시겠습니까?</p>
             <button @click="deleteQuestion">예, 삭제</button>
             <button @click="cancelDelete">취소</button>
+=======
+            <button type="button" title="글쓰기" class="new-btn mr10">
+                삭제
+            </button>
+            <button type="button" title="글쓰기" class="new-btn">
+                수정
+            </button>
+>>>>>>> e47769b90c7ad4f0b34f38bb2a56a8a69a894941
         </div>
     </div>
 </template>
@@ -103,12 +295,17 @@
 <script>
 import SvgIcon from '@jamescoyle/vue-icon';
 import { mdiMagnify } from '@mdi/js';
+<<<<<<< HEAD
 import axios from 'axios';
+=======
+
+>>>>>>> e47769b90c7ad4f0b34f38bb2a56a8a69a894941
 
 export default {
     data() {
         return {
             mdiMagnify: mdiMagnify,
+<<<<<<< HEAD
             questionTitle: '샘플 제목',
             questionExpln: '샘플 내용',
             questionFile: null, // 파일 URL을 여기에 저장
@@ -124,12 +321,16 @@
             questionScore: '',
             questionHint: '',
             questionExplanation: ''
+=======
+            selectedTab: 'tab1',
+>>>>>>> e47769b90c7ad4f0b34f38bb2a56a8a69a894941
         }
     },
     methods: {
         goToPage(page) {
             this.$router.push({ name: page });
         },
+<<<<<<< HEAD
         editQuestion() {
             // 수정 로직 추가
             console.log('수정 버튼 클릭');
@@ -180,11 +381,20 @@
             // 문제 ID를 얻는 로직을 추가하세요
             return 'sampleProblemId';
         }
+=======
+    },
+    watch: {
+
+    },
+    computed: {
+
+>>>>>>> e47769b90c7ad4f0b34f38bb2a56a8a69a894941
     },
     components: {
         SvgIcon
     },
     mounted() {
+<<<<<<< HEAD
         this.loadFromLocalStorage();
     }
 }
@@ -230,3 +440,12 @@
         cursor: pointer;
     }
 </style>
+=======
+        console.log('Main2 mounted');
+    }
+}
+</script>
+<style scoped>
+.ui-checkbox{width: 30px; height: 30px;}
+</style>
+>>>>>>> e47769b90c7ad4f0b34f38bb2a56a8a69a894941
client/views/pages/teacher/QuestionInsert.vue
--- client/views/pages/teacher/QuestionInsert.vue
+++ client/views/pages/teacher/QuestionInsert.vue
@@ -3,54 +3,183 @@
         <p class="title">문제 등록</p>
     </div>
     <div class="board-wrap">
-        <div class="flex align-center mb20">
-            <label for="" class="title2">제목</label>
-            <input type="text" class="data-wrap">
+        <div class="tab-box" >
+            <label class="mr20 title1">
+               <input type="radio" v-model="selectedTab" value="tab1" />
+               문제 유형 (일반형)
+            </label>
+            <label class="mr20 title1">
+               <input type="radio" v-model="selectedTab" value="tab2" />
+               문제 유형 (O,X형)
+            </label>
+            <label class="mr20 title1">
+               <input type="radio" v-model="selectedTab" value="tab3" />
+               문제 유형 (연결형)
+            </label>
+            <label class="mr20 title1">
+               <input type="radio" v-model="selectedTab" value="tab4" />
+               문제 유형 (다중 정답형)
+            </label>
         </div>
         <hr>
-        <div class="flex align-center">
+       <div class="gd-col2 " >
+            <div class="flex align-center mb20">
+                <label for="" class="title2">카테고리</label>
+                <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                    <option value="bbsTtl">제목</option>
+                    <option value="bbsCnt">내용</option>
+                    <option value="userNm">작성자</option>
+                    <option value="bbsCls">카테고리</option>
+                </select>
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">문제 유형</label>
+                <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                    <option value="bbsTtl">제목</option>
+                    <option value="bbsCnt">내용</option>
+                    <option value="userNm">작성자</option>
+                    <option value="bbsCls">카테고리</option>
+                </select>
+            </div>
+            <div class="flex align-center">
+                <label for="" class="title2">지문</label>
+                <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                    <option value="bbsTtl">제목</option>
+                    <option value="bbsCnt">내용</option>
+                    <option value="userNm">작성자</option>
+                    <option value="bbsCls">카테고리</option>
+                </select>
+            </div>
+            <div class="flex align-center">
+                <label for="" class="title2">문제 지표</label>
+                <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                    <option value="bbsTtl">제목</option>
+                    <option value="bbsCnt">내용</option>
+                    <option value="userNm">작성자</option>
+                    <option value="bbsCls">카테고리</option>
+                </select>
+            </div>
+       </div>
+        <div class="flex align-center mb20 mt40">
+            <label for="" class="title2">문제 배점</label>
+            <input type="text" class="data-wrap">
+        </div>
+
+        <div class="flex align-center mb20">
             <label for="" class="title2">내용</label>
             <textarea name="" id="" class="data-wrap"></textarea>
         </div>
-        <hr>
+        <div class="flex align-center mb20">
+            <label for="" class="title2">힌트</label>
+            <input type="text" class="data-wrap">
+        </div>
         <div class="flex align-center mb20">
             <label for="" class="title2">첨부파일</label>
             <input type="file" ref="fileInput" @change="handleFileUpload" />
         </div>
-        <div class="flex align-center mb20">
-            <label for="" class="title2">답</label>
-            <input type="text" class="data-wrap">
-        </div>
-        <div>
-            <label for="" class="title2">오답 학생</label>
-            <div class="table-wrap mt20">
-                <table>
-                    <thead>
-                        <td>No.</td>
-                        <td>이름</td>
-                        <td>학년</td>
-                        <td>반</td>
-                        <td>오답</td>
-                    </thead>
-                    <tbody>
-                        <tr @click="goToPage('noticeDetail')">
-                            <td></td>
-                            <td></td>
-                            <td></td>
-                            <td></td>
-                            <td></td>
-                        </tr>
-                    </tbody>
-                </table>
-                <!-- <article class="table-pagination flex justify-center align-center mb20 mt30" style="gap: 10px;">
-                    <button><img src="../../../resources/img/btn27_90t_normal.png" alt=""></button>
-                    <button class="selected-btn">1</button>
-                    <button>2</button>
-                    <button>3</button>
-                    <button><img src="../../../resources/img/btn28_90t_normal.png" alt=""></button>
-                </article> -->
+        <hr>
+        <div v-if="selectedTab === 'tab1'">
+            <div class="flex align-center mb20">
+                <label for="" class="title2">답1</label>
+                <input type="text" class="data-wrap">
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">답2</label>
+                <input type="text" class="data-wrap">
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">답3</label>
+                <input type="text" class="data-wrap">
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">답4</label>
+                <input type="text" class="data-wrap">
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">해설</label>
+                <textarea name="" id="" class="data-wrap"></textarea>
             </div>
         </div>
+       <div v-else-if="selectedTab === 'tab2'">
+            <div class="flex align-center mb20">
+                <label for="" class="title2">답</label>
+                    <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                            <option value="bbsTtl">O</option>
+                            <option value="bbsCnt">X</option>
+                        </select>
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">해설</label>
+                <textarea name="" id="" class="data-wrap"></textarea>
+            </div>
+       </div>
+       <div v-else-if="selectedTab === 'tab3'">
+            <div class="gd-col2 " >
+                <div class="flex align-center mb20 mr40" >
+                    <label for="" class="title2">문제1</label>
+                    <input type="text" class="data-wrap">
+                </div>
+                <div class="flex align-center mb20">
+                    <label for="" class="title2">답1</label>
+                    <input type="text" class="data-wrap">
+                </div>
+                <div class="flex align-center mb20 mr40">
+                    <label for="" class="title2">문제2</label>
+                    <input type="text" class="data-wrap">
+                </div>
+                <div class="flex align-center mb20">
+                    <label for="" class="title2">답2</label>
+                    <input type="text" class="data-wrap">
+                </div>
+                <div class="flex align-center mb20 mr40">
+                    <label for="" class="title2">문제3</label>
+                    <input type="text" class="data-wrap">
+                </div>
+                <div class="flex align-center mb20 ">
+                    <label for="" class="title2">답3</label>
+                    <input type="text" class="data-wrap">
+                </div>
+                <div class="flex align-center mb20 mr40">
+                    <label for="" class="title2">문제4</label>
+                    <input type="text" class="data-wrap">
+                </div>
+                <div class="flex align-center mb20">
+                    <label for="" class="title2">답4</label>
+                    <input type="text" class="data-wrap">
+                </div>
+            </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">해설</label>
+                <textarea name="" id="" class="data-wrap"></textarea>
+            </div>
+       </div>
+       <div v-else-if="selectedTab === 'tab4'">
+                <div class="flex align-center mb20 mr40" >
+                    <label for="" class="title2">문제1</label>
+                    <input type="text" class="data-wrap">
+                    <input type="checkbox" class="ui-checkbox ml30">
+                </div>
+                <div class="flex align-center mb20 mr40">
+                    <label for="" class="title2">문제2</label>
+                    <input type="text" class="data-wrap">
+                    <input type="checkbox" class="ui-checkbox ml30">
+                </div>
+                <div class="flex align-center mb20 mr40">
+                    <label for="" class="title2">문제3</label>
+                    <input type="text" class="data-wrap">
+                    <input type="checkbox" class="ui-checkbox ml30">
+                </div>
+                <div class="flex align-center mb20 mr40">
+                    <label for="" class="title2">문제4</label>
+                    <input type="text" class="data-wrap">
+                    <input type="checkbox" class="ui-checkbox ml30">
+                </div>
+            <div class="flex align-center mb20">
+                <label for="" class="title2">해설</label>
+                <textarea name="" id="" class="data-wrap"></textarea>
+            </div>
+       </div>
+       
     </div>
     <div class="flex justify-between mt50">
         <button type="button" title="글쓰기" class="new-btn" @click="goToPage('QuestionList')">
@@ -58,10 +187,10 @@
         </button>
         <div class="flex">
             <button type="button" title="글쓰기" class="new-btn mr10">
-                수정
+                취소
             </button>
             <button type="button" title="글쓰기" class="new-btn">
-                삭제
+                등록
             </button>
         </div>
     </div>
@@ -76,6 +205,7 @@
     data() {
         return {
             mdiMagnify: mdiMagnify,
+            selectedTab: 'tab1',
         }
     },
     methods: {
@@ -96,4 +226,7 @@
         console.log('Main2 mounted');
     }
 }
-</script>
(No newline at end of file)
+</script>
+<style scoped>
+.ui-checkbox{width: 30px; height: 30px;}
+</style>
(No newline at end of file)
client/views/pages/teacher/QuestionList.vue
--- client/views/pages/teacher/QuestionList.vue
+++ client/views/pages/teacher/QuestionList.vue
@@ -2,9 +2,15 @@
     <div class="title-box flex justify-between mb40">
         <p class="title">문제</p>
         <select name="" id="">
-            <option value="">1단원</option>
+            <option value="">A교재</option>
         </select>
     </div>
+    <label for="" class="title2">단원</label>
+        <div class="unit-pagination flex mt10 mb20" style="gap: 10px;">
+            <button class="selected-btn">1</button>
+            <button>2</button>
+            <button>3</button>
+        </div>
     <div class="search-wrap flex justify-end mb20">
         <select name="" id="" class="mr10 data-wrap" v-model="searchOption">
             <option value="">전체</option>
@@ -24,10 +30,11 @@
                 <tr>
                     <td>No.</td>
                     <td>제목</td>
-                    <td>문제</td>
-                    <td>작성자</td>
-                    <td>오답률</td>
+                    <td>내용</td>
+                    <td>유형</td>
+                    <td>지문</td>
                     <td>등록일</td>
+<<<<<<< HEAD
                 </tr>
             </thead>
             <tbody>
@@ -56,6 +63,32 @@
             <button type="button" title="등록" class="new-btn" @click="goToPage('QuestionInsert')">
                 등록
             </button>
+=======
+                </thead>
+                <tbody>
+                    <tr @click="goToPage('QuestionDetail')">
+                        <td></td>
+                        <td></td>
+                        <td></td>
+                        <td></td>
+                        <td></td>
+                        <td></td>
+                    </tr>
+                </tbody>
+            </table>
+            <article class="table-pagination flex justify-center align-center mb20 mt30" style="gap: 10px;">
+                    <button><img src="../../../resources/img/btn27_90t_normal.png" alt=""></button>
+                    <button class="selected-btn">1</button>
+                    <button>2</button>
+                    <button>3</button>
+                    <button><img src="../../../resources/img/btn28_90t_normal.png" alt=""></button>
+                </article>
+                <div class="flex justify-end ">
+                <button type="button" title="등록" class="new-btn" @click="goToPage('QuestionInsert')">
+                    등록
+                </button>
+        </div>
+>>>>>>> e47769b90c7ad4f0b34f38bb2a56a8a69a894941
         </div>
     </div>
 </template>
client/views/pages/teacher/TextBookDetail.vue
--- client/views/pages/teacher/TextBookDetail.vue
+++ client/views/pages/teacher/TextBookDetail.vue
@@ -1,18 +1,20 @@
 <template>
     <div class="title-box flex justify-between mb40">
         <p class="title">A교재</p>
-        <select name="" id="">
-            <option value="">A 교재</option>
-        </select>
+        <!-- <select name="" id="">
+            <option value="">1단원</option>
+        </select> -->
     </div>
-    <div class="board-wrap">
-        <label for="" class="title2">단원</label>
-        <div class="table-pagination flex mt10">
+    <label for="" class="title1">단원</label>
+        <div class="unit-pagination flex mt10" style="gap: 10px;">
             <button class="selected-btn">1</button>
             <button>2</button>
             <button>3</button>
+            <button ><svg-icon type="mdi" :path="mdiPlus" style="    padding-top: 6px;
+    width: 30px;
+    height: 30px;"></svg-icon></button>
         </div>
-        <hr>
+    <div class="board-wrap mt30">
         <div class="mb20 ">
             <div class="flex justify-between mb30 align-center">
                 <label for="" class="title1">지문</label>
@@ -175,7 +177,7 @@
 
 <script>
 import SvgIcon from '@jamescoyle/vue-icon';
-import { mdiMagnify, } from '@mdi/js';
+import { mdiMagnify,mdiPlus  } from '@mdi/js';
 import { mdilArrowRight } from '@mdi/light-js';
 import ProgressBar from '../../component/ProgressBar.vue';
 
@@ -183,6 +185,7 @@
 export default {
     data() {
         return {
+            mdiPlus :mdiPlus ,
             mdiMagnify: mdiMagnify,
             mdilArrowRight: mdilArrowRight,
             timer: "00:00",
 
client/views/pages/teacher/TextDetail.vue (added)
+++ client/views/pages/teacher/TextDetail.vue
@@ -0,0 +1,229 @@
+<template>
+    <div class="title-box flex justify-between mb40">
+        <p class="title">지문 상세 보기</p>
+    </div>
+    <div class="board-wrap">
+        <div class="flex align-center mb20">
+            <label for="" class="title2">제목</label>
+            <input v-if="isEditing" type="text" v-model="post.textTtl" class=" data-wrap">
+            <p class="data-wrap" v-else>{{ post.textTtl }}</p>
+        </div>
+        <div class="flex align-center mb20">
+            <label for="" class="title2">URL</label>
+            <input v-if="isEditing" type="text" v-model="post.textUrl" class="data-wrap">
+            <p v-else class="data-wrap">{{ post.textUrl }}</p>
+        </div>
+        <div class="flex align-center mb20" style="display: flex; flex-direction: row;">
+            <label class="title2">형식</label>
+            <label class="title2">
+                <input type="radio" v-model="post.textTypeId" value="1" class="data-wrap" :disabled="!isEditing"> 일반
+            </label>
+            <label class="title2">
+                <input type="radio" v-model="post.textTypeId" value="2" class="data-wrap" :disabled="!isEditing"> 대화
+            </label>
+            <label class="title2">
+                <input type="radio" v-model="post.textTypeId" value="3" class="data-wrap" :disabled="!isEditing"> 책 리스닝
+            </label>
+            <select v-if="isEditing" id="" v-model="post.bookId" @change="fetchUnits" class="ml20">
+                <option value="" disabled>교재를 선택하세요</option>
+                <option v-for="book in books" :key="book.book_id" :value="book.book_id">
+                    {{ book.book_nm }}
+                </option>
+            </select>
+            <label v-else for="" class="title2 flex align-center ml20" style="display: flex; width: 20rem;">교재:&nbsp;
+                <p class="title2">{{ post.bookName }}</p>
+            </label>
+                <select v-if="isEditing" name="" id="" v-model="post.unitId" class="ml20">
+                    <option value="" disabled>단원을 선택하세요</option>
+                    <option v-for="unit in units" :key="unit.unitId" :value="unit.unitId">
+                        {{ unit.unitName }}
+                    </option>
+                </select>
+            <label v-else  for="" class="title2 flex align-center ml20"
+                style="display: flex; width: 20rem; text-align: right;">단원:&nbsp;
+                <p class="title2">{{ post.unitName }}</p>
+            </label>
+        </div>
+        <hr>
+        <div class="flex align-center">
+            <label for="" class="title2">스크립트</label>
+            <textarea v-if="isEditing" v-model="post.textCnt" class="data-wrap"></textarea>
+            <p v-else class="data-wrap">{{ post.textCnt }}</p>
+        </div>
+    </div>
+    <div class="flex justify-between mt50">
+        <button type="button" title="목록으로" class="new-btn" @click="goToPage('TextList')">
+            목록
+        </button>
+        <div class="flex">
+            <button type="button" title="수정" class="new-btn mr10" @click="toggleEdit">
+                {{ isEditing ? '저장' : '수정' }}
+            </button>
+            <button type="button" title="삭제" class="new-btn" @click="deletePost">
+                삭제
+            </button>
+        </div>
+    </div>
+</template>
+
+<script>
+import axios from "axios";
+import SvgIcon from '@jamescoyle/vue-icon';
+import { mdiMagnify } from '@mdi/js';
+
+export default {
+    data() {
+        return {
+            mdiMagnify: mdiMagnify,
+            post: {
+                textId: "",
+                textTtl: "",
+                textCnt: "",
+                textUrl: "",
+                textTypeId: "",
+                bookId: "",
+                unitId: "",
+                bookName: "",  
+                unitName: ""
+            },
+            books: [],  
+            units: [], 
+            isEditing: false
+        };
+    },
+    computed: {
+        textId() {
+            return this.$route.query.textId;
+        },
+        selectedBookName() {
+            const book = this.books.find(book => book.book_id === this.post.bookId);
+            return book ? book.book_nm : '';
+        },
+        selectedUnitName() {
+            const unit = this.units.find(unit => unit.unit_id === this.post.unitId);
+            return unit ? unit.unit_nm : '';
+        }
+    },
+    methods: {
+        goToPage(page) {
+            this.$router.push({ name: page });
+        },
+        fetchPostDetail() {
+            const textId = this.$route.query.textId;
+            axios.post('/text/selectOneText.json', { textId }, {
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                }
+            })
+                .then(response => {
+                    if (response.data && response.data[0]) {
+                        this.post.textId = response.data[0].text_id;
+                        this.post.textTtl = response.data[0].text_ttl;
+                        this.post.textCnt = response.data[0].text_cnt;
+                        this.post.textUrl = response.data[0].text_url;
+                        this.post.textTypeId = response.data[0].text_type_id;
+                        this.post.bookId = response.data[0].book_id;
+                        this.post.unitId = response.data[0].unit_id;
+                        this.post.bookName = response.data[0].book_name;
+                        this.post.unitName = response.data[0].unit_name;
+                    } else {
+                        this.error = "Failed to fetch post details.";
+                    }
+                })
+                .catch(error => {
+                    console.error("Error fetching post detail:", error);
+                    this.error = "Failed to fetch post details.";
+                });
+        },
+        dataInsert() {
+            this.newPost = {
+                textId: this.post.textId,
+                textTtl: this.post.textTtl,
+                textCnt: this.post.textCnt,
+                textUrl: this.post.textUrl,
+                textTypeId: this.post.textTypeId,
+                bookId: this.post.bookId,
+                unitId: this.post.unitId
+            };
+            console.log(this.newPost)
+
+            axios.post("/text/textUpdate.json", this.newPost, {
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                }
+            })
+                .then(response => {
+                    alert(response.data.message);
+                    this.fetchPostDetail();
+                })
+                .catch(error => {
+                    console.log("dataInsert - error:", error.response.data);
+                    alert("게시글 등록에 오류가 발생했습니다.");
+                });
+        },
+        toggleEdit() {
+            if (this.isEditing) {
+                this.dataInsert();
+                this.isEditing = false;
+            } else {
+                this.isEditing = true;
+                this.fetchBooks();
+            }
+        },
+        deletePost() {
+            axios.post("/text/textDelete.json", { textId: this.textId }, {
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                }
+            })
+                .then(response => {
+                    alert(response.data.message);
+                    this.goToPage('TextList');
+                })
+                .catch(error => {
+                    console.error("Error deleting post:", error);
+                    alert("게시글 삭제에 오류가 발생했습니다.");
+                });
+        },
+        fetchBooks() {
+            axios.post("/book/findAll.json", {}, {
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                }
+            })
+                .then(response => {
+                    this.books = response.data;  
+                })
+                .catch(error => {
+                    console.error("fetchBooks - error:", error);
+                    alert("교재 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
+        fetchUnits() {
+            if (!this.post.bookId) return;  
+
+            axios.post("/unit/unitList.json", { bookId: this.post.bookId }, {
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                }
+            })
+                .then(response => {
+                    this.units = response.data;  
+                })
+                .catch(error => {
+                    console.error("fetchUnits - error:", error);
+                    alert("단원 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        }
+    },
+    mounted() {
+        this.fetchPostDetail();
+    },
+    components: {
+        SvgIcon
+    }
+};
+</script>
+
+<style scoped> 
+</style>
client/views/pages/teacher/TextInsert.vue
--- client/views/pages/teacher/TextInsert.vue
+++ client/views/pages/teacher/TextInsert.vue
@@ -1,63 +1,221 @@
+
 <template>
     <div class="title-box flex justify-between mb40">
         <p class="title">지문 등록</p>
     </div>
-        <div class="board-wrap">
-            <div class="flex align-center mb20">
-                <label for="" class="title2">제목</label>
-                <input type="text" class="data-wrap">
-            </div>
-            <div class="flex align-center">
-                <label for="" class="title2">URL</label>
-                <input type="text" class="data-wrap">
-            </div>
-            <hr>
-            <div class="flex align-center">
-                <label for="" class="title2">스크립트</label>
-                <textarea name="" id="" class="data-wrap"></textarea>
-            </div>
+    <div class="board-wrap">
+        <div class="flex align-center mb20">
+            <label for="" class="title2">제목</label>
+            <input type="text" class="data-wrap" v-model="newData.textTtl">
         </div>
-        <div class="flex justify-between mt50">
-                <button type="button" title="글쓰기" class="new-btn"  @click="goToPage('TextList')">
-                    목록
-                </button>
-               <div class="flex">
-                    <button type="button" title="글쓰기" class="new-btn mr10"  >
-                        수정
-                    </button>
-                    <button type="button" title="글쓰기" class="new-btn"  >
-                        삭제
-                    </button>
-               </div>
+        <div class="flex align-center mb20">
+            <label for="" class="title2">URL</label>
+            <input type="text" class="data-wrap" v-model="newData.textUrl">
         </div>
+        <div class="flex align-center">
+            <label class="title2">형식</label>
+            <label class="title2">
+                <input type="radio" v-model="newData.textTypeId" value="1" class="data-wrap"> 일반
+            </label>
+            <label class="title2">
+                <input type="radio" v-model="newData.textTypeId" value="2" class="data-wrap"> 대화
+            </label>
+            <label class="title2">
+                <input type="radio" v-model="newData.textTypeId" value="3" class="data-wrap"> 책 리스닝
+            </label>
+            <select name="" id="" v-model="newData.bookId" @change="fetchUnits" class="ml20">
+                <option value="" disabled>교재를 선택하세요</option>
+                <option v-for="book in books" :key="book.book_id" :value="book.book_id">
+                    {{ book.book_nm }}
+                </option>
+            </select>
+            <select name="" id="" v-model="newData.unitId" class="ml20">
+                <option value="" disabled>단원을 선택하세요</option>
+                <option v-for="unit in units" :key="unit.unitId" :value="unit.unitId">
+                    {{ unit.unitName }}
+                </option>
+            </select>
+        </div>
+        <hr>
+        <div class="flex align-center">
+            <label for="" class="title2">스크립트</label>
+            <textarea name="" id="" class="data-wrap" v-model="newData.textCnt"></textarea>
+        </div>
+    </div>
+    <div class="flex justify-between mt50">
+        <button type="button" title="글쓰기" class="new-btn" @click="goToPage('TextList')">
+            목록
+        </button>
+        <div class="flex">
+            <button type="button" title="글쓰기" class="new-btn mr10" @click="handleButtonAction">
+                작성
+            </button>
+        </div>
+    </div>
 </template>
 
 <script>
+import axios from "axios";
 import SvgIcon from '@jamescoyle/vue-icon';
-import { mdiMagnify} from '@mdi/js';
+import { mdiMagnify } from '@mdi/js';
 
 
 export default {
-    data () {
+    computed: {
+        textId() {
+            return this.$route.query.textId;
+        }
+    },
+    data() {
         return {
             mdiMagnify: mdiMagnify,
+            post: null,
+            newData: {
+                textId: "",
+                textTtl: "",
+                textCnt: "",
+                textUrl: "",
+                textTypeId: "",
+                //fileMngId:"",
+                // userId:"".
+                bookId: "",
+                unitId: ""
+            },
+            books: [],  
+            units: [],  
         }
     },
     methods: {
         goToPage(page) {
-         this.$router.push({ name: page });
-      },
+            this.$router.push({ name: page });
+        }, fetchPostDetail() {
+            const textId = this.$route.query.textId
+            axios({
+                url: `/text/selectOneText.json`,
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: { "textId": textId }
+            })
+                .then(response => {
+                    if (response.data && response.data[0]) {
+                        this.post = response.data[0];
+                        this.editTitle = this.post.title;
+                        this.editContent = this.post.content;
+                        console.log(this.post);
+                    } else {
+                        this.error = "Failed to fetch post details.";
+                    }
+                })
+                .catch(error => {
+                    console.error("Error fetching post detail:", error);
+                    this.error = "Failed to fetch post details.";
+                });
+        }, dataInsert() {
+            const vm = this;
+            axios({
+                url: "/text/insertText.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: vm.newData,
+            })
+                .then(response => {
+                    alert(response.data.message);
+                    this.goToPage('TextList')
+                })
+                .catch(error => {
+                    console.log("dataInsert - error : ", error.response.data);
+                    alert("게시글 등록에 오류가 발생했습니다.");
+                });
+        },
+        handleButtonAction() {
+            if (!this.newData.textTtl) {
+                alert("제목을 입력해 주세요.");
+                return;
+            }
+
+            if (!this.newData.textUrl) {
+                alert("url을 입력해 주세요.");
+                return;
+            }
+
+            if (!this.newData.textTypeId) {
+                alert("지문 형식을 입력해 주세요.");
+                return;
+            }
+
+            if (!this.newData.bookId) {
+                alert("교재를 선택해 주세요.");
+                return;
+            }
+
+            if (!this.newData.unitId) {
+                alert("단원을 선택해 주세요.");
+                return;
+            }
+
+            if (!this.newData.textCnt) {
+                alert("내용을 입력해 주세요.");
+                return;
+            }
+
+            this.dataInsert();
+        },
+        fetchBooks() {
+            axios({
+                url: "/book/findAll.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+            })
+                .then(response => {
+                    console.log(response.data)
+                    this.books = response.data;  
+                })
+                .catch(error => {
+                    console.error("fetchBooks - error: ", error);
+                    alert("교재 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
+        fetchUnits() {
+            if (!this.newData.bookId) return;  
+
+            axios({
+                url: "/unit/unitList.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    "bookId": this.newData.bookId
+                },
+            })
+                .then(response => {
+                    console.log(response.data)
+                    this.units = response.data;  
+                })
+                .catch(error => {
+                    console.error("fetchUnits - error: ", error);
+                    alert("단원 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
     },
     watch: {
 
     },
     computed: {
-       
+
     },
-    components:{
+    components: {
         SvgIcon
     },
     mounted() {
+        this.fetchPostDetail();
+        this.fetchBooks();  
         console.log('Main2 mounted');
     }
 }
client/views/pages/teacher/TextList.vue
--- client/views/pages/teacher/TextList.vue
+++ client/views/pages/teacher/TextList.vue
@@ -1,69 +1,109 @@
 <template>
-    <div class="title-box flex justify-between mb40">
+    <div class="title-box flex justify-between mb40 myplan">
         <p class="title">지문</p>
-        <select name="" id="">
-            <option value="">1단원</option>
+        <select name="" id="" v-model="selectedBook" @change="fetchUnits">
+            <option value="" disabled>교재를 선택하세요</option>
+            <option v-for="book in books" :key="book.book_id" :value="book.book_id">
+                {{ book.book_nm }}
+            </option>
         </select>
     </div>
+    <label for="" class="title2">단원</label>
+    <div class="unit-pagination flex mt10 mb20" style="gap: 10px;">
+        <button 
+            v-for="(unit, index) in units" 
+            :key="index" 
+            :class="{ 'selected-btn': selectedUnit === unit.unitId }"
+            @click="selectUnit(unit.unitId)">
+            {{ unit.unitName }}
+        </button>
+    </div>
     <div class="search-wrap flex justify-end mb20">
-            <select name="" id="" class="mr10 data-wrap">
-                <option value="">전체</option>
-            </select>
-                <input  type="text" placeholder="검색하세요.">
-                <button type="button" title="위원회 검색">
-                    <img src="../../../resources/img/look_t.png" alt="">
-                </button>
-        </div>
-        <div class="table-wrap">
-            <table>
-                <thead>
+        <select name="" id="" class="mr10 data-wrap" style="width: 15rem;" v-model="option">
+            <option value="" disabled>검색유형</option>
+            <option v-for="g in optionList" :key="g.value" :value="g.value">{{ g.text }}</option>
+        </select>
+        <input type="text" placeholder="검색하세요." v-model="keyword">
+        <button type="button" title="위원회 검색" @click="search">
+            <img src="../../../resources/img/look_t.png" alt="">
+        </button>
+    </div>
+    <div class="table-wrap">
+        <table>
+            <thead>
+                <tr>
                     <td>No.</td>
                     <td>제목</td>
                     <td>내용</td>
                     <td>작성자</td>
                     <td>등록일</td>
-                </thead>
-                <tbody>
-                    <tr @click="goToPage('TextInsert')">
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                    </tr>
-                </tbody>
-            </table>
-            <article class="table-pagination flex justify-center align-center mb20 mt30" style="gap: 10px;">
-                    <button><img src="../../../resources/img/btn27_90t_normal.png" alt=""></button>
-                    <button class="selected-btn">1</button>
-                    <button>2</button>
-                    <button>3</button>
-                    <button><img src="../../../resources/img/btn28_90t_normal.png" alt=""></button>
-                </article>
-                <div class="flex justify-end ">
-                <button type="button" title="등록" class="new-btn" @click="goToPage('TextInsert')">
-                    등록
-                </button>
+                </tr>
+            </thead>
+            <tbody>
+                <tr v-for="(post, index) in posts" :key="index" class="post"
+                    @click="goToPage('TextDetail', post.textId)">
+                    <td>{{ index + 1 + (currentPage - 1) * pageSize }}</td>
+                    <td>{{ post.textTtl.slice(0, 20) }}{{ post.textTtl.length > 20 ? '...' : '' }}</td>
+                    <td>{{ post.textCnt.slice(0, 20) }}{{ post.textCnt.length > 20 ? '...' : '' }}</td>
+                    <td>{{ post.userId }}</td>
+                    <td>{{ post.regDt }}</td>
+                </tr>
+            </tbody>
+        </table>
+        <article class="table-pagination flex justify-center align-center mb20 mt30" style="gap: 10px;">
+            <button @click="changePage(currentPage - 1)" :disabled="currentPage === 1">
+                <img src="../../../resources/img/btn27_90t_normal.png" alt="Previous">
+            </button>
+            <button v-for="page in totalPages" :key="page" @click="changePage(page)"
+                :class="{ 'selected-btn': currentPage === page }">
+                {{ page }}
+            </button>
+            <button @click="changePage(currentPage + 1)" :disabled="currentPage === totalPages">
+                <img src="../../../resources/img/btn28_90t_normal.png" alt="Next">
+            </button>
+        </article>
+        <div class="flex justify-end">
+            <button type="button" title="등록" class="new-btn" @click="goToPage('TextInsert')">
+                등록
+            </button>
         </div>
-        </div>
+    </div>
 </template>
 
 <script>
+import axios from "axios";
 import SvgIcon from '@jamescoyle/vue-icon';
-import { mdiMagnify} from '@mdi/js';
-
+import { mdiMagnify } from '@mdi/js';
 
 export default {
-    data () {
+    data() {
         return {
             mdiMagnify: mdiMagnify,
-        }
+            posts: [],
+            currentPage: 1,
+            pageSize: 10,
+            totalPosts: 0,
+            option: "",
+            keyword: "",
+            books: [], 
+            selectedBook: "", 
+            optionList: [
+                { text: "번호", value: "textId" },
+                { text: "제목", value: "textTtl" },
+                { text: "내용", value: "textCnt" },
+                { text: "작성자", value: "userId" },
+                { text: "등록일", value: "regDt" },
+            ],
+            units: [], 
+            selectedUnit: null, 
+            searching: false,
+        };
     },
     methods: {
-        goToPage(page) {
-         this.$router.push({ name: page });
-      },
-      showConfirm(type) {
+        goToPage(page, textId) {
+            this.$router.push({ name: page, query: { textId } });
+        },
+        showConfirm(type) {
             let message = '';
             if (type === 'cancel') {
                 message = '삭제하시겠습니까?';
@@ -77,19 +117,110 @@
                 this.goBack();
             }
         },
+        selectUnit(unitId) {
+            this.selectedUnit = unitId;
+            this.fetchData();  
+        },
+        search() {
+            if (!this.option) {
+                alert("검색유형을 선택해 주세요")
+            } else {
+                this.currentPage = 1; 
+                this.searching = true;
+                this.fetchData();  
+            }
+        },
+        fetchData() {
+            const idx = (this.currentPage - 1) * this.pageSize;
+            axios({
+                url: "/text/textSearch.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    "option": this.option,
+                    "keyword": this.keyword,
+                    "pageSize": this.pageSize,
+                    "startIndex": idx,
+                    "unitId": this.selectedUnit  
+                },
+            })
+                .then(response => {
+                    this.posts = response.data.list;
+                    if (!this.searching || this.keyword === "") {
+                        this.totalPosts = response.data.totalText;
+                    } else if (this.searching) {
+                        this.totalPosts = response.data.resultCount;
+                    }
+                    this.posts.forEach(post => {
+                        let regDt = post.regDt;
+                        let date = new Date(regDt);
+                        post.regDt = date.toISOString().split('T')[0];
+                    });
+                })
+                .catch(error => {
+                    console.error("fetchData - error: ", error);
+                    alert("검색 중 오류가 발생했습니다.");
+                });
+        },
+        changePage(page) {
+            if (page > 0 && page <= this.totalPages) {
+                this.currentPage = page;
+                this.fetchData();
+            }
+        },
+        fetchBooks() {
+            axios({
+                url: "/book/findAll.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+            })
+                .then(response => {
+                    console.log(response.data)
+                    this.books = response.data;  
+                })
+                .catch(error => {
+                    console.error("fetchBooks - error: ", error);
+                    alert("교재 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
+        fetchUnits() {
+            if (!this.selectedBook) return; 
 
-    },
-    watch: {
-
+            axios({
+                url: "/unit/unitList.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: {
+                    "bookId": this.selectedBook
+                },
+            })
+                .then(response => {
+                    console.log(response.data)
+                    this.units = response.data; 
+                })
+                .catch(error => {
+                    console.error("fetchUnits - error: ", error);
+                    alert("단원 목록을 불러오는 중 오류가 발생했습니다.");
+                });
+        },
     },
     computed: {
-       
+        totalPages() {
+            return Math.ceil(this.totalPosts / this.pageSize);
+        }
     },
-    components:{
+    components: {
         SvgIcon
     },
     mounted() {
-        console.log('Main2 mounted');
+        this.fetchBooks(); // Fetch books when the component is mounted
+        this.fetchData(); // Fetch data for the default view
     }
-}
-</script>
(No newline at end of file)
+};
+</script>
 
client/views/pages/teacher/VocaDetail.vue (added)
+++ client/views/pages/teacher/VocaDetail.vue
@@ -0,0 +1,102 @@
+<template>
+    <div class="title-box flex justify-between mb40">
+        <p class="title">단어장 등록</p>
+    </div>
+    <div class="board-wrap">
+        <div class="flex align-center mb20">
+            <label for="" class="title2">지문</label>
+            <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                <option value="bbsTtl">제목</option>
+                <option value="bbsCnt">내용</option>
+                <option value="userNm">작성자</option>
+                <option value="bbsCls">카테고리</option>
+            </select>
+        </div>
+        <div class="flex align-center">
+            <label for="" class="title2">단어 목록</label>
+            <div class="flex-column" style="gap: 10px;">
+                <div class="flex align-center" style="gap: 10px;">
+                    <input type="text" class="data-wrap" placeholder="영어">
+                    <input type="text" class="data-wrap" placeholder="한글">
+                    <button type="button" @click="addThesis">
+                        <img src="../../../resources/img/btn39_120t_normal.png" alt="">
+
+                    </button>
+                </div>
+                <div class="flex align-center " style="gap: 10px;" v-for="(thesis, index) in thesised" :key="thesis.id">
+                    <input type="text" class="data-wrap" placeholder="영어">
+                    <input type="text" class="data-wrap" placeholder="한글">
+                    <button type="button" @click="removeThesis(thesis.id)">
+                        <img src="../../../resources/img/btn38_120t_normal.png" alt="">
+                    </button>
+
+                </div>
+            </div>
+
+        </div>
+    </div>
+    <div class="flex justify-between mt50">
+        <button type="button" title="글쓰기" class="new-btn" @click="goToPage('TextList')">
+            목록
+        </button>
+        <div class="flex">
+            <button type="button" title="글쓰기" class="new-btn mr10">
+                취소
+            </button>
+            <button type="button" title="글쓰기" class="new-btn">
+                수정
+            </button>
+        </div>
+    </div>
+</template>
+
+<script>
+import SvgIcon from '@jamescoyle/vue-icon';
+import { mdiMagnify, mdiPlusCircleOutline, mdiWindowClose } from '@mdi/js';
+
+
+export default {
+    data() {
+        return {
+            thesised: [],
+            mdiPlusCircleOutline: mdiPlusCircleOutline,
+            mdiMagnify: mdiMagnify,
+            mdiWindowClose: mdiWindowClose,
+        }
+    },
+    methods: {
+        // 논문실적 버튼 추가
+        addThesis() {
+            // 고유 ID로 현재 시간의 타임스탬프를 사용
+            const uniqueId = Date.now();
+            this.thesised.push({
+                id: uniqueId, // 고유 ID 추가
+
+            });
+        },
+        removeThesis(thesisId) {
+            // ID를 기준으로 교육 정보 객체를 찾아서 삭제
+            const index = this.thesised.findIndex(thesis => thesis.id === thesisId);
+            if (index !== -1) {
+                this.thesised.splice(index, 1);
+            }
+        },
+        // 
+        goToPage(page) {
+            this.$router.push({ name: page });
+        },
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+        SvgIcon
+    },
+    mounted() {
+        console.log('Main2 mounted');
+    }
+}
+</script>(No newline at end of file)
 
client/views/pages/teacher/VocaInsert.vue (added)
+++ client/views/pages/teacher/VocaInsert.vue
@@ -0,0 +1,102 @@
+<template>
+    <div class="title-box flex justify-between mb40">
+        <p class="title">단어장 등록</p>
+    </div>
+    <div class="board-wrap">
+        <div class="flex align-center mb20">
+            <label for="" class="title2">지문</label>
+            <select v-model="selectedSearchOption" class="mr10 data-wrap">
+                <option value="bbsTtl">제목</option>
+                <option value="bbsCnt">내용</option>
+                <option value="userNm">작성자</option>
+                <option value="bbsCls">카테고리</option>
+            </select>
+        </div>
+        <div class="flex align-center">
+            <label for="" class="title2">단어 목록</label>
+            <div class="flex-column" style="gap: 10px;">
+                <div class="flex align-center" style="gap: 10px;">
+                    <input type="text" class="data-wrap" placeholder="영어">
+                    <input type="text" class="data-wrap" placeholder="한글">
+                    <button type="button" @click="addThesis">
+                        <img src="../../../resources/img/btn39_120t_normal.png" alt="">
+
+                    </button>
+                </div>
+                <div class="flex align-center " style="gap: 10px;" v-for="(thesis, index) in thesised" :key="thesis.id">
+                    <input type="text" class="data-wrap" placeholder="영어">
+                    <input type="text" class="data-wrap" placeholder="한글">
+                    <button type="button" @click="removeThesis(thesis.id)">
+                        <img src="../../../resources/img/btn38_120t_normal.png" alt="">
+                    </button>
+
+                </div>
+            </div>
+
+        </div>
+    </div>
+    <div class="flex justify-between mt50">
+        <button type="button" title="글쓰기" class="new-btn" @click="goToPage('TextList')">
+            목록
+        </button>
+        <div class="flex">
+            <button type="button" title="글쓰기" class="new-btn mr10">
+                취소
+            </button>
+            <button type="button" title="글쓰기" class="new-btn">
+                등록
+            </button>
+        </div>
+    </div>
+</template>
+
+<script>
+import SvgIcon from '@jamescoyle/vue-icon';
+import { mdiMagnify, mdiPlusCircleOutline, mdiWindowClose } from '@mdi/js';
+
+
+export default {
+    data() {
+        return {
+            thesised: [],
+            mdiPlusCircleOutline: mdiPlusCircleOutline,
+            mdiMagnify: mdiMagnify,
+            mdiWindowClose: mdiWindowClose,
+        }
+    },
+    methods: {
+        // 논문실적 버튼 추가
+        addThesis() {
+            // 고유 ID로 현재 시간의 타임스탬프를 사용
+            const uniqueId = Date.now();
+            this.thesised.push({
+                id: uniqueId, // 고유 ID 추가
+
+            });
+        },
+        removeThesis(thesisId) {
+            // ID를 기준으로 교육 정보 객체를 찾아서 삭제
+            const index = this.thesised.findIndex(thesis => thesis.id === thesisId);
+            if (index !== -1) {
+                this.thesised.splice(index, 1);
+            }
+        },
+        // 
+        goToPage(page) {
+            this.$router.push({ name: page });
+        },
+    },
+    watch: {
+
+    },
+    computed: {
+
+    },
+    components: {
+        SvgIcon
+    },
+    mounted() {
+        console.log('Main2 mounted');
+    }
+}
+</script>(No newline at end of file)
client/views/pages/teacher/VocaList.vue
--- client/views/pages/teacher/VocaList.vue
+++ client/views/pages/teacher/VocaList.vue
@@ -2,9 +2,15 @@
     <div class="title-box flex justify-between mb40">
         <p class="title">단어장</p>
         <select name="" id="">
-            <option value="UNIT_000000000000001">1단원</option>
+            <option value="UNIT_000000000000001">A교재</option>
         </select>
     </div>
+    <label for="" class="title2">단원</label>
+        <div class="unit-pagination flex mt10 mb20" style="gap: 10px;">
+            <button class="selected-btn">1</button>
+            <button>2</button>
+            <button>3</button>
+        </div>
     <div class="search-wrap flex justify-between mb20 align-center">
         <div class="title2 gray">?단원 전체 목록</div>
         <div>
@@ -28,7 +34,7 @@
                 <td>등록일</td>
             </thead>
             <tbody>
-                <tr v-for="(wordBook, index) in dataList" :key="wordBook.wdBookId" @click="goToViewPage('noticeDetail')">
+                <tr v-for="(wordBook, index) in dataList" :key="wordBook.wdBookId" @click="goToViewPage('VocaDetail')">
                     <td>{{ createNo(index) }}</td>
                     <td>{{ wordBook.textTtl }}</td>
                     <td>{{ wordBook.wordsPreview }}</td>
@@ -49,7 +55,7 @@
             </button>
         </article>
         <div class="flex justify-end ">
-            <button type="button" title="등록" class="new-btn" @click="goToPage('noticeInsert')">
+            <button type="button" title="등록" class="new-btn" @click="goToPage('VocaInsert')">
                 등록
             </button>
         </div>
client/views/pages/teacher/noticeDetail.vue
--- client/views/pages/teacher/noticeDetail.vue
+++ client/views/pages/teacher/noticeDetail.vue
@@ -1,78 +1,224 @@
 <template>
-    <div class="title-box flex justify-between mb40">
-        <p class="title">공지 등록</p>
+  <div class="title-box flex justify-between mb40">
+    <p class="title">{{ category }}</p>
+  </div>
+  <div class="board-wrap">
+    <div class="flex align-center">
+      <label for="" class="title2">{{ title }}</label>
     </div>
-        <div class="board-wrap">
-            <div class="flex align-center">
-                <label for="" class="title2">제목</label>
-                <input type="text" class="data-wrap">
-            </div>
-            <hr>
-            <textarea name="" id=""></textarea>
-            <hr>
-            <div class="flex align-center">
-                <label for="" class="title2">첨부파일</label>
-                <input type="file" ref="fileInput" @change="handleFileUpload" />
-            </div>
-            <hr>
-            <div class="flex justify-between">
-                <button type="button" class="flex align-center">
-                            <svg-icon type="mdi" :path="mdilChevronLeft" ></svg-icon>
-                   <p> 이전글</p>
-                        </button>
-                <button type="button" class="flex align-center"><p>다음글</p>
-                            <svg-icon type="mdi" :path="mdilChevronRight" ></svg-icon>
-
-                        </button>
-
-
-            </div>
-        </div>
-        <div class="flex justify-between mt50">
-                <button type="button" title="글쓰기" class="new-btn"  @click="goToPage('Board')">
-                    목록
-                </button>
-               <div class="flex">
-                    <button type="button" title="글쓰기" class="new-btn mr10"  >
-                        수정
-                    </button>
-                    <button type="button" title="글쓰기" class="new-btn"  >
-                        삭제
-                    </button>
-               </div>
-        </div>
+    <hr />
+    <textarea readonly name="" id="">{{ content }}</textarea>
+    <hr />
+    <div class="flex align-center">
+      <label for="" class="title2">첨부파일</label>
+      <label for="" class="title2" v-if="file">{{ file.fileNm }}</label>
+    </div>
+    <hr />
+    <div class="flex justify-between">
+      <button type="button" class="flex align-center" @click="prevBoard()">
+        <svg-icon type="mdi" :path="mdilChevronLeft"></svg-icon>
+        <p>이전글</p>
+      </button>
+      <button type="button" class="flex align-center" @click="nextBoard()">
+        <p>다음글</p>
+        <svg-icon type="mdi" :path="mdilChevronRight"></svg-icon>
+      </button>
+    </div>
+  </div>
+  <div class="flex justify-between mt50">
+    <button
+      type="button"
+      title="글쓰기"
+      class="new-btn"
+      @click="goToPage('Board')"
+    >
+      목록
+    </button>
+    <div class="flex">
+      <button
+        type="button"
+        title="글쓰기"
+        class="new-btn mr10"
+        @click="goToPage('noticeInsert')"
+      >
+        수정
+      </button>
+      <button
+        type="button"
+        title="글쓰기"
+        class="new-btn"
+        @click="[deleteBoard(), goToPage('Board')]"
+      >
+        삭제
+      </button>
+    </div>
+  </div>
 </template>
 
 <script>
-import SvgIcon from '@jamescoyle/vue-icon';
-import { mdiMagnify } from '@mdi/js';
-import { mdilChevronRight,mdilChevronLeft } from '@mdi/light-js';
-
+import SvgIcon from "@jamescoyle/vue-icon";
+import { mdiMagnify } from "@mdi/js";
+import { mdilChevronRight, mdilChevronLeft } from "@mdi/light-js";
+import axios from "axios";
 
 export default {
-    data () {
-        return {
-            mdiMagnify: mdiMagnify,
-            mdilChevronRight: mdilChevronRight ,
-            mdilChevronLeft:mdilChevronLeft,
-        }
-    },
-    methods: {
-        goToPage(page) {
-         this.$router.push({ name: page });
-      },
-    },
-    watch: {
+  data() {
+    return {
+      mdiMagnify: mdiMagnify,
+      mdilChevronRight: mdilChevronRight,
+      mdilChevronLeft: mdilChevronLeft,
 
+      dataList: null,
+      sclsId: "",
+      bbsId: "",
+
+      title: "",
+      content: "",
+      category: "",
+      file: "",
+      time: "",
+      user: "",
+    };
+  },
+  methods: {
+    goToPage(page) {
+      this.$router.push({ name: page });
     },
-    computed: {
-       
+
+    findBoard() {
+      const vm = this;
+      vm.bbsId = JSON.parse(sessionStorage.getItem("bbsId"));
+      console.log(vm.bbsId);
+      axios({
+        url: "/board/find.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: {
+          bbsId: vm.bbsId,
+        },
+      })
+        .then(function (res) {
+          console.log("boardData - response : ", res.data);
+          vm.dataList = res.data.result[0].boardClass[0].board[0];
+          vm.title = vm.dataList.bbsTtl;
+          vm.content = vm.dataList.bbsCnt;
+          vm.category = vm.dataList.bbsCls;
+          vm.time = vm.dataList.bbsTm;
+          vm.findFile();
+        })
+        .catch(function (error) {
+          console.log("boardData - error : ", error);
+        });
     },
-    components:{
-        SvgIcon
+
+    findFile() {
+      const vm = this;
+      axios({
+        url: "/file/find.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: {
+          file_mng_id: vm.dataList.fileMngId,
+        },
+      })
+        .then(function (res) {
+          console.log("fileInfo - response : ", res.data.list[0]);
+          vm.file = res.data.list[0];
+          sessionStorage.setItem("file", JSON.stringify(vm.file));
+        })
+        .catch(function (error) {
+          console.log("result - error : ", error);
+        });
     },
-    mounted() {
-        console.log('Main2 mounted');
-    }
-}
-</script>
(No newline at end of file)
+
+    prevBoard() {
+      const vm = this;
+      axios({
+        url: "/board/prevBoard.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: {
+          sclsId: vm.dataList.sclsId,
+          bbsId: vm.dataList.bbsId,
+        },
+      })
+        .then(function (res) {
+          console.log("dataList - response : ", res.data);
+          vm.dataList = res.data[0].boardClass[0].board[0];
+          vm.title = vm.dataList.bbsTtl;
+          vm.content = vm.dataList.bbsCnt;
+          vm.category = vm.dataList.bbsCls;
+          vm.time = vm.dataList.bbsTm;
+          vm.findFile();
+        })
+        .catch(function (error) {
+          console.log("result - error : ", error);
+          alert("첫번째 글 입니다.");
+        });
+    },
+
+    nextBoard() {
+      const vm = this;
+      axios({
+        url: "/board/nextBoard.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: {
+          sclsId: vm.dataList.sclsId,
+          bbsId: vm.dataList.bbsId,
+        },
+      })
+        .then(function (res) {
+          console.log("dataList - response : ", res.data);
+          vm.dataList = res.data[0].boardClass[0].board[0];
+          vm.title = vm.dataList.bbsTtl;
+          vm.content = vm.dataList.bbsCnt;
+          vm.category = vm.dataList.bbsCls;
+          vm.time = vm.dataList.bbsTm;
+          vm.findFile();
+        })
+        .catch(function (error) {
+          console.log("result - error : ", error);
+          alert("가장 최신 글 입니다.");
+        });
+    },
+    deleteBoard() {
+      const vm = this;
+      axios({
+        url: "/board/delete.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: {
+          bbsId: vm.dataList.bbsId,
+        },
+      })
+        .then(function (res) {
+          console.log("delete - response : ", res.data);
+          alert("삭제 되었습니다.");
+        })
+        .catch(function (error) {
+          console.log("rssult - error : ", error);
+        });
+    },
+  },
+  watch: {},
+  computed: {},
+  components: {
+    SvgIcon,
+  },
+  mounted() {
+    console.log("Main2 mounted");
+    this.findBoard();
+  },
+};
+</script>
client/views/pages/teacher/noticeInsert.vue
--- client/views/pages/teacher/noticeInsert.vue
+++ client/views/pages/teacher/noticeInsert.vue
@@ -1,9 +1,9 @@
 <template>
   <div class="title-box flex justify-between mb40">
-    <p class="title">공지 등록</p>
+    <p class="title">{{ titleLabel }}</p>
   </div>
   <div class="board-wrap">
-    <form @submit.prevent="handleSubmit">
+    <form @submit.prevent="handleButtonAction">
       <div class="flex align-center">
         <label for="title" class="title2">제목</label>
         <input type="text" id="title" class="data-wrap" v-model="title" />
@@ -13,13 +13,21 @@
       <hr />
       <div class="flex align-center">
         <label for="file" class="title2">첨부파일</label>
-        <input type="file" ref="fileInput" @change="handleFileUpload" />
+        <label for="file" class="title2"> {{ file.fileNm }}</label>
+        <input
+          type="file"
+          ref="fileInput"
+          @change="handleFileUpload"
+          multiple
+        />
       </div>
       <div class="flex justify-between mt50">
         <button title="글쓰기" class="new-btn" @click="goToPage('Board')">
           목록
         </button>
-        <button title="글쓰기" class="new-btn">등록</button>
+        <button title="글쓰기" class="new-btn">
+          {{ buttonLabel }}
+        </button>
       </div>
     </form>
   </div>
@@ -38,8 +46,16 @@
       title: "",
       content: "",
       category: "Notice",
+      file: "",
       sclsId: "",
-      selectedFile: null,
+      bbsId: "",
+      fileMngId: null,
+
+      selectedFiles: [],
+      dataList: "",
+
+      // 데이터 편집 상태 (true = 수정, false = 등록)
+      isEditMode: false,
     };
   },
   methods: {
@@ -48,26 +64,113 @@
     },
 
     handleFileUpload(e) {
-      this.selectedFile = e.target.files[0];
-      console.log(this.selectedFile);
+      this.selectedFiles = e.target.files;
     },
 
     created() {
       const vm = this;
       const sclsId = JSON.parse(sessionStorage.getItem("sclsId"));
+      const bbsId = JSON.parse(sessionStorage.getItem("bbsId"));
+      const file = JSON.parse(sessionStorage.getItem("file"));
 
       if (sclsId) {
         vm.sclsId = sclsId;
+        if (bbsId && file) {
+          vm.bbsId = bbsId;
+          vm.file = file;
+          vm.fileMngId = file.fileMngId;
+          vm.isEditMode = true;
+          vm.findBoard();
+        } else {
+          vm.isEditMode = false;
+        }
       }
-      console.log(sclsId);
     },
 
-    async handleSubmit() {
+    // 게시글 상세 조회
+    findBoard() {
+      const vm = this;
+      vm.bbsId = JSON.parse(sessionStorage.getItem("bbsId"));
+      console.log(vm.bbsId);
+      axios({
+        url: "/board/find.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: {
+          bbsId: vm.bbsId,
+        },
+      })
+        .then(function (res) {
+          console.log("boardData - response : ", res.data);
+          vm.dataList = res.data.result[0].boardClass[0].board[0];
+          vm.title = vm.dataList.bbsTtl;
+          vm.content = vm.dataList.bbsCnt;
+          vm.category = vm.dataList.bbsCls;
+          vm.time = vm.dataList.bbsTm;
+        })
+        .catch(function (error) {
+          console.log("boardData - error : ", error);
+        });
+    },
+
+    // 게시글 등록
+    async dataInsert() {
       const vm = this;
       try {
         // 파일 업로드
+        if (this.selectedFiles && this.selectedFiles.length > 0) {
+          // 파일 업로드
+          const formData = new FormData();
+          for (let i = 0; i < this.selectedFiles.length; i++) {
+            formData.append("files", this.selectedFiles[i]);
+          }
+
+          const fileUploadResponse = await axios.post(
+            "/file/upload.json",
+            formData,
+            {
+              headers: {
+                "Content-Type": "multipart/form-data",
+              },
+            }
+          );
+
+          // 업로드 후 파일 매니지 아이디 호출
+          fileMngId = fileUploadResponse.data.fileMngId;
+        }
+
+        // 게시물 등록
+        const newData = {
+          bbsTtl: vm.title,
+          bbsCls: vm.category,
+          bbsCnt: vm.content,
+          fileMngId: vm.fileMngId,
+          sclsId: vm.sclsId,
+        };
+
+        await axios.post("/board/insert.json", newData);
+
+        alert("게시물 등록 성공");
+        vm.$router.push({ name: "Board" });
+      } catch (error) {
+        console.error("게시물 등록 실패 ", error);
+        alert("게시물 등록 실패");
+      }
+    },
+
+    // 게시글 수정
+    async boardUpdate() {
+      const vm = this;
+
+      // 파일 업로드
+      if (this.selectedFiles && this.selectedFiles.length > 0) {
+        // 파일 업로드
         const formData = new FormData();
-        formData.append("files", this.selectedFile);
+        for (let i = 0; i < this.selectedFiles.length; i++) {
+          formData.append("files", this.selectedFiles[i]);
+        }
 
         const fileUploadResponse = await axios.post(
           "/file/upload.json",
@@ -80,28 +183,50 @@
         );
 
         // 업로드 후 파일 매니지 아이디 호출
-        const fileMngId = fileUploadResponse.data.fileMngId;
+        fileMngId = fileUploadResponse.data.fileMngId;
+      }
 
-        // 게시물 등록
-        const newData = {
-          bbsTtl: vm.title,
-          bbsCls: vm.category,
-          bbsCnt: vm.content,
-          fileMngId: fileMngId,
-          sclsId: vm.sclsId,
-        };
+      const newData = {
+        bbsTtl: vm.title,
+        bbsCls: vm.category,
+        bbsCnt: vm.content,
+        fileMngId: vm.fileMngId,
+        bbsId: vm.bbsId,
+      };
+      await axios({
+        url: "/board/update.json",
+        method: "post",
+        headers: {
+          "Content-Type": "application/json; charset=UTF-8",
+        },
+        data: newData,
+      })
+        .then(function (res) {
+          console.log("dataUpdate - response : ", res.data);
+        })
+        .catch(function (error) {
+          console.log("result - error : ", error);
+        });
+    },
 
-        await axios.post("/board/insert.json", newData);
-
-        alert("게시물 등록 성공");
-      } catch (error) {
-        console.error("게시물 등록 실패 ", error);
-        alert("게시물 등록 실패");
+    handleButtonAction() {
+      if (this.isEditMode) {
+        this.boardUpdate();
+      } else {
+        this.dataInsert();
       }
     },
   },
   watch: {},
-  computed: {},
+  computed: {
+    titleLabel() {
+      return this.isEditMode ? "수정" : "공지 등록";
+    },
+
+    buttonLabel() {
+      return this.isEditMode ? "수정" : "등록";
+    },
+  },
   components: {
     SvgIcon,
   },
client/views/pages/teacher/textbook.vue
--- client/views/pages/teacher/textbook.vue
+++ client/views/pages/teacher/textbook.vue
@@ -1,101 +1,166 @@
 <template>
     <div class="title-box flex justify-between mb40">
-        <p class="title">교재 관리</p>
-        <!-- <select name="" id="">
+        <p class="title">교재</p>
+        <select name="" id="">
             <option value="">A반</option>
-        </select> -->
-        </div>
-        <div class="content-t">
-            <div  class=" flex " style="gap: 50px;">
-                <div class="textbook">
-                    <div class="box " style="gap: 10px;" @click="goToPage('TextBookDetail')">
-                    </div>
-                    <div class="text ">
-                        <p class="title1" style="color: #fff;">A 교재</p>
-                        <div class="btnGroup mt15 flex align-center justify-end" style="gap: 10px;">
-                            <button>수정</button><p>&#124;</p>
-                            <button @click="showConfirm('delete')">삭제</button>
-                        </div>
-                    </div>
+        </select>
+    </div>
+    <div class="content-t">
+        <div class="flex" style="gap: 2.5%;" :style="{flexWrap: 'wrap'}">
+            <div class="textbook" v-for="textbookItem in textbookList" :key="textbookItem.book_id" style="width: 22.5%; margin-bottom: 30px;">
+                <div class="box" style="gap: 10px;" @click="goToPage('TextBookDetail', textbookItem.book_id)">
                 </div>
-                
-                <div class="textbook-add">
-                        <button  @click="buttonSearch"><img src="../../../resources/img/btn32_98t_normal.png" alt=""></button>
-        
+                <div class="text">
+                    <p class="title1" style="color: #fff;">{{ textbookItem.book_nm }}</p>
+                    <div class="btnGroup mt15 flex align-center justify-end" style="gap: 10px;">
+                        <button @click="editBook(textbookItem)">수정</button>
+                        <p>&#124;</p>
+                        <button @click="deleteBook(textbookItem.book_id)">삭제</button>
+                    </div>
                 </div>
             </div>
-        </div>
-        <div v-show="searchOpen" class="popup-wrap">
-                <div class="popup-box ">
+
+            <div class="textbook-add" style="width: 22.5%; margin-bottom: 30px;">
+                <button @click="buttonSearch">
+                    <img src="../../../resources/img/btn32_98t_normal.png" alt="">
+                </button>
+            </div>
+
+            <div v-show="searchOpen" class="popup-wrap">
+                <div class="popup-box">
                     <div class="flex justify-between mb30">
                         <p class="popup-title">교재 이름</p>
                         <button type="button" class="popup-close-btn" @click="closeBtn">
                             <svg-icon type="mdi" :path="mdiWindowClose" class="close-btn"></svg-icon>
-
                         </button>
                     </div>
                     <div class="search-wrap mb30">
-                        <input type="text" class="data-wrap" placeholder="">
-                        <!-- <button type="button" title="교재 검색">
+                        <input type="text" class="data-wrap" v-model="newBookName" placeholder="교재 이름을 입력하세요">
+                        <button type="button" title="교재 검색" @click="insertBook">
                             <img src="../../../resources/img/look_t.png" alt="">
-                        </button> -->
+                        </button>
                     </div>
-                    <div class="flex justify-center ">
-                        <button type="button" title="글쓰기" class="new-btn mr10">
+                    <div class="flex justify-center">
+                        <button type="button" title="취소" class="new-btn mr10" @click="closeBtn">
                             취소
                         </button>
-                        <button type="button" title="글쓰기" class="new-btn">
-                            생성
+                        <button type="button" title="생성" class="new-btn" @click="editMode ? updateBook() : insertBook()">
+                            {{ editMode ? '수정' : '생성' }}
                         </button>
                     </div>
                 </div>
             </div>
+        </div>
+    </div>
 </template>
+
+
 
 <script>
 import SvgIcon from '@jamescoyle/vue-icon';
-import { mdiMagnify, mdiWindowClose } from '@mdi/js';
+import { mdiWindowClose } from '@mdi/js';
+import axios from 'axios';
 
 export default {
-    data () {
+    data() {
         return {
             mdiWindowClose: mdiWindowClose,
             showModal: false,
             searchOpen: false,
+            textbookList: [],
+            newBookName: '',
+            editMode: false,
+            editBookId: null,
         }
     },
     methods: {
-        goToPage(page) {
-            this.$router.push({ name: page });
-        },
-        closeModal() {
-            this.showModal = false;
+        goToPage(page, book_id) {
+            this.$router.push({ name: page, query: { book_id }});
         },
         buttonSearch() {
             this.searchOpen = true;
         },
         closeBtn() {
             this.searchOpen = false;
-
+            this.editMode = false;
+            this.editBookId = null;
+            this.newBookName = '';
         },
-        showConfirm(type) {
-            let message = '';
-            if (type === 'delete') {
-                message = '삭제하시겠습니까?';
-            } else if (type === 'reset') {
-                message = '초기화하시겠습니까?';
-            } else if (type === 'save') {
-                message = '등록하시겠습니까?';
-            }
-
-            if (confirm(message)) {
-                this.goBack();
-            }
+        editBook(book) {
+            this.newBookName = book.book_nm;
+            this.editMode = true;
+            this.editBookId = book.book_id;
+            this.searchOpen = true;
         },
-
-    },
-    watch: {
-
+        bookList() {
+            axios({
+                url: "/book/findAll.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                }
+            })
+            .then(response => {
+                this.textbookList = response.data;
+                })
+            .catch(error => {
+                console.error('Error fetching books:', error);
+            });
+        },
+        insertBook() {
+            const newBook = {
+                book_nm: this.newBookName
+            };
+            axios.post('/book/insert.json', newBook)
+                .then(response => {
+                    this.bookList();
+                    this.closeBtn();
+                })
+                .catch(error => {
+                    console.error('Error creating book:', error);
+                });
+        },
+        updateBook() {
+            const updatedBook = {
+                book_id: this.editBookId,
+                book_nm: this.newBookName
+            };
+            axios({
+                url: "/book/update.json",
+                method: "post",
+                headers: {
+                    "Content-Type": "application/json; charset=UTF-8",
+                },
+                data: updatedBook,
+            })
+            .then(response => {
+                this.bookList();
+                this.closeBtn();
+            })
+            .catch(error => {
+                 console.error('Error updating book:', error);
+            });
+        },
+        deleteBook(book_id) {
+            if (confirm('삭제하시겠습니까?')) {
+                axios({
+                    url: "/book/delete.json",
+                    method: "post",
+                    headers: {
+                        "Content-Type": "application/json; charset=UTF-8",
+                    },
+                    data: {
+                        book_id: book_id
+                    },
+                })
+                .then(response => {
+                    this.bookList();
+                })
+                .catch(error => {
+                    console.error('Error deleting book:', error);
+                });
+            }
+        }
     },
     computed: {
 
@@ -105,9 +170,22 @@
     },
     mounted() {
         console.log('Main2 mounted');
+        this.bookList();
     }
 }
 </script>
-<style scoped>
-.textbook{width: 300px;}
+
+<style>
+.content-t {
+    flex-wrap: wrap; 
+    height: 90%;
+    overflow-y: scroll;
+}
+.flex {
+    display: flex;
+    flex-wrap: wrap;
+}
+.textbook, .textbook-add {
+    margin-bottom: 30px;
+}
 </style>
(No newline at end of file)
package-lock.json
--- package-lock.json
+++ package-lock.json
@@ -12,6 +12,7 @@
         "@mdi/light-js": "^0.2.63",
         "axios": "^1.7.3",
         "babel-loader": "8.2.5",
+        "compress.js": "^2.1.2",
         "css-loader": "6.7.1",
         "express": "^4.18.1",
         "express-http-proxy": "^2.0.0",
@@ -25,9 +26,11 @@
         "vue-router": "4.1.5",
         "vue-style-loader": "4.1.3",
         "vue3-sfc-loader": "^0.8.4",
-        "vuex": "^4.1.0",
-        "webpack": "5.74.0",
-        "webpack-cli": "4.10.0"
+        "vuex": "^4.1.0"
+      },
+      "devDependencies": {
+        "webpack": "^5.93.0",
+        "webpack-cli": "^5.1.4"
       }
     },
     "node_modules/@ampproject/remapping": {
@@ -316,6 +319,7 @@
       "version": "0.5.7",
       "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
       "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
+      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=10.0.0"
@@ -425,10 +429,9 @@
       }
     },
     "node_modules/@types/estree": {
-      "version": "0.0.51",
-      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
-      "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==",
-      "license": "MIT"
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
     },
     "node_modules/@types/json-schema": {
       "version": "7.0.15",
@@ -564,180 +567,173 @@
       "license": "MIT"
     },
     "node_modules/@webassemblyjs/ast": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
-      "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
-      "license": "MIT",
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz",
+      "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==",
       "dependencies": {
-        "@webassemblyjs/helper-numbers": "1.11.1",
-        "@webassemblyjs/helper-wasm-bytecode": "1.11.1"
+        "@webassemblyjs/helper-numbers": "1.11.6",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
       }
     },
     "node_modules/@webassemblyjs/floating-point-hex-parser": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
-      "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==",
-      "license": "MIT"
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
+      "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw=="
     },
     "node_modules/@webassemblyjs/helper-api-error": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
-      "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==",
-      "license": "MIT"
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
+      "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q=="
     },
     "node_modules/@webassemblyjs/helper-buffer": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
-      "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==",
-      "license": "MIT"
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz",
+      "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw=="
     },
     "node_modules/@webassemblyjs/helper-numbers": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
-      "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
-      "license": "MIT",
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
+      "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
       "dependencies": {
-        "@webassemblyjs/floating-point-hex-parser": "1.11.1",
-        "@webassemblyjs/helper-api-error": "1.11.1",
+        "@webassemblyjs/floating-point-hex-parser": "1.11.6",
+        "@webassemblyjs/helper-api-error": "1.11.6",
         "@xtuc/long": "4.2.2"
       }
     },
     "node_modules/@webassemblyjs/helper-wasm-bytecode": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
-      "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==",
-      "license": "MIT"
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
+      "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA=="
     },
     "node_modules/@webassemblyjs/helper-wasm-section": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
-      "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
-      "license": "MIT",
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz",
+      "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==",
       "dependencies": {
-        "@webassemblyjs/ast": "1.11.1",
-        "@webassemblyjs/helper-buffer": "1.11.1",
-        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
-        "@webassemblyjs/wasm-gen": "1.11.1"
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-buffer": "1.12.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+        "@webassemblyjs/wasm-gen": "1.12.1"
       }
     },
     "node_modules/@webassemblyjs/ieee754": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
-      "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
-      "license": "MIT",
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
+      "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
       "dependencies": {
         "@xtuc/ieee754": "^1.2.0"
       }
     },
     "node_modules/@webassemblyjs/leb128": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
-      "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
-      "license": "Apache-2.0",
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
+      "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
       "dependencies": {
         "@xtuc/long": "4.2.2"
       }
     },
     "node_modules/@webassemblyjs/utf8": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
-      "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==",
-      "license": "MIT"
+      "version": "1.11.6",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
+      "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA=="
     },
     "node_modules/@webassemblyjs/wasm-edit": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
-      "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
-      "license": "MIT",
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz",
+      "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==",
       "dependencies": {
-        "@webassemblyjs/ast": "1.11.1",
-        "@webassemblyjs/helper-buffer": "1.11.1",
-        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
-        "@webassemblyjs/helper-wasm-section": "1.11.1",
-        "@webassemblyjs/wasm-gen": "1.11.1",
-        "@webassemblyjs/wasm-opt": "1.11.1",
-        "@webassemblyjs/wasm-parser": "1.11.1",
-        "@webassemblyjs/wast-printer": "1.11.1"
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-buffer": "1.12.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+        "@webassemblyjs/helper-wasm-section": "1.12.1",
+        "@webassemblyjs/wasm-gen": "1.12.1",
+        "@webassemblyjs/wasm-opt": "1.12.1",
+        "@webassemblyjs/wasm-parser": "1.12.1",
+        "@webassemblyjs/wast-printer": "1.12.1"
       }
     },
     "node_modules/@webassemblyjs/wasm-gen": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
-      "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
-      "license": "MIT",
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz",
+      "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==",
       "dependencies": {
-        "@webassemblyjs/ast": "1.11.1",
-        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
-        "@webassemblyjs/ieee754": "1.11.1",
-        "@webassemblyjs/leb128": "1.11.1",
-        "@webassemblyjs/utf8": "1.11.1"
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+        "@webassemblyjs/ieee754": "1.11.6",
+        "@webassemblyjs/leb128": "1.11.6",
+        "@webassemblyjs/utf8": "1.11.6"
       }
     },
     "node_modules/@webassemblyjs/wasm-opt": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
-      "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
-      "license": "MIT",
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz",
+      "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==",
       "dependencies": {
-        "@webassemblyjs/ast": "1.11.1",
-        "@webassemblyjs/helper-buffer": "1.11.1",
-        "@webassemblyjs/wasm-gen": "1.11.1",
-        "@webassemblyjs/wasm-parser": "1.11.1"
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-buffer": "1.12.1",
+        "@webassemblyjs/wasm-gen": "1.12.1",
+        "@webassemblyjs/wasm-parser": "1.12.1"
       }
     },
     "node_modules/@webassemblyjs/wasm-parser": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
-      "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
-      "license": "MIT",
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz",
+      "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==",
       "dependencies": {
-        "@webassemblyjs/ast": "1.11.1",
-        "@webassemblyjs/helper-api-error": "1.11.1",
-        "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
-        "@webassemblyjs/ieee754": "1.11.1",
-        "@webassemblyjs/leb128": "1.11.1",
-        "@webassemblyjs/utf8": "1.11.1"
+        "@webassemblyjs/ast": "1.12.1",
+        "@webassemblyjs/helper-api-error": "1.11.6",
+        "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
+        "@webassemblyjs/ieee754": "1.11.6",
+        "@webassemblyjs/leb128": "1.11.6",
+        "@webassemblyjs/utf8": "1.11.6"
       }
     },
     "node_modules/@webassemblyjs/wast-printer": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
-      "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
-      "license": "MIT",
+      "version": "1.12.1",
+      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz",
+      "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==",
       "dependencies": {
-        "@webassemblyjs/ast": "1.11.1",
+        "@webassemblyjs/ast": "1.12.1",
         "@xtuc/long": "4.2.2"
       }
     },
     "node_modules/@webpack-cli/configtest": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
-      "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==",
-      "license": "MIT",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz",
+      "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==",
+      "dev": true,
+      "engines": {
+        "node": ">=14.15.0"
+      },
       "peerDependencies": {
-        "webpack": "4.x.x || 5.x.x",
-        "webpack-cli": "4.x.x"
+        "webpack": "5.x.x",
+        "webpack-cli": "5.x.x"
       }
     },
     "node_modules/@webpack-cli/info": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz",
-      "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==",
-      "license": "MIT",
-      "dependencies": {
-        "envinfo": "^7.7.3"
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz",
+      "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==",
+      "dev": true,
+      "engines": {
+        "node": ">=14.15.0"
       },
       "peerDependencies": {
-        "webpack-cli": "4.x.x"
+        "webpack": "5.x.x",
+        "webpack-cli": "5.x.x"
       }
     },
     "node_modules/@webpack-cli/serve": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz",
-      "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==",
-      "license": "MIT",
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz",
+      "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=14.15.0"
+      },
       "peerDependencies": {
-        "webpack-cli": "4.x.x"
+        "webpack": "5.x.x",
+        "webpack-cli": "5.x.x"
       },
       "peerDependenciesMeta": {
         "webpack-dev-server": {
@@ -748,14 +744,12 @@
     "node_modules/@xtuc/ieee754": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
-      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
-      "license": "BSD-3-Clause"
+      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="
     },
     "node_modules/@xtuc/long": {
       "version": "4.2.2",
       "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
-      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
-      "license": "Apache-2.0"
+      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
     },
     "node_modules/accepts": {
       "version": "1.3.8",
@@ -782,11 +776,10 @@
         "node": ">=0.4.0"
       }
     },
-    "node_modules/acorn-import-assertions": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz",
-      "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==",
-      "license": "MIT",
+    "node_modules/acorn-import-attributes": {
+      "version": "1.9.5",
+      "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
+      "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
       "peerDependencies": {
         "acorn": "^8"
       }
@@ -1134,6 +1127,7 @@
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
       "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "is-plain-object": "^2.0.4",
@@ -1163,6 +1157,7 @@
       "version": "2.0.20",
       "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
       "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+      "dev": true,
       "license": "MIT"
     },
     "node_modules/combined-stream": {
@@ -1190,6 +1185,11 @@
       "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
       "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
       "license": "MIT"
+    },
+    "node_modules/compress.js": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/compress.js/-/compress.js-2.1.2.tgz",
+      "integrity": "sha512-DBb6M4wwe0rRAPeiKQ8HJrWuocVppUw9Qte4rEXiDrc5X3TrzeRKLzpvSE9oZ0Nd4HTXSSFphj3/XWwuptkQqw=="
     },
     "node_modules/concat-map": {
       "version": "0.0.1",
@@ -1243,6 +1243,7 @@
       "version": "7.0.3",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
       "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "path-key": "^3.1.0",
@@ -1417,7 +1418,7 @@
       "version": "7.13.0",
       "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.13.0.tgz",
       "integrity": "sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==",
-      "license": "MIT",
+      "dev": true,
       "bin": {
         "envinfo": "dist/cli.js"
       },
@@ -1447,10 +1448,9 @@
       }
     },
     "node_modules/es-module-lexer": {
-      "version": "0.9.3",
-      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
-      "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==",
-      "license": "MIT"
+      "version": "1.5.4",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz",
+      "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw=="
     },
     "node_modules/es6-promise": {
       "version": "4.2.8",
@@ -1645,6 +1645,7 @@
       "version": "1.0.16",
       "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
       "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
+      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">= 4.9.1"
@@ -1783,6 +1784,7 @@
       "version": "5.0.2",
       "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
       "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+      "dev": true,
       "license": "BSD-3-Clause",
       "bin": {
         "flat": "cli.js"
@@ -2081,6 +2083,7 @@
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz",
       "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "pkg-dir": "^4.2.0",
@@ -2114,12 +2117,12 @@
       "license": "ISC"
     },
     "node_modules/interpret": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
-      "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
-      "license": "MIT",
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz",
+      "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==",
+      "dev": true,
       "engines": {
-        "node": ">= 0.10"
+        "node": ">=10.13.0"
       }
     },
     "node_modules/ipaddr.js": {
@@ -2148,7 +2151,7 @@
       "version": "2.15.0",
       "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz",
       "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==",
-      "license": "MIT",
+      "dev": true,
       "dependencies": {
         "hasown": "^2.0.2"
       },
@@ -2196,6 +2199,7 @@
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
       "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "isobject": "^3.0.1"
@@ -2208,12 +2212,14 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
       "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "dev": true,
       "license": "ISC"
     },
     "node_modules/isobject": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
       "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=0.10.0"
@@ -2303,6 +2309,7 @@
       "version": "6.0.3",
       "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
       "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=0.10.0"
@@ -2634,6 +2641,7 @@
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
       "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -2643,7 +2651,7 @@
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
       "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
-      "license": "MIT"
+      "dev": true
     },
     "node_modules/path-to-regexp": {
       "version": "0.1.7",
@@ -3006,22 +3014,22 @@
       }
     },
     "node_modules/rechoir": {
-      "version": "0.7.1",
-      "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
-      "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
-      "license": "MIT",
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
+      "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==",
+      "dev": true,
       "dependencies": {
-        "resolve": "^1.9.0"
+        "resolve": "^1.20.0"
       },
       "engines": {
-        "node": ">= 0.10"
+        "node": ">= 10.13.0"
       }
     },
     "node_modules/resolve": {
       "version": "1.22.8",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
       "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
-      "license": "MIT",
+      "dev": true,
       "dependencies": {
         "is-core-module": "^2.13.0",
         "path-parse": "^1.0.7",
@@ -3038,6 +3046,7 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
       "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "resolve-from": "^5.0.0"
@@ -3050,6 +3059,7 @@
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
       "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -3204,6 +3214,7 @@
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
       "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "kind-of": "^6.0.2"
@@ -3216,6 +3227,7 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
       "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "shebang-regex": "^3.0.0"
@@ -3228,6 +3240,7 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
       "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -3329,7 +3342,7 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
       "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
-      "license": "MIT",
+      "dev": true,
       "engines": {
         "node": ">= 0.4"
       },
@@ -3761,7 +3774,6 @@
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz",
       "integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==",
-      "license": "MIT",
       "dependencies": {
         "@vue/devtools-api": "^6.0.0-beta.11"
       },
@@ -3783,34 +3795,33 @@
       }
     },
     "node_modules/webpack": {
-      "version": "5.74.0",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
-      "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
-      "license": "MIT",
+      "version": "5.93.0",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz",
+      "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==",
       "dependencies": {
         "@types/eslint-scope": "^3.7.3",
-        "@types/estree": "^0.0.51",
-        "@webassemblyjs/ast": "1.11.1",
-        "@webassemblyjs/wasm-edit": "1.11.1",
-        "@webassemblyjs/wasm-parser": "1.11.1",
+        "@types/estree": "^1.0.5",
+        "@webassemblyjs/ast": "^1.12.1",
+        "@webassemblyjs/wasm-edit": "^1.12.1",
+        "@webassemblyjs/wasm-parser": "^1.12.1",
         "acorn": "^8.7.1",
-        "acorn-import-assertions": "^1.7.6",
-        "browserslist": "^4.14.5",
+        "acorn-import-attributes": "^1.9.5",
+        "browserslist": "^4.21.10",
         "chrome-trace-event": "^1.0.2",
-        "enhanced-resolve": "^5.10.0",
-        "es-module-lexer": "^0.9.0",
+        "enhanced-resolve": "^5.17.0",
+        "es-module-lexer": "^1.2.1",
         "eslint-scope": "5.1.1",
         "events": "^3.2.0",
         "glob-to-regexp": "^0.4.1",
-        "graceful-fs": "^4.2.9",
+        "graceful-fs": "^4.2.11",
         "json-parse-even-better-errors": "^2.3.1",
         "loader-runner": "^4.2.0",
         "mime-types": "^2.1.27",
         "neo-async": "^2.6.2",
-        "schema-utils": "^3.1.0",
+        "schema-utils": "^3.2.0",
         "tapable": "^2.1.1",
-        "terser-webpack-plugin": "^5.1.3",
-        "watchpack": "^2.4.0",
+        "terser-webpack-plugin": "^5.3.10",
+        "watchpack": "^2.4.1",
         "webpack-sources": "^3.2.3"
       },
       "bin": {
@@ -3830,42 +3841,40 @@
       }
     },
     "node_modules/webpack-cli": {
-      "version": "4.10.0",
-      "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
-      "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
-      "license": "MIT",
+      "version": "5.1.4",
+      "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz",
+      "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==",
+      "dev": true,
       "dependencies": {
         "@discoveryjs/json-ext": "^0.5.0",
-        "@webpack-cli/configtest": "^1.2.0",
-        "@webpack-cli/info": "^1.5.0",
-        "@webpack-cli/serve": "^1.7.0",
+        "@webpack-cli/configtest": "^2.1.1",
+        "@webpack-cli/info": "^2.0.2",
+        "@webpack-cli/serve": "^2.0.5",
         "colorette": "^2.0.14",
-        "commander": "^7.0.0",
+        "commander": "^10.0.1",
         "cross-spawn": "^7.0.3",
+        "envinfo": "^7.7.3",
         "fastest-levenshtein": "^1.0.12",
         "import-local": "^3.0.2",
-        "interpret": "^2.2.0",
-        "rechoir": "^0.7.0",
+        "interpret": "^3.1.1",
+        "rechoir": "^0.8.0",
         "webpack-merge": "^5.7.3"
       },
       "bin": {
         "webpack-cli": "bin/cli.js"
       },
       "engines": {
-        "node": ">=10.13.0"
+        "node": ">=14.15.0"
       },
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/webpack"
       },
       "peerDependencies": {
-        "webpack": "4.x.x || 5.x.x"
+        "webpack": "5.x.x"
       },
       "peerDependenciesMeta": {
         "@webpack-cli/generators": {
-          "optional": true
-        },
-        "@webpack-cli/migrate": {
           "optional": true
         },
         "webpack-bundle-analyzer": {
@@ -3877,18 +3886,19 @@
       }
     },
     "node_modules/webpack-cli/node_modules/commander": {
-      "version": "7.2.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
-      "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
-      "license": "MIT",
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
+      "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+      "dev": true,
       "engines": {
-        "node": ">= 10"
+        "node": ">=14"
       }
     },
     "node_modules/webpack-merge": {
       "version": "5.10.0",
       "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz",
       "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "clone-deep": "^4.0.1",
@@ -3930,6 +3940,7 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
       "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
       "license": "ISC",
       "dependencies": {
         "isexe": "^2.0.0"
@@ -3945,6 +3956,7 @@
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz",
       "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",
+      "dev": true,
       "license": "MIT"
     },
     "node_modules/wrappy": {
package.json
--- package.json
+++ package.json
@@ -7,6 +7,7 @@
     "@mdi/light-js": "^0.2.63",
     "axios": "^1.7.3",
     "babel-loader": "8.2.5",
+    "compress.js": "^2.1.2",
     "css-loader": "6.7.1",
     "express": "^4.18.1",
     "express-http-proxy": "^2.0.0",
@@ -20,9 +21,7 @@
     "vue-router": "4.1.5",
     "vue-style-loader": "4.1.3",
     "vue3-sfc-loader": "^0.8.4",
-    "vuex": "^4.1.0",
-    "webpack": "5.74.0",
-    "webpack-cli": "4.10.0"
+    "vuex": "^4.1.0"
   },
   "scripts": {
     "prod": "set NODE_ENV=production&&node ./server/modules/web/server.js",
@@ -33,5 +32,9 @@
     "linux-dev": "export NODE_ENV=development&&node ./server/modules/web/server.js",
     "webpack-build": "webpack",
     "webpack-build-watch": "webpack --watch"
+  },
+  "devDependencies": {
+    "webpack": "^5.93.0",
+    "webpack-cli": "^5.1.4"
   }
 }
Add a comment
List