Full-stack Developer

DB 설계부터 화면까지,
서비스를 끝까지 만드는 개발자 길동현입니다.

Java(JSP/Servlet, JavaFX)와 PHP로 회원·인증, 데이터 수집, 관리자 화면까지 갖춘 팀 프로젝트 4개를 만들었습니다. 비밀번호 해시를 Argon2id로 바꾸고 기존 사용자를 로그인 때 자동으로 옮기는 것처럼, 실제로 운영할 때 생기는 문제를 먼저 챙깁니다.

한눈에 보기

눌러서 각 프로젝트로 바로 이동합니다.

기술 스택

언어

  • Java 17/21
  • PHP
  • SQL
  • JavaScript
  • TypeScript

백엔드

  • JSP / Servlet (Jakarta)
  • Tomcat 10.1·11
  • PDO / mysqli
  • Java Socket (TCP/UDP)
  • Node.js

프론트엔드

  • HTML / CSS
  • JavaFX
  • React
  • Google Charts·Maps
  • Kakao 지도 API

데이터베이스

  • MySQL 8
  • MariaDB
  • HikariCP
  • 정규화 스키마 설계

보안·인증

  • Argon2id
  • bcrypt
  • Prepared Statement
  • 세션 관리

도구·배포

  • Git / GitHub (PR 리뷰)
  • GitHub Actions
  • Maven
  • Docker
  • Cloudflare Pages
  • LLM API

프로젝트

모두 팀 프로젝트입니다. 카드마다 제가 맡은 부분과 해결한 문제를 따로 적었습니다.

스펙 오디세이 대시보드
대시보드 · 테스트 계정 시뮬레이션으로 72일치 사용 기록을 쌓은 화면

스펙 오디세이

2026.09 ~ 2026.10 · 4주 · 팀 프로젝트

취업 준비생의 스펙을 진단하고 목표 직무까지의 로드맵과 매일 할 일을 제시하는 웹 서비스

문제비밀번호 해시 방식을 바꾸면 기존 회원의 해시는 검증할 수 없게 됨
해결예전 PBKDF2 해시도 검증되게 두고, 로그인에 성공하면 그 자리에서 Argon2id로 다시 저장
결과기존 회원이 비밀번호를 바꾸지 않아도 새 방식으로 옮겨짐
  • Java 17
  • JSP/Servlet
  • MySQL
  • HikariCP
  • LLM API
  • GitHub Actions
6.4s → 1.1s대시보드 응답 (팀 성과)
40% → 100%기술명 매칭 정확도 (팀 성과)

내 역할

  • 자소서·이력서 첨삭 AI: LLM 프롬프트를 설계하고 STAR 기법 기준으로 "추가하면 좋을 내용"을 안내하도록 개선
  • 비밀번호 해시 전환: PBKDF2 → Argon2id로 교체. 예전 해시도 검증되게 두고, 로그인할 때 자동으로 새 해시로 다시 저장해 기존 회원이 비밀번호를 바꾸지 않아도 옮겨지게 함
  • 테스트 계정 시뮬레이션: 목표 점수까지 하루치 활동을 몇 분 안에 쌓아, 오래 써야 보이는 화면을 시연·검증할 수 있게 함 (일시정지·초기화 지원)
  • 스펙 아카이브 게시판: 사진이 다른 팀원 서버에서도 보이도록 파일을 DB에 저장하는 방식으로 수정
  • 데스크톱 캐릭터 앱: 하루 요약·트렌드·자동 실행·설치 파일·사이트 내 업데이트 확인까지 구현
자세히 보기: 구조 · DB · 트러블슈팅 · 시연 영상
시연 영상
테스트 계정에서 목표 점수를 3,000점으로 올리고 시뮬레이션을 이어 실행한 화면 (1.4배속). 점수·등급·여정 지도가 새로고침 없이 바뀝니다.
구조

Jakarta Servlet 기반의 계층 구조입니다. 요청은 필터에서 로그인·권한을 확인한 뒤 서블릿 → 서비스 → DAO 순서로 내려가고, DB 연결은 HikariCP 풀로 관리합니다. 숫자는 저장소의 클래스 수입니다.

브라우저JSP 화면 · JS필터 12개로그인·권한·보안 헤더서블릿 54개요청 처리·화면 선택서비스 61개업무 규칙·트랜잭션DAO 61개SQL (PreparedStatement)MySQL테이블 55개스케줄러 5개트렌드·고용24 수집, 점수, 알림외부 APIGroq LLM · 고용24 · Judge0HikariCP커넥션 풀데스크톱 캐릭터 앱전용 API 서블릿으로 연결
DB (핵심 테이블 12개)

전체 55개 테이블 중 회원·직무·기술·로드맵 흐름과 제가 손댄 테이블만 골랐습니다. 진한 테두리가 직접 만들거나 바꾼 테이블이고, 선 끝의 점이 외래키를 가진 쪽입니다.

JOBidPKnameJOB_REQUIRED_SKILLjob_idFKskill_idFKrequired_levelimportanceSKILLidPKnameTECH_ARTICLEidPKuser_idFKskill_idFKUSERSidPKlogin_idpassword_hashrecovery_code_hashdesired_job_idFKis_testUSER_SKILLSuser_idFKskill_idFKproficiencyGAP_ANALYSISidPKuser_idFKjob_idFKTECH_ARTICLE_ATTACHMENTarticle_idFKmime_typefile_dataUSER_DAILY_MISSIONuser_idFKproblem_idFKassigned_dateis_correctSIMULATION_STATEuser_idFKpersonatarget_scoredays_doneROADMAPidPKuser_idFKgap_analysis_idFKROADMAP_STEProadmap_idFKstep_typetierrelated_skill_idFKis_completed
트러블슈팅
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로 날짜를 그날로 바꿔 끼웠습니다. 그래서 미션·점수·연속 보너스·로드맵이 실제 사용자와 같은 코드로 하루씩 실행되고, 다른 요청 스레드는 실제 날짜 그대로입니다. 성향(성실형·보통형·작심삼일형)마다 접속 확률과 정답률이 달라 판마다 결과가 다르고, 테스트 계정만 쓸 수 있으며 초기화도 그 계정 데이터만 지웁니다.
결과
목표 점수를 넣고 시작하면 수십 일치 기록이 몇 분 안에 쌓이고, 화면은 새로고침 없이 바뀝니다. 위 대시보드 스크린샷과 시연 영상이 이 기능으로 만든 화면입니다.
Dragon Fruits 원자재 대시보드
2024년 12월 실제 실행 화면 (최종 보고서 캡처 중 시세·차트 부분)

Dragon Fruits 원자재 가격 대시보드

2024.12 · 팀 프로젝트 (팀 내 최다 커밋)

금·구리·알루미늄·천연가스·국제유가 5대 원자재의 가격 추이, 시세, 관련 뉴스와 기업 통계를 한 화면에 모은 웹 대시보드

문제원자재 가격·시세·뉴스 정보가 여러 곳에 흩어져 있음
해결FRED·Polygon.io·NewsAPI 데이터를 수집해 MySQL에 저장하고 PHP로 조회
결과5대 원자재 정보를 한 화면에서 보는 대시보드 완성 · 팀 내 최다 커밋
  • PHP
  • MySQL
  • FRED API
  • Polygon.io
  • NewsAPI
  • Google Charts·Maps

내 역할

  • 외부 API 3종 연동: FRED(과거 가격), Polygon.io(ETF 시세), NewsAPI(뉴스)에서 받은 데이터를 MySQL 4개 DB에 저장하고 PHP로 조회
  • 대시보드 위젯: 실시간 시세 바, 가격 추이 그래프, 기업·수출국 통계 차트, Google Maps 지도 위젯 구현 및 화면 디자인 통일
  • 보안 정리: 하드코딩된 API 키를 걷어내고 .env + 자체 로더로 설정 분리
자세히 보기: 데이터 수집 흐름 · 트러블슈팅
데이터 수집 흐름
FRED과거 가격EDBFiller.php수집해 저장commodities_dataMySQL DBPolygon.ioETF 시세RealTimeDBFiller.php수집해 저장commodities_data_real_timeMySQL DBNewsAPI원자재 뉴스NewsDBFiller.php수집해 저장newsMySQL DB대시보드 (PHP)그래프 · 시세 바 · 뉴스 · 지도회원 정보는 df DB에 따로 저장. API 키는 .env에서 읽는다.
트러블슈팅
1코드에 박혀 있던 API 키 분리
상황
외부 API 3종을 연동하면서 API 키가 PHP 파일 곳곳에 그대로 적혀 있었습니다.
원인
키가 코드와 함께 저장소에 올라가면 누구나 볼 수 있고, 팀원마다 다른 키를 쓰려면 파일을 직접 고쳐야 했습니다.
해결
키와 DB 접속 정보를 .env 파일로 옮기고, 이를 읽는 작은 로더 함수(env())를 만들어 모든 수집 스크립트가 같은 방식으로 설정을 읽게 했습니다. 저장소에는 값이 빈 .env.example만 둡니다.
결과
코드에 비밀 값이 남지 않고, 팀원은 자기 .env만 만들면 바로 실행할 수 있습니다.
부산 민원 관리자 화면
관리자 화면 · 예시 데이터

부산 지하철 민원 신고 시스템

2025.11 ~ 2025.12 · 팀 프로젝트

지하철 승객이 열차 번호·호선을 골라 민원을 접수하고, 관리자가 처리 상태와 통계를 관리하는 웹 서비스

문제웹 신고 말고 문자로 오는 민원도 받아야 하는데, KT 회선 문자는 IMAP 방식으로 받아지지 않음
해결문자를 메일함으로 받아 DB에 자동 접수하고, 수신 방식을 POP3로 바꿈
결과통신사와 상관없이 문자 민원이 관리자 화면에 같이 쌓임
  • PHP
  • MySQL(MariaDB)
  • PDO
  • bcrypt
  • IMAP/POP3

내 역할

  • 문자 민원 수신: 문자로 들어온 신고를 메일함(Gmail)으로 받아 DB에 넣는 기능을 구현. IMAP에서 POP3로 바꿔 KT 회선 문자도 받을 수 있게 수정하고, 처리한 메시지는 삭제 대신 읽음 표시
  • 관리자 대시보드: 신고 처리 상태 일괄 변경, 통계 화면 추가
  • 입력 UX 개선: 열차 번호·호선 선택을 다이얼 방식으로 바꿔 잘못된 입력을 줄임
자세히 보기: 문자 접수 흐름 · 트러블슈팅
문자 민원 접수 흐름

신고자에게 전화번호를 직접 입력받지 않고, 신고자가 보낸 문자에서 번호를 확인합니다.

① 웹에서 신고번호 없이 익명 저장② 접수코드 발급예: [접수코드: AB12CD34]③ 문자 앱 열기본문에 접수코드 포함④ 문자가 메일로Gmail 메일함 도착⑤ POP3로 메일 읽기SSL 소켓 995 포트⑥ 번호·코드 찾기EUC-KR 변환 후 정규식⑦ 신고에 번호 연결token이 같은 익명 신고⑧ 관리자 화면처리 상태·통계5분이 지나도 번호가 붙지 않은 익명 신고는 자동으로 지운다. 진한 칸: 내가 만든 부분
트러블슈팅
1문자로 보낸 사람의 번호를 웹 신고와 연결하기
상황
웹 신고만 받으면 신고자에게 연락할 방법이 없고, 번호를 직접 입력받으면 잘못 적거나 남의 번호를 적을 수 있습니다.
원인
웹 화면은 신고자의 실제 전화번호를 알 수 없습니다. 번호를 확실히 아는 곳은 신고자가 보낸 문자뿐입니다.
해결
신고를 먼저 익명으로 저장하면서 접수코드를 발급하고, 본문에 [접수코드: …]가 들어간 문자 앱을 바로 엽니다. 문자는 메일 주소로 보내져 Gmail에 쌓이고, 관리자 쪽에서 메일을 읽어 보낸 번호와 접수코드를 찾은 뒤 같은 접수코드의 익명 신고에 번호를 붙입니다. 5분 안에 번호가 붙지 않은 익명 신고는 지웁니다.
결과
신고자는 번호를 입력하지 않아도 되고, 관리자는 실제 문자를 보낸 번호로 신고를 확인할 수 있습니다.
2메일 수신 방식을 IMAP에서 POP3로 바꾸기
상황
처음에는 PHP IMAP 함수로 메일함을 읽었는데, KT 회선에서 보낸 문자가 받아지지 않았습니다.
원인
수신 방식을 POP3로 바꿔 보니 KT 회선 문자도 받아졌습니다. 다만 문자가 메일로 바뀌어 올 때 통신사마다 형식이 달라, 본문이 EUC-KR이나 quoted-printable로 인코딩되어 오면 번호와 접수코드를 그대로 찾을 수 없었습니다.
해결
SSL 소켓(995 포트)으로 POP3 명령을 직접 주고받는 작은 클라이언트를 만들어 최근 메일을 읽고, 제목과 본문의 인코딩을 풀어 UTF-8로 바꾼 뒤 정규식으로 010 번호와 접수코드를 찾게 했습니다.
결과
통신사와 상관없이 문자 민원이 관리자 화면에 같이 쌓입니다.
SafeFood 알레르기 온보딩 화면
직접 만든 온보딩 2단계 · 알레르기와 심각도 선택

SafeFood 알레르기 안전 음식 추천

2026.08 · 3인 팀 프로젝트

취향·알레르기·기분·위치를 바탕으로 메뉴와 맛집을 추천하고, 여러 명이 함께 먹을 때 모두가 먹을 수 있는 메뉴를 찾아주는 데스크톱 앱

문제Controller가 DAO를 직접 부르고 화면이 임시 데모 데이터에 묶여 있음
해결서비스 계층을 두어 접근 경로를 정리하고 데모 데이터를 걷어내 DB와 연결
결과가입 → 온보딩 → 프로필 수정이 실제 DB로 동작
  • Java 21
  • JavaFX
  • MySQL (테이블 22개)
  • JDBC
  • TCP 소켓
  • Kakao 지도

내 역할: 계정·온보딩·기록·지도

  • 회원가입·로그인·온보딩: 가입 직후 취향·알레르기·가격·거리·맵기를 받아 DB에 저장하고 프로필에서 수정
  • 계층 분리: Controller가 DAO에 직접 접근하지 않도록 서비스 계층을 두고, 임시 데모 데이터를 걷어내 DB와 연결
  • 게스트 참여·같이 먹기 활성화, 식사 기록(차단한 메뉴는 추천에서 제외), 지도 보기·찜하기
자세히 보기: 구조 · 트러블슈팅
구조
JavaFX 화면FXML + Controller서비스 16개인증·온보딩·추천·기록DAO 17개JDBCMySQL테이블 22개외부 API나이스 급식 · Kakao 지도같이 먹기 방 (팀원 담당)TCP 소켓 서버·클라이언트, 초대 코드진한 칸: Controller가 DAO를 바로 부르지 않게 정리한 계층
트러블슈팅
1화면 코드가 DB를 직접 부르던 구조 정리
상황
로그인·온보딩 화면의 Controller가 DAO를 직접 불러 DB에 접근하고 있었습니다.
원인
화면 코드에 DB 접근과 업무 규칙이 섞여 있어, 같은 처리를 다른 화면에서 쓰려면 코드를 복사해야 했고 DB 구조가 바뀌면 화면 코드까지 고쳐야 했습니다.
해결
Controller → Service → DAO → DB 순서로 바꾸고, 로그인한 사용자 정보는 세션 객체로 넘기게 했습니다. 이어서 테이블 변경에 맞춰 DAO·DTO를 고치고, 화면이 쓰던 임시 데모 데이터 클래스를 지워 실제 DB와 연결했습니다.
결과
가입 → 온보딩 → 프로필 수정이 실제 DB로 동작하고, 식사 기록에서 차단한 메뉴가 추천에서 빠지도록 추천 서비스에 바로 연결할 수 있었습니다.

알고리즘: 백준 문제 풀이 기록 · BeakJoon-ascode

사이드 프로젝트

혼자 기획해서 직접 공개한 프로젝트입니다.

숫자 퍼즐 대전 4인 대전 화면▶ 플레이
4인 대전 화면 · 누르면 바로 플레이

숫자 퍼즐 대전 실시간 멀티플레이 웹게임

2026.09 ~ 10 · 1인 개발 · 배포 중

합이 10이 되게 숫자를 지우고, 지운 만큼 상대 판에 방해 블록을 보내는 4~8인 실시간 대전 게임. PC·모바일, 한국어·영어·일본어 지원

문제실시간 대전에서는 클라이언트가 점수·공격량을 조작할 수 있음
해결판정·점수·공격을 모두 서버에서 계산하고, 규칙 코드는 프론트·서버가 같은 패키지를 공유
결과자동 테스트 96개로 규칙·공격·방 시나리오를 검증하고 실제 배포
  • TypeScript
  • React 19
  • Node.js
  • Colyseus (WebSocket)
  • Vitest
  • Cloudflare Pages
96개자동 테스트 (규칙·공격·방 시나리오)
3개 언어한국어 · 영어 · 일본어

설계와 해결한 문제

  • 서버 권위 구조: 판정·점수·공격량을 모두 서버에서 계산하고 클라이언트는 입력만 보내 점수 조작을 막음
  • 규칙 코드 공유: 판 생성·합 판정·공격 계산을 shared 패키지로 분리해 프론트와 서버가 같은 코드를 사용. 시드값으로 모든 참가자에게 같은 판을 생성
  • 멀티플레이 기능: 방 코드·링크·QR 초대, 빈 방 자동 배정(랜덤 매칭), 서바이벌 모드, 상대 판 미니맵, 공격 대상 지정과 공격 상쇄
  • 운영 대비: 닉네임 금칙어 필터, 경기 기록 보기, 1분 튜토리얼, 마우스·터치 통합 입력
GroveMoss 섬 전체 화면▶ 둘러보기
섬 전체 보기 · 누르면 바로 둘러보기

GroveMoss 픽셀아트 힐링 웹게임

2026.11 ~ · 1인 기획 · 개발 중

계절·날씨·바람이 살아 움직이는 섬을 걸어 다니는 힐링 웹게임. 나무·물·절벽·잔디를 이미지 파일 대신 코드로 생성하고, 모바일 브라우저에서도 부드럽게 돌아가도록 최적화

문제효과가 많은 픽셀아트 화면은 모바일 브라우저 메모리에 부담이 됨
해결기기 메모리·코어 수로 그래픽 품질을 자동 선택하고, 안 쓰는 그림·캔버스를 해제
결과저사양 기기에서도 같은 화면을 절약 모드로 실행
  • JavaScript
  • Canvas 2D
  • 절차적 생성
  • Cloudflare
  • Claude Code

코드는 Claude Code(AI 코딩 도구)로 작성했습니다. 기획, 기능·우선순위 결정, 캐릭터 그림, 결과 검토는 직접 했습니다.

성능 최적화

  • 메모리 절감: 멀리 있는 나무는 흔들림 프레임 첫 장만 사용, 현재 계절이 아닌 그림은 해제, 빛 버퍼를 Uint8 배열로, 쓰지 않는 캔버스는 버림
  • 기기별 그래픽 품질: 기기 메모리·CPU 코어 수를 읽어 자동으로 높음/절약 모드를 고르고, 사용자가 직접 바꿀 수도 있게 함
  • 연산 줄이기: 물 영역 계산은 경계 상자(bbox) 밖을 건너뛰고, 나무 흔들림은 바람 세기 3단계를 미리 그려 두고 바꿔 끼움
  • 그 밖에: 클릭·터치 이동용 A* 길찾기, 월드 편집기(되돌리기 지원), main/test 브랜치별 분리 배포

Stardew Valley 한/영 전환 버그 해결 도구

2025.06 · 1인 개발

게임 채팅에서 한글 입력 중 한/영 키를 누르면 입력이 꼬이는 문제를, 채팅을 열고 닫는 키(T·Enter)에 맞춰 입력기를 자동 전환하는 방식으로 우회한 AutoHotkey 프로그램. 실행 파일(.exe)로 배포

  • AutoHotkey
  • Windows

프로젝트 타임라인

  1. 2026.11 –
    GroveMoss 1인 기획·개발 중 (Canvas·Claude Code)
  2. 2026.09 – 10
    숫자 퍼즐 대전 1인 개발·배포 (TypeScript·Colyseus)
  3. 2026.09 – 10
    스펙 오디세이 팀 개발 (JSP/Servlet·MySQL)
  4. 2026.08
    SafeFood 팀 개발 (JavaFX·MySQL·소켓)
  5. 2025.11 – 12
    부산 지하철 민원 신고 시스템 팀 개발 (PHP·MySQL)
  6. 2024.12
    Dragon Fruits 원자재 대시보드 팀 개발 (PHP·MySQL)