안녕하세요. 동방건우입니다.

배포된 화면에서 문제를 찾고 끝까지 고치는 개발자

기능 구현에서 멈추지 않고, 배포된 화면에서 다시 확인하며 오류와 흐름을 정리합니다. 공공데이터 프로젝트는 API 수집을 GitHub Actions로 옮겨 정적 JSON으로 만들었고, Review Tag와 PETIQUE는 비공개 저장소라 첨부된 ZIP, git.zip 작업 기록, 최종 점검 문서를 기준으로 보완 범위를 분리해 적었습니다.

5배포 프로젝트
3데이터 자동화
2리디자인 중 · 배포 중
코드수정 이유 기록
React
Spring
JSP
Oracle
PostgreSQL
Actions
📁 프로젝트별 배포 링크와 작업 기록 정리
🧩 문제 → 수정 → 이유 → 결과 흐름으로 설명
🔗 KAMIS · OpenDART · Opinet · TMDB · EmailJS · imgBB 연동
GitHub Actions, Vercel, Render, Cloudflare Pages 배포 경험

검토 포인트

작업 범위와 검증 기준을 먼저 정리했습니다

작성 기준

2026.04.29 기준으로 배포 링크, 상태, 최근 수정일을 정리했습니다. 날짜는 작업 시작일과 첨부 파일에서 확인된 Git/ZIP 기록을 분리했습니다.

개인 프로젝트

팜프라이스 노트, 상장노트, 리터세이브는 기획·데이터 처리·화면·배포 자동화를 직접 진행했습니다.

비공개 저장소 2개

Review Tag와 PETIQUE는 저장소 링크를 공개하지 않고, 첨부 ZIP과 git.zip 내부 기록 기준으로 기존 기능과 보완 범위를 정리했습니다.

검증 기준

“배포됨”에서 끝내지 않고 데이터 검증, QA 스크립트, 빌드 단계 확인까지 같이 적었습니다.

첨부 파일에서 확인한 근거

공개 개인 프로젝트

dailyfarmprice-note, SangJang-note, liter-save는 git.zip 안의 Git 로그, workflow, package.json, scripts 폴더를 확인해 날짜와 핵심 로직을 적었습니다.

Review Tag

비공개 저장소입니다. 기존 백엔드 Git 기록, 프론트/백엔드 최종 ZIP, round84 QA 문서, qa-* 스크립트, PointService/TokenService 코드를 기준으로 정리했습니다.

PETIQUE

비공개 저장소입니다. 기존 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는 리디자인만 적지 않고 기존 기능, 기존 문제, 보완한 내용을 따로 나눴습니다.

검토하기 애매했던 부분을 이렇게 보완했습니다

Before

프로젝트 설명이 기능 소개에 머무르면 실제 수정 범위가 잘 보이지 않았습니다.

After

핵심 코드, 수정 파일, 해결 이유, 검증 방법을 프로젝트별로 추가했습니다.

Before

Review Tag와 PETIQUE는 리디자인 내용만 보이면 기존 프로젝트 범위가 흐려질 수 있었습니다.

After

Review Tag는 2026.01.05, PETIQUE는 2026.01.08부터 진행한 보완 작업을 기존 기능과 분리해 적었습니다.

개인 작업과 기존 프로젝트를 분리해서 적었습니다

개인 프로젝트 3개

2026.03.02에 데이터형 미니 서비스 3종으로 같이 기획했습니다. 첨부 Git 기록 기준 팜프라이스는 2026.04.27~04.28, 상장노트는 2026.04.23~04.24, 리터세이브는 2026.04.23~04.28 사이에 자동화와 화면 보완 기록이 확인됩니다.

Review Tag · 비공개 저장소

기존 영화·애니 리뷰 커뮤니티입니다. Git 기록에는 2026.01.19 초기 업로드, 2026.02.11 배포용 /api 보완, 2026.03.14 메일/환경변수/인터셉터 수정, 2026.03.24 RefreshToken 수정이 확인됩니다. 최종 ZIP에는 2026.04.20 round84 QA 기록이 있습니다.

PETIQUE · 비공개 저장소

기존 반려동물 분양·후기·커뮤니티 서비스입니다. 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.28Git 로그 기준 QA 이슈 수정, Gemini 리포트 연동, validate-data.mjs 검증 보강
상장노트2026.03.02배포 중2026.04.24Git 로그 기준 마감 일정 우선순위, 리스트 정렬, OpenDART 원문 파싱 보완
리터세이브2026.03.02배포 중2026.04.28Git 로그 기준 일간·주간·월간 차트 추가, history JSON 누적 구조 보완
Review Tag2026.01.05리디자인 중 · 배포 중 · 비공개 저장소2026.04.20round84 최종 ZIP 기준 관리자 영화 검색 className, QA 스크립트, legacy selector 정리
PETIQUE2026.01.08리디자인 중 · 배포 중 · 비공개 저장소2026.04.21최종 ZIP 기준 BCrypt, CSRF, Render build cache guard, Supabase 배포 구조 보완

배포 확인 시 참고 사항

Render 첫 접속

PETIQUE는 Render 배포 환경이라 첫 접속 시 서버가 깨어나는 시간이 걸릴 수 있습니다. 포트폴리오에는 이 점을 숨기지 않고 배포 환경 특성으로 정리했습니다.

API Key 관리

공공데이터 프로젝트는 GitHub Secrets와 Actions 수집 단계로 API Key 노출 위험을 줄였습니다. 브라우저에서는 생성된 JSON만 읽습니다.

검증 방식

모든 수정을 숫자로 과장하지 않고, 실제로 확인한 검증 기준과 실패 방지 로직을 중심으로 적었습니다.

질문 정리

질문을 받았을 때 바로 설명할 수 있는 포인트

Q. 본인이 맡은 범위가 어디까지인가요?

개인 프로젝트 3개는 기획, 데이터 수집 스크립트, 화면 구현, GitHub Actions 자동화, GitHub Pages 배포까지 직접 진행했습니다. Review Tag와 PETIQUE는 비공개 저장소라 공개 링크 대신 첨부 ZIP과 git.zip 기록을 기준으로 기존 기능과 보완 범위를 나눠 설명했습니다.

Q. 왜 브라우저에서 API를 바로 호출하지 않았나요?

API Key 노출과 응답 지연, 화면별 예외 처리 반복을 줄이기 위해서입니다. GitHub Secrets와 GitHub Actions에서 데이터를 먼저 받아 JSON으로 만들고, React는 검증된 정적 JSON만 읽도록 분리했습니다.

Q. 배포 전에 무엇을 검증했나요?

농산물 가격값, 날짜 형식, 빈 배열 여부, OpenDART 인코딩 결과, Opinet 응답 누락, Review Tag CSS selector 충돌, PETIQUE Docker build cache guard와 CSRF 적용 흐름을 확인했습니다.

Q. Review Tag는 정확히 어떤 서비스인가요?

Review Tag는 태그 검색 서비스가 아니라 영화·애니 리뷰 커뮤니티 브랜드명입니다. 기존 리뷰/게시판/TMDB/퀴즈/포인트/상점/인벤토리/관리자 기능을 한 서비스처럼 보이도록 화면 흐름과 QA를 다시 정리했습니다.

Q. PETIQUE에서 가장 설명할 만한 문제는 무엇인가요?

분양 상태 전이가 핵심입니다. 신청, 승인, 거절, 완료, 후기 연결이 권한과 상태에 따라 다르게 동작해야 해서 서버 검증을 보완했고, Render 환경에서는 이미지 저장, 메일 인증, PostgreSQL/Supabase 연결도 함께 정리했습니다.

Q. 이 포트폴리오에서 가장 자신 있는 부분은요?

배포된 화면을 기준으로 고친 기록입니다. 기능 설명만 적지 않고, 실패 가능성이 있던 부분을 어떤 코드와 구조로 막았는지 프로젝트마다 남겼습니다.

프로젝트 모음

배포 중인 프로젝트

상세 기록 보기 →
팜프라이스 노트 미리보기
배포 중2026.03.02 기획 시작 · 2026.04.28 Git 기록 확인 · 배포 중

팜프라이스 노트

농산물 가격을 매번 검색하지 않아도 흐름을 볼 수 있게 만든 개인 프로젝트입니다.

ReactViteKAMIS API
GitHub Actions · GitHub Pages · 정적 JSON
상장노트 미리보기
배포 중2026.03.02 기획 시작 · 2026.04.24 Git 기록 확인 · 배포 중

상장노트

공모와 상장 일정을 보기 쉽게 정리해 두려고 만든 데이터 정리 프로젝트입니다.

ReactViteOpenDART API
ZIP 처리 · 인코딩 변환 · GitHub Actions
리터세이브 미리보기
배포 중2026.03.02 기획 시작 · 2026.04.28 Git 기록 확인 · 배포 중

리터세이브

주유 전 가격을 빠르게 비교할 수 있도록 데이터와 화면을 같이 정리한 프로젝트입니다.

ReactViteOpinet API
Static JSON · 가격 비교 · GitHub Pages
Review Tag 미리보기
리디자인 중 · 배포 중2026.01.05 리디자인 시작 · 2026.04.20 QA 기록 확인 · 리디자인 중 · 배포 중

Review Tag

영화·애니 리뷰 커뮤니티에 퀴즈와 포인트 보상 기능을 함께 넣은 서비스입니다.

ReactSpring BootPostgreSQL
TMDB · JWT · 포인트 시스템 · Vercel
상세 보기 배포 보기 비공개 저장소
PETIQUE 미리보기
리디자인 중 · 배포 중2026.01.08 리디자인 시작 · 2026.04.21 배포 검증 확인 · 리디자인 중 · 배포 중

PETIQUE

반려동물 분양 신청, 후기, 커뮤니티가 이어지도록 다시 다듬고 있는 서비스입니다.

JSPjQueryPostgreSQL
Spring MVC · Render · Supabase · EmailJS · imgBB
상세 보기 배포 보기 비공개 저장소

자동화 / 배포

CI/CD · 배포 경험

GitHub Actions외부 API 수집, JSON 생성, 빌드, 정적 배포 흐름을 자동화했습니다.
GitHub Pages팜프라이스 노트, 상장노트, 리터세이브를 정적 사이트로 배포했습니다.
VercelReview Tag 프론트엔드를 배포하고 수정 사항을 빠르게 확인했습니다.
RenderPETIQUE 서버 배포, 환경변수, Docker build cache guard를 점검했습니다.
Cloudflare Pages이 포트폴리오를 정적 사이트로 배포할 수 있게 구성했습니다.

데이터 처리

데이터 파이프라인 처리 경험

1API 수집
2정규화
3검증
4JSON 생성
5배포 반영

공공 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 단계로 옮겼습니다.”

개발일지

  1. 2026.03.02데이터형 미니 서비스 3종 중 농산물 가격 서비스를 먼저 기획했습니다. 이 날짜는 사용자 작업 기록 기준입니다.
  2. 2026.04.27첨부 Git 기록 기준 새 저장소 생성, 데이터 표시 오류 수정, UI 문구 개선, 농산물 대시보드와 품목 아이콘 개선 기록이 확인됩니다.
  3. 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 생성 결과를 배포 전 확인했습니다.

설명 포인트

“압축/인코딩 처리는 화면의 책임이 아니어서 데이터 파이프라인으로 옮겼습니다.”

개발일지

  1. 2026.03.02데이터형 미니 서비스 3종 중 상장 일정 정리 서비스로 기획했습니다. 이 날짜는 사용자 작업 기록 기준입니다.
  2. 2026.04.23첨부 Git 기록 기준 초기 프로젝트 구성, GitHub Pages 배포 설정, 빌드·배포 workflow 분리, npm 캐시 적용, 페이지네이션·상세 모달·DART 원문 파싱 추가 기록이 확인됩니다.
  3. 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

검증 방법

빈 응답, 잘못된 가격, 좌표 누락 데이터를 제외하고 사용자에게 대체 메시지를 보여주도록 했습니다.

설명 포인트

“최저가만 보여주면 실제 절약과 다를 수 있어 가격·지역·거리 기준을 함께 고려했습니다.”

개발일지

  1. 2026.03.02데이터형 미니 서비스 3종 중 주유소 가격 비교 서비스로 기획했습니다. 이 날짜는 사용자 작업 기록 기준입니다.
  2. 2026.04.23첨부 Git 기록 기준 초기 프로젝트 구성, 지역·유종 필터 동기화, 빈 데이터셋 예외 처리, Opinet 인증키 검증과 수집 오류 로그 개선 기록이 확인됩니다.
  3. 2026.04.24Opinet 인증 fallback과 진단 스크립트 추가, GitHub Actions 실행 환경 업데이트, 다크/화이트 모드 추가 기록이 확인됩니다.
  4. 2026.04.27현재 위치 기준 정렬, 가성비 추천, 공유 링크, 즐겨찾기, 가격 누적 기록 로직 추가 기록이 확인됩니다.
  5. 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 재발급 메서드 오류 수정 기록 반영

첨부 파일에서 확인한 작업 기록

2026.01.19

기존 백엔드 Git 로그에서 파이널 프로젝트 업로드와 README 추가 기록이 확인됩니다.

2026.02.11

배포용 /api 보완 기록이 확인됩니다.

2026.03.14

메일 설정 하드코딩 제거, 환경변수화, 비로그인 포인트상점 인터셉터 수정 기록이 확인됩니다.

2026.03.24

TokenService의 generateRefreshToken(TokenVO) 오류 수정 기록이 확인됩니다.

2026.04.20

최종 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 의존성 검사 추가

첨부 파일에서 확인한 작업 기록

2026.03.13

회원가입 관련 요청 로그인 예외 처리와 인증번호 만료 시간 체크 수정 기록이 확인됩니다.

2026.03.18

회원탈퇴 오류 수정 기록이 확인됩니다.

2026.03.20

닉네임 중복검사 로직 변경 기록이 확인됩니다.

2026.03.21

분양 완료 로직 변경 기록이 확인됩니다.

2026.03.25

AdoptionProcessService 미반영 수정 기록이 확인됩니다.

2026.04.21

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

ReactViteJavaScriptjQueryHTML5CSS3AxiosJotaiBootstrap

Backend & View

Java 21Spring BootSpring MVCSpring SecurityJWTJSPJSTLJdbcTemplateMyBatis

Database

PostgreSQLSupabaseOracleSQLCRUDSchema DesignTransaction

Data & API

KAMIS APIOpenDART APIOpinet APITMDB APIEmailJSimgBBJSONXMLNode.js Script

Deploy & CI/CD

GitHub ActionsGitHub PagesVercelRenderCloudflare PagesGitHub Secrets

추가 확인

2026.04.29 기준 공개 범위와 남은 확인

GitHub 공개 범위 정리

공개 가능한 3개 개인 프로젝트는 GitHub 링크를 연결했고, Review Tag와 PETIQUE는 비공개 저장소로 표시했습니다.

시연 계정 안내

관리자 기능은 공개 범위 안에서만 안내합니다. 계정 정보가 필요한 경우에는 별도 제출 자료나 면접 시연으로 설명하는 쪽이 안전합니다.

측정값은 추정하지 않기

Lighthouse, 번들 크기, 처리 속도처럼 숫자가 필요한 항목은 실제 측정 후에만 추가하고, 확인하지 않은 수치는 쓰지 않도록 남겨두었습니다.

작업 기록 근거 보강

첨부 git.zip의 커밋 메시지, 최종 ZIP의 update.md/QA 문서, 실제 코드 파일명을 함께 적어 설명이 과장되지 않도록 정리했습니다.

연락처

문제를 발견하면 원인부터 정리하고 고칩니다.

이 포트폴리오는 프로젝트 링크만 모아둔 페이지가 아니라, 기존 기능, 수정한 코드, 트러블슈팅, 배포 과정을 함께 설명하기 위해 만들었습니다. 질문을 받았을 때 각 프로젝트의 문제 상황과 해결 이유를 코드 기준으로 설명할 수 있도록 정리했습니다.