Vue.js 를 배워보자

Vue.js로 장바구니 수량 관리 기능 만들기 외 2개 예제

_Blue_Sky_ 2025. 3. 14. 20:35
728x90
 
 
  온라인 쇼핑몰 장바구니 수량 조절
당신은 온라인 쇼핑몰의 프론트엔드 개발자입니다. 사용자가 장바구니에 담긴 상품의 수량을 늘리거나 감소시킬 수 있는 기능을 만들어야 합니다. 또한 수량이 변경될 때마다 콘솔에 변경 내역을 기록해 디버깅하거나 추후 서버로 전송할 데이터를 준비하려고 합니다. 이를 Vue.js의 refwatch를 활용해 간단히 구현해보겠습니다.
코드 예제
 
<template>
  <div class="cart-item">
    <p>장바구니 상품 수량: {{ count }}</p>
    <button @click="count++">수량 증가</button>
  </div>
</template>

<script>
import { ref, watch } from 'vue';

export default {
  setup() {
    const count = ref(0); // 장바구니 상품 수량 초기값

    // 수량이 변경될 때마다 로그 기록
    watch(count, (newValue, oldValue) => {
      console.log(`수량이 ${oldValue}에서 ${newValue}로 변경되었습니다.`);
      // 실무에서는 여기서 서버로 업데이트 요청을 보낼 수 있음
      // 예: updateCartItem(newValue);
    });

    return {
      count
    };
  }
};
</script>

<style>
.cart-item {
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 5px;
  max-width: 300px;
  text-align: center;
}
</style>
시나리오 전개
  1. 초기 상태: 사용자가 장바구니에 상품을 추가하면 count 값이 0으로 시작합니다.
  2. 수량 증가: 사용자가 "수량 증가" 버튼을 클릭하면 count 값이 1씩 증가합니다.
  3. 변경 감지: watchcount의 변화를 감지하고, 변경 전(oldValue)과 변경 후(newValue) 값을 콘솔에 출력합니다.
  4. 실무 적용: 콘솔 로그 대신, 변경된 수량을 서버에 전송하거나, 수량이 10개를 넘으면 경고 메시지를 띄우는 로직을 추가할 수 있습니다.
실무에서의 확장 가능성
  • 감소 버튼 추가: <button @click="count > 0 ? count-- : null">수량 감소</button>를 추가해 수량이 0 이하로 내려가지 않게 제어.
  • 서버 연동: watch 내부에서 API 호출을 통해 실시간으로 장바구니 상태를 업데이트.
  • UI 개선: 수량이 변경될 때마다 애니메이션 효과를 추가해 사용자 경험을 향상.
마무리
이 간단한 예제는 Vue.js의 반응형 데이터 관리와 이벤트 감지의 기본을 보여줍니다. 실무에서는 여기에 에러 처리, 입력 검증, 사용자 피드백 등을 추가해 더 견고한 기능을 만들 수 있습니다. Vue.js의 setupwatch를 활용하면 코드도 깔끔하고 유지보수도 쉬워지니, 작은 기능부터 적용해보세요!

728x90
 

  Vue.js로 실시간 투표 시스템 구현하기
시나리오: 실시간 투표 모니터링 대시보드
당신은 이벤트 관리 플랫폼의 개발자로, 실시간 투표 시스템을 구축해야 합니다. 사용자가 특정 항목에 투표를 추가할 수 있고, 투표 수가 변경될 때마다 로그를 남기며, 투표 수가 100을 넘으면 관리자에게 경고를 띄워야 합니다. 또한, 사용자가 투표를 취소할 수 있는 기능도 필요합니다. Vue.js의 refwatch를 활용해 이를 구현해보겠습니다.
코드 예제
<template>
  <div class="vote-dashboard">
    <h2>실시간 투표 수: {{ voteCount }}</h2>
    <button @click="addVote" :disabled="isMaxReached">투표 추가</button>
    <button @click="removeVote" :disabled="voteCount === 0">투표 취소</button>
    <p v-if="warning" class="warning">경고: 투표 수가 100을 초과했습니다!</p>
  </div>
</template>

<script>
import { ref, watch } from 'vue';

export default {
  setup() {
    const voteCount = ref(0); // 투표 수 초기값
    const warning = ref(false); // 경고 상태
    const isMaxReached = ref(false); // 최대 투표 수 제한 플래그

    // 투표 수 변경 감지
    watch(voteCount, (newValue, oldValue) => {
      console.log(`투표 수가 ${oldValue}에서 ${newValue}로 변경됨`);
      
      // 투표 수가 100 초과 시 경고 활성화
      if (newValue > 100) {
        warning.value = true;
        // 실무에서는 서버로 알림 전송 가능
        // 예: sendNotificationToAdmin(newValue);
      } else {
        warning.value = false;
      }

      // 최대 투표 수 제한 (예: 200)
      isMaxReached.value = newValue >= 200;
    });

    // 투표 추가 함수
    const addVote = () => {
      if (!isMaxReached.value) {
        voteCount.value++;
      }
    };

    // 투표 취소 함수
    const removeVote = () => {
      if (voteCount.value > 0) {
        voteCount.value--;
      }
    };

    return {
      voteCount,
      warning,
      isMaxReached,
      addVote,
      removeVote
    };
  }
};
</script>

<style>
.vote-dashboard {
  padding: 20px;
  border: 2px solid #007bff;
  border-radius: 8px;
  max-width: 400px;
  text-align: center;
}
.warning {
  color: red;
  font-weight: bold;
  margin-top: 10px;
}
button {
  margin: 5px;
  padding: 8px 16px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
}
button:disabled {
  background-color: #ccc;
}
</style>
시나리오 전개
  1. 초기 상태: 투표 수가 0으로 시작하며, "투표 추가"와 "투표 취소" 버튼이 표시됩니다.
  2. 투표 추가: 사용자가 "투표 추가" 버튼을 클릭하면 voteCount가 증가하고, watch가 변경을 감지해 콘솔에 기록합니다.
  3. 경고 조건: 투표 수가 100을 넘으면 경고 메시지가 화면에 표시됩니다.
  4. 최대 제한: 투표 수가 200에 도달하면 "투표 추가" 버튼이 비활성화됩니다.
  5. 투표 취소: "투표 취소" 버튼으로 투표 수를 줄일 수 있으며, 0 이하로는 내려가지 않습니다.
실무에서의 고도화된 활용
  • 서버 동기화: watch 내에서 투표 수 변경 시 서버로 PATCH 요청을 보내 실시간 동기화.
  • 타임스탬프 기록: 투표 변경 시 Date.now()를 사용해 언제 변경되었는지 로그에 추가.
  • 사용자 피드백: 경고 메시지 대신 모달 창이나 토스트 알림으로 UX 개선.
  • 다중 항목 지원: voteCount를 객체 배열로 확장해 여러 투표 항목을 관리 가능.
추가 개선 예시
 
watch(voteCount, async (newValue) => {
  try {
    await fetch('/api/update-votes', {
      method: 'PATCH',
      body: JSON.stringify({ votes: newValue })
    });
    console.log('서버에 투표 수 업데이트 완료');
  } catch (error) {
    console.error('업데이트 실패:', error);
  }
});
마무리
이 예제는 단순한 카운터를 넘어 실시간 데이터 모니터링과 조건 기반 피드백을 포함한 고도화된 기능을 보여줍니다. Vue.js의 watchref를 활용하면 복잡한 비즈니스 로직도 깔끔하게 관리할 수 있습니다. 실무에서 이런 패턴을 적용하면 유지보수성과 확장성이 크게 향상될 겁니다!

728x90
 Vue.js로 지역별 판매량 그리드 구현하기
 판매 데이터 분석 대시보드
당신은 소매업체의 데이터 분석 대시보드를 개발 중입니다. 각 지역별로 월별 판매량을 표시하고, 각 행과 열의 합계 및 평균을 자동으로 계산해 보여줘야 합니다. 사용자는 데이터를 한눈에 파악하고, 필요 시 특정 값을 수정할 수 있어야 합니다. Vue.js의 ref로 데이터 상태를 관리하고, computed로 합계와 평균을 계산하며, 그리드 UI로 이를 시각화해보겠습니다.
코드 예제
 
<template>
  <div class="sales-grid">
    <h2>지역별 월별 판매량 (단위: 개)</h2>
    <table>
      <thead>
        <tr>
          <th>지역</th>
          <th v-for="month in months" :key="month">{{ month }}</th>
          <th>합계</th>
          <th>평균</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="(region, index) in salesData" :key="region.name">
          <td>{{ region.name }}</td>
          <td v-for="(sales, monthIdx) in region.sales" :key="monthIdx">
            <input
              type="number"
              v-model.number="salesData[index].sales[monthIdx]"
              min="0"
              @input="logChange(region.name, months[monthIdx], $event.target.value)"
            />
          </td>
          <td>{{ rowTotals[index] }}</td>
          <td>{{ rowAverages[index] }}</td>
        </tr>
        <tr class="totals-row">
          <td>합계</td>
          <td v-for="(total, idx) in columnTotals" :key="idx">{{ total }}</td>
          <td>{{ grandTotal }}</td>
          <td>-</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
import { ref, computed } from 'vue';

export default {
  setup() {
    const months = ['1월', '2월', '3월']; // 월 목록
    const salesData = ref([
      { name: '서울', sales: [120, 150, 180] },
      { name: '부산', sales: [90, 110, 130] },
      { name: '대구', sales: [70, 85, 95] }
    ]); // 지역별 판매량 데이터

    // 행별 합계 계산
    const rowTotals = computed(() => {
      return salesData.value.map(region =>
        region.sales.reduce((sum, sales) => sum + sales, 0)
      );
    });

    // 행별 평균 계산
    const rowAverages = computed(() => {
      return salesData.value.map(region =>
        (region.sales.reduce((sum, sales) => sum + sales, 0) / months.length).toFixed(2)
      );
    });

    // 열별 합계 계산
    const columnTotals = computed(() => {
      return months.map((_, monthIdx) =>
        salesData.value.reduce((sum, region) => sum + region.sales[monthIdx], 0)
      );
    });

    // 전체 합계 계산
    const grandTotal = computed(() => {
      return rowTotals.value.reduce((sum, total) => sum + total, 0);
    });

    // 변경 로그 기록
    const logChange = (region, month, newValue) => {
      console.log(`${region}${month} 판매량이 ${newValue}로 변경됨`);
      // 실무에서는 서버로 업데이트 요청 가능
      // 예: updateSalesData(region, month, newValue);
    };

    return {
      months,
      salesData,
      rowTotals,
      rowAverages,
      columnTotals,
      grandTotal,
      logChange
    };
  }
};
</script>

<style>
.sales-grid {
  padding: 20px;
  max-width: 800px;
}
table {
  width: 100%;
  border-collapse: collapse;
}
th, td {
  border: 1px solid #ccc;
  padding: 8px;
  text-align: center;
}
th {
  background-color: #f4f4f4;
}
input[type="number"] {
  width: 60px;
  padding: 4px;
  text-align: center;
}
.totals-row {
  font-weight: bold;
  background-color: #f9f9f9;
}
</style>
시나리오 전개
  1. 초기 상태: salesData에 서울, 부산, 대구의 1~3월 판매량이 초기값으로 설정됩니다.
  2. 데이터 수정: 사용자가 <input> 필드에서 값을 변경하면 salesData가 실시간으로 업데이트됩니다.
  3. 합계와 평균 계산:
    • rowTotals: 각 지역의 월별 판매량 합계.
    • rowAverages: 각 지역의 월별 평균 판매량.
    • columnTotals: 각 월의 지역별 합계.
    • grandTotal: 전체 판매량 합계.
  4. 변경 감지: 값이 변경될 때마다 logChange 함수가 호출되어 콘솔에 기록됩니다.
실무에서의 고도화된 활용
  • 데이터 유효성 검사: 입력값이 음수일 경우 경고를 띄우고 이전 값으로 롤백.
    logChange(region, month, newValue) {
      if (newValue < 0) {
        alert('판매량은 음수가 될 수 없습니다!');
        return;
      }
      console.log(`${region}${month} 판매량이 ${newValue}로 변경됨`);
    }
  • 서버 연동: watch를 추가해 데이터 변경 시 서버로 동기화.
    watch(salesData, async (newData) => {
      await fetch('/api/update-sales', {
        method: 'POST',
        body: JSON.stringify(newData)
      });
    }, { deep: true });
  • 필터링 및 정렬: 지역명 클릭 시 판매량 기준으로 정렬하거나, 특정 월만 표시하는 필터 추가.
마무리
이 예제는 computed를 활용해 복잡한 데이터 계산을 효율적으로 처리하며, 엑셀과 유사한 인터랙티브 그리드를 구현했습니다. 실무에서는 여기에 차트 연동, 데이터 내보내기(CSV/Excel), 다중 사용자 동시 편집 등의 기능을 추가해 더욱 강력한 대시보드로 확장할 수 있습니다.
 
728x90