BARO FARM
생산자와 소비자를 직접 잇는 농수산물 커머스를 Spring Cloud 기반 MSA로 만든 5인 팀 프로젝트(프로그래머스 데브코스)입니다. 이 중 인증·인가 경계 — 회원·인증 서비스, 게이트웨이 인증·인가 필터, OPA 정책과 정책 번들 서비스, 사용자·판매자 상태 전파 — 를 맡았습니다. 실서비스 운영 없이 발표 시연(2026-01-27)까지 진행했고, 설계는 그 시점 기준으로 설명합니다. 발표 시점의 정책은 기본 허용이었고, 지금 main에서는 라우트의 OPA 인가 필터가 빠져 있습니다.
Role
백엔드(5인 팀) — 회원·인증 서비스, Gateway 인증·인가 필터, OPA 정책·번들 서비스, 사용자·판매자 상태 전파
Period
2025.11.29–2026.01.27 팀 프로젝트(세미·파이널) · 2026.02 모듈 정리(팀 레포 main) · 2026.03 개인 브랜치(미병합)
84
서비스·역할별 경로 규칙
OPA 정책 데이터 22파일 · 78eaa987
10–60초
상태 변경이 OPA에 닿는 번들 폴링 주기
설정값, 측정 아님
주문·결제, 재고, 정산, AI 추천·검색, 배포 인프라는 팀원이 맡았습니다. 게이트웨이의 트래픽 설정(요청 수 제한·재시도·서킷브레이커)은 인증·인가 필터와 함께 맡았습니다.
맡은 범위
| 영역 | 본인 | 팀원 | 대표 커밋 |
|---|---|---|---|
회원·인증 서비스 baro-auth | 가입·이메일 인증 코드, JWT 로그인(HttpOnly 쿠키), 리프레시 회전·로그아웃, 비밀번호 변경·재설정, 카카오·네이버 OAuth와 계정 연결, 내 정보, 회원탈퇴(outbox), 관리자 사용자·판매자 상태 변경, hotlist 이벤트 발행 | — | 95946295 5739f946 |
| API Gateway | 인증 필터, OPA 인가 필터(서킷브레이커·벌크헤드·2초), 기본 필터(Redis RequestRateLimiter, Retry, CircuitBreaker)와 Resilience4j 설정, fallback | 일부 라우트 설정 | fb81d45f 8cc62c58 |
| OPA 정책 | rego, 서비스·역할별 경로 규칙(22파일, 경로 항목 84개), 정적 hotlist, opa-config | K8s DaemonSet 배포 매니페스트 | ff7f5015 f60ada73 |
정책 번들 서비스 opa-bundle | Kafka hotlist 소비, 메모리 hotlist, bundle.tar.gz 생성·제공, 내부 접근 필터 | — | c7adf324 |
판매자 도메인 baro-seller | 판매자 신청·사업자번호 검증, hotlist 이벤트로 판매자 상태 동기화, auth 호출용 Feign 클라이언트·토큰 전달 인터셉터(발표 시점 호출처 없음) | 농장(farm) | 5f76b5cd 1224e1a2 |
| 플랫폼 | — | Eureka·Config 서버, K8s 매니페스트, CI/CD, 모니터링 | — |
| 다른 서비스 | — | 주문·결제 SAGA, 재고, 정산 배치, AI 추천·검색 | — |
시스템 구성
구성도는 위 커버 그림입니다(금색 = 맡은 부분, 회색 = 팀원, 점선 = 폴링·비동기).
- 인가 판단은 OPA(PDP)가 하고, 게이트웨이는 그 결과를 집행(PEP)만 합니다.
- 상태 변경은 auth-service → Kafka(
opa-hotlist-events) → opa-bundle → OPA 번들 폴링으로 인가 데이터에 반영됩니다. 같은 토픽을 seller-service가 소비해 판매자 상태를 맞춥니다. - Spring Security는 auth-service에만 있습니다. 나머지 서비스는 게이트웨이가 쓴
X-User-*헤더를 그대로 사용자 식별에 씁니다. 그래서 게이트웨이 필터가 신뢰 경계입니다.
요청 하나가 지나가는 길
로그인하면 auth-service가 access·refresh JWT를 HttpOnly·Secure·SameSite=Strict 쿠키로만 내려 줍니다. access JWT는 HS256으로 서명하고 sub(이메일), uid, 역할, user_status, seller_status, flags를 담습니다. 유효기간은 access 7일, refresh 14일입니다.
판단과 집행을 나눴습니다. 경로·역할 규칙을 게이트웨이 코드 배포 없이 정책과 데이터로 바꾸기 위해서입니다. 대가는 요청마다 붙는 OPA 호출 한 번(2초 제한)입니다.
규칙은 서비스·역할별 데이터 묶음입니다. 게이트웨이는 질의에 route.service를 넣고, OPA는 그 서비스의 묶음만 평가합니다. 메서드 색인(index.method)으로 후보 규칙을 한 번 더 줄입니다. data[service] 가변 조회가 정책 패키지 자신을 가리켜 생긴 순환 오류는 명시적 데이터 루트로 풀었습니다. 대가로 서비스가 늘 때마다 rego를 고쳐야 하고, 라우트 id 규칙에 서비스 이름이 묶입니다. 규칙 한 건은 아래와 같습니다(baro-cloud/opa/policy/data/buyer/seller.json @78eaa987에서 발췌, 들여쓰기만 정리).
"buyer:products-create": {
"name": "buyer:products-create",
"path": "^(/buyer-service)?/api/v1/products$",
"methods": ["POST"],
"roles": ["SELLER", "ADMIN"],
"deny_flags": ["PRODUCT_PUBLISH_BLOCKED"]
}
OPA가 실패하면 닫습니다. OPA 오류, 2초 초과, 서킷 열림, 벌크헤드 포화는 모두 503입니다. 가용성을 내준 선택이고, fail-open 대안은 설계 문서에 남겼습니다.
상태가 바뀌면
토큰에는 굵은 권한만 두고, 바로 반영할 상태는 hotlist로 보냈습니다. 이미 발급된 JWT는 만료 전까지 옛 역할을 담고 있습니다(access 7일). 그래서 사용자 정지·차단·탈퇴와 판매자 승인·거절·정지는 hotlist 데이터로 OPA에 보냅니다. 토큰의 seller_status는 항상 UNKNOWN이라 판매자 상태는 hotlist로만 알 수 있습니다. 대가는 OPA 번들 폴링 주기(10–60초, 설정값)만큼의 지연입니다.
번들은 OPA가 가져가는(pull) 방식이고, 이벤트마다 전체를 다시 만듭니다. opa-bundle은 기동할 때 data.json에서 hotlist를 복구합니다. OPA는 마지막 정상 번들을 계속 씁니다. 대가로 메모리 저장소라 인스턴스는 하나만 둘 수 있고, 번들 파일은 임시 파일 없이 덮어씁니다.
Kafka 키는 subjectId입니다. 사용자별 순서를 지키기 위해서입니다. 번들 서비스는 eventId로 중복을 거릅니다(최근 10,000개).
판매자 hotlist가 게이트웨이 인가에 쓰인 것은 승인 규칙이 정책에 있던 01-22~01-26뿐입니다. 발표 커밋의 정책은 판매자 hotlist를 읽지 않습니다. 그 기간에도 상품 서비스는 토큰의 역할 헤더로 판매자를 다시 검사했습니다.
이벤트를 내보내는 세 가지 방식
| 경로 | 방식 | 지켜지는 것 | 빈틈 |
|---|---|---|---|
사용자·판매자 상태 → opa-hotlist-events (발표 시점) | 트랜잭션 안에서 KafkaTemplate., 결과를 기다리지 않음. 키 = subjectId | 사용자별 순서. 소비자가 eventId로 중복을 거름 | 롤백돼도 이벤트가 나갈 수 있음. 브로커 장애면 로그만 남고 사라짐 |
회원탈퇴 → user-events (발표 시점, 01-20) | outbox: 같은 트랜잭션에 이벤트 행 적재 → 1초 스케줄러 → send().get(5s) → PUBLISHED, 5회 실패면 FAILED | 탈퇴가 커밋되면 이벤트 행도 반드시 있음 | 소비자가 아직 없음. 여러 인스턴스용 잠금·백오프 없음. 같은 탈퇴의 hotlist 전파는 outbox가 아니라 커밋 전 직접 전송 |
| 판매자 승인 (2026-03 개인 브랜치, 미병합) | afterCommit 동기화 → @Async( → Kafka | 승인 커밋과 응답이 브로커에 막히지 않음. 롤백된 승인은 나가지 않음 | 전송이 실패하면 다음 토큰 갱신(최악 7일)까지 반영되지 않음. 관리자 경로만 해당(grantSeller는 여전히 트랜잭션 안 동기). 테스트 없음 |
즉시성이 필요한 인가 데이터와 유실되면 안 되는 탈퇴 이벤트를 다르게 다뤘지만, 발표 시점의 hotlist 경로는 유실도, 롤백된 변경의 이벤트(유령 이벤트)도 막지 못했습니다. 3월 브랜치의 커밋 뒤 전송은 그중 유령 이벤트 쪽만 막습니다.
회원·인증 데이터
- 회원·인증 표 사이 관계는 JPA 연관 없이
user_id(BINARY(16) UUID)로 잇는 논리 관계입니다. - 리프레시 토큰은 사용자당 1개이고 갱신 때 회전하며, 비밀번호 변경·재설정·정지·탈퇴 때 지웁니다. 탈퇴는 한 트랜잭션에서 사용자를 익명화하고 자격 증명·소셜 계정·리프레시 토큰을 지운 뒤 outbox 행을 남깁니다.
발표 이후
2026-02에는 팀 레포 main에서 모듈을 정리했습니다(PR #92·#94·#102). auth와 seller를 baro-user로 합치고, baro-cloud/*를 최상위 모듈로 올리고, 알림을 baro-notification으로 나눴습니다. 게이트웨이에서는 Eureka 클라이언트를 뺐습니다. 이 과정에서 라우트의 OPA 인가 필터가 빠졌습니다(본인 커밋 3f98d694). 지금 main의 prod 프로필 라우트는 K8s 서비스 DNS 주소를 씁니다.
2026-03에는 개인 브랜치(미병합)에서 판매자 관리자 API를 도메인으로 분리했습니다. 승인 뒤 OPA 전파는 커밋 뒤 별도 실행기로 옮겼고, 그 결정을 ADR(커밋 59f643a6)로 남겼습니다. 이 변경은 관리자 경로에만 해당하고 테스트가 없습니다. 브랜치의 테스트는 컴파일되지 않습니다.
현재 상태와 한계
- 2026-01-22 14:29
1224e1a2: 판매자 승인 규칙을 추가했습니다. 이때 정책은 기본 거부였습니다. - 01-25 19:15
218193b4부터 01-26 00:5136edd3fe까지 약 5.5시간 동안 정책이opa check를 통과하지 못했습니다. - 01-26 00:51
36edd3fe(본인): 판매자 승인 규칙을 지웠습니다. 00:54d4b67d1b(본인): 기본 허용으로 바꿨습니다. 발표 커밋78eaa987(01-27)도 기본 허용입니다. - 2026-02-12
801d3b9e(본인): OPA 번들 경로 설정과 컨트롤러 경로가 어긋났습니다(/baro-opa/bundle과/opa/bundle). 02-223f98d694(본인): 라우트에서 OPA 인가 필터를 뺐습니다. - 지금 main(
1ef2f99b)에는 OPA·번들 서비스 배포 매니페스트가 없고, 클린 클론에서baro-user가 빌드되지 않습니다. - rego 테스트는 도입(
ff7f5015)부터 main까지 한 번도 통과하지 않았습니다. 정책의 순환 오류(위 ‘규칙’ 문단)는f60ada73에서 풀었지만, 테스트는 실행하면rego_recursion_error로 끝납니다. - 다시 켜는 순서: 기본 거부 → 라우트 필터 재연결 → 번들 경로 일치 → rego 테스트가 실제로 도는 상태.
발표 시점 설계의 한계입니다.
| 한계 | 영향 | 고친다면 |
|---|---|---|
토큰이 없는 요청은 클라이언트가 보낸 X-User-* 헤더가 그대로 하류로 갑니다 | 하류 서비스가 이 헤더를 믿으므로, 헤더만으로 다른 사용자로 행세할 수 있습니다 | 들어온 X-User-*는 항상 지우고, 토큰이 없으면 익명 주체로만 넘깁니다 |
| 비밀번호에 64자 salt를 붙여 BCrypt에 넣습니다 | BCrypt 입력 한도(72바이트)를 넘겨 9자 이상 비밀번호의 가입·변경이 실패합니다 | 별도 salt를 없애고 BCrypt 내장 salt만 씁니다 |
grant-seller 엔드포인트가 인증 없이 열려 있습니다(커밋 메시지 “임시로(세미용)“) | 판매자 권한을 얻는 경로가 열려 있습니다 | 관리자 승인 경로로만 권한을 줍니다 |
| access 토큰 유효기간이 7일입니다(15분에서 “테스트용”으로 바꾼 값이 남음) | 옛 역할이 오래 남고, 상태 전파가 유실되면 다음 토큰 갱신까지 반영되지 않습니다 | 짧은 access 토큰과 리프레시 회전, 상태 전파는 outbox로 |
| 번들 엔드포인트는 사설 대역 전체를 허용하고, 내부 토큰 검사는 기본으로 꺼져 있습니다 | 클러스터 안의 누구나 번들을 받을 수 있습니다 | 토큰 검사를 기본으로 켜고 허용 대역을 줄입니다 |
팀 레포는 공개입니다. 설계 설명은 발표 커밋 78eaa987, 현재 상태는 main 1ef2f99b 기준입니다. 범위 표에 싣지 않은 대표 커밋은 회원·인증 5632084d 4fcc80c3 7baffb26 727501fb, 게이트웨이 b40ca9e9, 판매자 8e3decda a8aad588입니다.