Spring RSocket 마스터 노트 시리즈 5편. 메타데이터가 RSocket의 표준 채널 외 정보 전달 메커니즘인 이유, MIME Type 기반 다중 메타데이터(Composite Metadata) 패턴, 표준 메타데이터 종류(routing·auth·tracing·zipkin), 커스텀 메타데이터 등록과 인코더, 메타데이터로 인증·trace·feature flag 전달까지.
이 글은 Spring RSocket 마스터 노트 시리즈의 다섯 번째 편입니다. 4편(클라이언트)까지 단순 데이터 송수신이었다면, 이번엔 그 데이터에 부가 정보를 어떻게 실어 보내나 — 메타데이터.
HTTP의 헤더 같은 역할. 다만 RSocket 메타데이터는 MIME Type 기반 + 다중 동시. Composite Metadata가 핵심.
처음 메타데이터가 어렵게 느껴지는 이유
처음 이 단원이 어렵게 느껴지는 이유는 두 가지예요. 첫째, MIME Type 기반이 익숙하지 않습니다. HTTP 헤더는 단순 키-값. 둘째, Composite Metadata가 왜 필요한지 막연합니다.
해결법은 한 가지예요. "메타데이터 = MIME Type별 분류". 라우팅·인증·trace 각자 다른 MIME Type. Composite = 여러 MIME Type 묶음. 이 인식만 잡으면 끝.
메타데이터가 무엇인가
RSocket 프레임:
[Frame Header]
[Metadata] ← 부가 정보 (MIME Type 기반)
[Data] ← 실제 페이로드
용도:
- 라우팅 — 어느 라우트로 갈지
- 인증 — 토큰·API 키
- Tracing — trace ID·span ID
- 커스텀 — 비즈니스 메타 (테넌트 ID·feature flag)
여기서 정말 중요한 시험 함정 — 메타데이터 ≠ HTTP 헤더. MIME Type이 메타데이터의 종류 식별. 같은 메타데이터에 여러 MIME Type 동시.
표준 MIME Types
| MIME Type | 의미 |
|---|---|
message/x.rsocket.routing.v0 |
라우팅 |
message/x.rsocket.authentication.v0 |
인증 |
message/x.rsocket.tracing-zipkin.v0 |
Zipkin trace |
message/x.rsocket.composite-metadata.v0 |
Composite (다중) |
application/json |
JSON 메타데이터 |
text/plain |
단순 문자열 |
Composite Metadata — 다중 메타데이터
하나의 프레임에 여러 메타데이터 동시:
1. 라우팅 (어디로)
2. 인증 (누구)
3. Tracing (trace ID)
4. 커스텀 (테넌트)
→ Composite Metadata로 묶어 전송
기본 메타데이터 MIME Type = message/x.rsocket.composite-metadata.v0.
여기서 정말 중요한 시험 함정 — Spring Boot 기본 = Composite Metadata. 자동으로 라우팅·인증을 다중 메타데이터로 전송. 사용자가 명시 X.
클라이언트 — 메타데이터 추가
requester.route("user.create")
.metadata("trace-123", MimeTypeUtils.TEXT_PLAIN) // 단일
.metadata("Bearer abc", AUTHENTICATION_MIME_TYPE) // 다중
.data(user)
.retrieveMono(User.class);
각 .metadata() 호출 = Composite의 한 항목.
Spring Boot Authentication
import org.springframework.security.rsocket.metadata.BearerTokenMetadata;
requester.route("secure.endpoint")
.metadata(new BearerTokenMetadata("token-abc"),
MimeTypeUtils.parseMimeType("message/x.rsocket.authentication.bearer.v0"))
.data(req)
.retrieveMono(Result.class);
import org.springframework.security.rsocket.metadata.SimpleAuthenticationEncoder;
// builder에 인증 인코더 등록
RSocketStrategies strategies = RSocketStrategies.builder()
.encoder(new SimpleAuthenticationEncoder())
.build();
서버 — 메타데이터 받기
@Header — 단일
@MessageMapping("user.create")
public Mono<User> create(
User user,
@Header("trace-id") String traceId,
@Header("auth") String token
) {
log.info("Trace: {}", traceId);
return userService.create(user);
}
@Headers — 모든
@MessageMapping("user.create")
public Mono<User> create(
User user,
@Headers Map<String, Object> headers
) {
log.info("All metadata: {}", headers);
return userService.create(user);
}
여기서 시험 함정이 하나 있어요. @Header는 단순 메타데이터. Composite 안 특정 항목은 별도 처리.
라우팅 메타데이터 자동
requester.route("user.{id}", "123")
.data(req)
.retrieveMono(Result.class);
내부적으로:
Composite Metadata:
- routing: "user.123" # message/x.rsocket.routing.v0
- auth: ... # 추가했다면
라우트는 메타데이터의 일부. .route() 자동 인코딩.
인증 메타데이터 — Spring Security
Spring Security RSocket 의존성
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.security:spring-security-rsocket'
서버 보안 설정
@Bean
public PayloadSocketAcceptorInterceptor rsocketSecurity(
RSocketSecurity security
) {
return security
.authorizePayload(authorize -> authorize
.route("user.list").authenticated()
.route("admin.**").hasRole("ADMIN")
.anyRequest().permitAll()
)
.simpleAuthentication(Customizer.withDefaults())
.build();
}
클라이언트 — 자동 헤더
import org.springframework.security.rsocket.metadata.UsernamePasswordMetadata;
requester.route("user.list")
.metadata(new UsernamePasswordMetadata("alice", "pass"),
UsernamePasswordMetadata.BASIC_AUTHENTICATION_MIME_TYPE)
.retrieveFlux(User.class);
자세한 건 6편(보안).
Tracing 메타데이터
Zipkin
import io.rsocket.metadata.TracingMetadata;
TracingMetadata trace = new TracingMetadata(traceId, spanId, ...);
requester.route("user.{id}", id)
.metadata(trace, ZIPKIN_TRACING_MIME_TYPE)
.retrieveMono(User.class);
Spring Boot Sleuth/Micrometer
자동 메타데이터 주입. 별도 코드 X.
커스텀 메타데이터
비즈니스 메타데이터 (예: 테넌트 ID)
// 단순 문자열
requester.route("data.fetch")
.metadata("tenant-acme", MimeTypeUtils.TEXT_PLAIN)
.retrieveFlux(Data.class);
// JSON 객체
requester.route("data.fetch")
.metadata(Map.of("tenant", "acme", "region", "us"), MediaType.APPLICATION_JSON)
.retrieveFlux(Data.class);
서버
@MessageMapping("data.fetch")
public Flux<Data> fetch(
@Header("tenant-acme") String tenant // 단순
) { ... }
// 또는 JSON 디코딩
@MessageMapping("data.fetch")
public Flux<Data> fetch(
@Headers Map<String, Object> meta
) {
String tenant = ((Map) meta.get("...")).get("tenant");
...
}
RoutingMetadata
import io.rsocket.metadata.RoutingMetadata;
RoutingMetadata routing = TaggingMetadataCodec.createRoutingMetadata(
ByteBufAllocator.DEFAULT,
Collections.singletonList("user.123")
);
저수준 API. 보통 .route() 자동 사용.
인코더·디코더 등록
@Bean
public RSocketStrategies rSocketStrategies() {
return RSocketStrategies.builder()
.encoders(encoders -> {
encoders.add(new Jackson2JsonEncoder());
encoders.add(new SimpleAuthenticationEncoder());
encoders.add(new BearerTokenAuthenticationEncoder());
})
.decoders(decoders -> {
decoders.add(new Jackson2JsonDecoder());
})
.metadataExtractorRegistry(registry -> {
registry.metadataToExtract(
MediaType.APPLICATION_JSON,
Map.class,
"json-metadata"
);
})
.build();
}
메타데이터 보안 함정
여기서 정말 중요한 시험 함정 — 메타데이터는 평문 (TLS 없으면 노출). 인증 토큰·민감 정보 = TLS 필수. 6편에서 자세히.
양방향 메타데이터
// 서버 → 클라이언트로 호출 시 메타데이터
@MessageMapping("login")
public Mono<Void> login(LoginRequest req, RSocketRequester clientReq) {
return validate(req)
.doOnSuccess(user -> {
clientReq.route("notification")
.metadata("trace-456", MimeTypeUtils.TEXT_PLAIN)
.data("Welcome")
.send()
.subscribe();
})
.then();
}
시험 직전 한 번 더 — 자주 헷갈리는 함정 모음
여기까지가 5편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.
- 메타데이터 = MIME Type 기반 부가 정보
- HTTP 헤더와 다름 (단순 키-값 X)
- 표준 MIME Types — routing·authentication·tracing-zipkin·composite-metadata·json·text/plain
- Composite Metadata = 여러 메타데이터 다중 묶음
- Spring Boot 기본 = Composite Metadata
- 라우팅 + 인증 + tracing 자동 다중
- 클라이언트 —
.metadata(value, mimeType)다중 호출 - 서버 —
@Header(단일) /@Headers(모든 Map) - 라우팅 = 메타데이터의 일부 (
.route()자동 인코딩) - Spring Security RSocket —
RSocketSecurity+simpleAuthentication - 라우트별 권한 제어
- 인증 메타데이터 — UsernamePasswordMetadata / BearerTokenMetadata
- 인증 인코더 등록 필요
- Tracing — TracingMetadata (Zipkin)
- Spring Boot Sleuth·Micrometer = 자동
- 커스텀 메타데이터 — 텍스트·JSON·바이너리 자유
- 비즈니스 (테넌트·feature flag·region)
RSocketStrategies에 인코더·디코더 등록metadataExtractorRegistry로 메타데이터 자동 추출- 메타데이터 평문 (TLS 없으면 노출)
- 양방향 호출 시도 메타데이터 추가 가능
시리즈 다른 편
- 1편 — 기본 개념·프레임
- 2편 — 4 Interaction Models
- 3편 — Spring RSocket 서버
- 4편 — Spring RSocket 클라이언트
- 5편 — 메타데이터·Composite Metadata (현재 글)
- 6편 — 보안·Spring Security RSocket·TLS
- 7편 — 로드 밸런싱·확장
- 8편 — 테스트
- 9편 — RSocket vs gRPC vs WebSocket
공식 문서: RSocket Metadata / Spring RSocket Metadata 에서 더 깊이.
다음 글(6편)에서는 보안 — Spring Security RSocket·인증·인가·TLS·인증 메타데이터까지 풀어 갑니다.