UserContextHolder
는 일반적으로 사용자의 컨텍스트(Context)를 애플리케이션의 현재 요청 스레드에 안전하게 저장하고 관리하기 위한 클래스로 사용됩니다. 이는 마이크로서비스 아키텍처(MSA)에서 각 요청마다 고유한 사용자 정보를 유지하는 데 중요한 역할을 합니다.
1. UserContextHolder의 필요성
왜 필요한가?
요청별 사용자 정보 관리:
- 클라이언트의 각 요청은 고유한 사용자 정보를 포함합니다(예: JWT 토큰, 세션 정보, 권한).
- 마이크로서비스 환경에서는 각 서비스가 분리되어 있어 이러한 정보를 모든 요청에 전달하고 처리해야 합니다.
스레드 안전(Thread-Safe) 컨텍스트:
- 요청은 서버에서 처리되는 동안 동일한 스레드에서만 안전하게 사용자 정보를 참조해야 합니다.
- 사용자 정보를 ThreadLocal을 통해 저장하고 관리하여, 요청이 처리되는 동안 스레드 간 데이터 혼란을 방지.
로깅, 트랜잭션, 권한 검증 등:
- 사용자 컨텍스트는 로깅, 인증/인가, 분산 추적 등에 활용됩니다.
2. UserContextHolder 구성 요소
1. UserContext
사용자의 요청 정보를 저장하기 위한 데이터 객체로, 요청별로 필요한 데이터를 담습니다.
public class UserContext {
private String correlationId; // 요청 간 상관관계 ID
private String authToken; // 인증 토큰
private String userId; // 사용자 ID
private String organizationId; // 조직 ID
// Getter & Setter
public String getCorrelationId() {
return correlationId;
}
public void setCorrelationId(String correlationId) {
this.correlationId = correlationId;
}
public String getAuthToken() {
return authToken;
}
public void setAuthToken(String authToken) {
this.authToken = authToken;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getOrganizationId() {
return organizationId;
}
public void setOrganizationId(String organizationId) {
this.organizationId = organizationId;
}
}
2. UserContextHolder
ThreadLocal
을 사용하여 사용자 컨텍스트를 저장하고 관리합니다.
public class UserContextHolder {
private static final ThreadLocal<UserContext> userContext = new ThreadLocal<>();
public static final UserContext getContext() {
UserContext context = userContext.get();
if (context == null) {
context = new UserContext(); // 새로운 컨텍스트 생성
userContext.set(context);
}
return context;
}
public static final void setContext(UserContext context) {
userContext.set(context);
}
public static final void clearContext() {
userContext.remove(); // 요청 종료 시 컨텍스트 제거
}
}
3. UserContextHolder의 활용
1. 필터에서 사용자 정보 설정
요청(Request)에서 사용자 정보를 추출해 UserContext
에 저장합니다. Spring Security나 인증 토큰을 활용해 데이터를 가져올 수 있습니다.
@Component
public class UserContextFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// 사용자 컨텍스트 설정
UserContext context = UserContextHolder.getContext();
context.setCorrelationId(httpRequest.getHeader("X-Correlation-Id"));
context.setAuthToken(httpRequest.getHeader("Authorization"));
context.setUserId(httpRequest.getHeader("User-Id"));
context.setOrganizationId(httpRequest.getHeader("Organization-Id"));
chain.doFilter(request, response);
// 요청 종료 시 컨텍스트 제거
UserContextHolder.clearContext();
}
}
2. 서비스 레이어에서 컨텍스트 활용
필터를 통해 설정된 사용자 정보를 서비스 레이어에서 간단히 가져와 사용할 수 있습니다.
@Service
public class UserService {
public void performAction() {
UserContext context = UserContextHolder.getContext();
System.out.println("Correlation ID: " + context.getCorrelationId());
System.out.println("User ID: " + context.getUserId());
// 로직 처리
}
}
3. 로깅 및 분산 추적
UserContextHolder
를 사용해 로깅 및 분산 추적에서 중요한 상관관계 ID를 유지할 수 있습니다.
예: 상관관계 ID(Correlation ID) 로깅:
Logger logger = LoggerFactory.getLogger(UserService.class); public void logRequest() { String correlationId = UserContextHolder.getContext().getCorrelationId(); logger.info("Processing request with Correlation ID: {}", correlationId); }
예: OpenTelemetry 연동:
상관관계 ID를 OpenTelemetry와 같은 분산 추적 도구에 연결할 수 있습니다.
4. MSA에서 UserContextHolder의 중요성
인증 및 권한 관리:
- 요청마다 사용자 정보를 유지하므로 서비스 간 인증과 권한 검증을 쉽게 처리.
분산 추적(Distributed Tracing):
- 상관관계 ID를 통해 여러 서비스 간의 요청 흐름을 추적.
공통 정보 관리:
- 사용자 ID, 조직 ID 등 요청별 공통 정보를 서비스 전반에 쉽게 전달.
스레드 안정성:
ThreadLocal
을 사용해 요청 스레드 간 데이터 충돌 방지.
5. 주의 사항
ThreadLocal 사용 시 메모리 누수 방지:
- 요청이 끝난 후 반드시
UserContextHolder.clearContext()
를 호출하여 컨텍스트를 제거해야 합니다. - 누락 시 메모리 누수(memory leak) 위험이 있습니다.
- 요청이 끝난 후 반드시
비동기 환경에서의 한계:
ThreadLocal
은 스레드 간 데이터를 안전하게 관리하지만, 비동기 호출(예: CompletableFuture)에서는 동일한 스레드가 사용되지 않을 수 있습니다.- 해결 방법:
- Spring의
TaskDecorator
를 사용해 비동기 작업에도ThreadLocal
데이터를 전달. - 예:
@Bean public TaskDecorator taskDecorator() { return runnable -> { UserContext context = UserContextHolder.getContext(); return () -> { try { UserContextHolder.setContext(context); runnable.run(); } finally { UserContextHolder.clearContext(); } }; }; }
- Spring의
보안 데이터 관리:
- 인증 토큰 같은 민감한 정보를
UserContext
에 저장할 때는 암호화 또는 마스킹 처리를 고려.
- 인증 토큰 같은 민감한 정보를
결론
UserContextHolder
는 MSA 환경에서 사용자와 요청별로 고유한 컨텍스트 정보를 안전하게 관리하기 위한 필수 도구입니다. 필터를 통해 요청마다 정보를 설정하고, 서비스 레이어에서 이를 쉽게 사용할 수 있습니다. 또한 분산 추적과 로깅에 유용하며, 스레드 안정성을 보장하지만, 비동기 작업 시 주의가 필요합니다.
'MSA' 카테고리의 다른 글
서비스 게이트웨이 & API 게이트웨이 (3) | 2024.12.16 |
---|---|
스프링 클라우드(SPRING CLOUD)의 글로벌 필터(Global Filter) (0) | 2024.12.16 |
MSA에서의 필터(Filter)와 인터셉터(Interceptor) (0) | 2024.12.16 |
refresh (0) | 2024.12.16 |
Netflix Zuul ( API 게이트웨이 ) (1) | 2024.12.16 |