공공부하자개발 · 영어 학습 노트
자바
실무 확장Excel · 파일 업로드 · DB 연동0/22 완료
  • 01Excel(XLSX) 구조와 순수 JDK로 읽기/쓰기
  • 02Apache POI로 Excel 업로드/다운로드
  • 03파일 업로드/다운로드 서버 (HttpServer)
  • 04JDBC 기초와 트랜잭션 (H2)
  • 05MyBatis 어노테이션 매퍼로 쿼리 연동
  • 06MyBatis XML 매퍼 · Oracle 방언 · PageHelper · Spring Boot
  • 07REST API 서버와 JSON
  • 08Vue 3 SPA 와 Java 서버 연동
  • 09@Scheduled 운영
  • 10로깅 실무: 레벨·계층, MDC 추적, 예외·성능, 마스킹, 롤링, JSON 로그
  • 11외부 API 연동
  • 12테스트 실무
  • 13암호화·개인정보 보호
  • 14인코딩·한글 실무
  • 15@Transactional 심화
  • 16긴 작업 비동기 처리와 진행률
  • 17SFTP·FTP 파일 연계
  • 18로컬 캐시와 @Cacheable
  • 19메일·알림 발송
  • 20웹 보안 체크리스트
  • 21빌드 도구와 폐쇄망 의존성 반입
  • 22성능 측정: p50·p95·p99, 측정 계층, JFR, JMH 함정, 자체 부하 테스트, 병목 순위
사이트 소개개인정보처리방침연락처
© 2026 공부하자
홈 › 실무 확장 › 08 / 22

Vue 3 SPA 와 Java 서버 연동

정적 서빙, 같은 출처 API, 폐쇄망 라이브러리
섹션 7진행 0 / 22
1왜 배우는가2핵심 원리3코드 예제4응용 변형 예제5자주 하는 실수 (Tip)6연습 문제7정리‹ 이전다음 ›

5. 자주 하는 실수 (Tip)

❌ 실수 1: CDN 스크립트를 그대로 쓴다

html
<!-- ❌ 폐쇄망에서는 이 요청 자체가 실패한다 -->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

인터넷 연결이 없는 사내망에서는 DNS 조회부터 실패합니다. 화면이 완전히 빈 채로 뜨고 콘솔에만 오류가 남습니다. 파일을 미리 내려받아 static/js/plugin/ 에 두고 절대 경로로 로드해야 합니다.

powershell
# 폐쇄망 PC 에서 확인: CDN 요청은 타임아웃, 로컬 경로는 즉시 응답
curl -m 3 https://unpkg.com/vue@3          # 실패
curl http://127.0.0.1:8080/js/plugin/vue.global.prod.js   # 성공

❌ 실수 2: Content-Type 을 빠뜨린다

정적 서버가 .js 파일에 Content-Type 을 안 붙이면 브라우저가 nosniff 정책으로 실행을 거부합니다. 화면은 뜨지만 Vue 가 전혀 동작하지 않고 콘솔에 MIME 오류가 남습니다. 확장자별 MIME 매핑을 빠짐없이 채워야 합니다.

❌ 실수 3: 확장자 없는 경로를 폴백하지 않는다

/todos/3 을 새로고침하면 404 가 뜨고 화면이 사라집니다. Vue Router 는 브라우저 안에서만 경로를 알고, 서버는 그런 파일이 없기 때문입니다. 확장자 없는 경로는 index.html 로 돌려줘야 합니다.

java
// ✅ 확장자 없고 실제 파일도 없으면 index.html 로 폴백
boolean hasExt = file.getFileName().toString().contains(".");
if (!hasExt && !Files.isRegularFile(file)) file = root.resolve("index.html");

❌ 실수 4: 반대로 모든 404 를 index.html 로 돌린다

java
// ❌ 확장자 유무를 안 가리고 무조건 폴백
if (!Files.isRegularFile(file)) file = root.resolve("index.html");

오타로 /js/ap.js 를 요청하면 HTML 이 내려와 SyntaxError 로 보입니다. 원인 파악이 오래 걸립니다. 확장자가 있는 경로는 404 를 그대로 유지해야 진짜 원인이 드러납니다.

❌ 실수 5: index.html 이 캐시돼 배포 후 옛 화면이 보인다

index.html 에 Cache-Control 을 안 지정하면 브라우저가 임의로 캐시해, 새 버전을 배포해도 사용자는 옛 화면을 계속 봅니다. index.html 은 항상 no-cache 로 매번 서버에 확인시켜야 합니다.

java
// ✅ vendor 여부로 캐시 정책을 나눈다
boolean vendor = uri.startsWith("/js/plugin/");
ex.getResponseHeaders().set("Cache-Control", vendor ? "public, max-age=31536000, immutable" : "no-cache");

❌ 실수 6: 라이브러리 파일명을 버전 없이 덮어쓴다

vue.global.prod.js 를 새 버전으로 덮어쓰면, 1년 캐시를 받은 브라우저가 옛 파일을 계속 씁니다. 파일명이나 폴더에 버전을 넣어 경로 자체를 바꿔야 캐시가 새로 받힙니다.

html
<!-- ✅ 버전이 경로에 드러나야 교체 시 캐시가 자동으로 무효화된다 -->
<script src="/js/plugin/vue-3.5.13/vue.global.prod.js"></script>

❌ 실수 7: 204 응답에 res.json() 을 호출한다

javascript
// ❌ DELETE 는 본문이 없는데 무조건 json() 을 호출
const data = await res.json();   // 204 면 여기서 예외

본문이 없는 204 에 res.json() 을 부르면 파싱 오류가 납니다. 상태 코드를 먼저 검사해 204 면 즉시 null 을 반환해야 합니다.

❌ 실수 8: res.ok 를 안 보고 항상 성공 처리한다

fetch 는 400·404 같은 응답도 예외를 던지지 않고 정상적으로 리턴합니다. res.ok 를 확인하지 않으면 실패 응답을 성공으로 오인해 화면이 조용히 잘못된 상태로 남습니다.

javascript
// ✅ res.ok(200~299)가 아니면 서버의 message 로 예외를 던진다
if (!res.ok) throw new Error(`${res.status} ${data.message}`);

❌ 실수 9: v-for 에 :key 를 빠뜨린다

html
<!-- ❌ key 없이 반복 -->
<li v-for="t in todos">{{ t.title }}</li>

목록 순서가 바뀌거나 항목이 지워지면 Vue 가 DOM 을 잘못 재사용해 체크박스 상태가 다른 항목으로 옮겨붙는 버그가 생깁니다. :key="t.id" 처럼 고유값을 반드시 지정해야 합니다.

❌ 실수 10: ref 값에서 .value 를 빼먹는다

JS 코드 안에서 todos.push(...) 처럼 .value 없이 접근하면 배열이 아니라 ref 객체 자체를 다루게 되어 오류가 나거나 반응성이 끊깁니다. 템플릿 밖에서는 항상 todos.value 로 접근해야 합니다.

javascript
// ✅ JS 코드에서는 .value, 템플릿에서는 자동 언래핑
const load = async () => { todos.value = (await api('GET', '/api/todos')).items; };
자주 하는 실수 (Tip)
  • ❌ 실수 1: CDN 스크립트를 그대로 쓴다
  • ❌ 실수 2: Content-Type 을 빠뜨린다
  • ❌ 실수 3: 확장자 없는 경로를 폴백하지 않는다
  • ❌ 실수 4: 반대로 모든 404 를 index.html 로 돌린다
  • ❌ 실수 5: index.html 이 캐시돼 배포 후 옛 화면이 보인다
  • ❌ 실수 6: 라이브러리 파일명을 버전 없이 덮어쓴다
  • ❌ 실수 7: 204 응답에 res.json() 을 호출한다
  • ❌ 실수 8: res.ok 를 안 보고 항상 성공 처리한다
  • ❌ 실수 9: v-for 에 :key 를 빠뜨린다
  • ❌ 실수 10: ref 값에서 .value 를 빼먹는다
이전 섹션4 응용 변형 예제5 / 7다음 섹션6 연습 문제