
스펙 오디세이
취업 준비생의 스펙을 진단하고 목표 직무까지의 로드맵과 매일 할 일을 제시하는 웹 서비스
내 역할
- 자소서·이력서 첨삭 AI: LLM 프롬프트를 설계하고 STAR 기법 기준으로 "추가하면 좋을 내용"을 안내하도록 개선
- 비밀번호 해시 전환: PBKDF2 → Argon2id로 교체. 예전 해시도 검증되게 두고, 로그인할 때 자동으로 새 해시로 다시 저장해 기존 회원이 비밀번호를 바꾸지 않아도 옮겨지게 함
- 테스트 계정 시뮬레이션: 목표 점수까지 하루치 활동을 몇 분 안에 쌓아, 오래 써야 보이는 화면을 시연·검증할 수 있게 함 (일시정지·초기화 지원)
- 스펙 아카이브 게시판: 사진이 다른 팀원 서버에서도 보이도록 파일을 DB에 저장하는 방식으로 수정
- 데스크톱 캐릭터 앱: 하루 요약·트렌드·자동 실행·설치 파일·사이트 내 업데이트 확인까지 구현
자세히 보기: 구조 · DB · 트러블슈팅 · 시연 영상
시연 영상
구조
Jakarta Servlet 기반의 계층 구조입니다. 요청은 필터에서 로그인·권한을 확인한 뒤 서블릿 → 서비스 → DAO 순서로 내려가고, DB 연결은 HikariCP 풀로 관리합니다. 숫자는 저장소의 클래스 수입니다.
DB (핵심 테이블 12개)
전체 55개 테이블 중 회원·직무·기술·로드맵 흐름과 제가 손댄 테이블만 골랐습니다. 진한 테두리가 직접 만들거나 바꾼 테이블이고, 선 끝의 점이 외래키를 가진 쪽입니다.
트러블슈팅
1기존 회원을 깨지 않고 비밀번호 해시 방식 바꾸기
- 상황
- 회원 비밀번호와 계정 복구 코드를 PBKDF2로 저장하고 있었고, 메모리를 많이 쓰게 해서 GPU 대입 공격에 더 강한 Argon2id로 바꾸기로 했습니다.
- 원인
- 해시는 되돌릴 수 없어서, 저장 방식을 바꾸면 이미 가입한 회원의 해시를 새 방식으로 바꿀 방법이 없습니다. 전원에게 비밀번호를 다시 정하게 할 수도 없었습니다.
- 해결
- 저장된 문자열이
$argon2id$로 시작하는지로 형식을 구분해, 예전 PBKDF2 해시는 검증만 계속 지원했습니다. 로그인에 성공한 순간에는 평문 비밀번호를 알고 있으므로 그때 Argon2id로 다시 해시해 저장합니다. 설정값(OWASP 권장 최소치 m=19MiB, t=2, p=1)을 나중에 올려도 같은 경로로 갱신되고, 해시가 끝나면 평문 문자 배열을 지웁니다.// UserService.login: 비밀번호 확인 직후 if (PasswordUtil.needsRehash(user.getPasswordHash())) { String upgraded = PasswordUtil.hash(rawPassword); userDao.updatePasswordHash(user.getId(), upgraded); } - 결과
- 회원은 아무것도 하지 않아도 다음 로그인 때 새 방식으로 옮겨집니다. 복구 코드도 같은 유틸을 씁니다.
2다른 팀원 서버에서 게시판 사진이 안 보임
- 상황
- 팀원마다 자기 PC에서 서버를 켜고 같은 DB를 함께 쓰는 방식으로 개발했습니다. 스펙 아카이브 게시판에 사진을 올리면 올린 사람 화면에서만 보이고, 다른 팀원 서버에서는 깨졌습니다.
- 원인
- 글 정보는 공용 DB에 들어가지만 사진 파일은 올린 사람 PC의 업로드 폴더에 저장되고 있었습니다.
- 해결
- 첨부 테이블에
file_data(MEDIUMBLOB, 최대 16MB) 컬럼을 추가해 사진 내용을 DB에 저장했습니다. 목록·상세 조회는 이 컬럼을 읽지 않고, 이미지 주소(/spec-archive/image/{{id}})로 요청할 때만 읽어 조회가 무거워지지 않게 했습니다. 예전에 디스크에 저장된 사진은 기존 경로로 읽도록 남겨 두었습니다. - 결과
- 어느 팀원 서버에서 열어도 같은 사진이 보입니다.
3몇 달 쓴 화면을 몇 분 만에 만들기
- 상황
- 등급, 연속 기록, 로드맵 진행처럼 오래 써야 보이는 기능은 시연하거나 확인하려면 실제로 몇 주씩 써야 했습니다.
- 원인
- 점수를 DB에 직접 넣으면 화면은 채워지지만 실제 로직(미션 채점, 연속 보너스, 등급 계산)을 거치지 않아 검증이 되지 않습니다. 서비스 코드가 각자 현재 날짜를 읽고 있어서 과거 날짜로 실행할 수도 없었습니다.
- 해결
- 서비스가 "오늘"을
AppClock을 통해서만 읽게 바꾸고, 시뮬레이션 작업 스레드에서만 ThreadLocal로 날짜를 그날로 바꿔 끼웠습니다. 그래서 미션·점수·연속 보너스·로드맵이 실제 사용자와 같은 코드로 하루씩 실행되고, 다른 요청 스레드는 실제 날짜 그대로입니다. 성향(성실형·보통형·작심삼일형)마다 접속 확률과 정답률이 달라 판마다 결과가 다르고, 테스트 계정만 쓸 수 있으며 초기화도 그 계정 데이터만 지웁니다. - 결과
- 목표 점수를 넣고 시작하면 수십 일치 기록이 몇 분 안에 쌓이고, 화면은 새로고침 없이 바뀝니다. 위 대시보드 스크린샷과 시연 영상이 이 기능으로 만든 화면입니다.




