rfp-match:7

이지예·2026년 9월 17일

개발

목록 보기
36/58

코드가 돌아가는 것과 "공개해도 되는 것"은 다르다

MVP를 완성하고 나서 저장소를 공개하기로 했을 때, 코드가 잘 동작한다는 것과 별개로 한 가지를 다시 점검해야 했다. 실제 연동 대상 서비스의 명칭, 예시 데이터에 들어간 제품명·기관명이 코드 곳곳에 남아있었다. 개발 중에는 실제 값을 써야 테스트하기 편하지만, 공개 저장소에는 남기면 안 되는 정보였다.

무엇을 익명화했는가

정책은 단순했다. 연동 로직과 도메인 지식은 그대로 두고, 특정 서비스·기업을 특정할 수 있는 고유명사만 걷어낸다.

항목처리
외부 API 클라이언트/설정 클래스명특정 서비스명이 들어간 구체적 이름 → BidNoticeApiClient처럼 일반화된 이름으로 리네이밍
API 엔드포인트, 오퍼레이션 ID코드에서 완전히 제거, 환경변수로만 주입 (BID_API_BASE_URL, BID_API_OPERATION, BID_API_SERVICE_KEY)
예시 데이터의 제품명가상 제품명(NGIPS-2000)으로 교체
예시 데이터의 기관명A기관, B기관처럼 익명화
화면 문구·문서의 외부 링크전부 제거, "공공 입찰공고 Open API"로 일반화
CC인증 등급, 보안적합성 검증 여부 같은 도메인 용어그대로 유지

마지막 항목이 중요하다. "CC인증 EAL 등급"이나 "보안적합성 검증필" 같은 용어는 특정 기업이나 서비스를 가리키는 고유명사가 아니라 요구사항 유형 자체다. 매칭 엔진의 판정 규칙이 이 용어를 근거로 동작하기 때문에, 이걸 지우면 코드의 의미 자체가 사라진다. "무엇을 지울지"를 정할 때 "식별 가능한 고유명사"와 "도메인 지식"을 구분하는 게 핵심이었다.

설정을 코드에서 분리하기

가장 확실한 방법은 애초에 코드에 엔드포인트나 인증키를 하드코딩하지 않는 것이다.

# application.yml
bid-api:
  enabled: false
  base-url: ${BID_API_BASE_URL:}
  operation: ${BID_API_OPERATION:}
  service-key: ${BID_API_SERVICE_KEY:}

값이 비어있으면 호출 자체를 시도하지 않고 명시적으로 실패하도록 만들어서(4편 참고), 설정 누락이 조용히 묻히는 일도 막았다.

커밋 히스토리까지 정리하기

작업 파일만 고치는 걸로는 부족하다. git 히스토리에는 이전 커밋의 내용이 그대로 남아있다. 익명화 커밋을 새로 하나 얹어도, git log -p로 과거 커밋을 보면 원래 값이 그대로 드러난다.

이 프로젝트는 아직 커밋이 몇 개 안 됐던 시점이라 히스토리를 재작성하기로 했다.

git reset --soft <초기 커밋>
git commit -m "..."   # 익명화가 반영된 내용으로 새로 커밋
git push --force-with-lease origin main

이후 전체 히스토리를 다시 스캔해서 식별 가능한 문자열이 하나도 남지 않았는지 확인했다.

git log -p main | grep -c "제거 대상 키워드1\|제거 대상 키워드2\|..."
# 0

force-with-lease를 쓴 이유는 force와 달리 "내가 마지막으로 본 원격 상태와 실제 원격 상태가 같을 때만" 덮어쓴다. 다른 브랜치나 CI가 그 사이 뭔가를 푸시했다면 실패하고 멈춘다 — 히스토리를 재작성하는 위험한 작업일수록 이런 안전장치가 필요하다.

배포 전에 놓치기 쉬운 것들

배포를 검토하면서 "지금 바로 배포할 수 있나"를 점검하다가, 실제로는 준비가 안 된 부분들을 발견했다.

  • Docker Compose는 파일만 있고 실행해본 적이 없었다. docker-compose.yml을 작성은 했지만 실제로 docker compose up을 돌려본 적이 없다는 걸 인정해야 했다. "파일이 있다"와 "동작을 확인했다"는 다른 이야기다.
  • 인증 계층이 없었다. Spring Security 의존성 자체가 빠져 있어서, 배포하면 누구나 제품/요구사항을 마음대로 생성·수정·삭제할 수 있는 상태였다. 포트폴리오용으로 잠깐 공개하더라도 쓰기 API는 막거나 읽기 전용으로 운영해야 한다는 걸 다시 확인했다.
  • MySQL 프로필에서 예시 데이터가 꺼져 있었다. 배포하면 화면이 텅 빈 채로 열린다는 뜻이다. 검증용 환경변수 토글(SAMPLE_DATA, BID_API_ENABLED)을 추가해서, 인증키 없이도 MySQL 기동을 확인할 수 있게 했다.

이 점검을 하면서 얻은 교훈은, "README에 Dockerfile과 compose 파일이 있다"는 것만으로는 배포 준비가 됐다고 말할 수 없다는 것이다. 실행해서 확인한 것과 작성만 해둔 것은 명확히 구분해서 스스로에게 말해야 한다.

외부에서 올라온 변경을 병합하기 전 검토

작업 도중, 배포 준비 관련 커밋이 담긴 브랜치를 병합하기 전에 먼저 내용을 검토하는 과정을 거쳤다. 실무에서도 당연한 절차지만, 개인 프로젝트라고 생략하지 않으려 했다.

체크한 항목은 이랬다.

  1. diff 전체를 읽는다 — 어떤 파일이 왜 바뀌었는지 하나씩 확인
  2. 빌드 스크립트에 의심스러운 패턴이 있는지 — 외부 URL 호출, 알 수 없는 다운로드 명령 등
  3. 병합된 상태로 실제 테스트·빌드를 돌려본다 — "머지하면 될 것 같다"가 아니라 실제로 병합해서 확인
  4. fast-forward가 가능한지 확인 — 가능하면 불필요한 머지 커밋 없이 깔끔하게 히스토리를 유지
git merge-base --is-ancestor main <검토대상브랜치> && echo "fast-forward 가능"

혼자 개발하는 프로젝트라도 "이 변경을 왜 받아들이는지"를 매번 설명할 수 있어야 한다고 생각한다.

회고

배운 점

  • 도메인 규칙을 코드로 옮길 때 가장 어려운 부분은 알고리즘이 아니라 "모르는 것을 모른다고 답하게 만드는 설계"였다. 확인불가 상태를 1급 판정으로 둔 덕분에, 엔진이 틀린 확신을 주지 않는다.
  • 외부 API는 정상 응답보다 비정상 응답의 가짓수가 훨씬 많다. 응답 형태 여러 개와 실패 경로를 테스트로 고정해두니 나중에 뭔가 바꿔도 회귀가 바로 잡힌다.
  • "파일을 만들어뒀다"와 "동작을 확인했다"는 완전히 다른 상태다. Docker, MySQL 검증에서 이 차이를 명확히 인지하는 게 다음 프로젝트에도 도움이 될 것 같다.
  • 공개 저장소를 준비하는 과정 자체가 좋은 학습이었다. "이 코드가 남에게 보여도 되는가"를 기준으로 다시 훑으면, 평소엔 안 보이던 설계 결함(인증 계층 부재 같은)이 드러난다.

다음 단계

  • 텍스트형 요구사항 매칭 규칙 확장
  • 매칭 결과 기반 제안서 초안 문서 내보내기
  • 최소한의 인증/인가 계층 추가 (배포 전 필수)
  • 실제 배포 (단일 오리진 서빙 + 무료 도메인)

여기까지가 이 프로젝트의 개발 기록이다. 전체 흐름을 다시 정리하면: 문제를 자동화 대상과 사람 판단 대상으로 명확히 쪼개고(1편), 그 핵심을 도메인 규칙으로 구현하고(2편), API와 트랜잭션 정책으로 정합성을 지키고(3편), 외부 의존성의 실패를 격리하고(4편), 그 결과를 사람이 읽기 좋게 보여주고(5편), 여러 층위에서 검증하고(6편), 마지막으로 공개 가능한 상태로 정리했다(7편).


이 시리즈는 개인 학습 프로젝트 개발 기록입니다. 코드에 등장하는 기관명·제품명은 전부 가상이며, 실제 연동 대상 서비스의 명칭·엔드포인트는 기술적 세부사항 보호를 위해 일반화해서 서술합니다.

profile
github: @dlwldP

0개의 댓글