본문 바로가기
코딩과 알고리즘

자바 - 백앤드 학습 #3. 부스트코스 프로젝트 A 변형 제작

부스트 코스의 자바 백앤드 심화과정 학습을 하면서 '과제'란게 있어서
"엇? 이런것도 있네?" 하고 도전의욕을 불태우며(?) 내심 기대했다가..
아쉽게도 과제 제출 서비스는 종료되었더라구요.

원본 게시글 : https://www.boostcourse.org/web326/project/205/content/164?isDesc=false#summary

뭐.. 어파치 기술을 익히기 위한 용도이기 때문에..
약간 아쉽긴 하지만 기왕 하는 김에 제시된 '명함관리 프로젝트' 대신 이를 약간 흉내낸
'마트 상품 관리 프로젝트'를 개발해 보았습니다.
일부 소스만 채워 넣는게 아니라 전체 소스를 재구성해야 해서 난이도는 약간 더 높긴 하지만
29년의 개발 노하우가 있지 않습니까? :) 4시간 정도 투자해 끝마쳤습니다.

해당 부분을 공유하도록 하겠습니다.


메이븐 퀵스타트 프로젝트 생성

지난 게시글에서 웹사이트용 메이븐 프로젝트를 생성하여 웹사이트 기능을 다루었는데요.

https://itadventure.tistory.com/671
이번 프로젝트는 웹사이트용 프로젝트가 아닙니다.

 

자바 - 백앤드 학습 #2. 이클립스 + 메이븐 ( 2024. 5월 기준 )

부스트 코스의 백앤드 학습을 하면서 메이븐(Maven)이라는 약간 복잡한 부분을 진행해보았는데요.이클립스에서 메이븐을 사용하는 방법인데 이클립스 버전도 바뀌었고 설정이 그리 쉽지 않습니

itadventure.tistory.com

단순한 자바 콘솔 프로젝트이기 때문에 프로젝트 설정이 그리 어렵지 않습니다.
시작점 File - New - Maven Project 메뉴는 동일한데요.

중간에 아치타입(Archetype) 선택 과정에 maven-archetype-quickstart 항목을 선택해야 합니다.

그리고 Artifact Id 는 craymart 로 정해 주었지요.
'크레이마켓'이라는 통칭입니다 :)

중간에 콘솔창을 클릭해서 Enter 를 한번 눌러주어야 합니다.
이 부분을 모르면 마냥 언제 끝나는지 기다리고만 있을 수 있습니다. :)


MYSQL 모듈 설치, JDK 1.8 설정

이제 pom.xml 파일을 수정하면 되는데요.
웹 프로젝트가 아니라서 서블릿이나 JSTL 같은건 설치할 필요가 없습니다.

빨간색이 추가한 부분입니다.


       :
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.18</version>
    </dependency>
  </dependencies>
  
  <build>
    <plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.6.1</version>
        <configuration>
            <source>1.8</source>
            <target>1.8</target>
        </configuration>
    </plugin>
    </plugins>
</build>
</project>


그리고 프로젝트를 우클릭 - Maven - Update Project 를 선택하면 설치는 끝입니다.


MYSQL DDL(정의)

MYSQL 은 아래와 같이 테이블을 구성하였는데요.

create table cray_mart_goods (
	no int not null AUTO_INCREMENT,
	name varchar(30) not null,
	price int not null,
	stock int not null DEFAULT 0,
	PRIMARY KEY (no)
);

no 는 자동으로 일련번호가 배정되는 상품코드이고, 프라이머리 키(primary key)로 잡았습니다.
name상품명,
price상품가격,
stock상품 재고 갯수에 해당합니다.


상품 구조 정의 클래스

이제 이 데이터 베이스 구조에 맞는 자바쪽 상품 구조 정의 클래스를 생성해야 하는데요.
기본 클래스 구조만 잡아준 다음 나머지는 자동코드 생성기를 사용하면 됩니다.

클래스를 쉽게 추가하는 유용한 방법을 알아냈는데요.
프로젝트 하위 src/main/java 폴더를 펼쳐 패키지명을 선택한 후,

Ctrl + N 키를 누르면 아래와 같은 위자드(wizard) 창이 나올텐데요.
Class 선택 후 Next 버튼을 눌러주시면 됩니다.
( 보통은 기본 선택되어 Enter 만 눌러도 됩니다 )

그러면 패키지명이 이미 입력이 되어 있어, Name 만 정해주면 되는데요.
여기서는 크레이 마트의 상품이란 의미로 CrayMartGood 로 정하겠습니다.

이제 기본 소스는 아래와 같이 생성되는데요.

package com.cafe24.dreamplan7.craymart;

public class CrayMartGood {

}

금방 생성한 MYSQL 데이터 구조에 맞게 다음과 같이 멤버 변수를 구성한 후,

package com.cafe24.dreamplan7.craymart;

public class CrayMartGood {
	private int no;
	private String name;
	private int price;
	private int stock;
	
}

마지막 변수가 정의된 곳 아래 한줄을 벌린 다음, 해당 부분을 선택하고

Source - Genegate Getter and Setters.. 메뉴를 선택하면 되는데요.

Select All 버튼 - Generate 버튼을 선택하면 됩니다.

그러면 와우~
멤버 변수를 설정하거나 값을 받아오는 set 계열의 메소드와 get 계열의 메소드가 자동 생성된 것을 확인할 수 있습니다.

        :
    private int price;
	private int stock;
	public int getNo() {
		return no;
	}
	public void setNo(int no) {
		this.no = no;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	public int getStock() {
		return stock;
	}
	public void setStock(int stock) {
		this.stock = stock;
	}
	
}

내친 김에 toString() 함수도 생성해보겠습니다.
Source - Generate toString()... 메뉴를 선택하면,

역시 비슷한 화면이 등장하는데요.
멤버 변수가 모두 선택되어 있으니 Generate 버튼만 선택하면 됩니다.

그러면 역시 아래 코드가 자동 완성되지요. 이 것으로 클래스 하나는 뚝딱 완성되었습니다.

    @Override
	public String toString() {
		return "CrayMartGood2 [no=" + no + ", name=" + name + ", price=" + price + ", stock=" + stock + "]";
	}

상품 DAO 클래스 생성

다음은 상품 DAO 클래스 차례입니다.
DAO 는 Data Access Object 의 약어인데요.
과거 윈도우 프로그램 개발할 때 많이 접해본 용어입니다.

앞과 동일한 방법으로 클래스를 생성하되 ( Ctrl + Enter - Class )
클래스명은 CrayMartGoodsDao 로 정하였으며,

소스는 아래와 같이 구성하였습니다.

package com.cafe24.dreamplan7.craymart;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public class CrayMartGoodsDao {
	private static String dburl = 
			"jdbc:mysql://localhost:3306/connectdb?"
			+ "useSSL=false&serverTimezone=Asia/Seoul";
	private static String dbUser = "connectuser";
	private static String dbpasswd = "connect123!@#";
	
	// mysql 드라이버 세팅, 연결 -> conn 설정
	private void init()
	{
		try {
			// Class.forName("com.mysql.jdbc.Driver");
			Class.forName("com.mysql.cj.jdbc.Driver");
		} catch (ClassNotFoundException e) { e.printStackTrace(); }
	}
	
	// 상품명 검색
	public List<CrayMartGood> search(String keyword)
	{
		init();
		List<CrayMartGood> list = new ArrayList<CrayMartGood>();
		
		String sql = "SELECT * FROM cray_mart_goods "
				+ "WHERE name like ? order by name;";
		try (Connection conn = 
				DriverManager.getConnection(dburl, dbUser, dbpasswd);
				PreparedStatement ps = conn.prepareStatement(sql)) {
			ps.setString(1, "%" + keyword + "%");
			try (ResultSet rs = ps.executeQuery()) {
				while (rs.next()) {
					CrayMartGood good = new CrayMartGood(
						rs.getInt("no"),
						rs.getString("name"),
						rs.getInt("price"),
						rs.getInt("stock")
					);
					list.add(good);
				}
			}
			catch (Exception e) { e.printStackTrace(); }
		}
		catch (Exception e) { e.printStackTrace(); }
		
		return list;
	}
	
	// 상품 추가
	// 리턴) 성공=1, 실패:0
	public int add(String name, int price)
	{
		init();
		
		int result = 0;
		String sql = "INSERT INTO cray_mart_goods SET "
    			+ "name = ?, price = ?;";
		try (
			Connection conn = 
				DriverManager.getConnection(dburl, dbUser, dbpasswd);
			PreparedStatement ps = conn.prepareStatement(sql)) {
			ps.setString(1, name);
			ps.setInt(2, price);
			result = ps.executeUpdate();
		}
		catch (Exception e) { e.printStackTrace(); }
		
		return result;
	}
	
	// 상품 입고
	// 입력) no=상품코드
	// 리턴) 성공=1, 실패:0
	public int in_stock(int no, int stock)
	{
		init();
		
		int result = 0;
		String sql = "UPDATE cray_mart_goods SET "
    			+ "stock = stock + ? "
    			+ "WHERE no = ?";
		try (Connection conn = 
				DriverManager.getConnection(dburl, dbUser, dbpasswd);
				PreparedStatement ps = conn.prepareStatement(sql)) {
			ps.setInt(1, stock);
			ps.setInt(2, no);
			result = ps.executeUpdate();
		}
		catch (Exception e) { e.printStackTrace(); }
		
		return result;
	}
	
	// 상품 출고
	// 입력) no=상품코드
	// 리턴) 성공=1, 실패:0
	public int out_stock(int no, int count)
	{
		return in_stock(no, -count);
	}
}

그동안 흘러간 세월동안 많은 것이 바뀌어서 강의 내용에서 약간 상이합니다.
크레이가 알아낸 부분을 설명드리자면 아래와 같습니다.

mysql 8.0이 최신 DB 이지요. mysql 8.0에서 dburl 형식이 일부 바뀌었습니다.
원래 DB URL 은 아래 형식으로 충분했었는데요.

jdbc:mysql://localhost:3306/connectdb

아래와 같이 바뀌었습니다.
ssl=false 라는 부분은 보안 접속이 아니어도 허용하겠다는 의미이고,
serverTimezone=Asia/Seoul 부분은 시간대를 한국으로 하겠다는 것입니다.

jdbc:mysql://localhost:3306/connectdb?useSSL=false&serverTimezone=Asia/Seoul

그래서 dburl 선언 부분의 코드를 아래와 같이 바꿔주어야 합니다.

private static String dburl = 
	"jdbc:mysql://localhost:3306/connectdb?"
	+ "useSSL=false&serverTimezone=Asia/Seoul";

다음으로는 mysql DB 드라이버 명이 바뀌었습니다.
원래는 아래처럼 사용했던 드라이버명을

Class.forName("com.mysql.jdbc.Driver");

아래와 같이 바꿔주어야 경고로그가 발생하지 않습니다.

Class.forName("com.mysql.cj.jdbc.Driver");

그 밖에 소스를 좀 간결화하기 위해 init() 메소드를 추가한 정도입니다.


UI 처리 클래스

다음으로 UI 처리를 위해 UI 클래스를 추가했는데요.
버전 때문에 발생하는 기술적인 문제는 없었습니다.
크레이 마트에 맞게 아래와 같이 구성하였습니다.

CrayMartUI.java

 

package com.cafe24.dreamplan7.craymart;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.List;

public class CrayMartUI {
	private BufferedReader in;
	
	public CrayMartUI() {
		in = new BufferedReader(new InputStreamReader(System.in));
	}
	
	private static CrayMartUI instance = new CrayMartUI();
	
	public static CrayMartUI getInstance() {
		return instance;
	}
	
	public void printMainMenu() {
		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("------------------------");
        System.out.print("메뉴를 입력하세요 : ");
	}
	
	public int getMenuNumber(){
        try {
            int menuNumber = Integer.parseInt(in.readLine());
            return menuNumber;
        }catch(Exception ex){
            return -1; // 숫자로 변환 못할 경우 -1을 리턴한다.
        }
    }

	public CrayMartGood inputGood()
	{
		try {
            System.out.print("상품명을 입력하세요 : ");
            String name = in.readLine();
            System.out.print("가격을 입력하세요. : ");
            int price = Integer.parseInt(in.readLine());
            CrayMartGood good = new CrayMartGood(0, name, price, 0);
    		return good;
        }catch(Exception ex){
            System.out.println("잘못된 값을 입력했습니다. ");
            return null;
        }
	}

	public String getSearchKeyword(){
        try {
            System.out.print("검색할 이름을 입력하세요. (like검색) : ");
            String searchKeyword = in.readLine();
            return searchKeyword;
        }catch(Exception ex){
            System.out.println("잘못된 값을 입력했습니다. ");
            return null;
        }
    }

	public void printGoods(List<CrayMartGood> goods){
		for(CrayMartGood good: goods) {
			System.out.println(good);
			System.out.println("---------------------------------------------------------------");
		}
	}

	// 입고 재고 입력
	public CrayMartGood inputInStock()
	{
		try {
            System.out.print("상품코드를 입력하세요 : ");
            CrayMartGood good = new CrayMartGood();
            int no = Integer.parseInt(in.readLine());
            System.out.print("입고할 재고를 입력하세요 : ");
            int stock = Integer.parseInt(in.readLine());
            good.setNo(no);
            good.setStock(stock);	// 추가 재고
            return good;
        }catch(Exception ex){
            System.out.println("잘못된 값을 입력했습니다. ");
            return null;
        }
	}
	
	// 출고 재고 입력
	public CrayMartGood inputOutStock()
	{
		try {
            System.out.print("상품코드를 입력하세요 : ");
            CrayMartGood good = new CrayMartGood();
            int no = Integer.parseInt(in.readLine());
            System.out.print("출고할 재고를 입력하세요 : ");
            int stock = Integer.parseInt(in.readLine());
            good.setNo(no);
            good.setStock(stock);	// 차감 재고
            return good;
        }catch(Exception ex){
            System.out.println("잘못된 값을 입력했습니다. ");
            return null;
        }
	}

	// 잘못된 번호 입력
	public void invalidNumber()
	{
		System.out.println("잘못 입력하셨습니다.");
	}
	
	// 종료
	public void Finish() {
		System.out.println("프로그램이 종료되었습니다.");
	}
}

메인 클래스

마지막으로 실행 부분을 담당할 메인 클래스인데요.
아래와 같이 소스를 구성하였습니다.

package com.cafe24.dreamplan7.craymart;

import java.util.List;

public class CrayMart {

	public static void main(String[] args) {
		CrayMartUI UI = CrayMartUI.getInstance(); // 싱글턴
		CrayMartGoodsDao dao = new CrayMartGoodsDao();
		int menuNumber = 0;
		do {
			UI.printMainMenu();
			menuNumber = UI.getMenuNumber();
			switch(menuNumber) {
			case 1:	// 상품 입력
				{
					CrayMartGood good = UI.inputGood();
					if(good != null)
						dao.add(good.getName(), good.getPrice());
				}
				break;
			case 2:	// 상품 검색
				{
					String searchKeyword = UI.getSearchKeyword();
					List<CrayMartGood> goods = dao.search(searchKeyword);
					UI.printGoods(goods);
				}
				break;
			case 3:	// 상품 입고
				{
					CrayMartGood good = UI.inputInStock();
					dao.in_stock(good.getNo(), good.getStock());
				}
				break;
			case 4:	// 상품 출고
				{
					CrayMartGood good = UI.inputOutStock();
					dao.out_stock(good.getNo(), good.getStock());
				}
				break;
			default:
				UI.invalidNumber();
			}
		}while(menuNumber != 5);
		
		UI.Finish();
	}

}

case 문 후에 중괄호를 두는 것은 크레이가 C#에서 주로 사용하는 방법인데요.
중괄호가 없으면 중간에 good 변수를 선언하는 부분에서 중복 오류가 발생합니다.

하지만 아래와 같이 중괄호로 범위를 묶어 주면, good 변수는 중괄호 내에서만 유효한 지역변수가 되어 중복 선언 오류가 발생하지 않는 이점이 있습니다.


실행

이제 프로젝트를 실행해 결과를 확인해 보겠습니다.
콘솔 프로젝트이기 때문에 상단의 플레이 버튼을 눌러 시작하면 되는데요.

진행 결과는 콘솔 창에 표시됩니다.

이제 메뉴 번호를 입력하면 되는데요.
자바 콘솔 프로젝트는 콘솔창을 마우스로 선택 후 입력해야 합니다.
1 입력 후 Enter 키를 치면,

이어서 상품명을 입력하는 란이 나오고,

상품명을 입력하면 가격을 입력하는 란이 나옵니다.

이 것으로 입력이 되었습니다.
다시 메뉴 선택 창이 나왔는데요.

2번을 선택하고 검색 문장을 간단히 라고만 입력하면
상품명에 라는 글자가 들어간 모든 상품 정보를 출력해줍니다.

그러고 보니 재고(stock)가 하나도 없군요.
상품을 입고해보겠습니다. 이 때 상품코드(no)값이 4인 것을 잠깐 기억해두어야 합니다.

메뉴 3번, 상품코드 4, 재고 100개를 입력하면 상품재고 100개가 추가로 입고되는데요.

정말로 잘 들어왔는지 확인하려면 금방처럼, 2번 메뉴, 검색어를 라고 입력하면 됩니다.
stock=100으로 맞게 들어온 것을 확인할 수 있군요.

이번에는 상품 재고를 출고하겠습니다.
4번 메뉴, 상품코드 4, 재고 30을 입력한 다음,

다시 2번 메뉴, 검색어 웨를 입력하면 재고 100개에서 30이 차감된 70개로 변경된 것을 확인할 수 있습니다.

그리고 5번 메뉴를 입력하면 프로그램이 종료되는 것을 확인할 수 있습니다.
마지막에 약간의 버그가 있네요. 뭐 이건 건너뛰도록 하겠습니다 :)


마무~리

이상 부스트코스의 프로젝트 A. 명함 관리프로젝틀 크레이 전용 마트 상품 관리 프로젝트로 만들어 보았는데요.
초창기 DOS컴퓨터 시절에는 전산 업무를 이런 방식으로 처리했었던 기억이 새록 새록 납니다 :)
당시에는 마우스같은것도 없었거든요. 시커면 도스 화면이 모니터를 가득 매운 상태에서 컴퓨터를 사용했었지요.

아무쪼록 다녀가시는 모든 분들에게 하나님의 은혜가 가득하시길 두루두루(?) 소원합니다.
학습을 위해 방문하시는 분들도 방문차 오신 분들도 모두 감사합니다!


크레이는 성경에 등장하는 예수님께서 창조주 하나님의 아들이심을 믿습니다.
은혜가 많고, 거짓이 없으신 분이고, 믿음직스럽기까지 하다면 그 분을 믿지 않을 이유가 없지요.
여러분께도 하나님의 은혜가 영향을 주어 믿어지는 계기가 된다면 믿으시길 소원합니다 :)

"말씀이 육신이 되어 우리 가운데 거하시매 우리가 그의 영광을 보니 
아버지의 독생자의 영광이요 은혜와 진리가 충만하더라"

- 요한복음 1장 14절 말씀 -


다음 게시글 : https://itadventure.tistory.com/675

 

자바 - 백앤드 학습 #4. 스프링-JDBC

네이버에서 제공하는 부스트코스 강좌 "웹 백앤드 심화" 과정에서자바의 JDBC에 대한 기본 학습을 진행하였습니다.https://www.boostcourse.org/web326/lecture/58973?isDesc=false자바와 PHP 차이점을 살펴보니..잠

itadventure.tistory.com