Spring Boot 기반 백엔드와 Vue 3 프론트엔드로 구축한 가사 서비스 플랫폼 개발 가이드

가사 관리 솔루션의 아키텍처 설계 및 구현 방법론

현대적인 웹 애플리케이션을 구축하기 위해 마이크로서비스 아키텍처와 반응형 UI 프레임워크를 결합하는 것은 필수적인 역량입니다. 본 문서에서는 가정 서비스 매칭 시스템을 예로 들어, 백엔드는 Spring Boot 생태계를 활용하고 프론트는 Vue 3 기술 스택을 적용하여 클라이언트와 서버 간 효율적인 통신 구조를 만드는 과정을 다룹니다. 또한 데이터베이스 정상화 원리를 적용한 스키마 설계부터 프로덕션 환경 배포까지의 엔드투엔드 워크플로우를 제시합니다.

1. 서버 사이드 프레임워크 초기화 및 자동 설정 메커니즘

Java 기반 엔터프라이즈 애플리케이션에서 Spring Boot 는 빈 정의 파일의 부재감을 해소하고 컨테이너 내장 배포를 용이하게 합니다. 핵심 개념은 조건부 빈 등록과 자동 구성 Starter 입니다.

1.1 프로젝트 의존성 관리 및 모듈 구조

Maven 빌드 도구를 사용하여 외부 라이브러리를 통합 관리합니다. 웹 인터페이스 노출을 위해 Tomcat 은 내부에 포함되며, ORM 을 위한 MyBatis 와 인증을 위한 Spring Security 가 기본적으로 필요합니다.

<!-- pom.xml : 주요 라이브러리 의존성 -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>3.0.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

소스 코드 조직화는 계층적 패턴을 따릅니다. 도메인 모델링, 로직 처리, 레이어 접근 등 책임의 분리 (SoC) 를 명확히 하여 유지보수성을 높입니다.

src/main/java
└── com/housecare/app
    ├── config         # 보안, 시리얼화 설정
    ├── controller     # HTTP 요청 처리 (REST API)
    ├── service        # 비즈니스 규칙 구현
    ├── repository     # DB 페어싱 인터페이스 (DAO)
    ├── entity         # 테이블 매핑 클래스
    └── dto            #数据传输 객체 (데이터 입출력)

JDK 17 이상 환경에서 IntelliJ Idea 를 통해 프로젝트를 생성한 후, application.properties 또는 application.yml 파일을 통해 포트 번호, JDBC 연결 정보 등을 명시합니다.

2. 클라이언트 측 인터랙티브 앱 개발 전략

단일 페이지 애플리케이션 (SPA) 은 사용자의 체류 시간을 늘리고 복잡한 상태 관리를 지원합니다. Vue 3 의 컴포지션 API 를 사용하면 코드의 논리적 단위를 명확히 묶을 수 있습니다.

2.1 반응성 데이터 처리 및 라이프사이클

컴포넌트의 생명주기는 초기화부터 해체까지 이어집니다. 특히 DOM 이 준비된 시점과 데이터 변경 시점을 구분하여 자원을 효율적으로 관리해야 합니다.

// lifecycle-diagram.mermaid
sequenceDiagram
    participant C as Client
    participant F as Framework
    participant D as DOM

    C->>F: 컴포넌트 인스턴스 생성
    F->>D: 템플릿 렌더링
    D-->F: 마운트 완료 (onMounted)
    loop 데이터 바인딩
        C->>F: 속성 값 변경 (ref/update)
        F->>D: 뷰 업데이트 (Reactive Effect)
    end
    F->>D: 컴포넌트 해체 (onUnmounted)

Vue 3 에서 반응성은 Proxy 객체를 기반으로 동작합니다. 이를 이해하면 성능 저하 원인을 파악하는 데 도움이 됩니다.

// proxy-demo.ts
export function createReactive<T extends object>(target: T): T {
  return new Proxy(target, {
    get(targetObj, key: string) {
      console.log(`Reading property: ${key}`);
      return targetObj[key];
    },
    set(targetObj: any, key: string, value: any) {
      console.log(`Updating ${key} to ${value}`);
      targetObj[key] = value;
      return true; // 변경 감지 시 UI 갱신 트리거
    }
  });
}

실제 서비스 목록 조회 시에는 계산된 속성 (Computed Property) 을 활용해 불필요한 렌더링을 방지합니다.

<script setup lang="ts">
import { ref, computed } from 'vue';

const rawServices = ref([...]);
const searchQuery = ref('');

// 검색어 변경 시만 필터링 수행
const visibleItems = computed(() => 
  rawServices.value.filter(item => 
    item.name.includes(searchQuery.value)
  )
);
</script>

2.2 라우팅 관리 및 인증 보호

Vue Router 를 이용해 URL 변화에 따른 화면 전환을 제어하며, 민감한 페이지는 토큰 검증 후 접근 허용합니다.

// router/index.ts
import { createRouter, createWebHistory } from 'vue-router';

const routes = [
  { path: '/', component: () => import('../views/Home.vue') },
  { 
    path: '/services', 
    component: () => import('../views/List.vue'),
    meta: { requiresAuth: true } 
  }
];

const router = createRouter({ history: createWebHistory(), routes });

// 전역 미들웨어: 로그인 여부 확인
router.beforeEach((to, _, next) => {
  const token = localStorage.getItem('auth_token');
  if (to.meta.requiresAuth && !token) {
    return next('/login');
  }
  next();
});

개발 서버에서의 CORS 문제를 해결하기 위해 Webpack 또는 Vite 설정 파일 내에 프록시 규칙을 추가합니다. 이는 브라우저 레벨의 제한을 우회하여 로컬 백엔드 서버에 직접 연결되도록 합니다.

3. 관계형 데이터베이스 정규화 및 스키마 설계

데이터 무결성을 보장하기 위해 제 3 차 정규형 (3NF) 준수 여부를 검토합니다. 중복되는 데이터 저장소를 제거하여 일관된 정보 흐름을 만듭니다.

3.1 엔티티 관계 도식 (ERD)

시스템의 주체는 고객, 전문 요원, 서비스 항목, 그리고 거래 내역으로 나뉩니다. 각 테이블 간의 외래 키约束를 통해 참조 무결성을 확보합니다.

-- members 테이블 (사용자 정보)
CREATE TABLE `members` (
  `uid` BIGINT AUTO_INCREMENT PRIMARY KEY,
  `email` VARCHAR(100) UNIQUE NOT NULL,
  `password_enc` VARCHAR(255) NOT NULL,
  `role_type` ENUM('CLIENT','STAFF','ADMIN') DEFAULT 'CLIENT',
  `join_date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- services 테이블 (제공 가능한 서비스 정의)
CREATE TABLE `service_catalog` (
  `sid` BIGINT AUTO_INCREMENT PRIMARY KEY,
  `category_code` VARCHAR(20),
  `unit_price` DECIMAL(10, 2),
  `description` TEXT,
  INDEX idx_category (`category_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- transactions 테이블 (예약 및 주문 기록)
CREATE TABLE `transactions` (
  `tid` BIGINT AUTO_INCREMENT PRIMARY KEY,
  `requester_uid` BIGINT NOT NULL,
  `provider_uid` BIGINT, -- 수락된 경우 해당 ID 저장
  `catalog_sid` BIGINT NOT NULL,
  `scheduled_at` DATETIME NOT NULL,
  `status_enum` INT DEFAULT 0 COMMENT '0:pending, 1:confirmed, 2:done, 3:canceled',
  FOREIGN KEY (`requester_uid`) REFERENCES `members`(`uid`),
  FOREIGN KEY (`catalog_sid`) REFERENCES `service_catalog`(`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

쿼리 성능 향상을 위해 자주 검색되는 컬럼이나 조인 기준 컬럼에 인덱스를 적용합니다. 특히 상태 (Status) 와 날짜 범위查询에 대한 최적화가 중요합니다.

4. 보안 인증 및 비즈니스 로직 구현

분산 시스템 환경에서는 세션 대신 JSON Web Token(JWT) 방식을 선호합니다. 클라이언트가 서버로 전송할 때마다 인증 헤더에 토큰을 포함시키고, 서버는 이를 검증한 뒤 요청을 처리합니다.

4.1 인증 필터 구현

Spring Security 의 필터 체인에 커스텀 인터셉터를 등록하여 토큰 유효성을 사전에 체크합니다.

@Component
public class SecurityHeaderValidator implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
            throws IOException, ServletException {
        
        String authHeader = ((HttpServletRequest)req).getHeader("Authorization");
        
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String tokenPayload = authHeader.substring(7);
            // 서명 및 만료일 검증 로직 호출
            // 유효하다면 SecurityContext 에_principal_정보 저장
        }
        chain.doFilter(req, res);
    }
}

4.2 주문 상태 전이 관리

예약 건의 상태 변화는 엄격한 규칙을 따라야 합니다. 상태 머신 (State Machine) 을 단순 Enum 기반으로 설계하거나 전용 스프링 상태 머신 라이브러리를 도입할 수 있습니다.

  • NEW: 신규 접수
  • PAYING: 결제 진행 중
  • ACCEPTED: 전문가 수락 완료
  • IN_PROGRESS: 작업 시작
  • FINISHED: 완료 및 평가 대기
  • CANCELLED: 취소 처리

동시에 동일한 시간대를 여러 고객이 예약하지 않도록 락 (Lock) 매커니즘이나 데이터베이스 트랜잭션 격리 수준 (Transaction Isolation Level) 을 적절히 조정해야 충돌을 막을 수 있습니다.

5. 인프라 구축 및 운영 환경 설정

개발 완성 후에는 Linux 기반 서버에 안정적으로 배포되어야 합니다. JAR 파일 실행과 정적 리소스 호스팅을 위한 설정이 병행됩니다.

5.1 백엔드 배포 스크립트

서버 재시작 시에도 프로세스가 살아있게 만들기 위해 Bash 스크립트를 작성합니다. 로그 파일 회전 정책을 적용하여 디스크 공간을 절약합니다.

#!/bin/bash
SCRIPT_DIR="/opt/home-service"
JAR_FILE="app.jar"

case "$1" in
    start)
        cd $SCRIPT_DIR
        nohup java -jar $JAR_FILE & disown
        echo "App started."
        ;;
    stop)
        pid=$(ps -ef | grep $JAR_FILE | grep -v grep | awk '{print $2}')
        kill $pid
        echo "Process stopped."
        ;;
    *)
        echo "Usage: $0 {start|stop}"
        ;;
esac

5.2 Nginx 를 통한 로드 밸런싱 및 정적 파일 서빙

Nginx 는 프런트엔드 빌드 결과물을 제공하면서 동시에 API 요청을 백엔드 서버로 위임하는 프록시로 작동합니다.

server {
    listen 80;
    server_name home-care.example.com;

    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html;
    }

    location /api/v1 {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    
    # GZIP 압축 활성화
    gzip on;
    gzip_min_length 1k;
}

이러한 설정을 통해 사용자는 단일 도메인을 통해 모든 기능에 접근할 수 있으며, Nginx 가 네트워크 오버헤드를 줄여주는 역할을 수행하게 됩니다.

태그: spring-boot Vue.js MySQL jwt nginx

5월 21일 01:11에 게시됨