미검증 입력의 저장 위치 설계: 아직 확인 안 된 값은 어디에 둘 것인가

사용자가 입력만 하고 아직 확인 안 된 값을 어디에 저장할까. jsonb 임시 슬롯에 두고 확정 시점에 정식 컬럼으로 옮기는 방법과, 그 이동을 같은 트랜잭션에 두는 이유.

조회
목차

주문할 때 받던 배송지 정보를 뒷단계로 옮기는 작업이었다. 사용자가 주소를 입력하고, 나중에 배송 대행사에서 온 값과 대조해서 맞으면 확정하는 흐름.

정식 컬럼에 바로 쓰면 안 되는 이유

이 입력값은 아직 확인 전이다. 사용자가 입력만 하고 안 끝낼 수도 있고, 대행사 값과 안 맞을 수도 있다.

정식 컬럼(user.address)에 바로 쓰면 한 컬럼에 확인된 주소와 본인이 타이핑한 주소가 섞인다. 그러면 나중에 그 값을 믿을 근거가 사라진다. "이 회원의 주소"가 확인된 건지 그냥 쓴 건지 구분이 안 된다.

그렇다고 확인 전용 컬럼을 새로 만들면 마이그레이션이 필요하고, 확인이 끝나면 쓸모없어지는 컬럼이 여러 개 남는다.

임시 슬롯에 두고, 확정될 때 옮긴다

세션 테이블에 이미 properties라는 JSON 컬럼이 있었다. 원래 외부 응답 원문을 담던 자리인데, 정해진 형식이 없으니 키를 추가하면 그만이었다. 마이그레이션 0.

[입력]  properties.inputData  ←  사용자 입력 (미확인)
[대조]  대행사 값  ↔  properties.inputData
[확정]  통과일 때만  →  user 테이블 정식 컬럼

옮기는 건 결과 저장 함수 안에서 한다.

@Transactional
public void saveResult(int userSeq, Status status, Map<String, String> external) {
    repository.updateStatus(...);
    mergeProperties(...);

    if (status == Status.VERIFIED) {
        confirmAddress(userSeq, session, external);   // 여기서만 user 를 건드린다
    }
}

여기서 얻은 게 셋이다

1. 별도 API를 안 만들어도 된다

옮기는 걸 별도 엔드포인트로 뺐다면 "결과 저장은 성공 + 옮기기는 실패" 상태가 생길 수 있다.

콜백 처리 코드는 보통 예외를 삼키는 구조라 이 부분 실패가 조용히 지나간다. 같은 트랜잭션 안에 두면 그런 상태가 아예 존재하지 않는다.

2. 확정 값의 출처가 필드마다 다르다

주소는 대행사 값을, 요청사항은 사용자 입력값을 쓴다. 후자는 대행사가 알 수 없는 값이라 대조 대상이 아니다.

var address = firstNonBlank(external.get("address"), input.get("address"));
var memo    = input.get("memo");    // 대조 대상 아님 — 입력값이 유일한 출처

이걸 코드에 안 적어두면 나중에 "왜 요청사항은 안 덮어쓰지" 하고 누가 고친다.

3. 미완료 회원이 구조적으로 구분된다

확인을 안 끝낸 회원은 정식 컬럼이 비어 있다. is_verified 같은 별도 플래그가 필요 없고, 플래그와 실제 데이터가 어긋날 일도 없다.

대신 이런 뜻이기도 하다

관리자가 화면에서 상태만 "확인됨"으로 바꿔도 주소는 안 들어간다. 옮기는 코드가 콜백 처리 함수 안에 있으니까.

이건 의도한 동작이다 — "주소 수정"과 "상태 변경"은 별개 동작이라는 정책과 맞는다.

다만 뒤집으면 이런 뜻이 된다. 정상 경로를 검증하려면 실제 콜백을 태워야 한다. 관리자가 손으로 승인하면 이 코드가 한 줄도 안 돈다. 그래서 "상태를 손으로 바꿔서 확인해보자"는 검증이 되지 않는다.

남길 것

  • 확인 전 입력은 임시 슬롯에, 확정될 때 정식 컬럼으로 옮긴다. 마이그레이션이 필요 없고 "확인됨"과 "입력만 됨"이 구조적으로 구분된다.
  • 옮기는 건 상태를 확정하는 트랜잭션 안에서 한다. 별도 API로 빼면 중간 상태가 생기고, 콜백 처리는 예외를 삼키는 경우가 많아 더 위험하다.
  • 확정 값의 출처는 필드마다 다를 수 있다. 어느 필드가 어디서 오는지 코드에 명시한다.
  • 정식 컬럼이 비어 있는 것 자체가 "미완료" 신호다. 별도 플래그를 만들면 어긋날 여지만 생긴다.
  • 로직을 특정 경로 안에 넣으면 그 경로로만 검증된다. 우회 경로로는 그 코드가 안 돈다. 테스트 계획을 세울 때 이걸 먼저 확인한다.

더 봐야 할 것

  • 임시 값이 실패 후에도 남는데, 재시도 화면에 다시 채워주는 게 맞는지
  • 임시 값의 보관 기한. 확인을 영영 안 끝낸 사용자의 정보가 계속 남는다
  • JSON 슬롯이 편한 만큼 형식 검사가 없다. 키 오타가 실행할 때까지 안 잡히는데 이걸 막는 방법

사이트 내 검색

제목·태그·설명·본문으로 발행 글을 찾습니다