시스템 아키텍처 개요
본 프로젝트는 실종된 물건을 등록하고 조회할 수 있는 포털 시스템으로, 백엔드는 Spring Boot, 프론트엔드는 Vue.js, 모바일 클라이언트는 UniApp를 사용하여 다중 플랫폼 호환성을 확보하였습니다. 전체 시스템은 모듈화된 구조로 설계되어 유지보수성과 확장성이 뛰어납니다.
기술 스택 구성
- 백엔드: Spring Boot 3.x 기반, 내장형 Tomcat 서버, 자동 설정 기능 제공
- 데이터베이스 접근: MyBatis-Plus 3.5+, SQL 생성 최소화, 코드 생성기 포함
- 프론트엔드: Vue 3 + Vite, 컴포넌트 기반 개발, 반응형 상태 관리
- 모바일 앱: UniApp (Vue 기반 하이브리드 앱), 하나의 코드로 안드로이드/아이폰/웹 지원
- 인증 보안: JWT 기반 토큰 인증, 역할 기반 접근 제어 (RBAC)
핵심 기능 설계
사용자 인증 및 세션 관리
로그인 요청 시 입력된 계정 정보와 데이터베이스 저장값을 비교하며, 비밀번호 일치 여부를 검증합니다. 성공 시 1시간 유효기간의 임시 토큰을 발급하고, 해당 토큰은 세션에 저장됩니다.
@PostMapping("/auth/login")
public ResponseEntity<Map> authenticateUser(@RequestParam String username,
@RequestParam String password,
@RequestParam String captcha) {
UsersEntity user = userService.findByUsername(username);
if (user == null || !BCrypt.checkpw(password, user.getPassword())) {
return ResponseEntity.status(401).body(Map.of("msg", "계정 또는 비밀번호가 잘못되었습니다."));
}
String token = jwtTokenProvider.generateToken(user.getId(), user.getRole(), Duration.ofHours(1));
Map<String, Object> response = new HashMap<>();
response.put("token", token);
response.put("userId", user.getId());
response.put("role", user.getRole());
return ResponseEntity.ok(response);
}
JWT 인증 필터 구현
HTTP 요청 전에 토큰을 검증하는 인터셉터를 통해 권한 없는 접근을 차단합니다. 요청 헤더에서 Authorization: Bearer <token>를 추출하여 유효성 확인 후 세션에 사용자 정보를 저장합니다.
@Component
public class JwtAuthFilter implements Filter {
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String token = extractToken(httpRequest);
if (token != null && jwtTokenProvider.validateToken(token)) {
Long userId = jwtTokenProvider.getUserIdFromToken(token);
String role = jwtTokenProvider.getRoleFromToken(token);
// 세션에 사용자 정보 저장
httpRequest.getSession().setAttribute("userId", userId);
httpRequest.getSession().setAttribute("role", role);
} else {
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpResponse.getWriter().write("{\"error\": \"인증되지 않은 요청입니다.\"}");
return;
}
chain.doFilter(request, response);
}
private String extractToken(HttpServletRequest request) {
String bearer = request.getHeader("Authorization");
if (bearer != null && bearer.startsWith("Bearer ")) {
return bearer.substring(7);
}
return null;
}
}
데이터베이스 설계
실종물 정보, 사용자 정보, 로그인 토큰 등을 저장하기 위한 테이블 구조를 설계하였습니다.
-- 토큰 저장 테이블
CREATE TABLE auth_token (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
username VARCHAR(100) NOT NULL,
table_name VARCHAR(100),
role VARCHAR(50) NOT NULL,
token VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NOT NULL,
INDEX idx_token (token),
INDEX idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 실종물 등록 정보 테이블
CREATE TABLE lost_item (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(200) NOT NULL,
description TEXT,
category VARCHAR(50),
location VARCHAR(100),
contact_info VARCHAR(100),
status ENUM('등록', '확인됨', '처리완료') DEFAULT '등록',
creator_id BIGINT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_status (status),
INDEX idx_creator (creator_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
기능 테스트 사례
주요 기능에 대해 흑상자 테스트를 수행하여 정확성과 안정성을 검증했습니다.
| 테스트 항목 | 입력 값 | 예상 결과 | 실제 결과 |
|---|---|---|---|
| 로그인 실패 (비밀번호 오류) | username: admin, password: wrong123 | 비밀번호 오류 메시지 출력 | 정상 출력됨 |
| 토큰 만료 후 접근 시도 | 유효기간 지난 토큰 사용 | 401 Unauthorized 응답 반환 | 정상 처리됨 |
| 관리자 권한 없이 삭제 시도 | 학생 계정으로 다른 사용자 삭제 요청 | 권한 부족 메시지 표시 | 정상 작동 |
시스템 운영 및 배포 가이드
배포 시에는 Docker 컨테이너 기반으로 백엔드와 MySQL DB를 분리 구성하며, Nginx를 통해 프론트엔드 정적 파일을 제공합니다. CI/CD 파이프라인은 GitHub Actions 기반으로 자동 빌드 및 테스트를 수행합니다.
- 백엔드:
mvn clean package -DskipTests→ JAR 생성 - DB 초기화:
spring-boot:run시 자동 스키마 생성 - 프론트엔드:
npm run build→ dist 폴더 생성 → Nginx 매핑 - 배포 환경: Linux + Docker + Nginx + MySQL 8.0+
결론
이 시스템은 요구사항에 부합하는 기능성과 안정성을 갖추며, 다양한 플랫폼에서 동작 가능하도록 설계되었습니다. 유저 경험 중심의 인터페이스와 강력한 인증 메커니즘을 통해 신뢰성 높은 서비스를 제공합니다.