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

4. 응용 변형 예제

4.1 Spring Boot 로 옮기기

java
@RestController
@RequestMapping("/api/todos")
class TodoController {
    private final TodoService service;

    @GetMapping
    List<Todo> list() { return service.findAll(); }

    @PostMapping
    ResponseEntity<Todo> create(@RequestBody TodoRequest req) {
        Todo t = service.create(req.title());
        return ResponseEntity.status(201).body(t);
    }

    @PatchMapping("/{id}")
    Todo update(@PathVariable long id, @RequestBody TodoRequest req) {
        return service.update(id, req);
    }

    @DeleteMapping("/{id}")
    ResponseEntity<Void> delete(@PathVariable long id) {
        service.delete(id);
        return ResponseEntity.noContent().build();
    }
}

정적 파일은 src/main/resources/static/ 에 그대로 복사하면 Spring Boot 가 자동으로 서빙합니다. SPA 폴백은 2.4 의 SpaFallbackController 하나로 대체됩니다.

TodoApi 의 if-else 분기가 @GetMapping/@PostMapping/@PatchMapping/@DeleteMapping 어노테이션으로 나뉘고, id 형식 오류는 Spring 이 알아서 400 으로 바꿔줍니다. MiniJson.read/write 가 하던 일은 Jackson 이 @RequestBody/반환값 직렬화로 대신합니다.

StaticHandler 의 캐시 정책(2.3)은 application.yml 의 spring.web.resources.cache.cachecontrol 로 옮길 수 있습니다.

yaml
spring:
  web:
    resources:
      static-locations: classpath:/static/
      cache:
        cachecontrol:
          max-age: 0        # index.html 등 기본은 짧게, 라이브러리 경로는 별도 설정

4.2 Vue Router 를 플러그인 폴더에 추가

javascript
// vue-router.global.prod.js 도 static/js/plugin 에 둔다
const { createApp } = Vue;
const { createRouter, createWebHistory } = VueRouter;

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: TodoList },
    { path: '/todos/:id', component: TodoDetail },  // 상세 화면
  ],
});
createApp(App).use(router).mount('#app');

서버의 SPA 폴백(2.4) 덕분에 /todos/3 을 새로고침해도 index.html 이 내려오고, Vue Router 가 URL 을 읽어 같은 상세 화면을 다시 그립니다.

javascript
// TodoDetail 컴포넌트: 라우트 파라미터로 상세를 조회
const TodoDetail = {
  props: ['id'],
  template: `<div>{{ todo?.title }}</div>`,
  setup(props) {
    const todo = ref(null);
    onMounted(async () => { todo.value = await api('GET', `/api/todos/${props.id}`); });
    return { todo };
  },
};

props.id 는 라우트 정의의 :id 세그먼트에서 Vue Router 가 자동으로 채워 줍니다. 상세 컴포넌트가 마운트될 때마다 해당 id 로 서버를 다시 조회하므로, 목록과 상세가 서로 다른 시점의 데이터를 들고 있을 위험이 없습니다.

4.3 검색·페이징 파라미터 전달

javascript
// 목록 조회에 검색·페이징 조건을 붙인다
async function loadTodos(q, page, size) {
  const params = new URLSearchParams({ q, page, size });
  return api('GET', `/api/todos?${params}`);
}
java
// TodoApi: query 파싱은 07 레슨의 Page 와 같은 방식
Map<String, String> q = query(ex.getRequestURI().getQuery());
int page = Integer.parseInt(q.getOrDefault("page", "0"));

URLSearchParams 는 q, page, size 를 자동으로 URL 인코딩해 주므로 한글 검색어가 있어도 별도 처리가 필요 없습니다. 서버는 07 레슨의 Page 레코드를 그대로 재사용해 {"total","items"} 형태로 응답하면 화면 쪽 코드도 그대로 재사용됩니다.

4.4 로딩 중복 클릭 방지와 에러 토스트

javascript
const error = ref('');
let errorTimer = null;

function showError(message) {
  error.value = message;
  clearTimeout(errorTimer);
  errorTimer = setTimeout(() => { error.value = ''; }, 3000);  // 3초 후 자동 소거
}
// run() 의 catch 블록에서 error.value = e.message 대신 showError(e.message) 호출
// busy.value 는 그대로 :disabled 에 묶어 중복 클릭을 막는다

busy 는 run() 안에서만 켜고 끄므로, 버튼에 :disabled="busy" 를 걸어 두면 요청이 끝나기 전 두 번째 클릭이 서버에 아예 도달하지 않습니다. 에러 토스트를 자동으로 지우면 사용자가 매번 닫기 버튼을 누르지 않아도 됩니다.

응용 변형 예제
  • 4.1 Spring Boot 로 옮기기
  • 4.2 Vue Router 를 플러그인 폴더에 추가
  • 4.3 검색·페이징 파라미터 전달
  • 4.4 로딩 중복 클릭 방지와 에러 토스트
이전 섹션3 코드 예제4 / 7다음 섹션5 자주 하는 실수 (Tip)