사업자 진위확인 로직 개선: Insert 기반에서 조회 기반으로 전환

김연후·2025년 8월 17일
0

🛠️ 트러블슈팅: 사업자 진위확인 (ReceiptProcess)


1. 만나게 된 이슈

국세청 OpenAPI를 통한 사업자 진위 확인 기능을 구현하는 과정에서,
companyemployee 테이블에 중복 Insert가 발생하거나
로직과 맞지 않는 데이터가 삽입되는 문제가 있었다.


2. 문제 상황

과거 로직에서는 OpenAPI 응답의 valid=01(유효한 사업자) 결과가 나오면:

  • company 테이블에 즉시 INSERT
  • employee 테이블에도 직원 등록 INSERT

을 수행했다.

⚠️ 발생한 문제

  • 중복 키 충돌

    • 이미 등록된 companyId를 다시 INSERT → DuplicateKeyException 발생
    • 동일 userId+companyId 조합이 employee 테이블에 중복 삽입
  • 로직 불일치

    • 운영 로직상 employee 테이블에는 더 이상 데이터를 넣지 않기로 했으나,
      코드에서는 여전히 삽입
  • 데이터 무결성 위험

    • OpenAPI 호출만으로 DB에 데이터가 바로 들어가면서 잘못된 요청에도 INSERT 수행
    • 필드 포맷 오류나 invalid 값이 들어와도 DB 데이터가 꼬일 수 있음

3. 원인

  1. Insert 중심 설계
    → “외부 API 응답이 곧 DB 삽입 조건”이라는 단순한 구조
  2. DTO 타입 불일치
    → companyId가 String → Long 변환 없이 사용되며 오류 가능성
  3. 예외 처리 부족
    → OpenAPI 응답 포맷이 다르거나 필드 누락 시 NPE 발생

4. 어떻게 해결해야 하는가?

🛠️ A. Insert 제거

  • insertVerifiedCompany(), insertEmployeeIfNotExists() 삭제
  • OpenAPI 결과만으로 DB에 새로운 데이터 추가하지 않음

🛠️ B. 조회 기반 전환

  • Mapper에 findCompanyInfoByCompanyId(Long companyId) 추가
  • OpenAPI valid=01이라도 DB에서 조회만 수행
  • 존재하지 않으면 IllegalStateException → 클라이언트에 404 반환

🛠️ C. DTO 타입 일관성

  • companyId: String → Long 변경
  • openedDate: @JsonFormat("yyyyMMdd") 적용 → 파싱 일관성 보장

🛠️ D. OpenAPI 요청/응답 처리 개선

  • Long 타입 companyId를 String.valueOf()로 변환 후 전송
  • 응답 파싱 시 data 배열 존재 여부 체크 → NPE 방지
  • OpenAPI에 나타난 status_code를

Service에서 내부 예외로 매핑하고, Controller에서 이를 HTTP 응답 코드로 다시 매핑한다.

5. 수정 후 동작 확인 ✅

수정된 로직을 반영한 뒤, Postman을 통해 실제 호출을 테스트했다.
• 요청 시 companyId, ceoName, openedDate를 전달
• 응답으로 companyName, ceoName, openedDate, valid=true가 정상 반환됨


6. 배운 점 ✨

  1. Insert 중심보다는 조회 기반 검증이 데이터 무결성에 안전하다.
  2. DTO 타입은 반드시 DB 스키마와 일관성을 유지해야 한다.
  3. 외부 API 연동 시 에러 응답/포맷 누락을 가정한 방어적 파싱이 필요하다.
  4. 로직 변경(예: employee 등록 중단)은 반드시 코드와 DB 동작이 동기화되어야 한다.

📌 마무리

이번 트러블슈팅을 통해, 단순히 “동작하는 코드”가 아닌
로직과 일치하면서 데이터 무결성을 지킬 수 있는 구조가 얼마나 중요한지 배웠다.

0개의 댓글