본문 바로가기
백엔드/자바

자바 - 27 (게시판 만들기)

by study_yeon 2023. 6. 8.

2023.06.02

복습 

InputStream / OutputStream
바이트 기반 입출력 스트림의 최상위 클래스(완전히 구현되어있지 않다) -> 추상 클래스

Reader / Writer
문자 기반 입출력 스트림의 최상위 클래스(추상클래스)

다운캐스팅은 모두 가능한 것이 아니기 때문에 instance of로 같은 계열을 확인한다면 다운캐스팅이 가능하다

1byet = 8bit
2진수 개 = 16진수
- 색을 16진수로 표현하기도 함 ex) ccc, f2f2f2 


● 게시판 만들기

- 이것이 자바다54p

 

▶ 이론

* 커넥션 풀(Connection Pool) - 아직은 필요 없는 기능
- 다수의 클라이언트의 요청을 처리하는 서버 프로그램은 대부분 커넥션 풀(Connection Pool)을 사용한다. 
- 커넥션 풀은 일정량의 Connection을 미리 생성시켜놓고, 서버에서 클라이언트의 요청을 처리할 때 Connection을 제공해주고 다시 반환받는 역할을 수행한다. 
- Connection을 요청하면 만들어진 것이 있는지 확인하고 있으면 반환(재사용), 없으면 새로 만들어 반환

-> DB연결 속도가 빨라짐

* 게시판 구현
- 게시판은 기본적인 CRUD(Create, Read, Update, Delete) 기능이 포함되어 있음

* 콘솔프로그램 - 문자로 인터페이스 하는 것(cmd 활용?)


▶ 실습 

2023.06.02 - [백엔드/자바] - 자바 - 24

- 데이터베이스 테이블 만들기 참고

 

* 메소드 list( ), mainMenu ( ), main ( )
main ( ) 메소드는 BoardManager 객체를 생성
list() 메소드는 게시물 목록을 출력하고 mainMenu() 메소드를 호출


* 리스트 영역 / 메뉴 영역으로 메소드를 크게 나누기
  list()             / mainMenu()

 

package : bbs.mariadb.app
▷ class : BoardExamplePrototypeApp
- 실행클래스 

- 매니저 객체를 생성하고 list() 호출

package bbs.mariadb.app;

import bbs.mariadb.controller.BoardManager;
import static bbs.mariadb.util.BbsIO.*;

public class BoardExamplePrototypeApp {

	public static void main(String[] args) {
		BoardManager boardManager = new BoardManager();
		boardManager.list();
		
		pl(" >>> 메인 프로그램을 마칩니다. <<<");
	}
}


package : bbs.mariadb.controller
 class : BoardManager
- 실제 게시판을 관리하는 클래스

- list(), mainMenu() 메소드
- BoardExamplePrototypeApp(실행클래스)에서 접근 가능해야하므로 public 

- 데모 게시글은 틀을 확인하기위해 작성한 것으로 실제 게시글을 작성하면 주석(또는 삭제) 처리

* 유틸클래스(mariadb.util.BbsIO) 만들어 활용

package bbs.mariadb.controller;

import static bbs.mariadb.util.BbsIO.*; // static 속성을 추가

public class BoardManager {
	// 화면에 게시글 목록 출력 메소드
	public void list() { 
		pl(""); 
		pl("[게시물 목록]");
		pl("------------------------------------------------------");
		System.out.printf(
				"%-6s%-12s%-16s%-40s\n", // 왼쪽 정렬, s(문자열)
				"No", "writer", "date", "title");
		pl("------------------------------------------------------");
		// 데모 게시글(나중에 주석 처리)
		System.out.printf(
				"%-6s%-12s%-16s%-40s\n", 
				"1", "보미", "2023.05.30", "게시판을 만들었어요^_^");
		System.out.printf(
				"%-6s%-12s%-16s%-40s\n", 
				"2", "가을", "2023.06.01", "1등을 노렸는데...");
		System.out.printf(
				"%-6s%-12s%-16s%-40s\n", 
				"3", "여름", "2023.06.02", "올 여름은 비가 많이 온대요");
		// 데모 게시글 끝
		
		// 하단에 메인메뉴호출
		mainMenu();
	}
	
	// 화면 하단에 메인메뉴 메소드
	public void mainMenu() {
		pl("");
		pl("======================================================");
		pl("메인 메뉴 : 1. Create | 2. Read | 3. Clear | 4. Exit");
		p("메뉴 선택 : ");
		pl("");
		
	}
}


* BoardManager에 list(), mainMenu() 메소드 만들고 BoardExamplePrototypeApp클래스 main()에서 호출하기 


* 클래스의 패키지 옮기기 - 정석

패키지명 오른쪽 마우스 > Refactor > Move > Create Package...





 BoardManager2 만들기(기능 추가)
- BoardManager이어서 작업
- 추가사항 : 
1. 키보드 입력을 위해 Scanner 클래스 멤버변수로 추가

-  mainMenu() 메소드에서 스캐너 이용

public class BoardManager2 {
	// 1. 멤버변수
	private Scanner scanner;
	// 2. 생성자
	public BoardManager2() { // App에서 호출하기 위함
		scanner = new Scanner(System.in);
	}
}


2. 각 메인메뉴 기능을 위한 메소드들 추가 - 선택한 메뉴 처리(switch 이용)
Create 메뉴 -> create()
Read 메뉴 -> read()
Clear 메뉴 -> clear()
Exit 메뉴 -> exit()

	public void mainMenu() {
		pl("");
		pl("======================================================");
		pl("메인 메뉴 : 1. Create | 2. Read | 3. Clear | 4. Exit");
		p("메뉴 선택 : ");

		// 스캐너를 이용 메뉴를 선택한다
		String menuNo = scanner.nextLine();
		
		// 선택한 메뉴처리
		switch(menuNo) { // 람다식으로 활용 가능
			// 1. Create를 선택한 경우
			case "1" : 
				create();
				break;
			// 2. Read를 선택한 경우
			case "2" :
				read();
				break;
			// 3. Clear를 선택한 경우
			case "3" :
				clear();
				break;
			// 4. Exit 를 선택한 경우	
			case "4" :
				exit();
				break;	
			default :
				pl("메뉴를 잘못선택하셨습니다.");
				list();  // 재귀호출(list가 list를 부름)
				break;
		} // 선택한 메뉴처리 끝
		
		
	}

- 메소드(상세 기능 구현 전)

	// 1. Create
	public void create() {
		pl("create() 메소드 실행됨");
		list();
	}
	// 2. Read
	public void read() {
		pl("read() 메소드 실행됨");
		list();
	}
	// 3. Clear
	public void clear() {
		pl("clear() 메소드 실행됨");
		list();
	}
	// 4. Exit
	public void exit() {
		pl("\n**** 프로그램 종료 ****");
		// 프로그램을 종료한다
		// 0 : 프로그램이 잘 종료되었습니다. 
		System.exit(0);
	}


 boards 테이블의 한 개의 행(게시물)을 저장할 Board 클래스를 작성
- 파일을 넣는 bfilename과 bfiledata 컬럼은 지금 안할거라 제외
- 변수 선언을 위해 데이터베이스에서 sql 생성 활용 (오탈자 방지)


- 이클립스 generate 활용 (롬복의 @Data를 사용해도 됨)

package bbs.mariadb.model;

import java.sql.Blob;
import java.util.Date;

public class Board {
	
	private int bno;
	private String btitle;
	private String bcontent;
	private String bwriter;
	private Date bdate;
	private String bfilename;
	private Blob bfiledata;
	
	// 생성자
	public Board() {
		
	}

	// 파일을 넣는 bfilename과 bfiledata 컬럼은 지금 안할거라 제외
	public Board(int bno, String btitle, String bcontent, String bwriter, Date bdate) {
		super();
		this.bno = bno;
		this.btitle = btitle;
		this.bcontent = bcontent;
		this.bwriter = bwriter;
		this.bdate = bdate;
	}
	// 속성함수
	public int getBno() {
		return bno;
	}

	public void setBno(int bno) {
		this.bno = bno;
	}

	public String getBtitle() {
		return btitle;
	}

	public void setBtitle(String btitle) {
		this.btitle = btitle;
	}

	public String getBcontent() {
		return bcontent;
	}

	public void setBcontent(String bcontent) {
		this.bcontent = bcontent;
	}

	public String getBwriter() {
		return bwriter;
	}

	public void setBwriter(String bwriter) {
		this.bwriter = bwriter;
	}

	public Date getBdate() {
		return bdate;
	}

	public void setBdate(Date bdate) {
		this.bdate = bdate;
	}
	
	// toString 
	@Override
	public String toString() {
		return "Board [bno=" + bno + ", btitle=" + btitle + ", bcontent=" + bcontent + ", bwriter=" + bwriter
				+ ", bdate=" + bdate + "]";
	}
	
}



 BoardManager3 만들기(기능 추가)
BoardManager2 이어서
* 추가기능

- DB 연결이 필요하므로 Connection 필드 추가
- 진짜 게시물 목록 출력 시작
-> boards 테이블에서 모든 게시물 정보들을 가져온 다음 게시물 목록으로 출력

	// 멤버변수
	private Scanner scanner;
	private Connection conn;
    
	// 멤버상수
	private final String JDBC_DRIVER = "org.mariadb.jdbc.Driver";
	private final String JDBC_URL = "jdbc:mariadb://localhost/thisisjava";
	private final String USER = "root";
	private final String PASSWORD = "mariadb";

- 생성자에서 DB 연결

	public BoardManager3() { // App에서 호출하기 위함
		scanner = new Scanner(System.in);
		createConnection();
	}

 

	//// 3. 속성메소드
	public void createConnection() {
		
		try {
			Class.forName(JDBC_DRIVER);
			// 연결하기
			conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
		} catch (Exception e) {
			e.printStackTrace();
			exit();
		}
		
		
	}

- 빌드패스 마리아디비 3.0.10
- list() 메소드 수정(데모 게시글로 작성했던 부분을 board 클래스와 sql문을 이용하여 출력하기)

	//// 4. 기능메소드
	
	// 화면에 게시글 목록 출력 메소드 시작
	public void list() { 
		pl(""); // 화면에 빈줄 출력
		pl("[게시물 목록]");
		pl("------------------------------------------------------");
		System.out.printf(
				"%-6s%-12s%-16s%-40s\n", // 왼쪽 정렬, s(문자열)
				"No", "writer", "date", "title");
		pl("------------------------------------------------------");
		
		// 진짜 게시물 목록 출력 시작
		String sql = """
			SELECT bno, btitle, bcontent, bwriter, bdate
				FROM boards
				ORDER BY bno DESC;
			"""; // 가장 최근 게시물부터 보임
		try {
			PreparedStatement pstmt = conn.prepareStatement(sql);
			ResultSet rs = pstmt.executeQuery();
			
			while(rs.next()) { // 레코드를 가지고 있다면
				Board board = new Board();
				board.setBno(rs.getInt("bno"));
				board.setBtitle(rs.getString("btitle"));
				board.setBcontent(rs.getString("bcontent"));
				board.setBwriter(rs.getString("bwriter"));
				board.setBdate(rs.getDate("bdate"));
				
				// board 내용 출력
				System.out.printf(
						"%-6s%-12s%-16s%-40s\n", // 왼쪽 정렬, s(문자열)
						board.getBno(), 
						board.getBwriter(), 
						board.getBdate(), 
						board.getBtitle());
				
			}
			// 진짜 게시물 목록 출력 끝
			rs.close();
			pstmt.close();
		} catch(SQLException e) {
			e.printStackTrace();
			exit();
		}
		
		// 하단에 메인메뉴호출
		mainMenu();
	}


* 디버그

중단점 - 실행클래스의
BoardManager3 boardManager = new BoardManager3();
boardManager.list();
매니저클래스의
scanner = new Scanner(System.in);



 BoardManager4 만들기

* 기능추가 - 게시물 생성기능 
- 메뉴에서 ‘1.Create’를 선택할 경우
- 매니저클래스의 create()메소드에서 만들어야함
- 키보드로 새로운 게시물(제목, 내용, 작성자)을 입력받기

- 보조메뉴 만들기(OK | Cancel)
- OK눌러서 저장하기(list()화면 실행)
- cancel 누르면 다시 create 입력화면

public class BoardManager4 {

...앞단 동일

	// 화면 하단에 메인메뉴 메소드
	public void mainMenu() {
		pl("");
		pl("======================================================");
		pl("메인 메뉴 : 1. Create | 2. Read | 3. Clear | 4. Exit");
		p("메뉴 선택 : ");

		// 스캐너를 이용 메뉴를 선택한다
		String menuNo = scanner.nextLine();
		
		// 선택한 메뉴처리
		switch(menuNo) {
			// 1. Create를 선택한 경우
			case "1" : 
				create();
				break;
			case "2" :
				read();
				break;
			case "3" :
				clear();
				break;
			case "4" :
				exit();
				break;	
			default :
				pl("메뉴를 잘못선택하셨습니다.");
				list(); 
				break;
		} // 선택한 메뉴처리 끝
		
	}
	// 1. Create
	public void create() {
		// 자료(게시물) 입력하기 시작
		Board boardDTO = new Board();
		pl("[새로운 게시물을 입력해주세요]");
		p("제목 : ");
		// 스캐너에 입력한 내용을 boardDTO객체에 저장
		boardDTO.setBtitle(scanner.nextLine());
		p("내용 : ");
		boardDTO.setBcontent(scanner.nextLine());
		p("작성자 : ");
		boardDTO.setBwriter(scanner.nextLine());
		// 자료 입력하기 끝
		
		// 자료 저장하기전 확인 작업
		// 보조메뉴 출력
		pl("======================================================");
		// 메뉴출력
		pl("보조 메뉴 : 1. OK | 2. Cancel");
		p("메뉴 선택 : ");
		String menuNo = scanner.nextLine();
		// 선택된 메뉴 번호 처리
		if (menuNo.equals("1")) { // 내용물이 같은지 비교
			// 마리아 디비에 새게시물 저장
			String sql = """
				INSERT INTO boards 
				(btitle, bcontent, bwriter, bdate)
				VALUES (?, ?, ?, now()); 
				""";
		
			try {
				PreparedStatement pstmt;
				pstmt = conn.prepareStatement(sql);
				pstmt.setString(1, boardDTO.getBtitle()); // 값 저장
				pstmt.setString(2, boardDTO.getBcontent());
				pstmt.setString(3, boardDTO.getBwriter());
				
				pstmt.executeUpdate();
				
				pstmt.close();
				
			} catch(SQLException e) {
				e.printStackTrace();
				exit();
			} catch(Exception e) {
				e.printStackTrace();
				exit();
			}
		} else {
			// 입력 취소, 목록으로 돌아감
		}
		
		// 입력내용이 board에 저장
		list();
	}
... 뒷단 동일

}

* create() 메소드를 만들기 위해서
DTO 객체에 데이터를 담아야하기 때문에 DTO 객체가 비어있어야한다
-> board가 새로 만들어져야함(new Board())

* 보조메뉴
equals : 객체의 값(내용물) 비교
== : 객체의 주소 비교

* 마리아 디비에 새게시물 저장
1. INSERT sql 문자열을 만든다
2. PreparedStatement 생성
3. PreparedStatement 객체의 파라미터 저장
4. PreparedStatement 객체 질의문 실행 - executeUpdate()
5. PreparedStatement 객체 종료

 

* set 값 설정하기

  get 설장한 값 읽어오기


* 디버그

중단점 - Board boardDTO = new Board(); 

* next() 구분기호가 공백, nextLine() 구분기호가 \n 
-> 같이 쓰면 안됨



 BoardManager5 만들기
* 게시물 읽기 기능 추가
- 메뉴에서 ‘2.Read’를 선택할 경우
- 게시물의 번호를 키보드로 입력받기(보조메뉴-bno)
- boards 테이블에 있는 해당 게시물을 가져와서 출력

public class BoardManager5 {
... 앞단 동일

	// 2. Read
	public void read() {
		// 보조메뉴 구현(보고싶은 상세자료 게시물번호 입력)
		pl("[게시물 읽기]");
		p("bno : ");
		int bno = Integer.parseInt(scanner.nextLine());
		// 마리아디비로부터 레코드 가져오기
		try {
			String sql = """
				SELECT 
					bno, btitle, bcontent, bwriter, bdate
				FROM boards
				WHERE bno = ?;
				""";
			PreparedStatement pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, bno);
			
			// 쿼리실행
			ResultSet rs = pstmt.executeQuery();
			if (rs.next()) {
				// DTO객체에 레코드 저장
				Board board = new Board();
				board.setBno(rs.getInt("bno"));
				board.setBtitle(rs.getString("btitle"));
				board.setBcontent(rs.getString("bcontent"));
				board.setBwriter(rs.getString("bwriter"));
				board.setBdate(rs.getDate("bdate"));
				
				// 저장한 레코드를 화면에 출력(view)
				pl("===================================");
				pl("번호 : " + board.getBno());
				pl("제목 : " + board.getBtitle());
				pl("내용 : " + board.getBcontent());
				pl("작성자 : " + board.getBwriter());
				pl("작성일자 : " + board.getBdate());
				pl("===================================");
				
			}
			rs.close();
			pstmt.close();
		
		} catch(SQLException e) {
			e.printStackTrace();
			exit();
		}
		
		// 메소드 실행이 잘되었는지 확인은 리스트 메소드 호출로함
		// 문제 있으면 exit() 함수로 프로그램 종료
		list();
	}
... 뒷단 동일
}

* nextLine()으로 받은 숫자는 문자열이기 때문에 정수로 변환을 해주어야한다.

-> Integer.parseInt(scanner.nextLine());


 BoardManager6 만들기
* 게시물 수정기능
- 게시물 읽기( ‘2.Read’)에 보조 메뉴 추가 : ‘1.Update | 2.Delete | 3.List’

	public void read() {
.. 동일
				
				// 저장한 레코드를 화면에 출력(view)
				pl("===================================");
				pl("번호 : " + board.getBno());
				pl("제목 : " + board.getBtitle());
				pl("내용 : " + board.getBcontent());
				pl("작성자 : " + board.getBwriter());
				pl("작성일자 : " + board.getBdate());
				pl("===================================");
				
				// 보조메뉴 출력
				pl("---------------------------------------");
				pl("보조메뉴 : 1.Update | 2.Delete | 3.List");
				p("메뉴선택 : ");
				String menuNo = scanner.nextLine();
				pl("");
				
				if(menuNo.equals("1")) {
					update(board);
				} else if(menuNo.equals("2")) {
					delete(board);
				}
			}
			rs.close();	
			pstmt.close();
.. 동일
	}


- 보조 메뉴에서 ‘1.Update’를 선택했을 때 제목, 내용, 작성자의 수정 내용을 입력 기능

update() 메소드 추가

보조 메뉴에서 ‘1.Ok’를 선택했을 때 boards 테이블의 해당 게시물을 수정 기능

	public void update(Board board) {
		pl("[수정내용 입력]");
		p("제목 : ");
		board.setBtitle(scanner.nextLine());
		p("내용 : ");
		board.setBcontent(scanner.nextLine());
		p("작성자 : ");
		board.setBwriter(scanner.nextLine());
		
		// 보조메뉴 출력
		pl("---------------------------------------");
		pl("보조메뉴 : 1. Ok | 2. Cancel");
		pl("메뉴 선택 : ");
		String menuNo = scanner.nextLine();
		if(menuNo.equals("1")) {
			try {
				String sql = "" +
					"UPDATE boards SET btitle=?, bcontent=?, bwriter=? " +
					"WHERE bno= ?";
					PreparedStatement pstmt = conn.prepareStatement(sql);
					pstmt.setString(1, board.getBtitle());
					pstmt.setString(2, board.getBcontent());
					pstmt.setString(3, board.getBwriter());
					pstmt.setInt(4, board.getBno());
					pstmt.executeUpdate();
					pstmt.close();					
			} catch(Exception e) {
				e.printStackTrace();
				exit();
			}
			
		}

		list();
	}



- ‘2.Delete’를 선택했을 때 boards 테이블에서 해당 게시물을 삭제기능

delete() 메소드 추가

	public void delete(Board board) {
		try {
			String sql = "DELETE FROM boards WHERE bno = ?";
			PreparedStatement pstmt = conn.prepareStatement(sql);
			pstmt.setInt(1, board.getBno());
			pstmt.executeUpdate();
			pstmt.close();
		} catch(Exception e) {
			e.printStackTrace();
			exit();
		}

		list();
	}

 BoardManager7 만들기

* 게시물 전체 삭제 기능
메인 메뉴에서 ‘3.Clear’를 선택하고 보조 메뉴에서 ‘1.Ok’를 선택했을 때 boards 테이블의 전체 게시물 정보 삭제 기능

	// 3. Clear
	public void clear() {
		pl("[게시물 전체 삭제]");
		pl("---------------------------------------");
		pl("보조메뉴 : 1. Ok | 2. Cancel ");
		p("메뉴 선택 : ");
		String menuNo = scanner.nextLine();
		if(menuNo.equals("1")) {
			try {
				String sql = "TRUNCATE TABLE boards";
				PreparedStatement pstmt = conn.prepareStatement(sql);
				pstmt.executeUpdate();
				pstmt.close();
			} catch(Exception e) {
				e.printStackTrace();
				exit();
			}
		}
		
		list();
	}

- TRUNCATE 빠르게 지워짐 log에 남지 않음


* 게시판 종료기능

메뉴에서 ‘4.Exit’를 선택하면 Connection을 닫고 프로그램을 종료

	// 4. Exit
	public void exit() {
		// connection 객체 종료 후 프로그램 종료작업
		if( conn != null ) {
			try {
				conn.close();
			} catch(SQLException e) {
			}
		}
		pl("\n** 게시판 종료 **");
		// 프로그램을 종료한다
		// 0 : 프로그램이 잘 종료되었다. 
		System.exit(0);
	}
}

프로젝트에서 conn 닫기(위치는 매우 중요) - conn을 회수하고 종료를 찍어야함


 

'백엔드 > 자바' 카테고리의 다른 글

자바 - 29 (Stream)  (0) 2023.06.12
자바 - 28 (재귀(Recursion) 알고리즘)  (0) 2023.06.08
자바 - 26 (Stream)  (0) 2023.06.02
자바 - 25 (트랜잭션)  (0) 2023.06.02
자바 - 24  (0) 2023.06.02