공공부하자개발 · 영어 학습 노트
자바
실무 확장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정리‹ 이전다음 ›

3. 코드 예제

실행 방법입니다. 외부 jar 는 필요 없습니다.

powershell
cd java-src\extension\08_vue_spa
C:\project\jdk-21.0.8\bin\javac -encoding UTF-8 *.java
C:\project\jdk-21.0.8\bin\java -Dstdout.encoding=UTF-8 Main         # 8080, 브라우저 http://127.0.0.1:8080/
C:\project\jdk-21.0.8\bin\java -Dstdout.encoding=UTF-8 Main --demo  # 11개 시나리오 자동 실행

예제 1: 두 컨텍스트 등록 — 긴 접두사가 먼저 매칭

java
// Main.main
server.createContext("/api/todos", new TodoApi());          // 이 접두사가 더 길다
server.createContext("/", new StaticHandler(Path.of("static")));

HttpServer 는 요청 경로와 가장 길게 일치하는 컨텍스트를 고릅니다. /api/todos 가 / 보다 구체적이므로, /api/todos 로 시작하는 요청은 TodoApi 로, 나머지는 모두 StaticHandler 로 갑니다.

예제 2: 정적 서빙과 SPA 폴백

java
// StaticHandler.handle
boolean hasExt = file.getFileName().toString().contains(".");
if (!hasExt && !Files.isRegularFile(file)) file = root.resolve("index.html");
if (!Files.isRegularFile(file)) { send(ex, 404, "text/plain", "not found"); return; }

실행 결과입니다.

text
[1] GET / → index.html
  200 text/html; charset=utf-8 app 루트 포함=true
[2] GET /js/plugin/vue.global.prod.js → 장기 캐시
  200 application/javascript; charset=utf-8 Cache-Control=public, max-age=31536000, immutable 154807자
[3] GET /todos/3 (SPA 라우트, 파일 없음) → index.html 로 폴백
  200 text/html; charset=utf-8 Cache-Control=no-cache
[4] GET /css/none.css (확장자 있음, 파일 없음) → 404
  404
[5] GET /%2e%2e/Main.java (경로 탐색) → 404
  404

예제 3: TodoApi 의 메서드·id 분기

java
// TodoApi.handle
String rest = ex.getRequestURI().getPath().substring("/api/todos".length());
Long id = rest.length() > 1 ? Long.parseLong(rest.substring(1)) : null;
if (id == null && method.equals("GET")) { /* 목록 */ }
else if (id == null && method.equals("POST")) { /* 생성, title 검증 */ }
else if (id != null && method.equals("PATCH")) { /* 부분 수정 */ }
else if (id != null && method.equals("DELETE")) { /* 삭제, 204 */ }

Long.parseLong 이 던지는 NumberFormatException 은 IllegalArgumentException 의 하위 타입이라 같은 catch 블록에서 400 으로 처리됩니다.

실행 결과입니다.

text
[6] POST /api/todos 2건 → 201
  201 {"done":false,"id":1,"title":"우유 사기"}
  201 {"done":false,"id":2,"title":"보고서 쓰기"}
[7] POST /api/todos 제목 없음 → 400
  400 {"message":"title 은 필수입니다","status":400}
[8] PATCH /api/todos/1 완료 처리 → 200
  200 {"done":true,"id":1,"title":"우유 사기"}
[9] GET /api/todos → 목록
  200 {"total":2,"items":[{"done":true,"id":1,"title":"우유 사기"},{"done":false,"id":2,"title":"보고서 쓰기"}]}
[10] DELETE /api/todos/1 → 204, 다시 조회 → 404
  204
  404 {"message":"할 일이 없습니다","status":404}
[11] GET /api/todos/abc (id 형식 오류) → 400
  400 {"message":"For input string: \"abc\"","status":400}

예제 4: index.html 템플릿과 app.js

html
<!-- index.html: 반복·조건·양방향 바인딩 -->
<form @submit.prevent="add">
  <input v-model.trim="title" placeholder="할 일 입력" required>
  <button :disabled="busy">추가</button>
</form>
<li v-for="t in todos" :key="t.id">
  <input type="checkbox" :checked="t.done" @change="toggle(t)">
</li>
javascript
// app.js: 변경 후 재조회 패턴
const run = async (fn) => {
  busy.value = true; error.value = '';
  try { await fn(); await load(); }
  catch (e) { error.value = e.message; }
  finally { busy.value = false; }
};

브라우저에서 http://127.0.0.1:8080/ 을 열면 할 일을 추가·완료·삭제할 수 있고, 새로고침해도 서버가 다시 조회해 같은 화면이 나옵니다.

개발자 도구의 Network 탭을 열어 두면 이 레슨의 구조가 그대로 보입니다. 첫 진입 시 index.html·app.css·vue.global.prod.js·app.js 요청이 순서대로 뜨고, 이후 추가·완료·삭제마다 /api/todos 로 시작하는 요청만 새로 나갑니다.

vue.global.prod.js 요청의 응답 헤더에서 Cache-Control: public, max-age=31536000, immutable 을 확인할 수 있습니다. 새로고침을 반복해도 이 파일은 캐시에서 즉시 로드됩니다.

예제 직접 실행

아래 폴더를 JDK 21 로 컴파일하고 실행합니다. 외부 jar 를 쓰는 레슨은 java-src/lib 를 클래스패스에 넣습니다.

cd java-src\extension\08_vue_spa
javac -encoding UTF-8 *.java && java Main

:: 외부 jar 가 필요한 레슨
javac -encoding UTF-8 -cp "..\..\lib\*;." *.java && java -cp "..\..\lib\*;." Main
코드 예제
  • 예제 1: 두 컨텍스트 등록 — 긴 접두사가 먼저 매칭
  • 예제 2: 정적 서빙과 SPA 폴백
  • 예제 3: TodoApi 의 메서드·id 분기
  • 예제 4: index.html 템플릿과 app.js
이전 섹션2 핵심 원리3 / 7다음 섹션4 응용 변형 예제