ArgumentResolver가 Service레이어 클래스, DB접근에 접근하는 클래스에 의존하도록 설계해도 괜찮나요?
144
작성한 질문수 1
문제 상황을 설명드리겠습니다.
page 정보를 받아서 게시글 리스트, 페이지 정보를 View로 전달하는 Controller 코드 입니다.
@GetMapping("/posts")
public String getPosts(@RequestParam("page") String page, Model model)
{
// 사용자가 입력한 page문자열과 모든 post의 개수를 매개변수로 받아서 page 정보 생성
// 사용자가 입력한 page값을 검증 후 올바른 Page 정보를 반환한다.
// "@#$", "hello" 같은 이상한 page값이 들어오면 모두 1로 변경한다.
Page page = Page.validateAndCreate(page, postService.getPostsCount());
// page 정보를 사용하여 해당 page에 속하는 post들을 반환한다.
List<Post> posts = postService.getPostList(page);
// view로 데이터를 전달한다.
model.addAttribute("page", page);
model.addAttribute("posts", posts);
}처음에는 위 방식으로 코드를 작성했습니다.
그런데 page정보 뿐 아니라, 다른 sorting정보, searching정보 등 여러 매개변수들이 들어오다 보니, Controller 코드가 복잡해진다고 느꼈습니다. 간단한 예시 코드는 아래와 같습니다.
@GetMapping("/posts")
public String getPosts(@RequestParam("page") String page, @ModelAttribute("sort") Sort sort, @ModelAttribute("search") Search search, .... 등등 , Model model)
{
// 여러가지 전처리 로직들
Page page = Page.validateAndCreate(page, postService.getPostsCount());
Sort sort = Sort.validateAndCreate(sort, ... )
Search search = Search.validateAndCreate(search, ...)
....등등
// 위에서 생성한 정보들을 바탕으로 DB에 쿼리를 날려 posts 데이터를 받아온다.
List<Post> posts = postService.getPostList(page, sort, search, ...등등);
// view에 필요한 데이터를 전달한다.
model.addAttribute("page", page);
model.addAttribute("posts", posts);
model.addAttribute("sort", sort);
... 등등
}예시 코드는 문제상황 설명을 위해서 깔끔하게 작성했으나,
실제로는 제 부족한 실력 탓에 지저분합니다.
저는 Controller 코드를 지저분하게 만드는 원인으로
여러가지 전처리 로직들 (ex. Page.validateAndCreate(...) 코드들)
View에 필요한 데이터들을 전달하는 model.addAttribute(...) 코드들
때문이라고 생각했습니다.
위 문제에 대한 해결방안으로 저는 ArgumentResolver를 사용하는 것을 떠올렸습니다.
page에 대해서 @PageInfo라는 애노테이션을 정의하고, 이 애노테이션이 달린 매개변수에 대해서
1.여러가지 전처리 로직들을 수행해주고,
2.View에 자동으로 데이터들을 전달해주는,
ArgumentResolver를 만들어주었습니다.
Page에 대한 ArgumentResolver 코드는 다음과 같습니다.
@RequiredArgsConstructor
@Component
public class PageRequestArgumentResolver implements HandlerMethodArgumentResolver
{
// 이 부분이 걱정입니다! ArgumentResolver가 특정 Service클래스에 의존해도 될까요?
private final QuestionService questionService;
@Override
public boolean supportsParameter(MethodParameter parameter)
{
// @PageInfo를 가지고 있는 경우만 적용.
boolean hasPageInfoAnnotation = parameter.hasParameterAnnotation(PageInfo.class);
// 타겟변수가 Page 타입인 경우만 적용.
boolean isPageClass = parameter.getParameterType().equals(Page.class);
return hasPageInfoAnnotation && isPageClass;
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception
{
// url에서 page문자열 정보를 추출.
String pageString = webRequest.getParameter("page");
// page문자열 정보를 검증 및 생성.
Page page = Page.validateAndCreate(pageString, questionService.provideAllQuestionsCount());
// mavContainer에 해당 정보를 넣어서 View로 자동전달.
if (mavContainer != null) mavContainer.addAttribute("page", page);
// 컨트롤러의 매개변수에 page정보를 바인딩.
return page;
}
}
위 처럼 page정보 뿐 아니라, 다른 자주쓰는 매개변수들에도 ArgumentResolver를 정의하고 적용하면,
아래와 같이 Controller코드가 굉장히 깔끔해진다고 느꼈습니다.
@GetMapping("/posts")
public String getPosts(@PageInfo Page page, @SortInfo Sort sort, @SearchInfo Search search, ... 등등, Model model)
{
// 여러가지 전처리 로직들은 ArgumentResolver가 해주기 때문에 코드가 사라집니다.
// 비즈니스 로직을 수행합니다.
List<Post> posts = postService.getPostList(page, sort, search, ...등등);
// View에 필요한 데이터도 ArgumentResolver의 mavContainer를 통해서 자동 전달됩니다.
// 컨트롤러에서 관심이 있는 posts 정보만 전달합니다.
model.addAttribute("posts", posts);
}
그런데 이게 정말 좋은 코드인지 판단이 잘 서질 않습니다.
그리고 ArgumentResolver가 다른 Service 클래스에 의존을 해도 되는지가 의문입니다.
AI에게 물어보니, 순환참조 문제가 발생할 수 있기 때문에 웬만해서는 하지 말라고하는데...
답변 1
1
안녕하세요. dyl0115님, 공식 서포터즈 David입니다.
그런데 page정보 뿐 아니라, 다른 sorting정보, searching정보 등 여러 매개변수들이 들어오다 보니, Controller 코드가 복잡해진다고 느꼈습니다.
public String getPosts(@RequestParam("page") String page, @ModelAttribute("sort") Sort sort, @ModelAttribute("search") Search search, .... 등등 , Model model)
...
위 처럼 page정보 뿐 아니라, 다른 자주쓰는 매개변수들에도 ArgumentResolver를 정의하고 적용하면, 아래와 같이 Controller코드가 굉장히 깔끔해진다고 느꼈습니다.
public String getPosts(@PageInfo Page page, @SortInfo Sort sort, @SearchInfo Search search, ... 등등, Model model)
앞쪽에서 언급한 문제와 해결한 뒤의 결과가 잘 연결되지 않는 것 같습니다.
제가 이해한 바로는 질문자 분은 '여러 매개변수들이 들어와서' 복잡하다고 느끼셨는데 ArgumentResolver를 사용하여 문제를 해결한 후에도 동일하게 여러 매개변수들이 존재합니다.
이 부분을 다시 고민해 보시면 좋을 것 같습니다:)
감사합니다.
0
David님 답변을 해주셔서 정말 감사합니다.
제가 문제상황 설명을 장황하고 이해하기 어렵게 한 것 같아요. 다시 설명하면 다음과 같습니다.
1. 처음 작성했던 Controller는 매개변수 검증 + 비즈니스 로직 수행 + 뷰로 데이터 전달 3가지의 역할을 수행한다.
@GetMapping("/posts")
public String getPosts(@RequestParam("page") String page, Model model)
{
// 매개변수를 검증 후 올바른 값을 넣어줌.
Page page = Page.validateAndCreate(page, postService.getPostsCount());
// 비즈니스 로직 수행
List<Post> posts = postService.getPostList(page);
// 뷰로 데이터를 전달.
model.addAttribute("page", page);
model.addAttribute("posts", posts);
}
2.이런 Controller는 매개변수가 많아질 때 Controller가 비대해지고 가독성이 나빠지는 문제가 발생한다. (특히 매개변수 검증 + 뷰로 데이터를 전달. 이 2가지 때문에 지저분해진다.)
@GetMapping("/posts")
public String getPosts(@RequestParam("page") String page, @ModelAttribute("sort") Sort sort, @ModelAttribute("search") Search search, .... 등등 , Model model)
{
// 매개변수 검증 후 올바른 값을 넣어줌. (매개변수가 많아지며 복잡)
Page page = Page.validateAndCreate(page, postService.getPostsCount());
Sort sort = Sort.validateAndCreate(sort, ... )
Search search = Search.validateAndCreate(search, ...)
...
// 비즈니스 로직들 수행
List<Post> posts = postService.getPostList(page, sort, search);
...
// 뷰로 데이터를 전달. (매개변수가 많아지며 복잡해짐)
model.addAttribute("page", page);
model.addAttribute("posts", posts);
model.addAttribute("sort", sort);
...
}
3. 위 문제에 대한 해결방법으로 ArgumentResolver를 정의하여 위 2가지 역할(매개변수 검증 역할 + 뷰로 데이터를 전달 역할)을 Controller 대신 수행하도록 한다. Page말고 다른 매개변수에 대해서도 ArgumentResolver를 정의한다.
@RequiredArgsConstructor
@Component
public class PageRequestArgumentResolver implements HandlerMethodArgumentResolver
{
// 매개변수를 검증하고 값을 넣어주는 과정에서
// ArgumentResolver가 DB에 접근하는 스프링빈에 의존하게 됨.
// ArgumentResolver가 이렇게 의존해도 되는지 궁금합니다.
private final PostService postService;
@Override
public boolean supportsParameter(MethodParameter parameter)
{
boolean hasPageInfoAnnotation = parameter.hasParameterAnnotation(PageInfo.class);
boolean isPageClass = parameter.getParameterType().equals(Page.class);
return hasPageInfoAnnotation && isPageClass;
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception
{
// url에서 page문자열 정보를 추출.
String page = webRequest.getParameter("page");
// 매개변수 검증 역할을 대신 수행
Page page = Page.validateAndCreate(page, postService.getPostsCount());
// 뷰로 데이터 전달 역할을 대신 수행
if (mavContainer != null) mavContainer.addAttribute("page", page);
// 컨트롤러의 매개변수에 page정보를 바인딩.
return page;
}
}
4.ArgumentResolver를 정의하여 2가지 역할을 대신 수행하게 한 덕분에, 복잡했던 Controller 코드가 깔끔해졌다.
@GetMapping("/posts")
public String getPosts(@PageInfo Page page, @SortInfo Sort sort, @SearchInfo Search search, ... 등등, Model model)
{
// 여러가지 검증을 ArgumentResolver가 해줘서 관련된 코드가 사라짐.
// 비즈니스 로직 수행.
List<Post> posts = postService.getPostList(page, sort, search, ...등등);
// View에 필요한 page, sort, search 등도 ArgumentResolver가 대신 전달. 관련된 코드가 사라짐.
// 컨트롤러에서 생성된 posts 정보만 뷰로 전달.
model.addAttribute("posts", posts);
}이런 상황입니다.
그리고 이런 상황에서 궁금한 것이
1. 위 3번 과정에서 ArgumentResolver가 외부 DB에 접근하는 스프링빈에 의존하는데 이렇게 해도 되는지?
2. 이런 해결방법이 좋은 해결 방법인지?
이 두 가지가 궁금합니다.
이미지 업로드와 db 트랜잭션 묶는법
0
41
1
Could not resolve org.springframework.boot:spring-boot-starter-validation:2.4.4
0
50
2
MessageSourceTest 코드
0
46
1
인터셉터 에러 설정
0
48
1
resolveArgument()메서드 질문
0
55
1
43강 검증1 에서 실패 로직 관련 질문있습니다.
0
53
2
타임리프 3.X 버전 rendering, serializer 에러 해결 방법
2
131
3
스프링 빈에 등록이 안되는거 같은데 어떻게 하면 좋을까요?ㅠㅠ
0
87
3
pdf 오타 문의
0
56
1
ItemUpdateForm 검증 관련 질문입니다.
0
47
1
22page 링크 주소 변경
0
58
2
특정 데이터와 파일을 함께 저장 시, 테이블 구조 질문
0
52
1
섹션3번 수업에 대한 질문입니다.
0
79
2
@Autowired 보다 더 좋은 방법이 어떤 걸까요?
0
84
2
타입컨버터 가 람다랑 비슷해 보이는데 저의 생각이 맞는지?.
0
64
1
자바스크립트 인라인에서 객체 직렬화 시 오류가 납니다
0
141
3
스프링부트 - 오류페이지2 에서 500.html 에서 쓰인 객체 질문
0
63
1
톰캣 에러 페이지가 안보입니다.
0
100
2
apiEceptionController에서 센드 에러 호출하면 안되는지?
0
80
1
세션 타임아웃시 쿠키 삭제 방법이 없나요?
0
118
2
ApiExceptionController 질문드립니다.
0
62
1
셀렉박스 챕터에서 option value에 ==배송 방식 선택== 이것을 넣은 이유가 궁금함, 이렇게 구상해도 되는지?
0
64
1
MemberRepository 필드의 fianl 선언 유무
0
84
2
혹시 index.html 에서는 fragment 사용이 안되는건가요
0
57
1





