작성
·
123
0
문제 상황을 설명드리겠습니다.
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를 사용하여 문제를 해결한 후에도 동일하게 여러 매개변수들이 존재합니다.
이 부분을 다시 고민해 보시면 좋을 것 같습니다:)
감사합니다.
David님 답변을 해주셔서 정말 감사합니다.
제가 문제상황 설명을 장황하고 이해하기 어렵게 한 것 같아요. 다시 설명하면 다음과 같습니다.
1. 처음 작성했던 Controller는 매개변수 검증 + 비즈니스 로직 수행 + 뷰로 데이터 전달 3가지의 역할을 수행한다.
2.이런 Controller는 매개변수가 많아질 때 Controller가 비대해지고 가독성이 나빠지는 문제가 발생한다. (특히 매개변수 검증 + 뷰로 데이터를 전달. 이 2가지 때문에 지저분해진다.)
3. 위 문제에 대한 해결방법으로 ArgumentResolver를 정의하여 위 2가지 역할(매개변수 검증 역할 + 뷰로 데이터를 전달 역할)을 Controller 대신 수행하도록 한다. Page말고 다른 매개변수에 대해서도 ArgumentResolver를 정의한다.
4.ArgumentResolver를 정의하여 2가지 역할을 대신 수행하게 한 덕분에, 복잡했던 Controller 코드가 깔끔해졌다.
이런 상황입니다.
그리고 이런 상황에서 궁금한 것이
1. 위 3번 과정에서 ArgumentResolver가 외부 DB에 접근하는 스프링빈에 의존하는데 이렇게 해도 되는지?
2. 이런 해결방법이 좋은 해결 방법인지?
이 두 가지가 궁금합니다.