프로젝트-주문하기, 장바구니 담기

프로젝트-주문하기, 장바구니 담기

프로젝트-주문하기, 장바구니 담기에 대해 알아보자.


구매 구현(1)


마일리지 상점의 제일 방대한 기능인 ‘구매’를 구현해봅니다. 우선 구매를 선택했을 때 모든 항목과 정보들을 출력해야합니다. for문을 활용해 아이템 슬라이스의 모든 객체를 출력합니다.
그리고 아이템선택 int형 변수를 선언하고 “구매할 물품을 선택하세요: “를 출력해 아이템을 선택할 수 있도록 구현합니다. 아이템 선택은 마찬가지로 for문을 이용해 구현합니다. 잘못된 입력을 하면 계속 처음으로 돌아갑니다.

if menu == 1 { // 물건 구매			
	for {
		itemchoice := 0

		for i := 0; i < 5; i++ {
			fmt.Printf("물품%d: %s,  가격: %d,  잔여 수량: %d\n",i+1, items[i].name, items[i].price, items[i].amount)
		}
		
		fmt.Print("구매할 물품을 선택하세요 :")
		fmt.Scanln(&itemchoice)
		fmt.Println()
		
		if itemchoice == 1 {
			buying()
			break
		} else if itemchoice == 2 {
			buying()
			break
		} else if itemchoice == 3 {
			buying()
			break
		} else if itemchoice == 4 {
			buying()
			break
		} else if itemchoice == 5 {
			buying()
			break
		} else {
			fmt.Println("잘못된 입력입니다. 다시 입력해주세요.\n")
		}
	}
}

구매 과정은 buying() 함수를 이용해 구현할 것입니다. 물품마다 같은 기능이 반복되기 때문입니다.

바로 주문하기


일단 사용자가 물품을 선택하고 buying() 함수에 진입한 상황이라고 생각해봅시다. 그 다음은 선택한 물품을 몇 개 주문할 것인지 입력하고, 장바구니에 담거나 바로 구매하는 것입니다. 우선 바로 주문하는 것을 구현해봅니다. 올바른 수량을 입력했다는 가정하에(예외(에러) 처리는 나중에 합니다.) 주문을 완료했다면 필요한 기능은

주문한 물품과 수량만큼 사용자 객체의 포인트 차감
아이템 객체들의 수량 차감

입니다. 이 두 기능을 buying() 함수에 구현해봅니다. 우선 함수가 실행되면 선택한 물품을 몇 개 주문할지 알아야하기 때문에 사용자가 입력한 수량을 저장할 변수를 선언하고 “수량을 입력하시오 :” 를 출력해 수량을 입력받습니다. 그럼 지금까지 함수 내용은 아래와 같습니다.

func buying(itm []item, byr *buyer, itmchoice int) {
	inputamount := 0 // 구매 수량
	
	fmt.Print("수량을 입력하시오 :")
	fmt.Scanln(&inputamount)
	fmt.Println() // 한줄 띄어쓰기
}

구매자 객체는 포인터 구조체기 때문에 포인터 매개변수를 사용합니다. 수량을 입력했다면 1.바로 구매할 것인지 2. 장바구니에 담을 것인지 선택합니다. 잘못된 번호를 입력하면 “잘못된 입력입니다. 다시 입력해주세요.”를 출력하고 계속 다시 선택하게끔 for문을 이용합니다. 바로 주문을 할 경우 위에서 언급한 두 기능을 실행합니다. 그리고 “상품이 주문 접수 되었습니다.”를 출력하고 for문을 빠져나갑니다. 장바구니에 담는 기능은 나중에 구현할 것이기 때문에 비워둡니다.

구매 구현(2)


주문한 물품과 수량만큼 사용자 객체의 포인트 차감, 아이템 객체들의 수량 차감 기능을 어떻게 구현할지 생각해봤습니다.

for {
	buy := 0 // 살지 장바구니 담을지
	fmt.Println("1. 바로 주문\n2. 장바구니에 담기")
	fmt.Print("실행할 기능을 입력하시오 :")
	fmt.Scanln(&buy)
	fmt.Println()

	if buy == 1 { // 바로 주문			
		itm[itmchoice-1].amount -= inputamount
		byr.point -= itm[itmchoice-1].price * inputamount

		fmt.Println("상품이 주문 접수 되었습니다.")
		break
	} else if buy == 2 { // 장바구니에 담기

	} else {
		fmt.Println("잘못된 입력입니다. 다시 입력해주세요.\n")
	}
}

지금까지 메뉴를 선택할 때 모두 예외처리를 했습니다. 제시한 번호와 다른 것을 입력받으면 잘못된 입력임을 알려주고 for문의 처음으로 돌아갑니다. 그런데 수량 입력은 아직 예외 처리를 하지 않았습니다. 수량 입력을 제한하지 않는 것은 프로그램에 굉장히 치명적인 버그를 야기할 수 있습니다. 따라서 잘못된 수량을 입력하는 경우는 panic을 발생시키고 복구하겠습니다. 수량 입력에서 panic을 발생시키는 경우는 두 가지입니다.

소유 포인트나 잔여 수량이 주문 물품의 포인트와 수량보다 적은 경우("주문이 불가능합니다." 패닉 메시지 전달하고 패닉 발생.)
수량을 음수로 입력한 경우("올바른 수량을 입력하세요." 패닉 메시지 전달하고 패닉 발생.)

따라서 for문을 감싸는 if ~else문이 추가됩니다.

fmt.Print("수량을 입력하시오 :")
fmt.Scanln(&inputamount)
fmt.Println()
if inputamount <= 0 {
	panic("올바른 수량을 입력하세요.")
}
if byr.point < itm[itmchoice-1].price * inputamount || itm[itmchoice-1].amount < inputamount { // 수량, 포인트로 구매 가능 여부
	panic("주문이 불가능합니다.")
} else {
	for {
		buy := 0 // 살지 장바구니 담을지
		fmt.Println("1. 바로 주문\n2. 장바구니에 담기")
		fmt.Print("실행할 기능을 입력하시오 :")
		fmt.Scanln(&buy)
		fmt.Println()
		if buy == 1 {
			......

프로그램이 바로 종료되지 않게 기본적인 복구 코드를 defer와 recovery() 를 이용해 만듭니다. 물론 defer는 buying() 함수 맨 처음 부분에 나타나야합니다. 에러 메시지는 fmt.Println()을 이용해 출력합니다.

장바구니에 담기


이번에 2. 장바구니에 담기를 선택했을 때 선택한 물품을 장바구니에 추가하는 기능을 구현해봅니다. 드디어 구매자 객체(이하 buyer)에 point 필드 뿐만이 아니라 shoppingbucket 필드도 사용합니다. 장바구니 담기는 단순히 생각하면 사용자가 선택한 물품과 수량을 그대로 shoppingbucket에 key와 value 값으로 저장하는 것입니다. 단순하게 생각하면 byr.shoppingbucket[itm[itmchoice-1].name] = inputamount만 입력하면 될 것 같지만 이렇게 한다면 장바구니에 이미 존재하는 품목을 추가한다면 품목이 여러번 중복돼서 추가됩니다. 그래서 추가하는 품목이 장바구니 안에 존재 하는지 알아야합니다. 이미 존재한다면 수량만 증가시키고, 존재하지 않는다면 위에 방법대로 추가만 하면 됩니다.

else if buy == 2 { // 장바구니에 담기
	checkbucket := false // 중복 물품을 체크하기 위한 변수

	for itms := range byr.shoppingbucket { // 물품 체크
		if itms == itm[itmchoice-1].name {
			checkbucket = true
		}
	}

	if checkbucket == true { // 장바구니에 중복되는 물품이 있을 때
			byr.shoppingbucket[itm[itmchoice-1].name] += inputamount // 수량만 더함
	} else { // 장바구니에 중복되는 물품이 없을 때
		byr.shoppingbucket[itm[itmchoice-1].name] = inputamount // 새로 품목 추가
	}
	
	fmt.Println("상품이 장바구니에 추가되었습니다.")
	break //구매 for문을 빠져나감
}

여기서 예외처리를 해야하는 것이 있습니다. 현재 추가하려는 품목의 수량 + 기존 장바구니에 있는 품목의 수량 > 잔여 수량 일 때 추가할 수 없게끔 코드를 작성합니다.

	if checkbucket == true { // 장바구니에 중복되는 물품이 있을 때
		temp := byr.shoppingbucket[itm[itmchoice-1].name] + inputamount
		if temp > itm[itmchoice-1].amount{
			fmt.Println("물품의 잔여 수량을 초과했습니다.")
			break
		} else {
			byr.shoppingbucket[itm[itmchoice-1].name] += inputamount // 수량만 더함
		}	
	} else { // 장바구니에 중복되는 물품이 없을 때
		byr.shoppingbucket[itm[itmchoice-1].name] = inputamount // 새로 품목 추가
	}

장바구니 확인(1)


이전에 물품을 바로 주문하는 것이 아니라 장바구니에 추가하는 것을 구현했습니다. 그렇다면 사용자가 장바구니에 추가한 물품들이 어떤 것인지 알 수 있도록 출력하는 기능인 ‘장바구니 확인’ 기능을 구현해야합니다.

func emptyBucket(byr *buyer) {
	if len(byr.shoppingbucket) == 0 {
		fmt.Println("장바구니가 비었습니다.")
	} else {
		for index, val := range byr.shoppingbucket {
			fmt.Printf("%s, 수량: %d\n", index, val)
		}
	}
	fmt.Println()
}
else if menu == 5 { // 장바구니 확인
	emptyBucket(buyer)

	fmt.Print("엔터를 입력하면 메뉴 화면으로 돌아갑니다.")	
	fmt.Scanln()
}

장바구니 상품 주문


‘장바구니 확인’ 기능에 ‘주문’ 기능을 추가할 것입니다. 바로 상품들을 한번에 주문하는 ‘장바구니 상품 주문’ 기능입니다. 따라서 장바구니 물품 확인을 선택하면

장바구니 물품 주문
메뉴로 돌아가기
실행할 기능을 입력하시오:
else if menu == 5 { // 장바구니 확인
	bucketmenu := 0
	
	for {				
		emptyBucket(buyer) // 장바구니 비었는지 확인, 안비었으면 물품 출력

		fmt.Println("1. 장바구니 상품 주문")
		fmt.Println("2. 메뉴로 돌아가기")
		fmt.Print("실행할 기능을 입력하시오 :")
		fmt.Scanln(&bucketmenu)
		fmt.Println()

		if bucketmenu == 1 {
			//장바구니 상품 주문하는 기능
		} else if bucketmenu == 2{
			fmt.Println()
			break
		} else {
			fmt.Println("잘못된 입력입니다. 다시 입력해주세요.")
		}
	}
}

그런데 여기서 판별할 것이 있습니다.

장바구니에 추가한 물품의 포인트의 총 합이 사용자의 잔여 포인트보다 많을 경우('주문'을 해야 포인트가 차감되기 때문에)
담아놓은 상태에서 바로 주문을 해서 장바구니에 있는 물품의 수량이 잔여 수량보다 많을 경우(장바구니에 담을 당시에는 잔여 수량이 20개여서 15개를 담았는데 그 후에 10개를 주문하면 5개가 초과되는 상황이 발생함)

우선 requiredPoint() 함수를 만들어 1. 장바구니의 총 필요 마일리지를 계산하고 2. 필요 마일리지와 보유 마일리지를 출력하고 3. 마일리지가 부족할 시에는 “마일리지가 %d점 부족합니다.”라는 메시지와 false를 반환합니다.

func requiredPoint(itm []item, byr *buyer) (canbuy bool) {	
	bucketpoint := 0
	for index, val := range byr.shoppingbucket { // 총 필요 마일리지 계산
		for i := 0; i < len(itm); i++ {
			if itm[i].name == index {
				bucketpoint += itm[i].price * val
			}
		}		
	}
	fmt.Println("필요 마일리지 :", bucketpoint)
	fmt.Println("보유 마일리지 :", byr.point)
	fmt.Println()
	if byr.point < bucketpoint {
		fmt.Println("마일리지가 %d점 부족합니다.", bucketpoint - byr.point)
		return false
	}
	return true
}
func excessAmount(itm []item, byr *buyer) (canbuy bool){
	for index, val := range byr.shoppingbucket { 
		for i := 0; i < len(itm); i++ {
			if itm[i].name == index {
				if itm[i].amount < val { // 장바구니의 물품 총 개수가 판매하는 물품 개수보다 클 때
					fmt.Println("%s, %d개초과", itm[i].name, val-itm[i].amount)
					return false
				}
			}
		}
	}
	return true
}
emptyBucket(buyer) // 장바구니 비었는지 확인, 안비었으면 물품 출력

canbuy := requiredPoint(items, buyer)
canbuy = excessAmount(items, buyer)

이때 똑같은 변수에 반환값을 초기화하는데 이유는 둘중에 한개라도 false가 나오면 어차피 구매가 불가능하기 때문입니다.

장바구니 확인(2)


수량과 포인트에 대해 장바구니 물품을 가능한지 모두 확인했습니다. 이제 장바구니에 접근해 구매 기능과 마찬가지로 장바구니 품목의 수량과 사용자의 포인트가 차감되어야합니다. 차감했다면 장바구니를 초기화하고 “주문이 접수됐습니다.”를 출력합니다.

func bucketBuying(itm []item, byr *buyer) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("\n",r,"\n")
			
		}
	}()
		
	if len(byr.shoppingbucket) ==0 {
		panic("주문 가능한 목록이 없습니다.")
	} else { 
		for index, val := range byr.shoppingbucket {
			for i := range itm {
				if itm[i].name == index {
					itm[i].amount -= val // 수량 차감
					byr.point -= itm[i].price * val // 포인트 차감
				}
			}
		}
 	}

	byr.shoppingbucket = map[string]int{}  // 장바구니 초기화
	fmt.Println("상품이 주문 접수 되었습니다.")
}

그리고 bucketBuying() 함수 내에서 장바구니의 물품이 존재하지 않는데 주문할 경우”주문 가능한 목록이 없습니다.”를 panic으로 발생시키고 복구합니다.

fmt.Println("1. 장바구니 상품 주문")
fmt.Println("2. 장바구니 초기화")
fmt.Println("3. 메뉴로 돌아가기")
fmt.Print("실행할 기능을 입력하시오 :")
fmt.Scanln(&bucketmenu)
fmt.Println()

if bucketmenu == 1 {
	if canbuy {
		bucketBuying(items, buyer)
		break
	} else {
		fmt.Println("구매할 수 없습니다.")
		break
	}					
} else if bucketmenu == 2{
	buyer.shoppingbucket = map[string]int{}  // 장바구니 초기화
	fmt.Println("장바구니를 초기화했습니다.")
	break
} else if bucketmenu == 3 {
	fmt.Println()
	break
} else {
	fmt.Println("잘못된 입력입니다. 다시 입력해주세요.")
}

그런데 주문할 수 없으면 장바구니에 등록된 품목을 수정할 수 있어야합니다. 그렇지 못하면 계속 주문이 불가능한 상태로 남아있기 때문입니다. 그래서 ‘장바구니 초기화’ 기능을 추가합니다.

장바구니 상품 주문
장바구니 초기화
메뉴로 돌아가기

© 2022. All rights reserved. 신동민의 블로그