| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- js
- TIP
- programmers
- javascript
- SQL
- dbeaver
- STS
- 정보처리기사
- Linux
- reCAPTCHA
- urlshortner
- windows
- Eclipse
- Mac
- shorturl
- IntelliJ
- spring
- maria db
- devlog
- mybatis
- Oracle
- node.js
- html
- mysql
- SQL Server
- my sql
- jquery
- svn
- Tomcat
- Java
- Today
- Total
고양의 성장일기
[Spring] Service 레이어에서 Request, Session 객체 손쉽게 가져오기 본문
[Spring] Service 레이어에서 Request, Session 객체 손쉽게 가져오기
고 양 2025. 9. 3. 19:00Service 레이어에서 Request, Session 객체 손쉽게 가져오기
Interceptor를 통해 로그인 여부 확인이나 세션 갱신을 처리하던 중, 문득 이런 생각이 들었습니다.
"여러 인터셉터에서 실행되는 세션과 관련된 공통적인 로직을 한 서비스에 몰아넣으면 편하지 않을까?"
물론 프레젠테이션 계층과 직접적으로 접촉하는 로직이 비즈니스 계층에 존재하는 것은 책임 분리 측면에서 적절하지 않습니다.
하지만 여러 인터셉터에서 동일한 세션 관련 로직을 필요로 했고, 세션 값 조회나 수정도 반복적으로 발생했기 때문에, 우선은 깔끔한 아키텍처보다는 기술적 가능 여부를 살펴보기로 했습니다.
세션에 저장된 값 참조하기
이전의 저는 HttpServletRequest 객체는 보통 컨트롤러에서 메서드 파라미터로 전달받아 사용할 수 있는 줄로만 알고 있었습니다.
그래서 Service 계층에서 이를 참조하려면 아래와 같이 매개변수로 넘겨받아 사용해야 했었습니다.
public String getSomeSessionAttribute(HttpServletRequest request) {
HttpSession session = request.getSession();
String value = (String) session.getAttribute("abcde");
...
return value;
}
하지만 메서드 호출이나 설계에 있어서 특정 매개변수의 유무가 강제된다는 것은 유연성을 저하시키고 코드를 복잡하게 만드는 요소가 됩니다.
RequestContextHolder
대안으로 찾아낸 것이 스프링에서 제공하는 RequestContextHolder 객체였습니다.
이를 사용하면 프레젠테이션 계층으로부터 HttpServletRequest 객체를 넘겨받지 않고도 직접 접근할 수 있게 됩니다.
내부를 보면 아래와 같이 ThreadLocal 객체로 공간을 할당해 요청을 저장하고 있음을 알 수 있습니다.
private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
new NamedThreadLocal<>("Request attributes");
private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
new NamedInheritableThreadLocal<>("Request context");
Request 객체 가져오기
아래와 같은 방법으로 현재 HttpServletRequest 객체를 참조할 수 있습니다.
public HttpServletRequest getCurrentHttpRequest() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
return ((ServletRequestAttributes) requestAttributes).getRequest();
}
throw new IllegalStateException("Request not found");
}
RequestContextHolder에서 현재 Request 객체를 반환하는 메서드는 두 개가 있는데 각각 동작하는 방식이 조금 다릅니다.
- getRequestAttributes()
- Request 객체를 반환하되, 객체가 null이면 null을 반환합니다. - currentRequestAttributes()
- Request 객체를 반환하되, 객체가 null이면 IllegalStateException을 던집니다.
@Nullable
public static RequestAttributes getRequestAttributes() {
RequestAttributes attributes = requestAttributesHolder.get();
if (attributes == null) {
attributes = inheritableRequestAttributesHolder.get();
}
return attributes;
}
public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
RequestAttributes attributes = getRequestAttributes();
if (attributes == null) {
if (jsfPresent) {
attributes = FacesRequestAttributesFactory.getFacesRequestAttributes();
}
if (attributes == null) {
throw new IllegalStateException("요청 컨텍스트가 존재하지 않습니다");
}
}
return attributes;
}
Session 객체 가져오기
같은 방법으로 Session 객체도 간단하게 가져올 수 있게 됩니다.
public HttpSession getCurrentHttpSession() {
return getCurrentHttpRequest().getSession();
}
마치며
이 방법은 기술적으로 가능하지만, Interceptor 단계에서 처리해야 할 로직이 Service로 흘러 들어가기 때문에 테스트가 어렵고 계층 간 결합도를 높이는 문제가 있습니다.
하지만 이전에 비해서는 분명 코드를 개선할 수 있는 방법이기도 했습니다. 방법을 아는 것과 모르는 것엔 분명 차이가 있죠.
다음에 또 비슷한 기능을 구현할 기회가 있다면 Request와 Session과 관련된 부분은 Interceptor/Filter, ArgumentResolver를 통해 Controller 레이어에서 다루고 Service는 도메인 로직에 집중하도록 조금 더 정교하게 설계해 봐야겠습니다.
이번 시도를 통해 단순한 기능 구현을 넘어, 계층 간 책임 분리에 대해서도 생각해 볼 기회가 있었습니다. 뿌듯한 마음으로 글을 마무리합니다.
감사합니다.
'⚙️ Back-End > Spring Framework' 카테고리의 다른 글
| [Spring] @PathVariable과 @RequestParam 응용해보기 (8) | 2025.07.21 |
|---|---|
| [Spring] @UtilityClass (2) | 2025.07.17 |
| [Spring] @PathVariable과 @RequestParam의 공통점과 차이점 (5) | 2025.07.17 |
| [Spring] DTO를 이용해 중첩된 구조의 JSON 객체 전달 받기 (0) | 2025.03.18 |
| [Spring] There is no getter for property named 'url' in 'class java.lang.String' (1) | 2025.03.11 |