본 문서는 SpringBoot와 Vue를 활용한 학생 취업 정보 관리 시스템의 설계 및 구현 과정을 설명합니다. 전후방 분리 아키텍처를 채택하여 개발 효율성과 유지보수성을 높였으며, RESTful API 기반으로 데이터 통신을 수행합니다.
주요 기능 요약
- 사용자 인증 및 권한 관리
- 학생 정보 등록 및 조회
- 취업 상태 추적 및 통계 제공
- 웹소켓 기반 실시간 알림 기능
- 민감 정보 자동 탈민감화 처리
핵심 코드 예제
/**
* 사용자 비밀번호 초기화 엔드포인트
*/
@IgnoreAuth
@PostMapping("/init-password")
public ApiResponse initializePassword(
@RequestParam("account") String account,
HttpServletRequest request) {
UserRecord user = userService.findUserByAccount(account);
if (user == null) {
return ApiResponse.error("해당 계정이 존재하지 않습니다.");
}
// 기본 비밀번호로 재설정 (암호화 적용)
String defaultPwd = "123456";
user.setPassword(EncryptionTool.encrypt(defaultPwd));
userService.updateUser(user);
return ApiResponse.success("비밀번호가 성공적으로 '123456'로 초기화되었습니다.");
}
/**
* 페이징 기반 사용자 목록 조회 (관리자용)
*/
@GetMapping("/admin/users")
public ApiResponse getPagedUserList(
@RequestParam Map queryParams,
UserQueryCondition condition) {
EntityWrapper<UserRecord> wrapper = new EntityWrapper<>();
QueryUtil.buildSearchConditions(wrapper, condition, queryParams);
PaginationResult result = userService.getPagedData(queryParams,
QueryUtil.sort(QueryUtil.applyFilters(wrapper, queryParams), queryParams));
// 민감 필드 탈민감화 처리
Map maskFields = Map.of("phone", "PHONE", "email", "EMAIL");
DataMasker.anonymize(result, maskFields);
return ApiResponse.ok("조회 성공", result);
}
/**
* 일반 사용자용 목록 조회 (인증 불필요)
*/
@AnonymousAccess
@GetMapping("/public/users")
public ApiResponse getPublicUserList(
@RequestParam Map params,
UserQueryCondition queryObj) {
EntityWrapper<UserRecord> filter = new EntityWrapper<>();
QueryUtil.buildSearchConditions(filter, queryObj, params);
PaginationResult pageData = userService.getPagedData(params,
QueryUtil.orderBy(QueryUtil.rangeFilter(QueryUtil.keywordMatch(filter, queryObj), params), params));
DataMasker.anonymize(pageData, new HashMap<>());
return ApiResponse.ok().setData(pageData);
}
/**
* 웹소켓 메시지 수신 처리
*/
@OnMessage
public void handleMessage(String rawMessage) {
log.info("WebSocket 수신: 세션 ID = {}, 메시지 = {}", this.session.getId(), rawMessage);
if ("ping".equals(rawMessage.trim())) {
sendResponse("pong", this.senderId, this.targetId);
} else {
broadcastMessage(rawMessage, this.senderId, this.targetId);
}
}
/**
* URL 쿼리 파라미터 파싱 유틸리티
*/
private Map parseQueryParameters(String queryString) {
Map paramMap = new ConcurrentHashMap<>();
if (queryString == null || queryString.isEmpty()) return paramMap;
Arrays.stream(queryString.split("&"))
.map(param -> param.split("=", 2))
.forEach(pair -> {
try {
String key = URLDecoder.decode(pair[0], StandardCharsets.UTF_8);
String value = (pair.length > 1) ?
URLDecoder.decode(pair[1], StandardCharsets.UTF_8) : null;
paramMap.put(key, value);
} catch (Exception e) {
log.warn("파라미터 디코딩 실패: {}", pair[0]);
}
});
return paramMap;
}
데이터베이스 스키마 예시
DROP TABLE IF EXISTS `system_log`;
CREATE TABLE `system_log` (
`log_id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '로그 식별자',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '기록 시간',
`user_id` VARCHAR(255) NOT NULL COMMENT '사용자 ID',
`action` TEXT COMMENT '수행 작업',
`request_method` VARCHAR(100) COMMENT 'HTTP 메서드',
`request_params` LONGTEXT COMMENT '요청 파라미터',
`duration_ms` BIGINT COMMENT '처리 시간(밀리초)',
`client_ip` VARCHAR(100) COMMENT '클라이언트 IP 주소',
INDEX idx_user_time (`user_id`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='시스템 동작 로그';
기술 스택 설명
SpringBoot: 스프링 생태계 기반 자바 백엔드 프레임워크로, 자동 구성(auto-configuration), 내장 서버, 스타터 의존성 등을 통해 신속한 서비스 개발이 가능합니다. 복잡한 XML 설정 없이 애너테이션 기반으로 간결하게 구성할 수 있으며, 단일 JAR 파일로 배포가 가능한 독립 실행형 애플리케이션을 생성합니다.
Vue.js: 점진적으로 채택 가능한 프론트엔드 프레임워크로, 반응형 데이터 바인딩과 컴포넌트 기반 UI 개발을 지원합니다. 가상 DOM 기반 렌더링과 간결한 템플릿 문법을 통해 직관적인 뷰 개발이 가능하며, Vue Router와 Pinia를 조합하면 완전한 SPA 애플리케이션 구축이 가능합니다.
전후방 분리 아키텍처를 통해 각 계층의 독립성을 확보하고, JSON 기반 REST API로 HTTP 통신을 수행함으로써 유연한 시스템 확장과 팀별 병렬 개발이 가능합니다.