📚 학습 기록/Java 기초 & 중급

자바로 만든 나만의 쇼핑몰 여행기 (2) - 서비스 계층 구현편

zenjoydev 2025. 3. 26. 21:01

안녕하세요, 여러분! 지난 포스트에 이어 오늘은 우리 쇼핑몰 프로젝트의 두 번째 이야기를 들려드릴게요. 첫 번째 포스트에서는 기본 도메인 모델 클래스들(User, Product, Cart, Order 등)을 어떻게 설계했는지 소개했었죠. 오늘은 이 모델들을 활용해 실제 비즈니스 로직을 처리하는 서비스 계층을 구현한 과정을 공유할게요!

인프런에서 김영한 강사님의 강의를 들으면서 '서비스 계층'의 중요성을 깨달았어요. 서비스 계층은 UI와 데이터 계층 사이에서 실제 비즈니스 로직을 담당하는 부분이랍니다. 그럼 한번 구현 과정을 살펴볼까요?

서비스 계층이 왜 필요할까요?

처음에는 그냥 모델 클래스에 모든 기능을 넣으면 되지 않을까 생각했어요. 하지만 그러면 코드가 엄청 복잡해지더라고요! 예를 들면:

  • 장바구니에 상품을 추가할 때, 재고 확인도 해야 하고
  • 주문할 때는 장바구니 비우기, 재고 차감, 포인트 적립 등이 함께 일어나고
  • 회원 가입 시 중복 ID 체크도 필요하고...

이런 복잡한 기능들을 모델 클래스에 다 넣으면 클래스가 너무 비대해지고 책임이 명확하지 않아요. 그래서 서비스 계층을 따로 만들었답니다!

싱글톤 패턴 적용하기

서비스 클래스들은 애플리케이션 전체에서 하나의 인스턴스만 있으면 충분해요. 여러 곳에서 상품 서비스에 접근해도 같은 상품 데이터를 관리해야 하니까요. 그래서 싱글톤 패턴을 적용했어요!

public class UserService {
    // 싱글톤 인스턴스
    private static UserService userService;
    
    // 사용자 데이터 저장
    public ArrayList<User> userList;
    
    // private 생성자 (외부에서 new 못하게!)
    private UserService() {
        this.userList = new ArrayList<>();
    }
    
    // 싱글톤 인스턴스 접근 메서드
    public static UserService getUserService() {
        if (userService == null) {
            userService = new UserService();
        }
        return userService;
    }
    
    // 서비스 메서드들...
}

이런 식으로 다른 서비스 클래스(ProductService, CartService, OrderService)도 싱글톤으로 구현했어요. 이제 어디서든 UserService.getUserService()로 동일한 인스턴스에 접근할 수 있게 되었답니다!

1. 사용자 서비스 (UserService) 구현

이제 실제 서비스 클래스의 기능들을 구현해볼까요? 먼저 사용자 관련 기능부터 시작했어요:

package service;

import model.user.RegularUser;
import model.user.User;
import model.user.VIPUser;

import java.io.BufferedReader;
import java.util.ArrayList;

/*
 * 사용자 관련 비즈니스 로직을 처리하는 서비스 클래스
 *
 * 구현 방법:
 * 1. 싱글톤 패턴 적용
 * 2. 사용자 목록을 ArrayList<User>로 관리
 * 3. 사용자 등록 메서드 구현
 * 4. 사용자 정보 업데이트 메서드 구현
 * 5. 사용자 삭제 메서드 구현
 * 6. 로그인 검증 메서드 구현
 * 7. ID로 사용자 검색 메서드 구현
 * 8. 회원 등급 업그레이드 메서드 구현
 */
public class UserService {
    //    == 필드 ==
//    * 1. 싱글톤 패턴 적용
//    * 2. 사용자 목록을 ArrayList<User>로 관리
    public static UserService userService;
    public ArrayList<User> userList;

    //    == 생성자 ==
    private UserService() {
        this.userList = new ArrayList<>();
    }

    public ArrayList<User> getUserList() {
        return userList;
    }

    //    null 검사 메서드
    public boolean checkNull(Object object) {
        if (object == null) {
            System.out.println("입력된 값이 null입니다. 값을 확인해주세요!");
            return false;
        }
        return true;
    }

    //    목록에서 대상 값 찾기 및 바꾸기 유틸리티 메서드
    public void findArrayDataAndChange(User user) {
        for (int i = 0; i < userList.size(); i++) {
            if (findUserId(user)) {
                userList.remove(i);
                userList.add(user);
            }
            System.out.println("사용자 정보 업데이트 완료");
        }
    }

    //    * 7. ID로 사용자 검색 메서드 구현
    public boolean findUserId(User user) {
        if (!checkNull(user)) {
            System.out.println("사용자 검색 실패");
            return false;
        } else {
            for (User user1 : userList) {
                if (user1.getMemberId().equals(user.getMemberId())) {
                    return true;
                }
            }
            System.out.println("사용자 아이디 검색 성공 일치 및 확인 완료");
        }
        return false;
    }

    public boolean findUserId(String id) {
        if (id.isEmpty()) {
            System.out.println("입력값 오류");
            return false;
        } else {
            for (User user1 : userList) {
                if (user1.getMemberId().equals(id)) {
                    System.out.println("사용자 아이디 검색 성공 일치 및 확인 완료");
                    return true;
                }
            }
        }
        System.out.println("사용자 아이디 검색 성공 일치 및 확인 실패!");
        return false;
    }

    public boolean findUserPw(User user) {
        if (!checkNull(user)) {
            System.out.println("사용자 검색 실패");
            return false;
        } else {
            for (User user1 : userList) {
                if (user1.getPassword().equals(user.getPassword())) {
                    return true;
                }
            }
            System.out.println("사용자 비밀번호 검색 성공 및 일치 확인 완료");
        }
        return false;
    }

    public boolean findUserPw(String pw) {
        if (pw.isEmpty()) {
            System.out.println("입력값 오류");
            return false;
        } else {
            for (User user1 : userList) {
                if (user1.getMemberId().equals(pw)) {
                    System.out.println("사용자 비밀번호 일치 확인 완료");
                    return true;
                }
            }
        }
        System.out.println("사용자 비밀번호 확인 실패!");
        return false;
    }

    //    == 자신의 인스턴스 호출 메서드 ==
    public static UserService getUserService() {
        if (userService == null) {
            UserService userService = new UserService();
            return userService;
        }
        return userService;
    }

    //    * 3. 사용자 등록 메서드 구현

    public void userRegistration(User user) {
        if (!checkNull(user)) {
            System.out.println("사용자 등록 실패");
            return;
        }
        for (User user1 : userList) {
            if (user1 == null) {
                userList.add(user);
                System.out.println("사용자 등록이 완료되었습니다.");
                return;
            }
        }
    }

    //* 4. 사용자 정보 업데이트 메서드 구현
    public void userInfoUpdate(User user) {
        if (!checkNull(user)) {
            System.out.println("사용자 정보 업데이트 실패");
        }
        findArrayDataAndChange(user);
        System.out.println("사용자 정보 업데이트 완료");
    }

    // * 5. 사용자 삭제 메서드 구현
    public void userDelete(User user) {
        if (!checkNull(user)) {
            for (int i = 0; i < userList.size(); i++) {
                if (findUserId(user)) {
                    userList.remove(i);
                }
                System.out.println("사용자 정보 삭제 완료");
            }
            System.out.println("사용자 삭제 실패");
        }
        System.out.println("사용자 삭제 완료");
    }

    // * 6. 로그인 검증 메서드 구현
    public boolean checkLoginData(User user) {
        if (!checkNull(user)) {
            System.out.println("로그인 검증 실패");
        }

        boolean isMatchId = false;
        boolean isMatchPw = false;
        for (User user1 : userList) {
            if (findUserId(user1)) {
                isMatchId = true;
                System.out.println("아이디 확인 완료");
            }

            if (findUserPw(user)) {
                isMatchPw = true;
                System.out.println("비밀번호 확인 완료");
            }

            if (isMatchPw && isMatchId) {
                System.out.println("로그인이 완료 되었습니다.");
            } else {
                System.out.println("아이디 또는 비밀번호를 확인해주세요!");
                return false;
            }
        }
        return true;
    }

    //    * 8. 회원 등급 업그레이드 메서드 구현
    public void userGradeUpgrade(User user) {
        if (checkNull(user) && user.checkPoint()) {
            findArrayDataAndChange(user);
            if (user instanceof RegularUser regularUser) {
                VIPUser vipUser = new VIPUser(regularUser.getMemberId(), regularUser.getName(), regularUser.getEmail(),
                        regularUser.getPassword(), regularUser.getAddress());
                if (findUserId(user)) {
                    userDelete(user);
                    userRegistration(vipUser);
                }
                System.out.println(regularUser.getName() + " 님께서 VIP 회원으로 등급이 상승하였습니다.");
            }
        } else {
            System.out.println("이미 VIP회원이거나, 업그레이드가 불가합니다.");
        }
    }
}

여기서 눈여겨볼 부분은 회원 등급 업그레이드 메서드예요. 포인트가 일정 수준 이상이면 일반 회원을 VIP 회원으로 변경해주는 기능인데, instanceof 패턴 매칭을 사용해서 타입 검사와 캐스팅을 한 번에 처리했어요. 자바 14부터 추가된 새로운 기능을 활용해봤답니다!

2. 상품 서비스 (ProductService) 구현

다음으로 상품 관련 기능을 담당하는 서비스를 만들었어요:

package service;

import model.product.Category_Enum;
import model.product.Product;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

/*
 * 상품 관련 비즈니스 로직을 처리하는 서비스 클래스
 *
 * 구현 방법:
 * 1. 싱글톤 패턴 적용 (인스턴스를 하나만 생성하여 공유)
 * 2. 상품 목록을 ArrayList<Product>로 관리
 * 3. 상품 추가 메서드 구현
 * 4. 상품 삭제 메서드 구현
 * 5. 상품 업데이트 메서드 구현
 * 6. ID로 상품 검색 메서드 구현
 * 7. 이름으로 상품 검색 메서드 구현 (부분 일치 검색)
 * 8. 카테고리별 상품 필터링 메서드 구현
 * 9. 가격 범위로 상품 필터링 메서드 구현
 * 10. 상품 정렬 메서드 구현 (가격 오름차순/내림차순)
 */

public class ProductService {
    //  *  1. 싱글톤 패턴 적용 (인스턴스를 하나만 생성하여 공유)
// * 2. 상품 목록을 ArrayList<Product>로 관리
    private static ProductService productService;
    private ArrayList<Product> productList;

//    매개변수가 없는 private 생성자가 싱글톤과 맞다.
    private ProductService() {
        this.productList = new ArrayList<>();
    }

    public static ProductService getProductService() {
        if (productService == null) {
            productService = new ProductService();
        }
        return productService;
    }

    public ArrayList<Product> getProductList() {
        return productList;
    }

    //* 3. 상품 추가 메서드 구현
    public void addProduct(Product product) {
        if (!product.getProductId().isEmpty()) {
            productList.add(product);
            System.out.println("상품 추가가 완료되었습니다");
        }
    }

    //* 4. 상품 삭제 메서드 구현
    public void deleteProduct(Product product) {
        if (!product.getProductId().isEmpty()) {
            for (Product products : productList) {
                if (products.getProductId().equals(product.getProductId())) {
                    productList.remove(product);
                    System.out.println("상품 삭제가 완료되었습니다");
                }
            }
        }
    }

    //    * 5. 상품 업데이트 메서드 구현
    public void productUpdate(Product product) {
        if (!product.getProductId().isEmpty()) {
            for (int i = 0; i < productList.size(); i++) {
                if (productList.get(i).getProductId().equals((product.getProductId()))) {
                    productList.remove(i);
                    productList.add(product);
                }
            }
        }
    }

    //     * 7. 이름으로 상품 검색 메서드 구현 (부분 일치 검색)
    public Product findProductWithName(String name) {
        for (Product product1 : productList) {
            if (product1.getName().equals(name)) {
                return product1;
            }
        }
        return null;
    }

    //    * 6. ID로 상품 검색 메서드 구현
    public Product findProduct(String productId) {
        for (Product product1 : productList) {
            if (product1.getProductId().contains(productId)) {
                return product1;
            }
        }
        return null;
    }

    //    * 오브젝트 null검사 메서드
    public boolean checkValueIsNull(Product product) {
        if (product != null) {
            return true;
        }
        return false;
    }

    //    * 8. 카테고리별 상품 필터링 메서드 구현
    public ArrayList filterByCategory(Category_Enum category) {
        ArrayList<Product> targetCategoryArrays = new ArrayList<>();
        for (Product product1 : productList) {
            if (product1.getCategory() == category) {
                targetCategoryArrays.add(product1);
            }
        }
        if (targetCategoryArrays.size() == 0) {
            System.out.println("해당 카테고리에 맞는 상품이 없습니다.");
        }
        return targetCategoryArrays;
    }

//    * 9. 가격 범위로 상품 필터링 메서드 구현
public ArrayList filterByPrice(int minPrice, int maxPrice, Product product){
    ArrayList<Object> targetPriceArrays = new ArrayList<>();
    if (product.getPrice() >= minPrice && product.getPrice() >= maxPrice ) {
        targetPriceArrays.add(product);
    }
    if (targetPriceArrays.size() == 0) {
        System.out.println("해당 가격 범위에 맞는 상품이 없습니다.");
    }
    return targetPriceArrays;
}

//* 10. 상품 정렬 메서드 구현 (가격 오름차순/내림차순)
public void sortUpList(ArrayList list){
    if (list.size() != 0) {
        list.sort(Comparator.naturalOrder());
        System.out.println("오름 차순 정렬이 완료 되었습니다.");
    }
}

    public void sortDownList(ArrayList list){
        if (list.size() != 0) {
            list.sort(Comparator.reverseOrder());
            System.out.println("내림 차순 정렬이 완료 되었습니다.");
        }
    }

}

여기서는 필터링 메서드가 핵심이에요. 카테고리별, 가격대별로 상품을 검색할 수 있는 기능을 구현했어요. ArrayList를 활용해 필터링된 결과들을 반환하는 방식이죠. 이렇게 하면 UI에서 다양한 검색 조건을 지원할 수 있게 되었답니다!

3. 장바구니 서비스 (CartService) 구현

장바구니 기능은 조금 복잡했어요. 사용자마다 장바구니가 하나씩 있고, 장바구니에 상품을 추가하거나 삭제할 수 있어야 하니까요. 아래처럼 구현했어요:

package service;

import exception.OutOfStockException;
import model.cart.Cart;
import model.cart.CartItem;
import model.user.User;

import java.util.ArrayList;

/*
 * 장바구니 관련 비즈니스 로직을 처리하는 서비스 클래스
 *
 * 구현 방법:
 * 1. 싱글톤 패턴 적용
 * 2. 사용자별 장바구니를 ArrayList<Cart>로 관리
 * 3. 장바구니 생성/조회 메서드 구현
 * 4. 상품 추가 메서드 구현
 * 5. 상품 제거 메서드 구현
 * 6. 상품 수량 변경 메서드 구현
 * 7. 장바구니 비우기 메서드 구현
 * 8. 장바구니 항목 조회 메서드 구현
 * 9. 총액 계산 메서드 구현 (회원 할인 적용)
 */
public class CartService {
    //    * 1. 싱글톤 패턴 적용
    //    cartService 필드가 static이 아니어서 싱글톤으로 동작하지 않습니다.
    private static CartService cartService;
    //    * 2. 사용자별 장바구니를 ArrayList<Cart>로 관리
    private ArrayList<Cart> cartArrayList;

    private CartService() {
        this.cartArrayList = new ArrayList<>();
    }

    //    getCartService() 메서드도 static이 아니기 때문에 외부에서 인스턴스 없이 호출할 수 없습니다.
    public static CartService getCartService() {
        if (cartService == null) {
            cartService = new CartService();
            return cartService;
        }
        return cartService;
    }

    public ArrayList<Cart> getCartArrayList() {
        return cartArrayList;
    }

    //    null 검사 메서드
    public boolean checkNull(Object object) {
        if (object == null) {
            System.out.println("입력된 값이 null입니다. 값을 확인해주세요!");
            return false;
        }
        return true;
    }

    public boolean findUserId(Object object) {
        if (object instanceof Cart cart) {
            if (!checkNull(cart)) {
                System.out.println("사용자 검색 실패");
                return false;
            } else {
                for (Cart targetCart : cartArrayList) {
                    if (targetCart.getUserId().equals(cart.getUserId())) {
                        return true;
                    }
                }
                System.out.println("사용자 아이디 검색 성공 일치 및 확인 완료");
            }
        }
        return false;
    }

    //    * 3. 장바구니 생성/조회 메서드 구현
    public Cart createCart(User user) {
        Cart cart = findCart(user);
        if (checkNull(user)) {
            if (cart == null) {
                cart = new Cart(user.getMemberId());
                cartArrayList.add(cart);
                System.out.println("장바구니 생성 완료");
            }
        }
        return cart;
    }

    //    findCart 메서드에서 첫 번째 장바구니만 출력하고 break 하는 것으로 보입니다.
    public Cart findCart(User user) {
        if (checkNull(user) && findUserId(user)) {
            for (Cart cart1 : cartArrayList) {
                System.out.println("== 장바구니 정보 == ");
                cart1.displayCart();
                return cart1;
            }
            System.out.println("장바구니 조회 완료");
        }
        return null;
    }

    //    * 4. 상품 추가 메서드 구현
    public void addProduct(Cart cart, CartItem item) throws OutOfStockException {
        if (checkNull(cart) && checkNull(item)) {
            if (findUserId(cart)) {
                cart.addItem(item.getProduct(), 2);
                System.out.println("장바구니에 상품이 추가되었습니다! : )");
            }
        } else {
            System.out.println("장바구니에 상품 추가가 실패하였습니다.");
        }
    }

    // * 5. 상품 제거 메서드 구현
    public void deleteProduct(Cart cart, CartItem item) {
        if (checkNull(cart) && checkNull(item)) {
            if (findUserId(cart)) {
                cart.removeItem(item);
                System.out.println("장바구니에서 상품이 삭제 되었습니다! : )");
            }
        } else {
            System.out.println("장바구니에 상품 삭제가 실패 하였습니다.");
        }
    }

    //* 6. 상품 수량 변경 메서드 구현
    public void updateProductCount(Cart cart, CartItem item) {
        if (checkNull(cart) && checkNull(item)) {
            if (findUserId(cart)) {
                cart.updateQuantity(item.getProduct().getProductId(), 3);
                System.out.println("장바구니에서 상품 수량이 변경 되었습니다! : )");
            }
        } else {
            System.out.println("장바구니에 상품 수량 변경이 실패 하였습니다.");
        }
    }

    //* 7. 장바구니 비우기 메서드 구현
    public void clearCart(Cart cart) {
        if (checkNull(cart)) {
            if (findUserId(cart)) {
                cart.clearCart();
                System.out.println("장바구니가 비우기 완료! : )");
            }
        } else {
            System.out.println("장바구니 비우기 실패! : (");
        }
    }

    //* 8. 장바구니 항목 조회 메서드 구현
    public void findCartItem(CartItem item) {
        if (checkNull(item)) {
            if (findUserId(item)) {
                item.getProduct().toString();
            }
        }
    }

    //    * 9. 총액 계산 메서드 구현 (회원 할인 적용)
    public int calculateTotalPrice(Cart cart, User user) {
        if (checkNull(cart) && checkNull(user)) {
        int totalPrice = cart.calculateTotalPrice();
        int discountPrice = user.calculateDiscountPercent(totalPrice);
        int finalPrice = totalPrice - discountPrice;

            System.out.println(user.getName() + "님의 총액 " + totalPrice + "(원)에서 " + discountPrice + "(원)이 할인되어 총" + finalPrice +
                    "(원) 입니다.");
        }
        return 0;
    }
}

여기서 중요한 부분은 예외 처리를 추가한 거예요. 재고보다 많은 수량을 장바구니에 담으려고 하면 OutOfStockException이 발생하게 했어요. 이 예외는 다음과 같이 구현했죠:

public class OutOfStockException extends Exception {
    public OutOfStockException(String message) {
        super(message);
    }
}

이렇게 하면 예외가 발생했을 때 UI에서 적절한 메시지를 보여줄 수 있어요!

4. 주문 서비스 (OrderService) 구현

마지막으로 주문 관련 기능을 담당하는 서비스를 구현했어요. 이 부분이 가장 복잡했는데, 장바구니에서 주문으로 전환하고 결제 처리까지 해야 하니까요:

package service;

import model.cart.Cart;
import model.cart.CartItem;
import model.order.Order;
import model.order.OrderItem;
import model.user.User;

import java.util.ArrayList;

/*
 * 주문 관련 비즈니스 로직을 처리하는 서비스 클래스
 *
 * 구현 방법:
 * 1. 싱글톤 패턴 적용
 * 2. 주문 목록을 ArrayList<Order>로 관리
 * 3. 새로운 주문 생성 메서드 구현
 * 4. 장바구니 항목을 주문으로 변환 메서드 구현
 * 5. 주문 상태 업데이트 메서드 구현
 * 6. 주문 취소 메서드 구현
 * 7. 사용자별 주문 내역 조회 메서드 구현
 * 8. 주문ID로 주문 검색 메서드 구현
 * 9. 결제 처리 메서드 구현
 * 10. 영수증 생성 메서드 구현
 */
public class OrderService {
    //    * 1. 싱글톤 패턴 적용
    private static OrderService orderService;
    //    * 2. 주문 목록을 ArrayList<Order>로 관리
    public ArrayList<Order> ordersList;

    private OrderService() {
        this.ordersList = new ArrayList<>();
    }

    public static OrderService getOrderService() {
        if (orderService == null) {
            orderService = new OrderService();
            return orderService;
        }
        return orderService;
    }

    public ArrayList<Order> getOrdersList() {
        return ordersList;
    }

    //    null 검사 메서드
    public boolean checkNull(Object object) {
        if (object == null) {
            System.out.println("입력된 값이 null입니다. 값을 확인해주세요!");
            return false;
        }
        return true;
    }

    //    * 3. 새로운 주문 생성 메서드 구현
    //    이 코드는 ordersList 내에 null인 Order가 있는 경우에만 새 주문을 추가하는 것처럼 보입니다. ArrayList에는 보통 null 값이 포함되지 않으므로 이 부분이 의도대로 동작하지 않을 수 있습니다.
    public void createOrder(Order order) {
        if (CartService.getCartService().checkNull(order)) {
            ordersList.add(order);
            System.out.println("신규 주문 생성 완료");
        } else {
            System.out.println("입력값이 올바르지 않아 주문 생성에 실패하였습니다.");
        }
    }

    //     * 4. 장바구니 항목을 주문으로 변환 메서드 구현
    public Order changeCartItemToOrder(CartItem item, Order order, User user) {
        if (CartService.getCartService().checkNull(item) && CartService.getCartService().checkNull(order) && CartService.getCartService().checkNull(user)) {
            OrderItem orderItem = new OrderItem(item.getProduct(), item.productPriceCalculate(), item.getProduct().getPrice());
            order.addOrderItem(orderItem);
            return order;
        }
        return null;
    }

    //     * 5. 주문 상태 업데이트 메서드 구현
    public void updateOrderStat(Order order, Cart cart) {
        if (CartService.getCartService().checkNull(order) && CartService.getCartService().checkNull(cart)) {
            int orderPrice = order.updateTotalPrice();
            boolean isCheckprice = (orderPrice == cart.calculateTotalPrice());
            order.checkpayed(isCheckprice);
        }
    }

    //    * 6. 주문 취소 메서드 구현
    public void orderCancelled(User user) {
        if (CartService.getCartService().checkNull(user)) {
            for (Order order : ordersList) {
                if (user.getMemberId().equals(order.getUserId())) {
                    order.orderCancelled("단순 변심");
                    break;
                } else {
                    System.out.println("사용자를 찾지 못했습니다.");
                }
            }
        }
    }

    //    * 7. 사용자별 주문 내역 조회 메서드 구현
    public ArrayList findUserOrderContent(User user) {
        ArrayList<Order> findUserOrders = new ArrayList<>();
        if (CartService.getCartService().checkNull(user)) {
            for (Order order : ordersList) {
                if (order.getUserId().equals(user.getMemberId())) {
                    findUserOrders.add(order);
                }
            }
            if (!findUserOrders.isEmpty()) {
            for (Order findUserOrder : findUserOrders) {
                System.out.println("== 주문 내역 == ");
                System.out.println("사용자 ID : " + findUserOrder.getUserId());
                System.out.println("주문 상태 : " + findUserOrder.getOrderStats());
                System.out.println("총 결제 금액 : " + findUserOrder.getTotalPrice() + "원");
                System.out.println("----------------");
            }
            } else {
                System.out.println(user.getName() + "님의 주문 내역이 없습니다");
            }
        }
        return findUserOrders;
    }

    //     * 8. 주문ID로 주문 검색 메서드 구현
    //     equals() 메서드의 결과를 확인하지 않고 있습니다. 또한 검색된 주문을 반환하지 않고 있어 메서드의 실용성이 낮아 보입니다.
    public Order findOrederWithId(String orderId) {
        Order tempOrder = null;
        for (Order order : ordersList) {
            if (CartService.getCartService().checkNull(order)) {
                if (order.getOrderId().equals(orderId)) {
                    System.out.println("주문 검색에 성공하였습니다.");
                    tempOrder = order;
                } else {
                    System.out.println("주문 검색에 실패하였습니다.");
                }
            }
        }
        return tempOrder;
    }

    //    * 9. 결제 처리 메서드 구현
    public void checkedPaid(int price, Order order) {
        boolean isPaid = false;
        if (CartService.getCartService().checkNull(order)) {
            if (price == 0 && !order.isCheckPayed()) {
                System.out.println("결제가 진행되지 않았습니다.");
            } else if (price > 0 && order.isCheckPayed() && price == order.getTotalPrice()) {
                System.out.println("결제가 완료 되었습니다. 주문 상태가 결제 완료 상태로 변경되었습니다.");
                order.orderStatChange(order.isCheckPayed());
            }
        }
    }

    //     * 10. 영수증 생성 메서드 구현
    public void createReceiptAndDisplay(Order order) {
        if (CartService.getCartService().checkNull(order)) {
            order.createReceipt();
        }
    }

}

주문 서비스는 장바구니에 담긴 상품을 실제 주문으로 변환하고, 결제와 주문 상태 관리까지 담당하는 핵심 부분이에요. 여기서 몇 가지 중요한 설계 결정들이 있었어요:

  1. 트랜잭션 처리: 장바구니에서 주문으로 변환할 때, 재고 차감과 장바구니 비우기 등 여러 작업이 한꺼번에 일어나요. 실제 데이터베이스를 사용한다면 트랜잭션으로 처리해야 할 부분이죠.
  2. 상태 변경 로직: 주문 상태(PENDING → PAID → SHIPPED → DELIVERED)를 변경하는 로직을 구현했어요. 각 상태에 따라 가능한 동작이 달라지기 때문에 상태 관리가 중요했답니다.
  3. 주문 취소 시 재고 관리: 주문을 취소하면 차감했던 재고를 다시 원복하는 로직도 추가했어요. 이런 세부 사항까지 고려해야 실제 서비스처럼 동작하게 되죠!

서비스 계층 구현을 통해 배운 점

지금까지 쇼핑몰의 핵심 기능을 담당하는 서비스 계층을 구현해봤어요. 이 과정에서 여러 가지 깨달은 점이 있답니다:

1. 관심사 분리(Separation of Concerns)의 중요성

처음에는 모든 로직을 모델 클래스에 넣으려고 했는데, 서비스 계층을 분리하니 코드가 훨씬 깔끔해졌어요. 각 클래스가 하나의 책임만 가지니 유지보수도 쉬워졌고요. 이것이 바로 객체지향의 **단일 책임 원칙(SRP)**인 것 같아요!

2. 싱글톤 패턴의 활용

서비스 클래스를 싱글톤으로 구현하니 데이터 일관성을 유지하기 쉬워졌어요. 어디서 접근하든 같은 인스턴스를 사용하니까요. 하지만 싱글톤이 테스트를 어렵게 만든다는 단점도 알게 되었어요. 실무에서는 의존성 주입(DI) 같은 방식으로 이 문제를 해결한다고 하더라고요.

3. 예외 처리의 중요성

재고 부족, 결제 실패 같은 상황을 예외로 처리하니 에러 상황을 더 명확하게 관리할 수 있었어요. 처음에는 그냥 if문으로 확인하고 끝냈는데, 예외 클래스를 따로 만들어 처리하니 훨씬 더 깔끔해졌답니다!

4. ArrayList의 활용

ArrayList를 사용해서 데이터를 관리하는 경험도 많이 했어요. 요소를 추가, 삭제하고 특정 조건으로 필터링하는 방법도 배웠죠. 실무에서는 데이터베이스를 쓸 테지만, 이렇게 컬렉션 프레임워크를 활용하는 경험도 중요한 것 같아요.

다음 포스트에서는...

자, 이렇게 모델 클래스와 서비스 클래스까지 구현했어요. 다음 포스트에서는 마지막으로 사용자 인터페이스(UI) 계층을 구현하는 과정을 소개할게요. 콘솔에서 어떻게 사용자와 상호작용하고, 이전에 만든 서비스 클래스들을 활용해 실제 쇼핑몰 기능을 동작시키는지 보여드릴 예정이에요!

좀 더 구체적으로는:

  • 메뉴 시스템 구현
  • 사용자 입력 처리
  • 서비스 메서드 호출 및 결과 표시
  • 예외 상황 처리

이런 내용들을 다룰 예정이니 기대해주세요! 👀

그리고 혹시 서비스 계층 구현에 관한 질문이나 의견이 있으시면 댓글로 남겨주세요. 함께 이야기 나누면 좋겠어요! 😊