BackEND/Java

Vue 프론트엔드와 Spring Boot 백엔드 연동 구조 실전 가이드

mingmingIT 2025. 5. 13. 10:14

Vue 3를 이용한 프론트엔드와 Spring Boot를 이용한 백엔드를 연동하려면, 단순히 "주소만 맞추면 된다"가 아닙니다.
CORS, JWT 인증 처리, DTO 통신, 응답 규약, 예외 처리까지 고려한 구조 설계가 필요합니다.

이번 포스팅에서는 실전 기준으로 아래 내용을 정리해드립니다.


1. 시스템 구성 예시

  • 프론트엔드: Vue 3, TypeScript, Pinia, Axios
  • 백엔드: Spring Boot 3.x, Java 11+, Spring Security, JWT
  • 연동 방식: RESTful API 통신 (JSON 기반)
  • 빌드/배포: FE는 dist/로 빌드되어 Nginx에 정적 호스팅, BE는 API 서버로 독립 운영

2. CORS 설정 – 백엔드에서 허용

Vue 개발 서버는 기본적으로 localhost:5173, 백엔드는 localhost:8080 등 서로 다른 포트를 사용하므로 CORS 오류가 발생할 수 있습니다.

✅ Spring Boot CORS 설정 (전역)

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
                .allowedOrigins("http://localhost:5173")
                .allowedMethods("GET", "POST", "PUT", "DELETE")
                .allowedHeaders("*")
                .allowCredentials(true);
    }
}

💡 allowCredentials(true) 설정 시, 쿠키/Authorization 헤더 사용 가능


3. Axios 기본 설정

// src/lib/axios.ts
import axios from 'axios'

const api = axios.create({
  baseURL: 'http://localhost:8080/api',
  withCredentials: true, // 쿠키 사용 시 필요
})

api.interceptors.request.use((config) => {
  const token = localStorage.getItem('access_token')
  if (token) config.headers.Authorization = `Bearer ${token}`
  return config
})

export default api

4. DTO 연동 구조

🔹 프론트 요청 DTO 예시 (TypeScript)

interface LoginRequest {
  username: string
  password: string
}

🔸 백엔드 요청 DTO 예시 (Java)

public record LoginRequest(String username, String password) {}

✅ DTO 간 이름과 타입을 맞추면 JSON 직렬화/역직렬화가 자연스럽게 됩니다.


5. API 응답 구조 통일

🔹 프론트 기대 응답 형태

interface ApiResponse<T> {
  success: boolean
  data: T
  message?: string
}

🔸 백엔드 공통 응답 래퍼 클래스

public record ApiResponse<T>(boolean success, T data, String message) {
    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(true, data, null);
    }

    public static ApiResponse<?> error(String message) {
        return new ApiResponse<>(false, null, message);
    }
}

📦 사용 예시

@PostMapping("/login")
public ResponseEntity<ApiResponse<TokenResponse>> login(@RequestBody LoginRequest request) {
    TokenResponse token = authService.login(request);
    return ResponseEntity.ok(ApiResponse.success(token));
}

6. 예외 및 에러 처리 구조

프론트에서는 서버 응답이 200이 아니거나 success: false인 경우를 모두 처리해야 합니다.

🔸 Spring @ControllerAdvice 활용

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(CustomAuthException.class)
    public ResponseEntity<ApiResponse<?>> handleAuth(CustomAuthException e) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                             .body(ApiResponse.error(e.getMessage()));
    }
}

🔹 프론트 Axios 에러 핸들링

try {
  const res = await api.post<ApiResponse<TokenResponse>>('/auth/login', payload)
  if (!res.data.success) {
    alert(res.data.message)
  }
} catch (err: any) {
  alert(err.response?.data?.message || '네트워크 오류')
}

7. 인증 흐름 구성 예시 (JWT 기반)

  1. 로그인 요청 → JWT 발급 (/api/auth/login)
  2. Axios 인터셉터로 토큰 자동 전송
  3. 서버는 Spring Security + JWT Filter로 토큰 인증
  4. 인증된 사용자만 데이터 조회 가능 (@PreAuthorize("isAuthenticated()") 등)

8. 실전에서의 팁

항목 설명
CORS 개발 시 허용 포트 설정 필요 (5173 ↔ 8080)
DTO record, interface를 이용한 명확한 타입 정의
인증 JWT → 헤더 처리, 필요 시 쿠키 방식으로 전환
전역 응답 ApiResponse 구조 통일로 클라이언트 처리 일관성
에러 처리 백엔드에서 예외 래핑 + 클라이언트에서 사용자 메시지 표시

✍️ 마무리

Vue와 Spring Boot를 연동할 때 가장 중요한 건 계층 간 통신의 규약을 명확히 하는 것입니다.
DTO와 공통 응답 구조, 인증 흐름, CORS 처리까지 갖추면 프론트-백엔드 개발 분리가 훨씬 원활해집니다.