[한화시스템 BEYOND SW캠프] 22기 15주차 회고

dev_ho·2026년 2월 8일

레포지토리 링크

프로젝트명: Ideal Type World Cup & Quiz Game Platform
기간: 2026.01.29 ~ 2026.02.08
: 한화 SW Camp 22기 3조
작성자: gusgh075


1. 프로젝트 개요

  • 한화 SWCamp 22기 3번째 단위 프로젝트로, 프론트엔드 중심의 인터랙티브 웹 게임 플랫폼을 개발하였다.
  • 이상형 월드컵타임 어택 퀴즈 두 가지 게임 모드를 제공하며, 사용자가 직접 콘텐츠를 제작하고 공유할 수 있는 커뮤니티 기반 플랫폼을 목표로 하였다.
  • 이상형 월드컵은 기본적으로 64강 토너먼트 방식으로 진행되며 후보자 통계(승률, 출전 횟수, 우승 횟수)와 랭킹 시스템을 갖추었고, 타임 어택 퀴즈는 문제당 제한 시간 내 정답을 맞히는 방식으로 6단계 티어 시스템과 TOP 10 명예의 전당 기능을 포함하였다.

2. 기술 스택(package.json참고)

분류기술
프론트엔드 프레임워크Vue.js 3.5.24, Vite 5.4.11
라우팅Vue Router 5.0.1
상태 관리Pinia 3.0.4
UI 라이브러리Element Plus 2.13.2
HTTP 통신Axios 1.13.4
백엔드 (Mock)JSON Server 1.0, json-server-auth (JWT 인증)
기타UUID, html2canvas, Galmuri Font (픽셀 한글 폰트)

3. 협업 경험

  • 팀원 모두가 적극적으로 코드 리뷰에 참여하며, 총 60개 이상의 Pull Request를 통해 체계적인 Git Flow 기반 협업을 진행하였다.
  • 기능별로 브랜치를 분리하고(feat/, fix/, hotfix/), PR 단위로 머지하는 워크플로우를 철저히 지켰다.
  • 팀원 간 서로의 PR을 리뷰하고 머지해 주며 상호 검증 체계를 유지하였다. 특히 긴급한 버그 수정 시에도 hotfix 브랜치를 통해 신속하면서도 안정적인 배포 프로세스를 유지한 점이 인상적이었다.
  • 코드 리뷰 파일, 요구사항 정리 파일, 주요 기능 주석 설명 등 문서화 작업도 함께 진행하며(PR #63) 팀 내 지식 공유에 힘썼다.
  • 프로젝트 기간 내내 열정적인 팀 분위기 속에서 서로 부족한 부분을 채워가며 협업한 경험이 가장 값진 자산이었다.

4. 주요 구현 및 트러블슈팅

4-1. 핵심 기능 구현

  • 월드컵 멀티파일 업로드 기능 (PR #21)

    • 월드컵 등록 시 후보 이미지를 한 번에 여러 장 업로드할 수 있는 멀티파일 업로드 기능을 구현하였다.
    • Multer를 활용한 파일 처리와 프론트엔드 파일 입력 핸들링을 연동하였다.
  • 파일 업로드 확장자 제한 (PR #5)

    • 이미지 파일만 업로드되도록 jpg, jpeg, png, gif, webp 확장자 필터링 로직을 추가하여 보안성과 안정성을 강화하였다.
  • 월드컵 8강/16강/32강/64강 라운드 시스템 (PR #42, #52)

    • 기존 단일 토너먼트 구조에서 8강, 16강, 32강, 64강을 선택할 수 있는 라운드 선택 기능을 구현하였다.
    • 라운드별 매칭 로직과 결과 화면을 함께 수정하여 사용자 경험을 개선하였다.
  • 조회수/플레이수 집계 로직 (PR #75)

    • 회원/비회원의 플레이 및 조회 시 db.json에서 해당 데이터를 자동으로 카운트하는 집계 로직을 구현하였다.
    • 메인 페이지와 상세 페이지에서 조회수, 플레이수, 후보 수를 이모지와 함께 시각적으로 표시하였다(PR #54, #68, #80).
  • 월드컵 랭킹 페이지 및 승률 계산 (PR #77)

    • 월드컵 랭킹 페이지의 라우팅을 설정하고, help.js에 승률 계산 유틸리티 함수를 정의하였다.
    • 후보자별 승률, 출전 횟수, 우승 횟수를 기반으로 한 랭킹 로직을 개선하였다.
  • 이미지 URL 관리 체계 구축

    • .env 파일에서 json-server 주소를 통합 관리하고, getImageURL 헬퍼 함수를 구현하여 프로젝트 전반의 이미지 경로 처리를 일원화하였다.
    • vite.config.js에서 /api 프록시를 localhost:3000으로 설정하여 개발 환경의 API 통신을 안정화하였다.

4-2. 트러블슈팅

🔧 1. 64강에서 결승으로 표시되는 버그 해결 (PR #84)

📌 문제 상황
  • 월드컵 진행 시 실제 라운드는 64강인데, 화면에는 "결승"으로 잘못 표시되는 버그 발생
  • 라운드 네이밍 로직의 오류로 인한 사용자 혼란
✅ 해결 방법
🔴 Before: 잘못된 라운드 계산 로직
// stores/worldcup.js (수정 전)
const getRoundName = () => {
  const remainingCount = currentRound.value.length;
  
  // 잘못된 조건문: 64강인데 결승으로 표시됨
  if (remainingCount === 2) return '결승';
  if (remainingCount <= 4) return '4강';
  if (remainingCount <= 8) return '8강';
  // ... 이하 생략
}
🟢 After: 정확한 라운드 네이밍
// stores/worldcup.js (수정 후)
const getRoundName = () => {
  const remainingCount = currentRound.value.length;
  
  // 정확한 조건문 순서와 비교 연산자 수정
  if (remainingCount === 64) return '64강';
  if (remainingCount === 32) return '32강';
  if (remainingCount === 16) return '16강';
  if (remainingCount === 8) return '8강';
  if (remainingCount === 4) return '4강';
  if (remainingCount === 2) return '결승';
  return '게임 종료';
}
💡 핵심 개선사항
  • <= 비교 연산자 대신 === 정확한 일치 비교 사용
  • 라운드 순서를 큰 수부터 작은 수로 재배치하여 로직 명확화
  • 64강 케이스 명시적 추가

🖼️ 2. 월드컵 후보 사진 미표시 문제 (PR #9)

📌 문제 상황
  • 후보 사진 등록 시 화면에 이미지가 렌더링되지 않음
  • 이미지 소스 바인딩 방식의 오류
✅ 해결 방법
🔴 Before: 직접 경로 하드코딩
<!-- views/worldcup/WorldcupGame.vue (수정 전) -->
<template>
  <div class="candidate-card">
    <img :src="`/uploads/${candidate.image}`" />
  </div>
</template>
🟢 After: 헬퍼 함수 활용
<!-- views/worldcup/WorldcupGame.vue (수정 후) -->
<script setup>
import { getImageURL } from '@/utils/helper.js'
</script>

<template>
  <div class="candidate-card">
    <img :src="getImageURL(candidate.image)" />
  </div>
</template>
// utils/helper.js (새로 추가)
export const getImageURL = (filename) => {
  if (!filename) return '/default-avatar.png';
  
  // 환경변수 기반 통합 URL 관리
  const baseURL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000';
  return `${baseURL}/uploads/${filename}`;
}
💡 핵심 개선사항
  • 환경변수(.env) 기반의 통합 이미지 URL 관리 체계 도입
  • 개발/프로덕션 환경 분리 용이
  • 코드 재사용성 향상

🎯 3. 썸네일 미표시 및 결과 이미지 오류 (PR #28, #37)

📌 문제 상황
  • 월드컵 목록 페이지에서 썸네일이 표시되지 않음
  • 결과 화면에서 우승자 이미지가 깨짐
  • 공통 원인: 이미지 경로 처리 불일치
✅ 해결 방법
🔴 Before: 컴포넌트마다 다른 경로 방식
<!-- views/worldcup/WorldcupList.vue (수정 전) -->
<img :src="worldcup.thumbnail" />

<!-- views/worldcup/WorldcupResult.vue (수정 전) -->
<img :src="`http://localhost:3000${winner.image}`" />
🟢 After: 통일된 헬퍼 함수 사용
<!-- views/worldcup/WorldcupList.vue (수정 후) -->
<script setup>
import { getImageURL } from '@/utils/helper.js'
</script>

<img :src="getImageURL(worldcup.thumbnail)" />

<!-- views/worldcup/WorldcupResult.vue (수정 후) -->
<img :src="getImageURL(winner.image)" />
// .env (환경변수 설정)
VITE_API_BASE_URL=http://localhost:3000
// vite.config.js (프록시 설정 추가)
export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd());
  
  return {
    server: {
      proxy: {
        '/api': {
          target: env.VITE_API_BASE_URL || 'http://localhost:3000',
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, '')
        }
      }
    }
  }
})
💡 핵심 개선사항
  • 모든 컴포넌트에서 getImageURL() 헬퍼 함수 통일 사용
  • .env 파일로 API 주소 중앙 관리
  • Vite 프록시 설정으로 개발 환경 CORS 이슈 해결

📦 4. identity.js 누락 오류 (PR #76)

📌 문제 상황
  • 빌드 시 identity.js 파일이 누락되어 런타임 에러 발생
  • 번들링 과정에서 특정 유틸리티 모듈 누락
✅ 해결 방법
🔴 Before: 모듈 import 경로 불명확
// utils/index.js (수정 전)
export * from './validators'
// identity 모듈 누락
🟢 After: 명시적 모듈 선언
// utils/identity.js (새로 추가)
export const identity = (value) => value;

export const generateId = () => {
  return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
// utils/index.js (수정 후)
export * from './validators'
export * from './identity'  // 명시적 추가
export * from './storage'
export * from './helper'
💡 핵심 개선사항
  • 유틸리티 모듈 명시적 export 추가
  • 빌드 번들에 필수 모듈 포함 보장

⚠️ 5. 월드컵 등록 에러 메시지 미표시 (PR #15)

📌 문제 상황
  • 월드컵 콘텐츠 등록 시 유효성 검증 실패해도 사용자에게 에러 메시지가 표시되지 않음
  • UX 문제로 사용자가 오류 원인을 파악할 수 없음
✅ 해결 방법
🔴 Before: 에러 처리 누락
<!-- views/create/WorldcupCreate.vue (수정 전) -->
<script setup>
const submitWorldcup = async () => {
  try {
    await worldcupApi.create(formData);
    router.push('/worldcup');
    // 에러 처리 없음
  } catch (error) {
    console.error(error); // 콘솔에만 출력
  }
}
</script>
🟢 After: Element Plus 메시지 표시
<!-- views/create/WorldcupCreate.vue (수정 후) -->
<script setup>
import { ElMessage } from 'element-plus'

const submitWorldcup = async () => {
  // 유효성 검증
  if (!formData.title || !formData.description) {
    ElMessage.error('제목과 설명을 모두 입력해주세요.');
    return;
  }
  
  if (candidates.length < 32) {
    ElMessage.error('최소 32명의 후보를 등록해야 합니다.');
    return;
  }
  
  try {
    await worldcupApi.create(formData);
    ElMessage.success('월드컵이 생성되었습니다!');
    router.push('/worldcup');
  } catch (error) {
    console.error(error);
    ElMessage.error(error.response?.data?.message || '월드컵 생성에 실패했습니다.');
  }
}
</script>
💡 핵심 개선사항
  • Element Plus의 ElMessage 컴포넌트를 활용한 사용자 친화적 피드백
  • 프론트엔드 유효성 검증 강화
  • 백엔드 에러 메시지 사용자에게 전달

🔄 6. 의존성 에러 수정

📌 문제 상황
  • Vite 보안 업데이트 과정에서 의존성 충돌 발생
  • npm install 실패
✅ 해결 방법
🔴 Before: 버전 충돌
// package.json (수정 전)
{
  "dependencies": {
    "vite": "^5.0.0",
    "vue": "^3.3.0"
  }
}
🟢 After: 호환 버전으로 업데이트
// package.json (수정 후)
{
  "dependencies": {
    "vite": "^5.4.11",
    "vue": "^3.5.24",
    "vue-router": "^5.0.1"
  }
}
# 의존성 재설치 명령어
rm -rf node_modules package-lock.json
npm install
💡 핵심 개선사항
  • 최신 안정 버전으로 업데이트
  • package-lock.json 재생성으로 의존성 트리 정리

🎓 학습 포인트 정리

트러블슈팅 항목핵심 배운 점
라운드 버그조건문 순서와 비교 연산자의 중요성 (=== vs <=)
이미지 경로환경변수 기반 중앙 집중식 설정 패턴
모듈 누락명시적 export와 빌드 번들링 이해
에러 UX사용자 피드백의 중요성과 UI 라이브러리 활용
의존성 관리npm 패키지 버전 호환성 체크의 필요성

5. 성장 포인트

  • 이번 프로젝트를 통해 Vue.js 3의 Composition API와 Pinia 상태 관리에 대한 이해도가 크게 향상되었다. 특히 컴포넌트 간 상태 공유, 반응형 데이터 흐름, 그리고 Store 모듈화 설계의 중요성을 직접 구현하며 체득하였다.
  • Vue Router를 활용한 도메인 기반 라우팅 설계를 경험하며, SPA에서의 페이지 전환과 네비게이션 가드의 원리를 깊이 이해하게 되었다.
  • Axios와 API 레이어 분리 패턴을 적용하며, 프론트엔드에서 백엔드 통신을 체계적으로 관리하는 방법을 습득하였다. 도메인별 API 파일 분리(authApi, worldcupApi, quizApi, commonApi)를 통해 단일 책임 원칙의 실질적 적용을 경험하였다.
  • json-server와 Multer를 활용한 Mock 백엔드 구축 경험을 통해, 프론트엔드 개발 시 백엔드 의존성을 최소화하면서도 실제와 유사한 환경에서 개발하는 방법을 배웠다.
  • 18개의 PR을 직접 작성하고, 다수의 hotfix를 신속하게 처리하면서 Git 브랜치 전략과 PR 기반 협업 프로세스에 대한 실전 역량을 크게 키울 수 있었다.
  • 이미지 관련 연쇄 버그를 추적하는 과정에서, 문제의 근본 원인을 파악하고 환경변수 기반의 통합 관리 체계를 도입한 경험은 체계적인 디버깅 사고력을 길러주었다.

이번 프로젝트는 팀원간 협업을 제대로 경험해볼 수 있었던 것 같다. 기술적 이해도와 별개로 모두가 적극적으로 참여했고, 다양한 의견을 냈다. 또한 다들 새벽까지 프로젝트를 진행하는 피곤한 스케쥴임이였음에도 불구하고 긍정적인 마인드로 진행함에 있어 깊은 감사를 표하고 싶다. 앞으로도 서로의 열정을 불태워 모두가 만족할만한 결과물을 내는 경험을 많이 하고 싶다.
다들 너무 수고 많았고, 자주자주 봤으면 좋겠다! 곧 다가오는 최종프로젝트도 다같이 화이팅이다!

profile
새로운 기술에 열린 개발자

4개의 댓글

comment-user-thumbnail
2026년 2월 8일

우우우우우ㅜ 내꺼 따라쟁이 우우우우우우

1개의 답글
comment-user-thumbnail
2026년 2월 9일

굿굿 현호님 고생 많으셨어요!! 저는 평소에도 이런 월드컵을 하는 것을 좋아하는데 훈련생들이 직접 구현한 것으로 해 보니 더욱 재미있었습니당~ 핑크색 UI도 귀여웠어요!

1개의 답글