SpringBoot + Vue.js + UniApp 기반 일러스트레이터 의뢰 플랫폼 구축 및 배포 가이드

기술 스택 개요

본 프로젝트는 SpringBoot 백엔드 프레임워크와 Vue.js 프론트엔드 프레임워크를 기반으로 하며, 모바일 클라이언트는 UniApp을 사용하여 개발되었습니다. 이 시스템은 일러스트레이터와 고객 간의 효율적인 협업 플랫폼을 제공합니다.

백엔드 구성 - SpringBoot

SpringBoot는 독립 실행 가능한 프로덕션급 애플리케이션을 빠르게 개발할 수 있도록 설계된 프레임워크입니다. 기본 설정을 통해 복잡한 XML 설정 없이도 애플리케이션을 신속하게 구동할 수 있으며, 내장 서버(Tomcat, Jetty 등)를 지원하여 배포 과정을 단순화합니다.

프론트엔드 구성 - Vue.js

Vue.js는 반응형 데이터 바인딩과 가상 DOM 기술을 활용하여 사용자 인터페이스 개발을 용이하게 만듭니다. 컴포넌트 기반 아키텍처를 통해 코드 재사용성을 높이고 유지보수성을 향상시킬 수 있습니다.

데이터 영속성 - MyBatis

MyBatis는 SQL 쿼리와 자바 코드를 분리하여 데이터베이스 작업을 관리합니다. 동적 SQL 기능을 통해 다양한 조건에 따른 유연한 쿼리 생성이 가능하며, 캐싱 메커니즘을 통해 성능 최적화를 실현합니다.

핵심 코드 예제

// 인증 무시 어노테이션
@SkipAuth
@PostMapping("/authenticate")
public ResponseEntity<Map<String, Object>> authenticate(
    @RequestParam String userCode, 
    @RequestParam String passCode,
    HttpServletRequest req) {
    
    Userinfo userInfo = userService.queryByAccount(userCode);
    
    if(userInfo == null || !userInfo.getPassCode().equals(passCode)) {
        return ResponseEntity.status(401).body(Map.of("message", "인증 실패"));
    }
    
    String sessionToken = tokenGenerator.createSessionToken(
        userInfo.getId(), 
        userCode, 
        userInfo.getUserType()
    );
    
    return ResponseEntity.ok(Map.of("token", sessionToken));
}

// 세션 토큰 생성 로직
@Override
public String createSessionToken(Long userId, String account, String userType) {
    SessionToken existingToken = tokenRepository.findByUserIdAndType(userId, userType);
    String newToken = UUID.randomUUID().toString();
    Date expiryTime = new Date(System.currentTimeMillis() + 3600000); // 1시간 후 만료
    
    if(existingToken != null) {
        existingToken.setTokenValue(newToken);
        existingToken.setExpiryDate(expiryTime);
        tokenRepository.update(existingToken);
    } else {
        tokenRepository.save(new SessionToken(userId, account, userType, newToken, expiryTime));
    }
    
    return newToken;
}

/**
 * 인증 인터셉터 구현
 */
@Component
public class AuthInterceptor implements HandlerInterceptor {
    
    private static final String AUTH_HEADER = "Authorization-Token";
    
    @Autowired
    private TokenService authService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        
        if(request.getMethod().equals("OPTIONS")) {
            response.setStatus(200);
            return false;
        }
        
        SkipAuth skipAnnotation = ((HandlerMethod) handler).getMethodAnnotation(SkipAuth.class);
        if(skipAnnotation != null) {
            return true;
        }
        
        String authToken = request.getHeader(AUTH_HEADER);
        SessionToken tokenInfo = StringUtils.hasText(authToken) ? 
            authService.validateToken(authToken) : null;
            
        if(tokenInfo != null) {
            request.setAttribute("userId", tokenInfo.getUserId());
            request.setAttribute("userType", tokenInfo.getUserType());
            return true;
        }
        
        response.setStatus(401);
        return false;
    }
}

데이터베이스 구조

DROP TABLE IF EXISTS user_addresses;
CREATE TABLE user_addresses (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    user_id BIGINT NOT NULL,
    full_address VARCHAR(200) NOT NULL,
    recipient_name VARCHAR(100) NOT NULL,
    contact_number VARCHAR(20) NOT NULL,
    is_primary BOOLEAN DEFAULT FALSE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS community_posts;
CREATE TABLE community_posts (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    post_title VARCHAR(200),
    post_content TEXT NOT NULL,
    parent_post_id BIGINT,
    author_id BIGINT NOT NULL,
    author_name VARCHAR(100),
    profile_image TEXT,
    status VARCHAR(50),
    pinned BOOLEAN DEFAULT FALSE,
    pin_time DATETIME
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS clients;
CREATE TABLE clients (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    registered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    account_name VARCHAR(100) NOT NULL UNIQUE,
    encrypted_password VARCHAR(100) NOT NULL,
    full_name VARCHAR(100) NOT NULL,
    gender VARCHAR(10),
    mobile_phone VARCHAR(20),
    profile_picture TEXT,
    reward_points DOUBLE DEFAULT 0,
    account_balance DOUBLE DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

샘플 데이터 삽입

INSERT INTO products (product_name, unit_price, product_desc, inventory_count)
VALUES ('iPhone 13 Pro', 1099.99, '고성능 스마트폰', 75);

INSERT INTO products (product_name, unit_price, product_desc, inventory_count)
VALUES ('Galaxy S22 Ultra', 999.99, '안드로이드 플래그십', 120);

INSERT INTO products (product_name, unit_price, product_desc, inventory_count)
VALUES ('PlayStation 5 Digital', 399.99, '차세대 게임 콘솔', 40);

테스트 시나리오

입력값예상 결과실제 결과검증 상태
계정: admin / 비밀번호: 123456 / 인증코드: 정확로그인 성공시스템 접속 완료일치
계정: admin / 비밀번호: wrong / 인증코드: 정확비밀번호 오류"비밀번호 불일치" 표시일치
계정: admin / 비밀번호: 123456 / 인증코드: 잘못됨인증 실패"인증코드 오류" 알림일치

사용자 관리 테스트

작업 내용예상 결과실제 결과검증 상태
신규 사용자 등록 (user1)등록 성공사용자 목록에 추가됨일치
중복 계정 등록 시도등록 거부"계정 중복" 경고 발생일치
사용자 정보 수정변경사항 적용업데이트 완료일치
사용자 삭제 처리목록에서 제거삭제 확인됨일치

태그: SpringBoot Vue.js MyBatis UniApp java

7월 11일 19:58에 게시됨