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

자바로 만든 나만의 쇼핑몰 여행기 (3) - UI 계층 구현편

zenjoydev 2025. 3. 27. 20:28

안녕하세요, 여러분! 드디어 우리 쇼핑몰 프로젝트의 마지막 이야기를 들려드릴 시간이 왔네요. 지난 두 포스트에서는 기본 도메인 모델 클래스들과 서비스 계층을 구현하는 과정을 공유했었죠. 오늘은 프로젝트의 최종 단계인 사용자 인터페이스(UI) 계층을 구현한 과정을 소개해드릴게요!

UI 계층은 사용자와 직접 상호작용하는 부분으로, 이번 프로젝트에서는 콘솔 기반의 텍스트 인터페이스로 구현했어요. 복잡한 그래픽이나 웹 인터페이스는 아니지만, 객체지향적인 설계 원칙을 적용하면서 사용자가 쉽게 이용할 수 있는 UI를 만들기 위해 노력했답니다!

ShopConsoleUI 클래스 설계하기

UI 계층의 핵심은 ShopConsoleUI 클래스에요. 이 클래스는 사용자의 입력을 받고 적절한 서비스 메서드를 호출하여 결과를 보여주는 역할을 담당합니다. 클래스 상단에 구현 방법을 주석으로 정리해 놓았어요:

/*
 * 콘솔 기반 사용자 인터페이스 클래스
 *
 * 구현 방법:
 * 1. Scanner 객체로 사용자 입력 처리
 * 2. 각 서비스 클래스의 인스턴스 참조
 * 3. 메인 메뉴 표시 및 처리 메서드 구현
 * 4. 상품 목록 표시 메서드 구현
 * 5. 상품 상세 정보 표시 메서드 구현
 * 6. 장바구니 표시 및 관리 메서드 구현
 * 7. 주문 과정 안내 메서드 구현
 * 8. 결제 처리 메서드 구현
 * 9. 사용자 로그인/등록 메서드 구현
 * 10. 입력값 검증 및 예외 처리 메서드 구현
 */

이 계획에 따라 순차적으로 기능을 구현해 나갔어요. Scanner 대신 BufferedReader를 사용해서 사용자 입력을 처리했는데, 이게 예외 처리 측면에서 더 유연하더라고요!

메인 메뉴 구현하기

가장 먼저 메인 메뉴를 구현했어요. 사용자가 프로그램을 실행하면 가장 먼저 보게 될 메뉴니까요:

public static void main(String[] args) throws Exception {
    // 버퍼 리더 호출
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    User newUser = null;
    boolean isEndRoop = false;
    // 메인 메뉴 표시 및 처리 메서드 구현
    while (!isEndRoop) {
        displayMainMenu();
        String choice = reader.readLine();

        // 사용자 선택에 따른 호출 로직 구현하기
        if (choice.contains("5")) {
            System.out.println("쇼핑몰 프로그램을 종료합니다! 오늘도 좋은 하루 보내세요!");
            isEndRoop = true;
            break;
        }
        if (choice.contains("1")) {
            // 사용자 정보 관리
            handleUserManagement(reader, newUser);
        } else if (choice.contains("2")) {
            // 장바구니 관리
            handleCartManagement(reader, newUser);
        } else if (choice.contains("3")) {
            // 상품 관리
            handleProductManagement(reader);
        } else if (choice.contains("4")) {
            // 주문 관리
            System.out.println("주문 관리 기능으로 이동합니다.");
            orderProcessingGuide(newUser);
        } else {
            System.out.println("올바른 값을 입력해주세요!");
        }
    }
}

public static void displayMainMenu() {
    System.out.println("안녕하세요! 오늘도 저희 클릭스에 방문하신것을 환영합니다!");
    System.out.println("어떤 기능을 이용하시겠어요?");
    System.out.println("1. 사용자 정보 관리");
    System.out.println("2. 장바구니 관리");
    System.out.println("3. 상품 관리");
    System.out.println("4. 주문 내역 관리");
    System.out.println("5. 종료");
    System.out.println("-- 원하시는 기능의 번호를 입력해주세요! ex)1번 or 1 --");
}

여기서 특이한 점은 choice.contains("1") 같은 방식으로 사용자 입력을 검증한 거예요. 이렇게 하면 "1번", "1" 등 다양한 형태의 입력을 모두 처리할 수 있어서 사용자 편의성이 높아졌답니다!

상품 관련 기능 구현하기

상품 목록과 상세 정보를 보여주는 메서드는 다음과 같이 구현했어요:

// 상품 목록 표시 메서드 구현
public static void productListDisplay() {
    ProductService productService = ProductService.getProductService();

    ArrayList<Product> productList = productService.getProductList();
    if (!productList.isEmpty()) {
        System.out.println(" == 상품 목록 == ");
        for (Product product : productService.getProductList()) {
            for (int i = 0; i < productList.size(); i++) {
                System.out.println((i + 1) + "번 상품 : " + product.getName());
            }
        }
        System.out.println(" == 상품 목록 출력 종료 == ");
    } else {
        System.out.println("등록된 상품이 없습니다.");
    }
}

// 상품 상세 정보 표시 메서드 구현
public static void productInfoDisplay() {
    ProductService productService = ProductService.getProductService();

    ArrayList<Product> productList = productService.getProductList();
    if (!productList.isEmpty()) {
        System.out.println(" == 상품 상세 정보 == ");
        for (Product product : productService.getProductList()) {
            System.out.println(product.toString());
        }
        System.out.println(" == 상품 상세 정보 출력 종료 == ");
    } else {
        System.out.println("등록된 상품이 없습니다.");
    }
}

여기서는 상품 서비스의 싱글톤 인스턴스를 가져와서 상품 목록을 조회하고 표시하고 있어요. 목록이 비어있는 경우도 처리하여 사용자에게 적절한 메시지를 보여주도록 했습니다!

장바구니 관련 기능 구현하기

장바구니 기능은 조금 복잡했어요. 장바구니 내용을 보여주는 기능뿐만 아니라, 상품을 장바구니에 추가하고 관리하는 기능까지 구현해야 했거든요:

// 장바구니 표시 및 관리 메서드 구현
public static void displayCart() {
    CartService cartService = CartService.getCartService();
    ArrayList<Cart> cartList = cartService.getCartArrayList();
    if (!cartList.isEmpty()) {
        System.out.println("== 장바구니입니다! ==");
        for (Cart cart : cartList) {
            cart.displayCart();
        }
        System.out.println("== 장바구니 표시 종료 ==");
    }
}

public static boolean cartManagers(User user, BufferedReader reader) throws Exception {
    if (UserService.getUserService().checkNull(user)) {
        System.out.println("입력값이 올바르지 않습니다.");
        return false;
    }
    //어떤 사용자의 장바구니에 추가할지 > 사용자 검증
    if (UserService.getUserService().checkLoginData(user)) {
        System.out.println("사용자 검증 완료");
    } else {
        System.out.println("사용자 검증 실패");
        return false;
    }
    
    // 장바구니 찾기
    Cart cart = CartService.getCartService().findCart(user);
    if (cart == null) {
        cart = new Cart(user.getMemberId());
        System.out.println("징바구니 생성이 완료되었습니다.");
    }

    // 상품 정보 입력 받기
    ArrayList<Object> productInfos = new ArrayList<>();
    Category_Enum newProductCategoryName = null;
    
    // 상품 정보 입력 받는 코드 (생략)...
    
    // 상품 객체 생성 하기
    Product newProduct = new Product(newProductId, newProductName, newProductPrice, 3, newProductCategoryName);

    // 상품 > 카트 아이템으로 변환 > 카트에 추가
    CartService cartService = CartService.getCartService();
    CartItem cartItem = new CartItem(newProduct, 1);
    
    try {
        cart.addItem(newProduct, 1);
    } catch (OutOfStockException e) {
        System.out.println(e.getMessage());
        return false;
    }

    System.out.println("== 장바구니 관리 종료 ==");
    return true;
}

여기서 주목할 만한 부분은 예외 처리예요! OutOfStockException이 발생할 경우 사용자에게 적절한 메시지를 보여주고 있죠. 이런 예외 처리는 프로그램의 안정성을 높이는 데 큰 역할을 했답니다.

사용자 관리 기능 구현하기

로그인과 회원가입 기능도 중요한 부분이었어요:

public static User userLogin(BufferedReader reader) throws Exception {
    User targetUser = null;
    System.out.print("아이디를 입력해 주세요 : ");
    String inputId = reader.readLine();
    System.out.println();
    System.out.print("비밀번호를 입력해 주세요 : ");
    String inputPw = reader.readLine();
    System.out.println();

    if (checkValue(inputId) && checkValue(inputPw)) {
        System.out.println("사용자 정보 확인 중입니다. 잠시 기다려 주세요.");
        for (User user : UserService.getUserService().userList) {
            if (UserService.getUserService().findUserId(inputId) && UserService.getUserService().findUserPw(inputPw)) {
                if (user.getMemberId().equals(inputId) && user.getPassword().equals(inputPw)) {
                    System.out.println("로그인 완료!");
                    return user;
                }
            }
        }
    }
    System.out.println("로그인 실패! 아이디와 비말번호를 다시 확인해 주세요!");
    return null;
}

public static User registNewUser(BufferedReader reader) throws Exception {
    User newUser = null;

    System.out.println(" -- 유저 등록 시작 -- ");
    // 상품 정보 입력 받기
    ArrayList<Object> userInfos = new ArrayList<>();
    
    // 사용자 정보 입력 받는 코드 (생략)...
    
    System.out.println("-- 유저 정보 검증 시작 --");
    for (int i = 0; i < userInfos.size(); i++) {
        if (!checkValue(userInfos.get(i))) {
            System.out.println("검증에 실패하였습니다. 다시 시도해 주세요");
            return null;
        }
    }
    newUser = new User(newUserId, newUserName, newUserEmail, newUserPw, newUserAddress);
    System.out.println("-- 유저 정보 검증 완료 --");
    System.out.println("-- 유저 등록 완료 --");
    // 유저 서비스에 사용자 정보 저장
    UserService.getUserService().userRegistration(newUser);
    return newUser;
}

이 부분에서는 사용자 입력값을 검증하는 로직이 중요했어요. checkValue 메서드를 만들어서 입력값이 유효한지 검사하고, 문제가 있을 경우 적절한 메시지를 출력하도록 했죠!

주문 처리 기능 구현하기

마지막으로 주문 처리 기능을 구현했어요:

// 주문 과정 안내 메서드 구현
public static boolean orderProcessingGuide(User user) {
    if (!checkUser(user)) {
        System.out.println("유저 검증 실패");
        return false;
    }
    // 유저의 주문 서비스에서 주문 상태 정보를 읽어와서 각 상태에 맞는 메시지 표출
    for (Order order : OrderService.getOrderService().getOrdersList()) {
        if (order.findAndMatchUserId(order, user)) {
            order.orderStatToKor(order.getOrderStats());
        }
    }
    System.out.println(" -- 주문 과정 안내 완료 -- ");
    return true;
}

// 결제 처리 메서드 구현
public static boolean checkPaied(User user) {
    if (!checkUser(user)) {
        System.out.println("유저 검증 실패");
        return false;
    } else {
        for (Order order : OrderService.getOrderService().getOrdersList()) {
            order.findAndMatchUserId(order, user);
            OrderService.getOrderService().checkedPaid(order.getTotalPrice(), order);
        }
    }
    System.out.println(" -- 결제 확인 완료 -- ");
    return true;
}

이 부분에서는 사용자 검증 로직이 중요했어요. 로그인하지 않은 사용자가 주문 기능을 사용하려고 하면 적절한 오류 메시지를 보여주도록 했죠. 또한 주문 상태에 따라 다른 메시지를 보여주어 사용자가 현재 주문 상태를 쉽게 파악할 수 있도록 했답니다!

유틸리티 메서드 구현하기

마지막으로, 여러 곳에서 사용하는 유틸리티 메서드도 구현했어요:

// 입력값 검증 메서드
public static boolean checkValue(Object value) {
    if (value instanceof String str) {
        if (str.isEmpty()) {
            System.out.println("상품명이나 상품 아이디의 입력값이 올바르지 않습니다.");
            return false;
        }
    } else if (value instanceof Integer nums) {
        if (nums == 0 || nums < 0) {
            System.out.println("상품 가격이나, 재고의 값이 올바르지 않습니다.");
            return false;
        }
    }
    System.out.println("입력값 검증에 성공하였습니다.");
    return true;
}

// 카테고리 문자열 매칭 메서드
public static Category_Enum matchForCategory(String target) {
    Category_Enum categoryEnum = null;
    if (target.contains("전자")) {
        categoryEnum = Category_Enum.ELECTRONICS;
    } else if (target.contains("의류")) {
        categoryEnum = Category_Enum.CLOTHING;
    } else if (target.contains("음식") || target.contains("식") || target.contains("식품")) {
        categoryEnum = Category_Enum.FOOD;
    } else if (target.contains("도서") || target.contains("책")) {
        categoryEnum = Category_Enum.BOOKS;
    }
    if (categoryEnum == null) {
        System.out.println("카테고리명이 올바르지 않습니다.");
    }
    return categoryEnum;
}

// 사용자 검증 메서드
public static boolean checkUser(User user) {
    if (!UserService.getUserService().checkNull(user)) {
        System.out.println("입력값이 올바르지 않습니다.");
        return false;
    }
    // 어떤 사용자의 장바구니에 추가할지 > 사용자 검증
    if (UserService.getUserService().checkLoginData(user)) {
        System.out.println("사용자 검증 완료");
    } else {
        System.out.println("사용자 검증 실패");
        return false;
    }
    return true;
}

이런 유틸리티 메서드들은 코드의 재사용성을 높이고 중복을 줄이는 데 큰 도움이 되었어요. 특히 checkValue 메서드는 다양한 타입의 값을 검증할 수 있도록 instanceof 패턴 매칭을 활용했어요. 자바 14의 새로운 기능을 적용해본 좋은 경험이었답니다!

UI 계층 구현을 통해 배운 점

지금까지 쇼핑몰 프로젝트의 UI 계층을 구현하면서 많은 것을 배웠어요:

1. 사용자 경험(UX)의 중요성

콘솔 기반 UI라도 사용자 입장에서 생각하면 더 편리한 인터페이스를 만들 수 있다는 것을 깨달았어요. 명확한 메뉴 구성, 직관적인 입력 방식, 풍부한 피드백 메시지 등이 사용자 경험을 크게 향상시켰죠.

2. 예외 처리의 중요성

사용자는 예상치 못한 입력을 할 수 있어요. 숫자를 입력해야 하는데 문자를 입력하거나, 필수 정보를 비워두는 등의 상황에 대비해야 했죠. try-catch 블록과 입력값 검증 로직을 통해 프로그램의 안정성을 크게 높일 수 있었어요.

3. 모듈화의 장점

각 기능을 별도의 메서드로 분리하니 코드가 훨씬 깔끔해졌어요. 특히 자주 사용하는 기능은 유틸리티 메서드로 만들어 재사용성을 높였답니다. 이런 모듈화 덕분에 코드 유지보수도 훨씬 쉬워졌어요!

4. 객체지향 설계의 실제 적용

지금까지 배웠던 객체지향 개념들을 실제로 적용해볼 수 있었어요. 특히 각 계층(모델, 서비스, UI)의 역할을 명확히 분리하는 것이 얼마나 중요한지 체감했답니다. UI 계층은 사용자와의 상호작용만 담당하고, 비즈니스 로직은 서비스 계층에 맡기는 구조가 코드를 훨씬 깔끔하게 만들었어요.

마치며

이렇게 세 편에 걸쳐 자바로 쇼핑몰을 만드는 여정을 공유해 드렸어요. 모델 클래스부터 시작해서 서비스 계층을 거쳐 UI 계층까지, 점진적으로 기능을 구현하며 실제 프로젝트가 어떻게 발전해 나가는지 경험할 수 있었답니다.

이 프로젝트를 통해 자바의 기본 문법뿐만 아니라 객체지향 설계 원칙, 예외 처리, 싱글톤 패턴 등 다양한 개념을 실전에서 활용해 볼 수 있었어요. 무엇보다 작은 기능들이 모여 하나의 완성된 애플리케이션이 되는 과정이 정말 보람찼습니다!

여러분도 이 포스트 시리즈가 자바 학습에 도움이 되었기를 바랍니다. 혹시 질문이나 의견이 있으시면 언제든지 댓글로 남겨주세요. 다음에 또 다른 프로젝트로 찾아뵙겠습니다! 👋

 

https://github.com/getplay0131/-BE-25.03.24-_Java_Progress_Conceptual_Arrangement_Project-Fin