2026.04.29 기준으로 배포 링크, 상태, 최근 수정일을 정리했습니다. 날짜는 작업 시작일과 첨부 파일에서 확인된 Git/ZIP 기록을 분리했습니다.
안녕하세요. 동방건우입니다.
배포된 화면에서 문제를 찾고 끝까지 고치는 개발자
기능 구현에서 멈추지 않고, 배포된 화면에서 다시 확인하며 오류와 흐름을 정리합니다. 공공데이터 프로젝트는 API 수집을 GitHub Actions로 옮겨 정적 JSON으로 만들었고, Review Tag와 PETIQUE는 비공개 저장소라 첨부된 ZIP, git.zip 작업 기록, 최종 점검 문서를 기준으로 보완 범위를 분리해 적었습니다.
검토 포인트
작업 범위와 검증 기준을 먼저 정리했습니다
팜프라이스 노트, 상장노트, 리터세이브는 기획·데이터 처리·화면·배포 자동화를 직접 진행했습니다.
Review Tag와 PETIQUE는 저장소 링크를 공개하지 않고, 첨부 ZIP과 git.zip 내부 기록 기준으로 기존 기능과 보완 범위를 정리했습니다.
“배포됨”에서 끝내지 않고 데이터 검증, QA 스크립트, 빌드 단계 확인까지 같이 적었습니다.
첨부 파일에서 확인한 근거
dailyfarmprice-note, SangJang-note, liter-save는 git.zip 안의 Git 로그, workflow, package.json, scripts 폴더를 확인해 날짜와 핵심 로직을 적었습니다.
비공개 저장소입니다. 기존 백엔드 Git 기록, 프론트/백엔드 최종 ZIP, round84 QA 문서, qa-* 스크립트, PointService/TokenService 코드를 기준으로 정리했습니다.
비공개 저장소입니다. 기존 semi Git 기록, 최종 Render/Supabase ZIP, SECURITY_AND_QA, BUILD_CACHE_CRYPTO_RECHECK_REPORT, CSRF/BCrypt 코드를 기준으로 정리했습니다.
작업 시작일은 사용자 작업 기록 기준으로 두고, Git/ZIP에서 확인되는 날짜는 별도로 표시했습니다. 확인하지 못한 성능 수치나 커밋은 만들지 않았습니다.
개인 프로젝트는 기획, 데이터 수집, 화면, 배포 자동화까지 직접 진행한 범위를 적었습니다. 팀 프로젝트는 기존 기능과 리디자인/보완 범위를 분리했습니다.
“했다”에서 끝내지 않고 확인 가능한 파일·로직을 적었습니다. 예: validate-data.mjs, update-ipos.mjs, fetch-opinet.mjs, qa-selector-check.mjs, CsrfProtectionFilter.java, Dockerfile cache guard.
각 프로젝트에 문제 → 수정 → 이유 → 결과를 넣었습니다. 질문을 받아도 같은 순서로 설명할 수 있게 구성했습니다.
데이터 검증, QA 스크립트, 빌드 단계 검사, 배포 확인일을 함께 적었습니다. “배포했다”보다 “배포 전에 무엇을 확인했는지”가 보이도록 했습니다.
같은 날 시작한 3개 데이터 프로젝트는 묶음 기획으로 설명하고, 이후 수정일은 프로젝트별 작업 내용에 맞춰 따로 기록했습니다.
Review Tag와 PETIQUE는 리디자인만 적지 않고 기존 기능, 기존 문제, 보완한 내용을 따로 나눴습니다.
검토하기 애매했던 부분을 이렇게 보완했습니다
프로젝트 설명이 기능 소개에 머무르면 실제 수정 범위가 잘 보이지 않았습니다.
핵심 코드, 수정 파일, 해결 이유, 검증 방법을 프로젝트별로 추가했습니다.
Review Tag와 PETIQUE는 리디자인 내용만 보이면 기존 프로젝트 범위가 흐려질 수 있었습니다.
Review Tag는 2026.01.05, PETIQUE는 2026.01.08부터 진행한 보완 작업을 기존 기능과 분리해 적었습니다.
개인 작업과 기존 프로젝트를 분리해서 적었습니다
2026.03.02에 데이터형 미니 서비스 3종으로 같이 기획했습니다. 첨부 Git 기록 기준 팜프라이스는 2026.04.27~04.28, 상장노트는 2026.04.23~04.24, 리터세이브는 2026.04.23~04.28 사이에 자동화와 화면 보완 기록이 확인됩니다.
기존 영화·애니 리뷰 커뮤니티입니다. Git 기록에는 2026.01.19 초기 업로드, 2026.02.11 배포용 /api 보완, 2026.03.14 메일/환경변수/인터셉터 수정, 2026.03.24 RefreshToken 수정이 확인됩니다. 최종 ZIP에는 2026.04.20 round84 QA 기록이 있습니다.
기존 반려동물 분양·후기·커뮤니티 서비스입니다. Git 기록에는 2026.03.13 회원가입 예외/인증번호, 2026.03.18 회원탈퇴, 2026.03.20 닉네임 중복검사, 2026.03.21~03.25 분양 완료 로직 수정이 확인됩니다. 최종 ZIP에는 2026.04.21 Render build cache 재점검 기록이 있습니다.
프로젝트 상태와 날짜
| 프로젝트 | 시작일 | 상태 | 주요 수정일 | 설명 핵심 |
|---|---|---|---|---|
| 팜프라이스 노트 | 2026.03.02 | 배포 중 | 2026.04.28 | Git 로그 기준 QA 이슈 수정, Gemini 리포트 연동, validate-data.mjs 검증 보강 |
| 상장노트 | 2026.03.02 | 배포 중 | 2026.04.24 | Git 로그 기준 마감 일정 우선순위, 리스트 정렬, OpenDART 원문 파싱 보완 |
| 리터세이브 | 2026.03.02 | 배포 중 | 2026.04.28 | Git 로그 기준 일간·주간·월간 차트 추가, history JSON 누적 구조 보완 |
| Review Tag | 2026.01.05 | 리디자인 중 · 배포 중 · 비공개 저장소 | 2026.04.20 | round84 최종 ZIP 기준 관리자 영화 검색 className, QA 스크립트, legacy selector 정리 |
| PETIQUE | 2026.01.08 | 리디자인 중 · 배포 중 · 비공개 저장소 | 2026.04.21 | 최종 ZIP 기준 BCrypt, CSRF, Render build cache guard, Supabase 배포 구조 보완 |
배포 확인 시 참고 사항
PETIQUE는 Render 배포 환경이라 첫 접속 시 서버가 깨어나는 시간이 걸릴 수 있습니다. 포트폴리오에는 이 점을 숨기지 않고 배포 환경 특성으로 정리했습니다.
공공데이터 프로젝트는 GitHub Secrets와 Actions 수집 단계로 API Key 노출 위험을 줄였습니다. 브라우저에서는 생성된 JSON만 읽습니다.
모든 수정을 숫자로 과장하지 않고, 실제로 확인한 검증 기준과 실패 방지 로직을 중심으로 적었습니다.
질문 정리
질문을 받았을 때 바로 설명할 수 있는 포인트
개인 프로젝트 3개는 기획, 데이터 수집 스크립트, 화면 구현, GitHub Actions 자동화, GitHub Pages 배포까지 직접 진행했습니다. Review Tag와 PETIQUE는 비공개 저장소라 공개 링크 대신 첨부 ZIP과 git.zip 기록을 기준으로 기존 기능과 보완 범위를 나눠 설명했습니다.
API Key 노출과 응답 지연, 화면별 예외 처리 반복을 줄이기 위해서입니다. GitHub Secrets와 GitHub Actions에서 데이터를 먼저 받아 JSON으로 만들고, React는 검증된 정적 JSON만 읽도록 분리했습니다.
농산물 가격값, 날짜 형식, 빈 배열 여부, OpenDART 인코딩 결과, Opinet 응답 누락, Review Tag CSS selector 충돌, PETIQUE Docker build cache guard와 CSRF 적용 흐름을 확인했습니다.
Review Tag는 태그 검색 서비스가 아니라 영화·애니 리뷰 커뮤니티 브랜드명입니다. 기존 리뷰/게시판/TMDB/퀴즈/포인트/상점/인벤토리/관리자 기능을 한 서비스처럼 보이도록 화면 흐름과 QA를 다시 정리했습니다.
분양 상태 전이가 핵심입니다. 신청, 승인, 거절, 완료, 후기 연결이 권한과 상태에 따라 다르게 동작해야 해서 서버 검증을 보완했고, Render 환경에서는 이미지 저장, 메일 인증, PostgreSQL/Supabase 연결도 함께 정리했습니다.
배포된 화면을 기준으로 고친 기록입니다. 기능 설명만 적지 않고, 실패 가능성이 있던 부분을 어떤 코드와 구조로 막았는지 프로젝트마다 남겼습니다.
프로젝트 모음
배포 중인 프로젝트
자동화 / 배포
CI/CD · 배포 경험
데이터 처리
데이터 파이프라인 처리 경험
공공 API를 화면에서 바로 호출하면 키 노출, 응답 지연, 실패 처리 문제가 생길 수 있어 GitHub Actions에서 먼저 데이터를 수집했습니다. 수집 뒤 가격/날짜/인코딩/좌표를 정리하고, 검증을 통과한 정적 JSON만 React 화면에서 읽도록 구성했습니다.
개발 기록
프로젝트별 역할 · 핵심 코드 · 해결 근거
개인 프로젝트 · 데이터 자동화
팜프라이스 노트
2026.03.02 기획 시작 · 2026.04.28 Git 기록 확인 · 배포 중
왜 만들었는지
농산물 가격은 매일 달라지는데, 실제로 비교하려고 하면 여러 페이지를 다시 찾아봐야 했습니다. 그래서 자주 보는 품목의 가격 흐름을 따로 모으고, 전일 대비 변화와 최근 가격 범위를 한 화면에서 확인할 수 있게 만들었습니다.
기존 기능과 보강 방향
개인 프로젝트로 시작했기 때문에 초기 기능은 품목별 가격 카드, 기간별 가격 흐름, 관심 품목 저장, 일자별 가격 테이블, AI 리포트였습니다. 그다음 배포 자동화와 데이터 검증을 붙이면서 “화면만 있는 서비스”가 아니라 데이터가 자동으로 갱신되는 서비스로 정리했습니다.
담당 작업
- KAMIS 품목 설정 파일 구성
- 가격/날짜 정규화 함수 작성
- 최근 7일·14일·30일 가격 범위 계산
- GitHub Actions에서 데이터 생성 후 GitHub Pages 배포
- 실데이터 검증 실패 시 배포 중단 처리
코드 근거
API 직접 호출 구조를 데이터 생성 스크립트 + 정적 JSON 구조로 변경했습니다.
scripts/update-crop-prices.mjs, scripts/generate-ai-reports.mjs, scripts/validate-data.mjs, .github/workflows/deploy-github-pages.yml, src/services/cropPriceApi.js
데이터 source, 날짜 형식, 가격 값, 빈 배열 여부를 검사하고 실패하면 배포가 멈추도록 했습니다.
“정적 배포 환경에서 API Key를 숨기기 위해 데이터 수집을 CI 단계로 옮겼습니다.”
개발일지
- 2026.03.02데이터형 미니 서비스 3종 중 농산물 가격 서비스를 먼저 기획했습니다. 이 날짜는 사용자 작업 기록 기준입니다.
- 2026.04.27첨부 Git 기록 기준 새 저장소 생성, 데이터 표시 오류 수정, UI 문구 개선, 농산물 대시보드와 품목 아이콘 개선 기록이 확인됩니다.
- 2026.04.28QA 이슈 수정, Gemini 리포트 연동 개선, update-crop-prices.mjs·generate-ai-reports.mjs·validate-data.mjs 기반 데이터 생성/검증 흐름을 정리했습니다.
문제 해결 기록
브라우저에서 API를 직접 호출하면 API Key가 노출되고, 응답 실패 시 화면에서 예외 처리를 반복해야 했습니다. 또한 샘플 데이터로 대체하면 실제 서비스처럼 보이기 어렵다고 판단했습니다.
API 호출을 GitHub Actions 단계로 옮기고, 성공한 실데이터만 정적 JSON으로 저장했습니다. React는 API를 직접 호출하지 않고 public/data/*.json만 fetch하도록 수정했습니다.
정적 사이트인 GitHub Pages에서도 키를 숨길 수 있고, 배포 전 데이터 검증을 통과한 파일만 화면에 반영할 수 있기 때문입니다.
키 노출 위험을 줄였고, 데이터가 비정상일 때 잘못된 화면을 배포하지 않게 되었습니다.
핵심 코드 / 로직
KAMIS 실데이터 검증 후 배포
// scripts/validate-data.mjs
assert(data.source === 'KAMIS Open API', 'source는 KAMIS Open API여야 합니다.');
assert(Array.isArray(data.items), 'items 배열이 필요합니다.');
assert(data.items.length > 0, '최소 1개 이상의 KAMIS 실데이터 품목이 필요합니다.');
for (const item of data.items) {
assert(item.id && item.name, '품목 id/name 값이 필요합니다.');
for (const point of item.series) {
assert(/^\d{4}-\d{2}-\d{2}$/.test(point.date), '날짜 형식 오류');
assert(Number(point.price) > 0, '가격 값은 0보다 커야 합니다.');
}
}
개인 프로젝트 · 공시 데이터 정리
상장노트
2026.03.02 기획 시작 · 2026.04.24 Git 기록 확인 · 배포 중
왜 만들었는지
상장 일정과 공시 정보를 확인할 때 필요한 정보가 흩어져 있어 다시 찾아보는 시간이 많았습니다. 기업명, 일정, 상태를 한 화면에서 확인할 수 있게 정리하면 나중에 다시 보기 편하겠다고 생각했습니다.
기존 기능과 보강 방향
초기에는 상장 관련 목록을 단순히 보여주는 형태에서 시작했습니다. 그다음 OpenDART 데이터의 압축 파일 처리, 한글 인코딩 변환, 일정 필터링을 붙이면서 데이터 전처리 흐름을 설명할 수 있는 프로젝트로 보강했습니다.
담당 작업
- OpenDART 데이터 수집 흐름 구성
- ZIP 압축 파일 추출 처리
- 한글 인코딩 변환 후 JSON 생성
- 기업명·일정·상태 중심 데이터로 재구성
- React 검색/필터 화면 구성
코드 근거
브라우저에서 처리하기 어려운 ZIP 추출과 인코딩 변환을 Node.js 수집 단계로 분리했습니다.
scripts/update-ipos.mjs, scripts/validate-data.mjs, public/data/ipos.json, .github/workflows/deploy-github-pages.yml
기업명, 일정, 링크가 없는 데이터는 필터링하고 JSON 생성 결과를 배포 전 확인했습니다.
“압축/인코딩 처리는 화면의 책임이 아니어서 데이터 파이프라인으로 옮겼습니다.”
개발일지
- 2026.03.02데이터형 미니 서비스 3종 중 상장 일정 정리 서비스로 기획했습니다. 이 날짜는 사용자 작업 기록 기준입니다.
- 2026.04.23첨부 Git 기록 기준 초기 프로젝트 구성, GitHub Pages 배포 설정, 빌드·배포 workflow 분리, npm 캐시 적용, 페이지네이션·상세 모달·DART 원문 파싱 추가 기록이 확인됩니다.
- 2026.04.24마감 일정 우선순위 하향 조정과 리스트 정렬 수정 기록이 확인됩니다. update-ipos.mjs와 validate-data.mjs 기준으로 데이터 생성/검증 흐름을 정리했습니다.
문제 해결 기록
OpenDART 데이터는 압축 파일 형태로 내려오고, 파일을 그대로 읽으면 한글이 깨지는 경우가 있었습니다. 브라우저에서 처리하기에는 무겁고, 화면 코드에 데이터 가공이 섞이는 문제가 있었습니다.
Node.js 단계에서 ZIP을 먼저 풀고, iconv-lite로 문자 인코딩을 변환한 뒤 화면에 필요한 값만 JSON으로 저장하는 방식으로 바꿨습니다.
압축 해제와 인코딩 변환은 화면에서 할 일이 아니라 데이터 파이프라인에서 처리해야 유지보수가 쉽기 때문입니다.
React 화면은 이미 정리된 일정 JSON만 읽게 되었고, 한글 깨짐과 불필요한 원본 데이터 처리 부담을 줄였습니다.
핵심 코드 / 로직
ZIP 추출 → 인코딩 변환 → JSON 정리
// scripts/update-ipos.mjs 핵심 흐름
const zip = new AdmZip(buffer);
const entries = zip.getEntries().filter((entry) => !entry.isDirectory);
const textEntries = entries.filter((entry) => /\.(xml|xbrl|htm|html|txt)$/i.test(entry.entryName));
const documentText = textEntries
.map((entry) => decodeMarkupBuffer(entry.getData()))
.filter(Boolean)
.join(' ');
await writeFile(OUTPUT_PATH, `${JSON.stringify(output, null, 2)}\n`, 'utf8');
// OUTPUT_PATH = public/data/ipos.json
개인 프로젝트 · 가격 비교
리터세이브
2026.03.02 기획 시작 · 2026.04.28 Git 기록 확인 · 배포 중
왜 만들었는지
주유소 가격은 한 번에 비교해서 보고 싶은 정보인데, 실제로는 지역과 유종을 바꿔가며 찾아야 했습니다. 필요한 조건을 고르면 저렴한 주유소와 예상 절약 금액을 바로 볼 수 있는 화면을 목표로 만들었습니다.
기존 기능과 보강 방향
초기 기능은 지역/유종별 최저가 목록과 가격 카드였습니다. 그다음 좌표 변환, 거리 계산, 가성비 정렬, 누적 가격 데이터 저장을 붙이면서 단순 목록보다 비교 기준이 명확한 서비스로 바꿨습니다.
담당 작업
- Opinet API 인증 파라미터 fallback 처리
- 지역/유종 데이터 수집 및 정렬
- KATEC 좌표를 WGS84로 변환
- 거리·예상 절약 금액 기반 가성비 점수 계산
- GitHub Actions로 가격 데이터 자동 갱신
코드 근거
Opinet 인증 파라미터를 순차 시도하고, 좌표·가격 데이터를 화면에서 바로 쓰기 좋은 구조로 바꿨습니다.
scripts/fetch-opinet.mjs, scripts/opinet-local-check.mjs, scripts/opinet-smoke-test.mjs, src/utils/oilData.js, src/utils/mapLinks.js, public/data/oil-prices.json, public/data/oil-history.json
빈 응답, 잘못된 가격, 좌표 누락 데이터를 제외하고 사용자에게 대체 메시지를 보여주도록 했습니다.
“최저가만 보여주면 실제 절약과 다를 수 있어 가격·지역·거리 기준을 함께 고려했습니다.”
개발일지
- 2026.03.02데이터형 미니 서비스 3종 중 주유소 가격 비교 서비스로 기획했습니다. 이 날짜는 사용자 작업 기록 기준입니다.
- 2026.04.23첨부 Git 기록 기준 초기 프로젝트 구성, 지역·유종 필터 동기화, 빈 데이터셋 예외 처리, Opinet 인증키 검증과 수집 오류 로그 개선 기록이 확인됩니다.
- 2026.04.24Opinet 인증 fallback과 진단 스크립트 추가, GitHub Actions 실행 환경 업데이트, 다크/화이트 모드 추가 기록이 확인됩니다.
- 2026.04.27현재 위치 기준 정렬, 가성비 추천, 공유 링크, 즐겨찾기, 가격 누적 기록 로직 추가 기록이 확인됩니다.
- 2026.04.28일간·주간·월간 가격 흐름 차트 추가 기록이 확인됩니다. oil-prices.json과 oil-history.json을 분리해 가격 데이터와 누적 기록을 나눠 설명했습니다.
문제 해결 기록
Opinet API는 인증 파라미터 방식이 환경마다 달라질 수 있고, 좌표가 바로 지도에서 쓰기 어려운 KATEC 값으로 내려왔습니다. 또 단순 가격순만 보여주면 거리 때문에 실제 절약 판단이 어려웠습니다.
code/certkey/both 방식으로 요청을 순차 시도하는 fallback을 만들고, proj4로 좌표를 WGS84로 변환했습니다. 가격, 거리, 예상 절약 금액을 분리 계산해 정렬 기준을 나눴습니다.
외부 API는 응답이 비거나 인증 방식이 바뀔 수 있으므로 수집 단계에서 진단 정보를 남겨야 하고, 지도/거리 계산은 화면이 아니라 데이터 정규화 단계에서 준비해야 하기 때문입니다.
데이터 수집 실패 원인을 더 빨리 찾을 수 있게 되었고, 사용자는 가격순뿐 아니라 가까운 순과 가성비 기준으로도 비교할 수 있게 되었습니다.
핵심 코드 / 로직
인증 fallback과 좌표 변환
// scripts/fetch-opinet.mjs
const strategies = ['code', 'certkey', 'both'];
for (const strategy of strategies) {
const url = buildApiUrl(endpoint, params, strategy);
const raw = await fetchJson(url);
const items = extractOilItems(raw).filter((item) => requiredKeys.every((key) => key in item));
if (items.length > 0) return { items, strategy };
}
const [longitude, latitude] = proj4(KATEC_CRS, 'WGS84', [katecX, katecY]);
팀 프로젝트 보완 · 비공개 저장소
Review Tag
2026.01.05 리디자인 시작 · 2026.04.20 round84 QA 기록 확인 · 리디자인 중 · 배포 중
Review Tag는 기존 팀 프로젝트를 보완 중인 비공개 저장소입니다. 포트폴리오에는 첨부된 기존 백엔드 Git 기록, 최종 프론트/백엔드 ZIP, update.md, qa-* 스크립트, 실제 코드 파일 기준으로만 정리했습니다.
서비스 설명
Review Tag는 “리뷰를 태그로 검색하는 사이트”가 아니라 영화·애니 리뷰 커뮤니티 브랜드명입니다. 기존 기능은 TMDB 콘텐츠 탐색, 리뷰, 게시판, 퀴즈, 랭킹, 출석, 포인트, 룰렛, 상점, 인벤토리, 관리자 대시보드로 구성되어 있습니다.
보완한 범위
- 관리자 영화 검색 모달 className을 owner prefix 기준으로 정리
- legacy selector가 다시 들어오지 않도록 QA 스크립트 보강
- 포인트 상점 구매 흐름을 트랜잭션 단위로 설명 가능하게 정리
- 메일/DB/TMDB/JWT 설정을 환경변수 기반으로 관리
- RefreshToken 재발급 메서드 오류 수정 기록 반영
첨부 파일에서 확인한 작업 기록
기존 백엔드 Git 로그에서 파이널 프로젝트 업로드와 README 추가 기록이 확인됩니다.
배포용 /api 보완 기록이 확인됩니다.
메일 설정 하드코딩 제거, 환경변수화, 비로그인 포인트상점 인터셉터 수정 기록이 확인됩니다.
TokenService의 generateRefreshToken(TokenVO) 오류 수정 기록이 확인됩니다.
최종 ZIP의 round84 update.md에서 관리자 영화 검색 className, legacy selector, QA 검사 보강 기록이 확인됩니다.
프론트 최종 ZIP에는 lint, qa:static, qa:selectors, qa:design, qa:screen-css, qa:a11y, qa:security, build, qa:bundle 점검 항목이 정리되어 있습니다.
문제 해결 기록
관리자 영화 검색 모달에 ms-*와 movie-search-* 같은 짧은 className이 남아 있으면 다른 화면 CSS와 충돌하거나 리디자인 후 다시 깨질 수 있었습니다. 또한 JWT 재발급에서 TokenVO 기반 refresh token 생성 흐름이 access token 생성 흐름과 섞일 위험이 있었습니다.
프론트에서는 관리자 영화 검색 className을 admin-movie-search-*로 통일하고, qa-selector-check.mjs와 qa-screen-css-risk-check.mjs에서 legacy selector가 다시 들어오면 잡도록 했습니다. 백엔드에서는 generateRefreshToken(TokenVO)가 generateRefreshToken(MemberDto)로 이어지도록 수정했습니다.
리디자인은 색만 바꾸는 작업이 아니라, 화면별 스타일 소유권과 인증 흐름을 분리해서 이후 수정 때 다시 깨지지 않게 만드는 작업이기 때문입니다.
관리자 모달 스타일은 owner prefix 기준으로 설명할 수 있게 되었고, QA 스크립트로 회귀 가능성을 줄였습니다. 토큰 재발급 흐름도 access/refresh 역할을 분리해 설명할 수 있게 정리했습니다.
핵심 코드 / 로직
관리자 영화 검색 className 정리와 QA 검사
// 변경 전
<div className="ms-container movie-search-modal">...</div>
// 변경 후
<div className="admin-movie-search-container">...</div>
// qa-screen-css-risk-check.mjs
/(?<!admin-)\bmovie-search-(?:modal|input|icon)\b/g
/\.loader\b/g
핵심 코드 / 로직
RefreshToken 생성 오류 수정
// TokenService.java
public String generateRefreshToken(TokenVO tokenVO) {
return generateRefreshToken(
MemberDto.builder()
.memberId(tokenVO.getLoginId())
.memberLevel(tokenVO.getLoginLevel())
.memberNickname(tokenVO.getLoginNickname())
.build()
);
}
핵심 코드 / 로직
포인트 상점 구매 흐름
// PointService.java
@Transactional
public void purchaseItem(String loginId, long itemNo) {
PointItemStoreDto item = findItemOrThrow(itemNo);
validateCommonPurchaseRules(loginId, item);
validateInventoryDuplicate(loginId, item, itemNo, "이미 보유 중인 상품입니다.");
addPoint(loginId, -(int)item.getPointItemPrice(), "USE", "아이템 구매: " + item.getPointItemName());
item.setPointItemStock(item.getPointItemStock() - 1);
pointItemDao.update(item);
if ("HEART_RECHARGE".equals(item.getPointItemType())) chargeHeart(loginId, 5);
else if ("DECO_ICON".equals(item.getPointItemType())) giveStoreIcon(loginId, item);
else giveItemToInventory(loginId, itemNo);
}
팀 프로젝트 보완 · 비공개 저장소
PETIQUE
2026.01.08 리디자인 시작 · 2026.04.21 Render 빌드 캐시 재점검 · 리디자인 중 · 배포 중
PETIQUE는 기존 팀 프로젝트를 보완 중인 비공개 저장소입니다. 포트폴리오에는 첨부된 기존 Git 기록, 최종 Render/Supabase ZIP, SECURITY_AND_QA, BUILD_CACHE_CRYPTO_RECHECK_REPORT, 실제 코드 파일 기준으로만 정리했습니다.
서비스 설명
PETIQUE는 반려동물 분양, 신청, 예약, 완료, 취소, 후기, 커뮤니티, 댓글, 추천, 북마크, 신고, 쪽지, 마이페이지, 관리자 기능이 있던 프로젝트입니다. 보완 작업은 이 기존 기능 위에 보안, 배포, 상태 전이를 정리한 작업입니다.
보완한 범위
- 분양 신청/승인/거절/완료 상태 전이 서버 검증 보완
- BCrypt 기반 비밀번호 저장과 기존 평문 DB 업그레이드 처리
- double-submit cookie 기반 CSRF 필터와 전역 csrf.js 추가
- JSP + jQuery Ajax/fetch/XHR 요청의 CSRF 토큰 자동 연결
- Render + PostgreSQL/Supabase + imgBB + EmailJS 배포 구조 정리
- Docker build cache guard와 spring-security-crypto 의존성 검사 추가
첨부 파일에서 확인한 작업 기록
회원가입 관련 요청 로그인 예외 처리와 인증번호 만료 시간 체크 수정 기록이 확인됩니다.
회원탈퇴 오류 수정 기록이 확인됩니다.
닉네임 중복검사 로직 변경 기록이 확인됩니다.
분양 완료 로직 변경 기록이 확인됩니다.
AdoptionProcessService 미반영 수정 기록이 확인됩니다.
BUILD_CACHE_CRYPTO_RECHECK_REPORT와 update.md에서 Render build cache, spring-security-crypto 의존성, Dockerfile guard 재점검 기록이 확인됩니다.
문제 해결 기록
분양 완료는 신청 상태, 글 작성자 권한, 동물 소유자 변경, 게시글 상태 변경이 같이 맞아야 합니다. 하나라도 빠지면 승인되지 않은 신청이 완료되거나, 동물 소유자와 게시글 상태가 어긋날 수 있었습니다. 배포 쪽에서는 Render가 오래된 pom.xml dependency layer를 재사용하면 BCrypt 의존성 누락을 늦게 발견할 수 있었습니다.
AdoptionProcessService.complete()에서 작성자 확인, 완료 여부, 승인된 신청 조회, 동물 소유자 변경, 게시글 상태 변경을 @Transactional 흐름으로 묶었습니다. 배포 쪽은 Dockerfile에 PETIQUE_BUILD_CACHE_BUST와 spring-security-crypto grep 검사를 넣고 preflight_checks.py에서도 같은 조건을 확인하게 했습니다.
분양 상태 전이는 핵심 데이터라 중간 실패 시 롤백되어야 하고, 의존성 누락은 런타임보다 빌드 초기에 실패시키는 편이 원인 파악이 빠르기 때문입니다. CSRF도 JSP, jQuery Ajax, fetch, XHR이 섞여 있어 화면마다 수동 처리하면 누락될 수 있었습니다.
분양 완료 흐름은 권한과 상태를 기준으로 설명할 수 있게 되었고, Render 배포 실패 가능성은 Docker build 단계에서 먼저 잡도록 바뀌었습니다. CSRF 토큰도 전역 스크립트로 연결되어 화면별 누락 위험을 줄였습니다.
핵심 코드 / 로직
분양 완료 상태 전이
// AdoptionProcessService.java
@Transactional
public boolean complete(int boardNo, String ownerId) {
if (ownerId == null) return false;
AdoptDetailVO detail = adoptionBoardDao.selectAdoptDetail(boardNo);
if (detail == null) return false;
if (!ownerId.equals(detail.getBoardWriter())) return false;
if ("f".equals(detail.getAnimalPermission())) return false;
AdoptionApplyVO approved = adoptionApplyDao.selectApprovedByBoardNo(boardNo);
if (approved == null) return false;
boolean completed = adoptionApplyDao.completeApproved(boardNo);
if (!completed) return false;
boolean masterUpdated = animalDao.updateMaster(detail.getAnimalNo(), approved.getApplicantId());
if (!masterUpdated) throw new IllegalStateException("동물 소유자 변경 실패");
int updated = adoptionBoardDao.updatePermissionToF(boardNo);
if (updated <= 0) throw new IllegalStateException("분양 완료 상태 반영 실패");
return true;
}
핵심 코드 / 로직
Render build cache guard와 BCrypt dependency 검사
# Dockerfile
ARG PETIQUE_BUILD_CACHE_BUST=20260421-crypto-pom-guard01
RUN echo "PETIQUE build cache: ${PETIQUE_BUILD_CACHE_BUST}"
COPY mvnw pom.xml ./
RUN chmod +x mvnw \
&& grep -q "<artifactId>spring-security-crypto</artifactId>" pom.xml
핵심 코드 / 로직
JSP + jQuery 환경의 CSRF 자동 연결
// csrf.js
if (window.jQuery && window.jQuery.ajaxPrefilter) {
window.jQuery.ajaxPrefilter(function (options, originalOptions, jqXHR) {
var method = String(options.type || options.method || 'GET').toUpperCase();
if (isUnsafe(method) && sameOrigin(options.url)) {
var value = token();
if (value) jqXHR.setRequestHeader('X-CSRF-TOKEN', value);
}
});
}
기술 스택
사용해 본 기술
Frontend
Backend & View
Database
Data & API
Deploy & CI/CD
연락처
문제를 발견하면 원인부터 정리하고 고칩니다.
이 포트폴리오는 프로젝트 링크만 모아둔 페이지가 아니라, 기존 기능, 수정한 코드, 트러블슈팅, 배포 과정을 함께 설명하기 위해 만들었습니다. 질문을 받았을 때 각 프로젝트의 문제 상황과 해결 이유를 코드 기준으로 설명할 수 있도록 정리했습니다.