레시픽은 전통적인 서버 사이드 MVC 패턴을 사용
Model: CartVO, OrderVO와 같은 데이터 모델 클래스와 CartMapper, CartService와 같은 비즈니스 로직 계층이 모델 역할
View: JSP 파일들이 뷰를 담당하며, JSTL 태그를 사용해 서버에서 전달받은 데이터를 표시
Controller: CartController, CheckoutController 같은 클래스들이 컨트롤러 역할을 하며, 요청을 받아 적절한 서비스를 호출하고, 결과를 뷰에 전달
Spring MVC 프레임워크의 전형적인 사용 방식으로, 요청-응답 사이클이 서버에서 완결되는 방식
서버 사이드 렌더링(SSR)
처음에 컨트롤러가 요청을 받아 서버에서 JSP로 페이지를 렌더링
(예를 들면 shop-cart.jsp에서 전체 페이지를 서버에서 렌더링하여 클라이언트에게 전송)
클라이언트 사이드 업데이트(Ajax)
사용자 상호작용(장바구니 수량 변경, 삭제 등)에 따라 jQuery를 사용한 Ajax로 부분 업데이트
예를 들어 $('.bi-x').click(function() 에서 Ajax 호출로 장바구니 항목 삭제
초기 페이지 로드는 서버에서 처리하므로 SEO에 유리
부분 업데이트는 클라이언트에서 처리하므로 사용자 경험 향상 (불필요한 새로고침 등의 동작이 필요 없음)
서버와의 통신 횟수 감소
// 데이터 모델 (Model)
public class CartVO {
private int cart_id;
private int member_id;
private int ing_id;
private int recipe_id;
private int qty;
// ...
}
public class OrderVO {
private int oh_id;
private int memberId;
private double price;
private String orderState;
// ...
}
<!-- 뷰 (View) - JSP 파일 -->
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<div class="container">
<div class="row">
<c:forEach var="item" items="${cartItems}">
<!-- 데이터 표시 -->
</c:forEach>
</div>
</div>
// 컨트롤러 (Controller)
@Controller
public class CartController {
@Autowired
private CartService cartService;
@GetMapping("/shop-cart")
public String shopCart(Model model, HttpSession session) {
// 모델에서 데이터를 가져와 뷰로 전달
List<CartVO> cartItems = cartService.getCartItems(memberId);
model.addAttribute("cartItems", cartItems);
return "cart/shop-cart"; // 뷰 이름 반환
}
@PostMapping("/cart/delete")
@ResponseBody
public Map<String, Object> deleteCartItem(@RequestParam("cart_id") int id, @RequestParam("type") int type) {
// 모델 업데이트 로직
cartService.deleteCartItem(id, type);
// ...
}
}
솔픽은 백엔드 API와 프론트엔드 React를 분리한 조금 더 현대적인 웹 아키텍처를 사용
Model:
Controller:
View:
레시픽과 다르게 백엔드와 프론트엔드를 명확히 분리하여 각 부분이 독립적이다. API 서버는 데이터만 제공하고, 클라이언트가 UI 렌더링을 담당하는 방식
// 엔티티 (Model)
@Entity
@Table(name = "point")
public class Point {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "user_id", nullable = false)
private Integer userId;
// ...
}
// DTO (Model의 일부)
@Data
@Builder
public class PointHistoryResponseDTO {
private Integer pointId;
private String date;
private String description;
private Integer amount;
private String type;
}
@RestController
@RequestMapping("/api/points")
public class PointController {
@Autowired
private PointService pointService;
@GetMapping("/summary")
public ResponseEntity<PointSummaryResponseDTO> getPointSummary() {
// 사용자 인증 처리
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication.getPrincipal();
Map<String, Object> claims = (Map<String, Object>) principal;
Integer userId = /* 사용자 ID 추출 */;
// 모델에서 데이터 가져오기
PointSummaryResponseDTO summary = pointService.getPointSummaryByUserId(userId);
return ResponseEntity.ok(summary);
}
}
// React 컴포넌트 (View)
const PointHistoryList = ({ pointHistory = [] }) => {
// 데이터 가공 로직
const groupedPoints = useMemo(() => {
// 날짜별로 포인트 내역 그룹화
// ...
}, [pointHistory]);
// UI 렌더링
return (
<div className="point-list">
{groupedPoints.map(([date, points], groupIndex) => (
<div key={date} className="point-group">
<div className="point-date-header">{date}</div>
{points.map((point, index) => (
<PointHistoryItem
key={`${date}-${index}`}
description={point.description}
amount={point.amount}
date={point.date}
type={point.type}
/>
))}
</div>
))}
</div>
);
};
두 서비스 모두 데이터(Model), 표현(View), 제어 로직(Controller)을 분리하는 MVC의 원칙을 준수하며 구현했다. 레시픽은 서버에서 전체 MVC 사이클을 처리하는 반면, 솔픽은 백엔드(Model+Controller)와 프론트엔드(View)로 역할을 분리했다. 솔픽의 방식이 더 현대적인 방식이다.