SSR(Server-Side Rendering)에서 Controller의 구조 (#서버사이드 렌더링)
서버에서는 요청에 대한 반환만 전달하고, 클라이언트가 화면을 렌더링하는 CSR(Client-Side Rendering)
방식에서는,Controller
가 기능(또는 도메인) 기준으로 분리되는 것이 일반적입니다.
그런데 서버에서 요청에 대한 수행 뿐만 아니라, 데이터를 렌더링한 화면을 반환하는 SSR(Server-Side Rendering)
방식에서,Controller
는 어떻게 분리되어야 할지 고민이 되었습니다.
화면 단위로 구분해야 하는 것이 맞을지, 기능(또는 도메인 / 이하 도메인)단위로 구분해야 하는 것이 맞을지,
고민의 대한 개인적인 결론을 공유드려봅니다.
먼저, 각각의 방식에 대한 예시와 특징을 살펴보고,
마지막으로 각각을 비교하면서 나름의 결론을 내려보겠습니다.
화면 단위로 구분하는 예시
현재 웹 사이트가 domain.com/
과 domain.com/admin
의 2가지 화면을 가지고 있다고 가정하겠습니다.
아래는 Spring 프레임워크
로 구현된 Controller
의 의사코드입니다.
// domain.com/ 에서 사용되는 기능을 모아둔 Controller
@Controller
public class HomeController {
@GetMapping
public String productList(final Model model) {
List<Product> products = productDao.findAll();
model.addAttribute("products", products);
return "index";
}
}
// domain.com/admin 에서 사용되는 기능을 모아둔 Controller
@RequestMapping("/admin")
@Controller
public class AdminController {
@PostMapping("/products")
public void saveProduct(@RequestBody Request request) {
// (프로덕트 저장 로직)
// 이후 Javascript 파일에서 이전 뷰를 reload함
}
@GetMapping
public String getAllProducts(final Model model) {
List<Product> products = productService.findAll();
model.addAttribute("products", products); // 상품 정보 렌더링 로직
return "admin";
}
...
}
화면에 필요한 기능들이 모아져 있습니다.
도메인 단위로 구분하는 예시
// Product 관련 기능을 모아둔 Controller
@RequestMapping("/products")
@Controller
public class ProductController {
@PostMapping
public void saveProduct(@RequestBody Request request) {
// (프로덕트 저장 로직)
// 이후 Javascript 파일에서 이전 뷰를 reload함
}
...
}
이 경우, 아래와 같이 뷰를 반환하는 Controller
가 별도로 존재해야 합니다.
(아니면 기능 단위로 뷰를 분리할 수도 있겠으나 개인적으로는 구조가 직관적이지 않다고 생각합니다.)
// View를 반환하는 Controller
@Controller
public class ViewController {
@GetMapping
public String getHomeView() {
// (렌더링 로직)
return "index";
}
@GetMapping("/admin")
public void getAdminView() {
// (렌더링 로직)
return "admin";
}
}
두 가지 방식 비교
Controller
의 구조가 화면에 의존하느냐, 도메인에 의존하느냐,
두 가지 관점에서 고려해보겠습니다.
1. 화면과 도메인 중 어느 것이 자주 바뀔까
일반적으로는 화면이 더 자주 바뀔 것입니다.
사용자에게 보여지고, 사용자와 직접 Interaction하는 화면은
사용자의 편의를 직접적으로 고려하고 반영해야 할 일이 많을 것입니다.
하지만 도메인의 변경은 자주 일어나서는 안 되는 일입니다.OCP(Open-Close Principle / 개방-폐쇄 원칙)
에 의하면,
객체지향적 설계를 위해 도메인 구조는 변경에는 닫혀 있고, 확장에만 열려 있어야 합니다.
따라서 구조의 변경이 자주 일어날 것으로 예상되는 화면보다는,
도메인을 기준으로 Controller
구조를 설계하는 것이 맞다고 생각합니다.
변경 가능성이 적어 안정적인 구조를 위해서 그렇습니다.
2. Controller는 Service에 의존한다.
Service
는 아마 기능 단위로 분리되어 있을 것입니다. (도메인 주도 개발의 관점에서 유즈케이스를 고려할 수 있으나, 이 또한 기능 중심의 분리입니다.)
만약 Controller
가 화면의 구조에 맞게 설계된다면,
의존하는 Service
가 많아질 수 있습니다.
예를 들면,ProductService
, UserService
, OrderService
가 존재할 때,
도메인 기준으로 설계된 ProductController
는 ProductService
에만 의존하겠지만, (물론 의존성이 추가될 수 있습니다.)
화면 기준으로 설계된 AdminController
는 ProductService
, UserService
, OrderService
를 모두 의존할 수 있습니다.
Admin 화면에서 상품, 유저, 주문 정보에 모두 접근할 수 있기 때문입니다.
따라서 도메인 기준으로 설계된 Controller
의 의존 관계가 화면 기준으로 설계된 Controller
보다 간결할 가능성이 높습니다.
위의 두 가지 이유를 종합하여,
화면 기준이 아닌 도메인(기능) 기준으로 Controller
를 설계해야 한다고 결론을 내렸습니다.
물론 정답이 있는 문제는 아니라고 생각합니다. (진리)
사용자가 접근할 URL이 너무나도 중요해서 절대 확장하지 않고 2가지 링크에 대응되는 화면만 사용한다면,
화면이 도메인보다 안정적인 구조일 수도 있습니다.
설계에 따라서는 도메인 중심으로 나눈 Controller
도 Service
에 대해 복잡한 의존 관계를 지닐 수 있습니다.
그렇기에 무작정 설계를 하기 보다는,
지금의 설계가 추후의 비즈니스적 문제에 어떤 영향을 미칠 것인지를 고민하는 것이 중요하다고 생각합니다.
개인적인 생각인지라 오류가 있을지 모릅니다.
지적이나, 다른 의견 공유를 환영합니다.
감사합니다.