약관 관리 서비스
사이트별 HTML과 익일 반영 방식에서는 실제 노출한 약관 버전과 동의 기록을 연결하기 어려웠습니다. 약관 내용·버전·시행일과 화면별 그룹·순서를 나누고, 노출 전환은 자동화하면서 버전 등록은 운영자가 관리하도록 구현했습니다. 주요 모델·개발을 맡고 동료와 공동 발표했습니다.
지마켓 · 옥션 · ESMPLUS 공개 약관 페이지를 한 서비스에서 운영 중
- 기간
- 2024.06 — 2024.12
- 팀
- 4인 팀
- 역할
- 약관 모델 · 주요 구현 · 동료와 공동 발표
약관을 고쳐도 다음 날까지 기다려야 했다
약관을 수정했는데 공개 페이지에는 다음 날 0시가 되어야 반영됐습니다. 지마켓과 옥션은 서로 다른 어드민을 사용했고, 본문 HTML을 DB에 넣거나 iframe으로 보여주는 구조였습니다. URL만 보고 어떤 약관인지 구분하기도 어려웠습니다.
문제는 갱신 속도에서 끝나지 않았습니다. 버전과 시행일자를 명확히 관리하지 못하면 회원이 동의한 당시의 내용을 확인하기도 어렵습니다. 실제로 화면에 보이지 않는 약관이 동의 기록에 남거나, 필수 약관인데 동의값이 비어 있는 사례가 있었습니다. 약관을 코드·버전·시행일자로 식별하고, 운영자가 정한 시점에 노출할 수 있도록 관리 방식을 바꾸고자 했습니다.
4인 팀에서 약관 모델을 정하고 백엔드와 어드민을 개발했습니다. 완성 후에는 기존 구조, 변경 내용과 후속 과제를 사내 리뷰에서 발표했습니다.
전체 아키텍처
운영자는 어드민에서 약관과 시행일을 미리 등록하고, 그룹별 항목과 순서를 편집합니다. 서비스는 조회할 때 현재 시각에 맞는 버전을 선택합니다. 약관 메타 정보와 매핑 이력은 RDB에, 편집 데이터와 본문은 Blob 저장소에 보관합니다.
미리 등록한 약관을 조회 시점의 버전으로 제공하는 구조
같은 약관을 여러 화면에서 쓰려면
화면마다 여러 약관을 보여주지만, 서로 다른 화면에서 같은 약관을 사용하는 경우가 있었습니다. 약관을 화면마다 중복 등록하면 같은 내용을 여러 곳에서 관리해야 합니다. 약관 내용과 화면 구성을 나누고, 하나의 약관을 여러 그룹에서 재사용할 수 있도록 모델을 정했습니다.
약관은 코드·버전·시행일로 내용을 관리하고, 약관그룹은 한 화면에 함께 노출할 약관의 묶음을 나타냅니다. 그룹 매핑은 각 그룹에 포함할 약관과 노출 순서를 연결합니다. 예를 들어 그룹 A와 그룹 B의 전체 구성은 달라도 같은 약관 하나를 함께 사용할 수 있습니다.
운영자는 어드민에서 약관 항목을 그룹에 끼워 넣고 드래그 앤 드롭으로 순서를 정합니다. 저장할 때는 변경 전의 약관 목록과 순서도 이력으로 남깁니다. 약관 본문의 개정 이력과 화면 구성의 변경 이력을 구분해, 내용과 배치를 각각 확인할 수 있게 했습니다.
미리 등록하고, 조회할 때 버전을 고른다
운영자가 새 버전의 약관과 시행일자를 미리 등록하면, 서비스는 조회할 때 현재 시각에 맞는 버전을 선택합니다. 새 시행일 전의 조회에는 기존 버전을, 시행일부터의 조회에는 새 버전을 제공합니다. 같은 약관을 사용하는 모든 그룹에 이 기준을 공통으로 적용하므로 그룹별로 적용 버전을 다시 지정할 필요가 없습니다.
화면의 약관 조합을 바꾸는 일과 약관 내용을 개정하는 일을 분리한 이유도 여기에 있습니다. 그룹 매핑은 어떤 약관을 어떤 순서로 보여줄지 정하고, 약관 조회는 그 시점에 유효한 내용을 선택합니다. 운영자는 개발자 배포를 기다리지 않고 다음 개정을 준비할 수 있습니다. 등록 시에는 이전 버전보다 과거의 시행일자나 더 작은 버전이 들어오지 않도록 검증했습니다.
본문은 HTML 에디터로 편집합니다. 에디터의 편집 데이터(JSON)와 생성된 HTML을 함께 Blob 저장소에 두고, 메타 정보만 RDB에 둡니다. 다른 사이트의 약관을 그대로 가져다 쓰는 "외부 약관 링크"도 지원해서, 예를 들어 판매자 사이트에서 지마켓 · 옥션 약관을 다시 만들지 않고 연결할 수 있습니다.
여러 그룹이 약관을 공유하고, 조회 시 적용 버전을 선택합니다
어드민 조회는 Querydsl로
어드민은 사이트, 약관코드, 버전, 노출 여부 같은 조건을 조합해 조회하는 화면이 대부분입니다. 조건마다 JPQL 문자열의 분기를 관리하는 대신 Querydsl로 동적 쿼리를 구성했습니다. 그룹에 매핑된 약관 목록 페이징처럼 조인이 섞인 조회도 같은 방식으로 다뤘습니다.
프론트는 Spring + Thymeleaf 위에 NPM으로 JS 모듈을 관리하는 구조를 잡았고, 공개 약관 페이지는 약관코드와 사이트 코드가 URL에 드러나 어떤 약관인지 바로 알 수 있게 했습니다.
세 사이트를 한 서비스로
지마켓 · 옥션에 이어 ESMPLUS 약관까지 같은 서비스로 통합하면서, 사이트별 테이블과 시퀀스 이름이 다른 문제를 만났습니다. 이름의 차이에는 규칙이 있었으므로 공통 서비스 코드는 유지하고, 사이트별 접두사를 Hibernate Naming Strategy에서 적용하도록 했습니다.
사이트별 profile·config로 이름 규칙을 나누고, 같은 JPA 모델을 각 사이트의 기존 테이블·시퀀스 이름에 매핑했습니다. Querydsl 조회도 JPA 매핑을 통해 처리되어 함께 재사용했습니다. 이 범위의 이름 차이는 업무 코드를 수정하지 않고 설정으로 대응할 수 있었습니다.
지마켓 · 옥션 · ESMPLUS의 개인정보 처리방침과 이용약관 페이지를 같은 서비스 코드로 운영합니다. 약관의 등록·조회 기능은 공통으로 유지하고, 사이트별 이름 차이는 매핑과 설정에서 다루도록 정리했습니다.
공통 코드는 유지하고, 사이트별 이름은 설정으로 매핑
한 번에 끝나지 않은 과제
완성 후 사내 리뷰에서 이 프로젝트를 과거 · 현재 · 미래편으로 나눠 발표했습니다. 과거편은 두 어드민에 흩어져 있던 시절, 현재편은 위의 모델과 구조, 미래편은 다음 단계였습니다. 그 미래편의 핵심이 약관 동의 서비스입니다. 약관을 관리하는 것과 "이 회원이 어떤 버전의 약관에 언제 동의했는가"를 관리하는 것은 다른 문제라서, 회원가입 · 전환 흐름에 동의 API를 붙이고 사이트별로 따로 있던 동의 테이블을 정리하는 일을 다음 과제로 잡았습니다.
이후 팀이 같은 모델을 바탕으로 회원별 동의 관리를 확장했습니다. 저는 약관 모델과 주요 구현을 맡고 동료와 설계·운영 방식을 공동 발표했습니다. 동의 관리 확장 전체를 단독 구현한 것은 아닙니다. 처음 둔 동의 코드와 재동의·선동의·대체동의 필드는 후속 과제로 연결됐습니다.
버전 번호는 등록 오류를 운영자가 수정할 수 있도록 수동 관리로 남겼습니다. 조회 시점의 버전 선택은 서비스가 맡고, 새 버전의 등록과 검토는 운영 절차에서 관리하도록 구분했습니다.
구현 범위 · 사용 기술
상세 기능과 사용 기술 보기
- 약관 모델사이트 · 약관코드 · 버전 · 시행일자 · 필수 · 노출 여부
- 약관그룹 · 매핑같은 약관을 여러 그룹에서 재사용 · 항목과 순서의 변경 이력
- 시행일자 버저닝미리 등록한 버전 중 조회 시 현재 시각에 맞는 버전 선택
- 공개 약관 페이지세 사이트 각각의 약관 페이지, 약관코드로 URL이 정해짐
- 어드민 · 에디터본문 편집 · 사전 등록 · 드래그 앤 드롭으로 그룹 항목과 순서 구성
- 약관 동의 서비스회원별 동의 내역 · 가입 · 전환 시 동의 API — 후속 과제로 개발해 운영 중
돌아보면
약관 내용은 여러 화면에서 공유하고, 화면마다 필요한 조합과 순서는 그룹 매핑으로 관리합니다. 운영자는 다음 개정을 미리 등록하고, 서비스는 조회 시점에 맞는 버전을 선택합니다. 본문과 화면 구성의 변경 이력을 구분한 모델은 후속 약관 동의 서비스에도 이어졌습니다.
지금 운영 중인 약관 페이지
회사 프로젝트의 소스 코드는 공개하지 않습니다. 구조도는 업무 흐름을 설명하기 위한 개념도입니다.


